summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--CMakeLists.txt29
-rw-r--r--bindings/python/clang/cindex.py1759
-rw-r--r--bindings/python/clang/enumerations.py34
-rw-r--r--bindings/python/tests/cindex/INPUTS/compile_commands.json17
-rw-r--r--bindings/python/tests/cindex/test_cdb.py89
-rw-r--r--bindings/python/tests/cindex/test_cursor.py151
-rw-r--r--bindings/python/tests/cindex/test_location.py9
-rw-r--r--bindings/python/tests/cindex/test_token_kind.py43
-rw-r--r--bindings/python/tests/cindex/test_tokens.py52
-rw-r--r--bindings/python/tests/cindex/test_translation_unit.py187
-rw-r--r--bindings/python/tests/cindex/test_type.py26
-rw-r--r--bindings/python/tests/cindex/util.py42
-rw-r--r--bindings/xml/comment-xml-schema.rng434
-rw-r--r--docs/AddressSanitizer.html57
-rw-r--r--docs/AutomaticReferenceCounting.html4
-rw-r--r--docs/ClangPlugins.html170
-rw-r--r--docs/ClangTools.html118
-rw-r--r--docs/HowToSetupToolingForLLVM.html186
-rw-r--r--docs/InternalsManual.html32
-rw-r--r--docs/IntroductionToTheClangAST.html139
-rw-r--r--docs/JSONCompilationDatabase.html89
-rw-r--r--docs/LanguageExtensions.html120
-rw-r--r--docs/LibTooling.html229
-rw-r--r--docs/ObjectiveCLiterals.html199
-rw-r--r--docs/RAVFrontendAction.html224
-rw-r--r--docs/ReleaseNotes.html273
-rw-r--r--docs/ThreadSanitizer.html125
-rw-r--r--docs/Tooling.html120
-rw-r--r--docs/UsersManual.html202
-rw-r--r--docs/tools/clang.pod8
-rw-r--r--examples/CMakeLists.txt2
-rw-r--r--examples/PrintFunctionNames/CMakeLists.txt19
-rw-r--r--examples/analyzer-plugin/CMakeLists.txt17
-rw-r--r--examples/clang-interpreter/CMakeLists.txt59
-rw-r--r--include/clang-c/CXCompilationDatabase.h146
-rw-r--r--include/clang-c/CXString.h61
-rw-r--r--include/clang-c/Index.h932
-rw-r--r--include/clang-c/Platform.h45
-rw-r--r--include/clang/AST/ASTContext.h152
-rw-r--r--include/clang/AST/ASTImporter.h3
-rw-r--r--include/clang/AST/ASTVector.h4
-rw-r--r--include/clang/AST/Attr.h46
-rw-r--r--include/clang/AST/BaseSubobject.h6
-rw-r--r--include/clang/AST/CMakeLists.txt5
-rw-r--r--include/clang/AST/CXXInheritance.h3
-rw-r--r--include/clang/AST/Comment.h1059
-rw-r--r--include/clang/AST/CommentBriefParser.h56
-rw-r--r--include/clang/AST/CommentCommandTraits.h156
-rw-r--r--include/clang/AST/CommentDiagnostic.h29
-rw-r--r--include/clang/AST/CommentLexer.h353
-rw-r--r--include/clang/AST/CommentParser.h124
-rw-r--r--include/clang/AST/CommentSema.h230
-rw-r--r--include/clang/AST/CommentVisitor.h66
-rw-r--r--include/clang/AST/Decl.h114
-rw-r--r--include/clang/AST/DeclBase.h64
-rw-r--r--include/clang/AST/DeclCXX.h193
-rw-r--r--include/clang/AST/DeclContextInternals.h4
-rw-r--r--include/clang/AST/DeclFriend.h8
-rw-r--r--include/clang/AST/DeclObjC.h233
-rw-r--r--include/clang/AST/DeclTemplate.h160
-rw-r--r--include/clang/AST/DeclarationName.h39
-rw-r--r--include/clang/AST/EvaluatedExprVisitor.h2
-rw-r--r--include/clang/AST/Expr.h94
-rw-r--r--include/clang/AST/ExprCXX.h179
-rw-r--r--include/clang/AST/ExprObjC.h84
-rw-r--r--include/clang/AST/ExternalASTSource.h3
-rw-r--r--include/clang/AST/Makefile8
-rw-r--r--include/clang/AST/Mangle.h4
-rw-r--r--include/clang/AST/NSAPI.h74
-rw-r--r--include/clang/AST/NestedNameSpecifier.h15
-rw-r--r--include/clang/AST/OperationKinds.h2
-rw-r--r--include/clang/AST/RawCommentList.h220
-rw-r--r--include/clang/AST/RecordLayout.h88
-rw-r--r--include/clang/AST/RecursiveASTVisitor.h180
-rw-r--r--include/clang/AST/Redeclarable.h29
-rw-r--r--include/clang/AST/Stmt.h122
-rw-r--r--include/clang/AST/StmtObjC.h56
-rw-r--r--include/clang/AST/TemplateBase.h101
-rw-r--r--include/clang/AST/Type.h137
-rw-r--r--include/clang/AST/TypeLoc.h13
-rw-r--r--include/clang/ASTMatchers/ASTMatchFinder.h141
-rw-r--r--include/clang/ASTMatchers/ASTMatchers.h1799
-rw-r--r--include/clang/ASTMatchers/ASTMatchersInternal.h901
-rw-r--r--include/clang/ASTMatchers/ASTMatchersMacros.h224
-rw-r--r--include/clang/Analysis/Analyses/FormatString.h65
-rw-r--r--include/clang/Analysis/Analyses/ThreadSafety.h9
-rw-r--r--include/clang/Analysis/Analyses/UninitializedValues.h60
-rw-r--r--include/clang/Analysis/AnalysisContext.h50
-rw-r--r--include/clang/Analysis/CFG.h12
-rw-r--r--include/clang/Analysis/CallGraph.h10
-rw-r--r--include/clang/Analysis/ProgramPoint.h169
-rw-r--r--include/clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h2
-rw-r--r--include/clang/Basic/ABI.h43
-rw-r--r--include/clang/Basic/AddressSpaces.h18
-rw-r--r--include/clang/Basic/AllDiagnostics.h9
-rw-r--r--include/clang/Basic/Attr.td397
-rw-r--r--include/clang/Basic/AttrKinds.h9
-rw-r--r--include/clang/Basic/Builtins.def80
-rw-r--r--include/clang/Basic/Builtins.h35
-rw-r--r--include/clang/Basic/BuiltinsHexagon.def1531
-rw-r--r--include/clang/Basic/BuiltinsMips.def125
-rw-r--r--include/clang/Basic/BuiltinsNVPTX.def (renamed from include/clang/Basic/BuiltinsPTX.def)0
-rw-r--r--include/clang/Basic/BuiltinsX86.def125
-rw-r--r--include/clang/Basic/CMakeLists.txt1
-rw-r--r--include/clang/Basic/CommentNodes.td27
-rw-r--r--include/clang/Basic/ConvertUTF.h35
-rw-r--r--include/clang/Basic/Diagnostic.h490
-rw-r--r--include/clang/Basic/Diagnostic.td1
-rw-r--r--include/clang/Basic/DiagnosticCommentKinds.td125
-rw-r--r--include/clang/Basic/DiagnosticCommonKinds.td6
-rw-r--r--include/clang/Basic/DiagnosticDriverKinds.td21
-rw-r--r--include/clang/Basic/DiagnosticFrontendKinds.td16
-rw-r--r--include/clang/Basic/DiagnosticGroups.td44
-rw-r--r--include/clang/Basic/DiagnosticIDs.h96
-rw-r--r--include/clang/Basic/DiagnosticLexKinds.td30
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td65
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td1131
-rw-r--r--include/clang/Basic/DiagnosticSerializationKinds.td4
-rw-r--r--include/clang/Basic/ExceptionSpecificationType.h15
-rw-r--r--include/clang/Basic/ExpressionTraits.h7
-rw-r--r--include/clang/Basic/FileManager.h93
-rw-r--r--include/clang/Basic/FileSystemOptions.h7
-rw-r--r--include/clang/Basic/FileSystemStatCache.h18
-rw-r--r--include/clang/Basic/IdentifierTable.h103
-rw-r--r--include/clang/Basic/LLVM.h7
-rw-r--r--include/clang/Basic/Lambda.h15
-rw-r--r--include/clang/Basic/LangOptions.def10
-rw-r--r--include/clang/Basic/LangOptions.h29
-rw-r--r--include/clang/Basic/Linkage.h23
-rw-r--r--include/clang/Basic/MacroBuilder.h13
-rw-r--r--include/clang/Basic/Makefile1
-rw-r--r--include/clang/Basic/Module.h13
-rw-r--r--include/clang/Basic/ObjCRuntime.h264
-rw-r--r--include/clang/Basic/OnDiskHashTable.h8
-rw-r--r--include/clang/Basic/OpenCL.h9
-rw-r--r--include/clang/Basic/OperatorKinds.h13
-rw-r--r--include/clang/Basic/PartialDiagnostic.h61
-rw-r--r--include/clang/Basic/PrettyStackTrace.h15
-rw-r--r--include/clang/Basic/SourceLocation.h102
-rw-r--r--include/clang/Basic/SourceManager.h659
-rw-r--r--include/clang/Basic/SourceManagerInternals.h55
-rw-r--r--include/clang/Basic/Specifiers.h52
-rw-r--r--include/clang/Basic/StmtNodes.td5
-rw-r--r--include/clang/Basic/TargetBuiltins.h36
-rw-r--r--include/clang/Basic/TargetInfo.h273
-rw-r--r--include/clang/Basic/TargetOptions.h7
-rw-r--r--include/clang/Basic/TemplateKinds.h7
-rw-r--r--include/clang/Basic/TokenKinds.def64
-rw-r--r--include/clang/Basic/TokenKinds.h21
-rw-r--r--include/clang/Basic/TypeTraits.h15
-rw-r--r--include/clang/Basic/Version.h38
-rw-r--r--include/clang/Basic/VersionTuple.h45
-rw-r--r--include/clang/Basic/Visibility.h19
-rw-r--r--include/clang/Basic/arm_neon.td1
-rw-r--r--include/clang/Driver/Arg.h42
-rw-r--r--include/clang/Driver/ArgList.h8
-rw-r--r--include/clang/Driver/CC1Options.h32
-rw-r--r--include/clang/Driver/CC1Options.td366
-rw-r--r--include/clang/Driver/CMakeLists.txt4
-rw-r--r--include/clang/Driver/Compilation.h2
-rw-r--r--include/clang/Driver/Driver.h8
-rw-r--r--include/clang/Driver/Makefile8
-rw-r--r--include/clang/Driver/ObjCRuntime.h49
-rw-r--r--include/clang/Driver/OptParser.td5
-rw-r--r--include/clang/Driver/OptTable.h50
-rw-r--r--include/clang/Driver/Option.h6
-rw-r--r--include/clang/Driver/Options.td566
-rw-r--r--include/clang/Driver/ToolChain.h21
-rw-r--r--include/clang/Driver/Types.def12
-rw-r--r--include/clang/Driver/Types.h6
-rw-r--r--include/clang/Frontend/ASTConsumers.h8
-rw-r--r--include/clang/Frontend/ASTUnit.h42
-rw-r--r--include/clang/Frontend/Analyses.def4
-rw-r--r--include/clang/Frontend/AnalyzerOptions.h2
-rw-r--r--include/clang/Frontend/CodeGenOptions.h180
-rw-r--r--include/clang/Frontend/CompilerInstance.h3
-rw-r--r--include/clang/Frontend/CompilerInvocation.h42
-rw-r--r--include/clang/Frontend/DiagnosticOptions.h3
-rw-r--r--include/clang/Frontend/DiagnosticRenderer.h34
-rw-r--r--include/clang/Frontend/FrontendAction.h2
-rw-r--r--include/clang/Frontend/FrontendActions.h6
-rw-r--r--include/clang/Frontend/FrontendOptions.h20
-rw-r--r--include/clang/Frontend/HeaderSearchOptions.h28
-rw-r--r--include/clang/Frontend/LangStandards.def6
-rw-r--r--include/clang/Frontend/PreprocessorOutputOptions.h4
-rw-r--r--include/clang/Frontend/TextDiagnostic.h26
-rw-r--r--include/clang/Frontend/TextDiagnosticPrinter.h2
-rw-r--r--include/clang/Frontend/VerifyDiagnosticConsumer.h138
-rw-r--r--include/clang/Lex/CodeCompletionHandler.h6
-rw-r--r--include/clang/Lex/DirectoryLookup.h10
-rw-r--r--include/clang/Lex/HeaderMap.h2
-rw-r--r--include/clang/Lex/HeaderSearch.h138
-rw-r--r--include/clang/Lex/Lexer.h35
-rw-r--r--include/clang/Lex/LiteralSupport.h1
-rw-r--r--include/clang/Lex/MacroInfo.h15
-rw-r--r--include/clang/Lex/ModuleMap.h6
-rw-r--r--include/clang/Lex/MultipleIncludeOpt.h43
-rw-r--r--include/clang/Lex/PPCallbacks.h123
-rw-r--r--include/clang/Lex/Pragma.h10
-rw-r--r--include/clang/Lex/PreprocessingRecord.h18
-rw-r--r--include/clang/Lex/Preprocessor.h134
-rw-r--r--include/clang/Lex/PreprocessorLexer.h55
-rw-r--r--include/clang/Lex/Token.h53
-rw-r--r--include/clang/Parse/Parser.h281
-rw-r--r--include/clang/Rewrite/FrontendActions.h5
-rw-r--r--include/clang/Rewrite/Rewriter.h7
-rw-r--r--include/clang/Rewrite/Rewriters.h5
-rw-r--r--include/clang/Rewrite/TokenRewriter.h2
-rw-r--r--include/clang/Sema/AttributeList.h81
-rw-r--r--include/clang/Sema/CodeCompleteConsumer.h142
-rw-r--r--include/clang/Sema/CodeCompleteOptions.h37
-rw-r--r--include/clang/Sema/DeclSpec.h188
-rw-r--r--include/clang/Sema/DelayedDiagnostic.h66
-rw-r--r--include/clang/Sema/Designator.h8
-rw-r--r--include/clang/Sema/Initialization.h8
-rw-r--r--include/clang/Sema/Overload.h23
-rw-r--r--include/clang/Sema/ParsedTemplate.h10
-rw-r--r--include/clang/Sema/Scope.h4
-rw-r--r--include/clang/Sema/ScopeInfo.h19
-rw-r--r--include/clang/Sema/Sema.h1016
-rw-r--r--include/clang/Sema/Template.h11
-rw-r--r--include/clang/Sema/TemplateDeduction.h39
-rw-r--r--include/clang/Sema/Weak.h2
-rw-r--r--include/clang/Serialization/ASTBitCodes.h63
-rw-r--r--include/clang/Serialization/ASTReader.h36
-rw-r--r--include/clang/Serialization/ASTWriter.h13
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h20
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h6
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h36
-rw-r--r--include/clang/StaticAnalyzer/Core/Checker.h65
-rw-r--r--include/clang/StaticAnalyzer/Core/CheckerManager.h71
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h106
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h10
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h39
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h962
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h9
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h19
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h6
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h94
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h7
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h86
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h293
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h102
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h25
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h42
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/Store.h70
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h2
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h14
-rw-r--r--include/clang/Tooling/ArgumentsAdjusters.h59
-rw-r--r--include/clang/Tooling/CommandLineClangTool.h80
-rw-r--r--include/clang/Tooling/CompilationDatabase.h28
-rw-r--r--include/clang/Tooling/Refactoring.h151
-rw-r--r--include/clang/Tooling/RefactoringCallbacks.h90
-rw-r--r--include/clang/Tooling/Tooling.h85
-rw-r--r--lib/ARCMigrate/ARCMT.cpp67
-rw-r--r--lib/ARCMigrate/CMakeLists.txt18
-rw-r--r--lib/ARCMigrate/FileRemapper.cpp4
-rw-r--r--lib/ARCMigrate/Internals.h4
-rw-r--r--lib/ARCMigrate/ObjCMT.cpp2
-rw-r--r--lib/ARCMigrate/TransAPIUses.cpp1
-rw-r--r--lib/ARCMigrate/TransARCAssign.cpp1
-rw-r--r--lib/ARCMigrate/TransAutoreleasePool.cpp3
-rw-r--r--lib/ARCMigrate/TransBlockObjCVariable.cpp19
-rw-r--r--lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp7
-rw-r--r--lib/ARCMigrate/TransGCAttrs.cpp9
-rw-r--r--lib/ARCMigrate/TransGCCalls.cpp9
-rw-r--r--lib/ARCMigrate/TransProperties.cpp11
-rw-r--r--lib/ARCMigrate/TransRetainReleaseDealloc.cpp99
-rw-r--r--lib/ARCMigrate/TransUnbridgedCasts.cpp85
-rw-r--r--lib/ARCMigrate/TransUnusedInitDelegate.cpp3
-rw-r--r--lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp1
-rw-r--r--lib/ARCMigrate/TransformActions.cpp1
-rw-r--r--lib/ARCMigrate/Transforms.cpp59
-rw-r--r--lib/ARCMigrate/Transforms.h14
-rw-r--r--lib/AST/APValue.cpp6
-rw-r--r--lib/AST/ASTContext.cpp569
-rw-r--r--lib/AST/ASTDiagnostic.cpp934
-rw-r--r--lib/AST/ASTImporter.cpp117
-rw-r--r--lib/AST/CMakeLists.txt30
-rw-r--r--lib/AST/CXXABI.h2
-rw-r--r--lib/AST/CXXInheritance.cpp22
-rw-r--r--lib/AST/Comment.cpp264
-rw-r--r--lib/AST/CommentBriefParser.cpp122
-rw-r--r--lib/AST/CommentCommandTraits.cpp134
-rw-r--r--lib/AST/CommentDumper.cpp231
-rw-r--r--lib/AST/CommentLexer.cpp815
-rw-r--r--lib/AST/CommentParser.cpp722
-rw-r--r--lib/AST/CommentSema.cpp739
-rw-r--r--lib/AST/Decl.cpp346
-rw-r--r--lib/AST/DeclBase.cpp57
-rw-r--r--lib/AST/DeclCXX.cpp179
-rw-r--r--lib/AST/DeclFriend.cpp6
-rw-r--r--lib/AST/DeclObjC.cpp56
-rw-r--r--lib/AST/DeclPrinter.cpp54
-rw-r--r--lib/AST/DeclTemplate.cpp18
-rw-r--r--lib/AST/DeclarationName.cpp55
-rw-r--r--lib/AST/DumpXML.cpp15
-rw-r--r--lib/AST/Expr.cpp522
-rw-r--r--lib/AST/ExprCXX.cpp67
-rw-r--r--lib/AST/ExprClassification.cpp25
-rw-r--r--lib/AST/ExprConstant.cpp346
-rw-r--r--lib/AST/ItaniumCXXABI.cpp2
-rw-r--r--lib/AST/ItaniumMangle.cpp38
-rw-r--r--lib/AST/LambdaMangleContext.cpp2
-rw-r--r--lib/AST/Mangle.cpp29
-rw-r--r--lib/AST/MicrosoftCXXABI.cpp4
-rw-r--r--lib/AST/MicrosoftMangle.cpp822
-rw-r--r--lib/AST/NSAPI.cpp107
-rw-r--r--lib/AST/ParentMap.cpp13
-rw-r--r--lib/AST/RawCommentList.cpp271
-rw-r--r--lib/AST/RecordLayout.cpp12
-rw-r--r--lib/AST/RecordLayoutBuilder.cpp307
-rw-r--r--lib/AST/Stmt.cpp132
-rw-r--r--lib/AST/StmtDumper.cpp22
-rw-r--r--lib/AST/StmtPrinter.cpp116
-rw-r--r--lib/AST/StmtProfile.cpp9
-rw-r--r--lib/AST/TemplateBase.cpp33
-rw-r--r--lib/AST/Type.cpp51
-rw-r--r--lib/AST/TypeLoc.cpp1
-rw-r--r--lib/AST/TypePrinter.cpp1453
-rw-r--r--lib/AST/VTTBuilder.cpp1
-rw-r--r--lib/AST/VTableBuilder.cpp17
-rw-r--r--lib/ASTMatchers/ASTMatchFinder.cpp547
-rw-r--r--lib/ASTMatchers/ASTMatchersInternal.cpp102
-rw-r--r--lib/ASTMatchers/CMakeLists.txt17
-rw-r--r--lib/ASTMatchers/Makefile13
-rw-r--r--lib/Analysis/AnalysisDeclContext.cpp63
-rw-r--r--lib/Analysis/CFG.cpp500
-rw-r--r--lib/Analysis/CMakeLists.txt18
-rw-r--r--lib/Analysis/CallGraph.cpp7
-rw-r--r--lib/Analysis/CocoaConventions.cpp2
-rw-r--r--lib/Analysis/FormatString.cpp138
-rw-r--r--lib/Analysis/LiveVariables.cpp19
-rw-r--r--lib/Analysis/PrintfFormatString.cpp122
-rw-r--r--lib/Analysis/ProgramPoint.cpp6
-rw-r--r--lib/Analysis/PseudoConstantAnalysis.cpp1
-rw-r--r--lib/Analysis/ScanfFormatString.cpp193
-rw-r--r--lib/Analysis/ThreadSafety.cpp1641
-rw-r--r--lib/Analysis/UninitializedValues.cpp793
-rw-r--r--lib/Basic/CMakeLists.txt33
-rw-r--r--lib/Basic/ConvertUTF.c3
-rw-r--r--lib/Basic/ConvertUTFWrapper.cpp70
-rw-r--r--lib/Basic/Diagnostic.cpp129
-rw-r--r--lib/Basic/DiagnosticIDs.cpp32
-rw-r--r--lib/Basic/FileManager.cpp61
-rw-r--r--lib/Basic/IdentifierTable.cpp29
-rw-r--r--lib/Basic/ObjCRuntime.cpp86
-rw-r--r--lib/Basic/SourceManager.cpp96
-rw-r--r--lib/Basic/TargetInfo.cpp1
-rw-r--r--lib/Basic/Targets.cpp588
-rw-r--r--lib/Basic/Version.cpp5
-rw-r--r--lib/Basic/VersionTuple.cpp52
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/CodeGen/ABIInfo.h32
-rw-r--r--lib/CodeGen/BackendUtil.cpp32
-rw-r--r--lib/CodeGen/CGBlocks.cpp41
-rw-r--r--lib/CodeGen/CGBuilder.h2
-rw-r--r--lib/CodeGen/CGBuiltin.cpp2040
-rw-r--r--lib/CodeGen/CGCXX.cpp4
-rw-r--r--lib/CodeGen/CGCXXABI.cpp71
-rw-r--r--lib/CodeGen/CGCXXABI.h51
-rw-r--r--lib/CodeGen/CGCall.cpp198
-rw-r--r--lib/CodeGen/CGClass.cpp202
-rw-r--r--lib/CodeGen/CGCleanup.cpp8
-rw-r--r--lib/CodeGen/CGCleanup.h2
-rw-r--r--lib/CodeGen/CGDebugInfo.cpp228
-rw-r--r--lib/CodeGen/CGDebugInfo.h5
-rw-r--r--lib/CodeGen/CGDecl.cpp48
-rw-r--r--lib/CodeGen/CGDeclCXX.cpp80
-rw-r--r--lib/CodeGen/CGException.cpp80
-rw-r--r--lib/CodeGen/CGExpr.cpp239
-rw-r--r--lib/CodeGen/CGExprAgg.cpp253
-rw-r--r--lib/CodeGen/CGExprCXX.cpp136
-rw-r--r--lib/CodeGen/CGExprConstant.cpp18
-rw-r--r--lib/CodeGen/CGExprScalar.cpp30
-rw-r--r--lib/CodeGen/CGObjC.cpp192
-rw-r--r--lib/CodeGen/CGObjCGNU.cpp97
-rw-r--r--lib/CodeGen/CGObjCMac.cpp75
-rw-r--r--lib/CodeGen/CGObjCRuntime.cpp17
-rw-r--r--lib/CodeGen/CGObjCRuntime.h20
-rw-r--r--lib/CodeGen/CGRTTI.cpp3
-rw-r--r--lib/CodeGen/CGRecordLayout.h7
-rw-r--r--lib/CodeGen/CGRecordLayoutBuilder.cpp16
-rw-r--r--lib/CodeGen/CGStmt.cpp94
-rw-r--r--lib/CodeGen/CGVTables.cpp23
-rw-r--r--lib/CodeGen/CGValue.h9
-rw-r--r--lib/CodeGen/CMakeLists.txt20
-rw-r--r--lib/CodeGen/CodeGenFunction.cpp136
-rw-r--r--lib/CodeGen/CodeGenFunction.h47
-rw-r--r--lib/CodeGen/CodeGenModule.cpp146
-rw-r--r--lib/CodeGen/CodeGenModule.h15
-rw-r--r--lib/CodeGen/CodeGenTBAA.cpp8
-rw-r--r--lib/CodeGen/CodeGenTBAA.h6
-rw-r--r--lib/CodeGen/CodeGenTypes.cpp4
-rw-r--r--lib/CodeGen/CodeGenTypes.h36
-rw-r--r--lib/CodeGen/ItaniumCXXABI.cpp257
-rw-r--r--lib/CodeGen/MicrosoftCXXABI.cpp98
-rw-r--r--lib/CodeGen/TargetInfo.cpp383
-rw-r--r--lib/Driver/ArgList.cpp62
-rw-r--r--lib/Driver/CC1Options.cpp38
-rw-r--r--lib/Driver/CMakeLists.txt22
-rw-r--r--lib/Driver/Compilation.cpp2
-rw-r--r--lib/Driver/Driver.cpp189
-rw-r--r--lib/Driver/OptTable.cpp2
-rw-r--r--lib/Driver/ToolChain.cpp27
-rw-r--r--lib/Driver/ToolChains.cpp338
-rw-r--r--lib/Driver/ToolChains.h109
-rw-r--r--lib/Driver/Tools.cpp989
-rw-r--r--lib/Driver/Tools.h38
-rw-r--r--lib/Driver/Types.cpp4
-rw-r--r--lib/Edit/CMakeLists.txt17
-rw-r--r--lib/Edit/Commit.cpp1
-rw-r--r--lib/Edit/EditedSource.cpp5
-rw-r--r--lib/Edit/RewriteObjCFoundationAPI.cpp506
-rw-r--r--lib/Frontend/ASTConsumers.cpp99
-rw-r--r--lib/Frontend/ASTUnit.cpp224
-rw-r--r--lib/Frontend/CMakeLists.txt45
-rw-r--r--lib/Frontend/CompilerInstance.cpp23
-rw-r--r--lib/Frontend/CompilerInvocation.cpp326
-rw-r--r--lib/Frontend/CreateInvocationFromCommandLine.cpp8
-rw-r--r--lib/Frontend/DiagnosticRenderer.cpp127
-rw-r--r--lib/Frontend/FrontendAction.cpp62
-rw-r--r--lib/Frontend/FrontendActions.cpp11
-rw-r--r--lib/Frontend/InitHeaderSearch.cpp15
-rw-r--r--lib/Frontend/InitPreprocessor.cpp72
-rw-r--r--lib/Frontend/LayoutOverrideSource.cpp1
-rw-r--r--lib/Frontend/PrintPreprocessedOutput.cpp66
-rw-r--r--lib/Frontend/SerializedDiagnosticPrinter.cpp40
-rw-r--r--lib/Frontend/TextDiagnostic.cpp217
-rw-r--r--lib/Frontend/TextDiagnosticPrinter.cpp30
-rw-r--r--lib/Frontend/VerifyDiagnosticConsumer.cpp471
-rw-r--r--lib/Frontend/Warnings.cpp14
-rw-r--r--lib/FrontendTool/CMakeLists.txt21
-rw-r--r--lib/FrontendTool/ExecuteCompilerInvocation.cpp12
-rw-r--r--lib/Headers/CMakeLists.txt3
-rw-r--r--lib/Headers/ammintrin.h68
-rw-r--r--lib/Headers/avx2intrin.h240
-rw-r--r--lib/Headers/bmiintrin.h6
-rw-r--r--lib/Headers/emmintrin.h5
-rw-r--r--lib/Headers/float.h2
-rw-r--r--lib/Headers/fmaintrin.h229
-rw-r--r--lib/Headers/immintrin.h26
-rw-r--r--lib/Headers/stddef.h16
-rw-r--r--lib/Headers/wmmintrin.h18
-rw-r--r--lib/Headers/x86intrin.h10
-rw-r--r--lib/Headers/xopintrin.h411
-rw-r--r--lib/Lex/CMakeLists.txt12
-rw-r--r--lib/Lex/HeaderSearch.cpp42
-rw-r--r--lib/Lex/Lexer.cpp91
-rw-r--r--lib/Lex/LiteralSupport.cpp154
-rw-r--r--lib/Lex/PPDirectives.cpp183
-rw-r--r--lib/Lex/PPLexerChange.cpp2
-rw-r--r--lib/Lex/PPMacroExpansion.cpp46
-rw-r--r--lib/Lex/Pragma.cpp78
-rw-r--r--lib/Lex/PreprocessingRecord.cpp4
-rw-r--r--lib/Lex/Preprocessor.cpp127
-rw-r--r--lib/Lex/PreprocessorLexer.cpp2
-rw-r--r--lib/Lex/TokenConcatenation.cpp1
-rw-r--r--lib/Lex/TokenLexer.cpp12
-rwxr-xr-xlib/Makefile2
-rw-r--r--lib/Parse/CMakeLists.txt21
-rw-r--r--lib/Parse/ParseAST.cpp45
-rw-r--r--lib/Parse/ParseCXXInlineMethods.cpp8
-rw-r--r--lib/Parse/ParseDecl.cpp838
-rw-r--r--lib/Parse/ParseDeclCXX.cpp327
-rw-r--r--lib/Parse/ParseExpr.cpp158
-rw-r--r--lib/Parse/ParseExprCXX.cpp77
-rw-r--r--lib/Parse/ParseObjc.cpp242
-rw-r--r--lib/Parse/ParsePragma.h21
-rw-r--r--lib/Parse/ParseStmt.cpp127
-rw-r--r--lib/Parse/ParseTemplate.cpp135
-rw-r--r--lib/Parse/ParseTentative.cpp93
-rw-r--r--lib/Parse/Parser.cpp182
-rw-r--r--lib/Parse/RAIIObjectsForParser.h298
-rw-r--r--lib/Rewrite/CMakeLists.txt17
-rw-r--r--lib/Rewrite/FrontendActions.cpp11
-rw-r--r--lib/Rewrite/HTMLRewrite.cpp11
-rw-r--r--lib/Rewrite/InclusionRewriter.cpp361
-rw-r--r--lib/Rewrite/RewriteModernObjC.cpp759
-rw-r--r--lib/Rewrite/RewriteObjC.cpp105
-rw-r--r--lib/Rewrite/Rewriter.cpp76
-rw-r--r--lib/Sema/AnalysisBasedWarnings.cpp502
-rw-r--r--lib/Sema/AttributeList.cpp39
-rw-r--r--lib/Sema/CMakeLists.txt39
-rw-r--r--lib/Sema/CodeCompleteConsumer.cpp27
-rw-r--r--lib/Sema/DeclSpec.cpp39
-rw-r--r--lib/Sema/Sema.cpp177
-rw-r--r--lib/Sema/SemaAccess.cpp83
-rw-r--r--lib/Sema/SemaCXXScopeSpec.cpp12
-rw-r--r--lib/Sema/SemaCast.cpp30
-rw-r--r--lib/Sema/SemaChecking.cpp931
-rw-r--r--lib/Sema/SemaCodeComplete.cpp210
-rw-r--r--lib/Sema/SemaDecl.cpp911
-rw-r--r--lib/Sema/SemaDeclAttr.cpp1637
-rw-r--r--lib/Sema/SemaDeclCXX.cpp1738
-rw-r--r--lib/Sema/SemaDeclObjC.cpp311
-rw-r--r--lib/Sema/SemaExceptionSpec.cpp41
-rw-r--r--lib/Sema/SemaExpr.cpp1155
-rw-r--r--lib/Sema/SemaExprCXX.cpp410
-rw-r--r--lib/Sema/SemaExprMember.cpp95
-rw-r--r--lib/Sema/SemaExprObjC.cpp925
-rw-r--r--lib/Sema/SemaFixItUtils.cpp71
-rw-r--r--lib/Sema/SemaInit.cpp372
-rw-r--r--lib/Sema/SemaLambda.cpp316
-rw-r--r--lib/Sema/SemaLookup.cpp198
-rw-r--r--lib/Sema/SemaObjCProperty.cpp375
-rw-r--r--lib/Sema/SemaOverload.cpp456
-rw-r--r--lib/Sema/SemaPseudoObject.cpp60
-rw-r--r--lib/Sema/SemaStmt.cpp735
-rw-r--r--lib/Sema/SemaStmtAttr.cpp36
-rw-r--r--lib/Sema/SemaTemplate.cpp238
-rw-r--r--lib/Sema/SemaTemplateDeduction.cpp124
-rw-r--r--lib/Sema/SemaTemplateInstantiate.cpp266
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp162
-rw-r--r--lib/Sema/SemaTemplateVariadic.cpp113
-rw-r--r--lib/Sema/SemaType.cpp568
-rw-r--r--lib/Sema/TargetAttributesSema.cpp63
-rw-r--r--lib/Sema/TreeTransform.h1264
-rw-r--r--lib/Serialization/ASTCommon.h2
-rw-r--r--lib/Serialization/ASTReader.cpp150
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp51
-rw-r--r--lib/Serialization/ASTReaderStmt.cpp32
-rw-r--r--lib/Serialization/ASTWriter.cpp82
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp26
-rw-r--r--lib/Serialization/ASTWriterStmt.cpp19
-rw-r--r--lib/Serialization/CMakeLists.txt14
-rw-r--r--lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp46
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp228
-rw-r--r--lib/StaticAnalyzer/Checkers/CMakeLists.txt14
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp28
-rw-r--r--lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp304
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp1
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp63
-rw-r--r--lib/StaticAnalyzer/Checkers/Checkers.td40
-rw-r--r--lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp265
-rw-r--r--lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp179
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp122
-rw-r--r--lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp603
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp20
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp626
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp37
-rw-r--r--lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp23
-rw-r--r--lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp1
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp102
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp739
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/StreamChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/TraversalChecker.cpp84
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp1
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/APSIntType.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/BasicConstraintManager.cpp241
-rw-r--r--lib/StaticAnalyzer/Core/BasicValueFactory.cpp1
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp179
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp86
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt23
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp856
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp133
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp29
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp17
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp18
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp538
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp270
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp366
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp822
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp211
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp84
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp274
-rw-r--r--lib/StaticAnalyzer/Core/ObjCMessage.cpp90
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp204
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp31
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp99
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp275
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp949
-rw-r--r--lib/StaticAnalyzer/Core/SValBuilder.cpp76
-rw-r--r--lib/StaticAnalyzer/Core/SVals.cpp20
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp103
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.h6
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp338
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp34
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp14
-rw-r--r--lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp5
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp71
-rw-r--r--lib/StaticAnalyzer/Frontend/CMakeLists.txt15
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp2
-rw-r--r--lib/Tooling/ArgumentsAdjusters.cpp34
-rw-r--r--lib/Tooling/CMakeLists.txt22
-rw-r--r--lib/Tooling/CommandLineClangTool.cpp80
-rw-r--r--lib/Tooling/CompilationDatabase.cpp106
-rw-r--r--lib/Tooling/CustomCompilationDatabase.h42
-rw-r--r--lib/Tooling/Refactoring.cpp186
-rw-r--r--lib/Tooling/RefactoringCallbacks.cpp81
-rw-r--r--lib/Tooling/Tooling.cpp136
-rw-r--r--runtime/compiler-rt/Makefile6
-rw-r--r--test/ARCMT/Common.h36
-rw-r--r--test/ARCMT/autoreleases.m47
-rw-r--r--test/ARCMT/autoreleases.m.result43
-rw-r--r--test/ARCMT/checking.m33
-rw-r--r--test/ARCMT/cxx-rewrite.mm.result2
-rw-r--r--test/ARCMT/dispatch.m11
-rw-r--r--test/ARCMT/dispatch.m.result11
-rw-r--r--test/ARCMT/nonobjc-to-objc-cast-2.m31
-rw-r--r--test/ARCMT/nonobjc-to-objc-cast.m27
-rw-r--r--test/ARCMT/nonobjc-to-objc-cast.m.result57
-rw-r--r--test/ARCMT/objcmt-boxing.m102
-rw-r--r--test/ARCMT/objcmt-boxing.m.result102
-rw-r--r--test/ARCMT/objcmt-numeric-literals.m1
-rw-r--r--test/ARCMT/objcmt-numeric-literals.m.result5
-rw-r--r--test/ARCMT/objcmt-subscripting-literals-in-arc.m106
-rw-r--r--test/ARCMT/objcmt-subscripting-literals-in-arc.m.result106
-rw-r--r--test/ARCMT/objcmt-subscripting-literals.m102
-rw-r--r--test/ARCMT/objcmt-subscripting-literals.m.result102
-rw-r--r--test/ARCMT/objcmt-subscripting-unavailable.m79
-rw-r--r--test/ARCMT/objcmt-subscripting-unavailable.m.result79
-rw-r--r--test/ARCMT/releases-driver.m3
-rw-r--r--test/ARCMT/releases-driver.m.result3
-rw-r--r--test/ARCMT/releases.m3
-rw-r--r--test/ARCMT/releases.m.result3
-rw-r--r--test/ARCMT/verify.m13
-rw-r--r--test/ASTMerge/function.c6
-rw-r--r--test/Analysis/MissingDealloc.m6
-rw-r--r--test/Analysis/NoReturn.m12
-rw-r--r--test/Analysis/PR12905.c8
-rw-r--r--test/Analysis/additive-folding-range-constraints.c191
-rw-r--r--test/Analysis/additive-folding.c203
-rw-r--r--test/Analysis/additive-folding.cpp196
-rw-r--r--test/Analysis/array-struct-region.c171
-rw-r--r--test/Analysis/auto-obj-dtors-cfg-output.cpp295
-rw-r--r--test/Analysis/base-init.cpp9
-rw-r--r--test/Analysis/blocks-no-inline.c13
-rw-r--r--test/Analysis/blocks.m4
-rw-r--r--test/Analysis/bstring.c89
-rw-r--r--test/Analysis/casts.c8
-rw-r--r--test/Analysis/comparison-implicit-casts.cpp98
-rw-r--r--test/Analysis/constant-folding.c90
-rw-r--r--test/Analysis/coverage.c8
-rw-r--r--test/Analysis/cstring-syntax.c2
-rw-r--r--test/Analysis/ctor-inlining.mm41
-rw-r--r--test/Analysis/cxx-crashes.cpp23
-rw-r--r--test/Analysis/cxx-for-range-cfg.cpp16
-rw-r--r--test/Analysis/cxx-method-names.cpp21
-rw-r--r--test/Analysis/cxx11-crashes.cpp66
-rw-r--r--test/Analysis/dead-stores.m20
-rw-r--r--test/Analysis/delegates.m25
-rw-r--r--test/Analysis/derived-to-base.cpp124
-rw-r--r--test/Analysis/diagnostics/undef-value-callee.h4
-rw-r--r--test/Analysis/diagnostics/undef-value-caller.c238
-rw-r--r--test/Analysis/domtest.c45
-rw-r--r--test/Analysis/dtor.cpp164
-rw-r--r--test/Analysis/dynamic-cast.cpp25
-rw-r--r--test/Analysis/engine/replay-without-inlining.c57
-rw-r--r--test/Analysis/global-region-invalidation.c13
-rw-r--r--test/Analysis/html-diags.c2
-rw-r--r--test/Analysis/initializer.cpp74
-rw-r--r--test/Analysis/initializers-cfg-output.cpp2
-rw-r--r--test/Analysis/inline-plist.c834
-rw-r--r--test/Analysis/inline-unique-reports.c26
-rw-r--r--test/Analysis/inline.c22
-rw-r--r--test/Analysis/inline.cpp168
-rw-r--r--test/Analysis/inlining/DynDispatchBifurcate.m181
-rw-r--r--test/Analysis/inlining/InlineObjCClassMethod.m181
-rw-r--r--test/Analysis/inlining/InlineObjCInstanceMethod.h46
-rw-r--r--test/Analysis/inlining/InlineObjCInstanceMethod.m86
-rw-r--r--test/Analysis/inlining/ObjCDynTypePopagation.m84
-rw-r--r--test/Analysis/inlining/ObjCImproperDynamictallyDetectableCast.m37
-rw-r--r--test/Analysis/inlining/RetainCountExamples.m33
-rw-r--r--test/Analysis/inlining/path-notes.c1291
-rw-r--r--test/Analysis/iterators.cpp105
-rw-r--r--test/Analysis/ivars.m132
-rw-r--r--test/Analysis/malloc-annotations.c8
-rw-r--r--test/Analysis/malloc-plist.c1876
-rw-r--r--test/Analysis/malloc-sizeof.c27
-rw-r--r--test/Analysis/malloc.c278
-rw-r--r--test/Analysis/malloc.cpp21
-rw-r--r--test/Analysis/malloc.m2
-rw-r--r--test/Analysis/malloc.mm80
-rw-r--r--test/Analysis/method-call-path-notes.cpp664
-rw-r--r--test/Analysis/method-call.cpp28
-rw-r--r--test/Analysis/misc-ps-arm.m18
-rw-r--r--test/Analysis/misc-ps-cxx0x.cpp14
-rw-r--r--test/Analysis/misc-ps-region-store.cpp50
-rw-r--r--test/Analysis/misc-ps-region-store.m22
-rw-r--r--test/Analysis/misc-ps.c7
-rw-r--r--test/Analysis/misc-ps.m36
-rw-r--r--test/Analysis/new.cpp99
-rw-r--r--test/Analysis/nonnull.m23
-rw-r--r--test/Analysis/nullptr.cpp2
-rw-r--r--test/Analysis/objc-boxing.m44
-rw-r--r--test/Analysis/objc-for.m58
-rw-r--r--test/Analysis/objc-subscript.m89
-rw-r--r--test/Analysis/operator-calls.cpp18
-rw-r--r--test/Analysis/out-of-bounds.c6
-rw-r--r--test/Analysis/outofbound-notwork.c2
-rw-r--r--test/Analysis/outofbound.c36
-rw-r--r--test/Analysis/plist-output-alternate.m49
-rw-r--r--test/Analysis/plist-output.m647
-rw-r--r--test/Analysis/ptr-arith.c217
-rw-r--r--test/Analysis/rdar-7168531.m2
-rw-r--r--test/Analysis/refcnt_naming.m2
-rw-r--r--test/Analysis/reference.cpp79
-rw-r--r--test/Analysis/region-store.c23
-rw-r--r--test/Analysis/retain-release-path-notes-gc.m1339
-rw-r--r--test/Analysis/retain-release-path-notes.m4489
-rw-r--r--test/Analysis/retain-release.m130
-rw-r--r--test/Analysis/self-init.m31
-rw-r--r--test/Analysis/stack-addr-ps.cpp6
-rw-r--r--test/Analysis/string-fail.c40
-rw-r--r--test/Analysis/string.c397
-rw-r--r--test/Analysis/svalbuilder-logic.c7
-rw-r--r--test/Analysis/system-header-simulator-objc.h16
-rw-r--r--test/Analysis/system-header-simulator.h24
-rw-r--r--test/Analysis/taint-generic.c29
-rw-r--r--test/Analysis/taint-tester.c2
-rw-r--r--test/Analysis/temp-obj-dtors-cfg-output.cpp205
-rw-r--r--test/Analysis/templates.cpp44
-rw-r--r--test/Analysis/test-variably-modified-types.c13
-rw-r--r--test/Analysis/traversal-algorithm.mm213
-rw-r--r--test/Analysis/uninit-sometimes.cpp387
-rw-r--r--test/Analysis/unused-ivars.m21
-rw-r--r--test/CMakeLists.txt172
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp69
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.classref/p4-cxx11.cpp21
-rw-r--r--test/CXX/basic/basic.types/p10.cpp6
-rw-r--r--test/CXX/class.access/class.friend/p1.cpp2
-rw-r--r--test/CXX/class.access/class.friend/p9-cxx0x.cpp117
-rw-r--r--test/CXX/class.derived/class.virtual/p3-0x.cpp49
-rw-r--r--test/CXX/class/class.mem/p14.cpp5
-rw-r--r--test/CXX/class/class.union/p1.cpp5
-rw-r--r--test/CXX/class/p6-0x.cpp2
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp48
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp37
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp3
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp12
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.typedef/p2-0x.cpp4
-rw-r--r--test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp76
-rw-r--r--test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.delete/p4.cpp8
-rw-r--r--test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp2
-rw-r--r--test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp113
-rw-r--r--test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp14
-rw-r--r--test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-cxx03-extra-copy.cpp2
-rw-r--r--test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp16
-rw-r--r--test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp27
-rw-r--r--test/CXX/dcl.decl/dcl.meaning/dcl.fct/p9-0x.cpp6
-rw-r--r--test/CXX/except/except.spec/p14.cpp24
-rw-r--r--test/CXX/except/except.spec/p5-pointers.cpp6
-rw-r--r--test/CXX/expr/expr.const/p2-0x.cpp6
-rw-r--r--test/CXX/expr/expr.post/expr.reinterpret.cast/p1-0x.cpp7
-rw-r--r--test/CXX/expr/expr.prim/expr.prim.lambda/blocks-irgen.mm29
-rw-r--r--test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm38
-rw-r--r--test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp4
-rw-r--r--test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp8
-rw-r--r--test/CXX/expr/expr.unary/expr.unary.op/p6.cpp2
-rw-r--r--test/CXX/expr/p10-0x.cpp46
-rw-r--r--test/CXX/special/class.conv/class.conv.ctor/p1.cpp21
-rw-r--r--test/CXX/special/class.copy/implicit-move.cpp7
-rw-r--r--test/CXX/special/class.copy/p11.0x.copy.cpp16
-rw-r--r--test/CXX/special/class.copy/p13-0x.cpp56
-rw-r--r--test/CXX/special/class.copy/p15-inclass.cpp2
-rw-r--r--test/CXX/special/class.copy/p23-cxx11.cpp148
-rw-r--r--test/CXX/special/class.copy/p8-cxx11.cpp2
-rw-r--r--test/CXX/special/class.ctor/p5-0x.cpp6
-rw-r--r--test/CXX/special/class.ctor/p6-0x.cpp39
-rw-r--r--test/CXX/special/class.dtor/p5-0x.cpp8
-rw-r--r--test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp9
-rw-r--r--test/CXX/temp/temp.decls/temp.friend/p1.cpp3
-rw-r--r--test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp43
-rw-r--r--test/CXX/temp/temp.names/p3-0x.cpp12
-rw-r--r--test/CXX/temp/temp.spec/p5.cpp2
-rw-r--r--test/CXX/temp/temp.spec/temp.expl.spec/p14.cpp2
-rw-r--r--test/CXX/temp/temp.spec/temp.expl.spec/p3.cpp8
-rw-r--r--test/CXX/temp/temp.spec/temp.explicit/p12.cpp49
-rw-r--r--test/CXX/temp/temp.spec/temp.explicit/p9-linkage.cpp2
-rw-r--r--test/CodeCompletion/auto.cpp2
-rw-r--r--test/CodeCompletion/documentation.cpp33
-rw-r--r--test/CodeCompletion/objc-expr.m17
-rw-r--r--test/CodeGen/2005-07-20-SqrtNoErrno.c2
-rw-r--r--test/CodeGen/2007-02-25-C-DotDotDot.c2
-rw-r--r--test/CodeGen/2008-01-25-ByValReadNone.c2
-rw-r--r--test/CodeGen/2008-03-05-syncPtr.c2
-rw-r--r--test/CodeGen/2008-04-08-NoExceptions.c2
-rw-r--r--test/CodeGen/2009-02-13-zerosize-union-field-ppc.c1
-rw-r--r--test/CodeGen/2009-09-24-SqrtErrno.c2
-rw-r--r--test/CodeGen/2009-10-20-GlobalDebug.c4
-rw-r--r--test/CodeGen/2010-02-10-PointerName.c2
-rw-r--r--test/CodeGen/2010-05-26-AsmSideEffect.c1
-rw-r--r--test/CodeGen/Atomics.c2
-rw-r--r--test/CodeGen/address-safety-attr.cpp6
-rw-r--r--test/CodeGen/address-space.c6
-rw-r--r--test/CodeGen/alloca.c (renamed from test/CodeGen/2002-05-24-Alloca.c)0
-rw-r--r--test/CodeGen/altivec.c1
-rw-r--r--test/CodeGen/annotations-builtin.c14
-rw-r--r--test/CodeGen/arm-aapcs-vfp.c12
-rw-r--r--test/CodeGen/arm-aapcs-zerolength-bitfield.c1
-rw-r--r--test/CodeGen/arm-apcs-zerolength-bitfield.c1
-rw-r--r--test/CodeGen/arm-arguments.c13
-rw-r--r--test/CodeGen/arm-asm-variable.c1
-rw-r--r--test/CodeGen/arm-asm.c1
-rw-r--r--test/CodeGen/arm-cc.c1
-rw-r--r--test/CodeGen/arm-clear.c1
-rw-r--r--test/CodeGen/arm-homogenous.c3
-rw-r--r--test/CodeGen/arm-inline-asm.c1
-rw-r--r--test/CodeGen/arm-pcs.c1
-rw-r--r--test/CodeGen/arm-vaarg-align.c1
-rw-r--r--test/CodeGen/arm-vector-align.c1
-rw-r--r--test/CodeGen/arm-vector-arguments.c1
-rw-r--r--test/CodeGen/asm.c10
-rw-r--r--test/CodeGen/asm_arm.c1
-rw-r--r--test/CodeGen/atomic_ops.c15
-rw-r--r--test/CodeGen/attr-coldhot.c9
-rw-r--r--test/CodeGen/attributes.c8
-rw-r--r--test/CodeGen/avx-builtins.c70
-rw-r--r--test/CodeGen/avx2-builtins.c156
-rw-r--r--test/CodeGen/block-3.c12
-rw-r--r--test/CodeGen/block-byref-aggr.c71
-rw-r--r--test/CodeGen/blocks.c28
-rw-r--r--test/CodeGen/blocksignature.c2
-rw-r--r--test/CodeGen/blockwithlocalstatic.c6
-rw-r--r--test/CodeGen/bmi-builtins.c12
-rw-r--r--test/CodeGen/bool_test.c1
-rw-r--r--test/CodeGen/bounds-checking.c26
-rw-r--r--test/CodeGen/branch-target-layout.c40
-rw-r--r--test/CodeGen/builtin-attributes.c1
-rw-r--r--test/CodeGen/builtins-arm.c1
-rw-r--r--test/CodeGen/builtins-mips-args.c14
-rw-r--r--test/CodeGen/builtins-mips.c324
-rw-r--r--test/CodeGen/builtins-nvptx.c (renamed from test/CodeGen/builtins-ptx.c)5
-rw-r--r--test/CodeGen/builtins-ppc-altivec.c1
-rw-r--r--test/CodeGen/builtins-x86.c1
-rw-r--r--test/CodeGen/builtins.c6
-rw-r--r--test/CodeGen/capture-complex-expr-in-block.c2
-rw-r--r--test/CodeGen/catch-undef-behavior.c12
-rw-r--r--test/CodeGen/compound-literal.c2
-rw-r--r--test/CodeGen/debug-info-gline-tables-only.c33
-rw-r--r--test/CodeGen/debug-info-gline-tables-only2.c13
-rw-r--r--test/CodeGen/debug-info-line2.c5
-rw-r--r--test/CodeGen/debug-info-line3.c4
-rw-r--r--test/CodeGen/debug-info-vla.c12
-rw-r--r--test/CodeGen/debug-line-1.c2
-rw-r--r--test/CodeGen/fma-builtins.c166
-rw-r--r--test/CodeGen/fma4-builtins.c64
-rw-r--r--test/CodeGen/forceinline.c14
-rw-r--r--test/CodeGen/fp-contract.c9
-rw-r--r--test/CodeGen/fp16-ops.c1
-rw-r--r--test/CodeGen/func-aligned.c2
-rw-r--r--test/CodeGen/func-in-block.c4
-rw-r--r--test/CodeGen/func-return-member.c2
-rw-r--r--test/CodeGen/incomplete-function-type.c2
-rw-r--r--test/CodeGen/integer-overflow.c8
-rw-r--r--test/CodeGen/libcalls.c45
-rw-r--r--test/CodeGen/mips-byval-arg.c15
-rw-r--r--test/CodeGen/mips-vector-arg.c28
-rw-r--r--test/CodeGen/mips-vector-return.c31
-rw-r--r--test/CodeGen/mips64-class-return.cpp2
-rw-r--r--test/CodeGen/ms-declspecs.c8
-rw-r--r--test/CodeGen/ms-inline-asm.c40
-rw-r--r--test/CodeGen/no-common.c2
-rw-r--r--test/CodeGen/nobuiltin.c8
-rw-r--r--test/CodeGen/nvptx-cc.c (renamed from test/CodeGen/ptx-cc.c)6
-rw-r--r--test/CodeGen/nvptx-inlineasm.c15
-rw-r--r--test/CodeGen/object-size.c16
-rw-r--r--test/CodeGen/packed-nest-unpacked.c18
-rw-r--r--test/CodeGen/pclmul-builtins.c11
-rw-r--r--test/CodeGen/powerpc_types.c1
-rw-r--r--test/CodeGen/pr12251.c2
-rw-r--r--test/CodeGen/pr13168.c7
-rw-r--r--test/CodeGen/pr5406.c1
-rw-r--r--test/CodeGen/pragma-visibility.c2
-rw-r--r--test/CodeGen/rdrand-builtins.c27
-rw-r--r--test/CodeGen/regparm-flag.c3
-rw-r--r--test/CodeGen/regparm-struct.c177
-rw-r--r--test/CodeGen/sse-builtins.c6
-rw-r--r--test/CodeGen/sse4a-builtins.c39
-rw-r--r--test/CodeGen/struct-init.c1
-rw-r--r--test/CodeGen/struct-matching-constraint.c1
-rw-r--r--test/CodeGen/tbaa-for-vptr.cpp12
-rw-r--r--test/CodeGen/thread-specifier.c15
-rw-r--r--test/CodeGen/tls-model.c28
-rw-r--r--test/CodeGen/varargs.c8
-rw-r--r--test/CodeGen/vector-alignment.c38
-rw-r--r--test/CodeGen/vla.c49
-rw-r--r--test/CodeGen/vld_dup.c1
-rw-r--r--test/CodeGen/x86_32-arguments-darwin.c13
-rw-r--r--test/CodeGen/xop-builtins.c326
-rw-r--r--test/CodeGenCUDA/address-spaces.cu24
-rw-r--r--test/CodeGenCUDA/ptx-kernels.cu2
-rw-r--r--test/CodeGenCXX/anonymous-union-member-initializer.cpp10
-rw-r--r--test/CodeGenCXX/block-in-ctor-dtor.cpp10
-rw-r--r--test/CodeGenCXX/blocks.cpp6
-rw-r--r--test/CodeGenCXX/class-layout.cpp14
-rw-r--r--test/CodeGenCXX/const-init-cxx11.cpp32
-rw-r--r--test/CodeGenCXX/constructor-init.cpp20
-rw-r--r--test/CodeGenCXX/cxx0x-delegating-ctors.cpp11
-rw-r--r--test/CodeGenCXX/cxx0x-initializer-constructors.cpp37
-rw-r--r--test/CodeGenCXX/cxx0x-initializer-references.cpp4
-rw-r--r--test/CodeGenCXX/cxx11-initializer-aggregate.cpp25
-rw-r--r--test/CodeGenCXX/cxx11-vtable-key-function.cpp38
-rw-r--r--test/CodeGenCXX/debug-info-artificial-arg.cpp10
-rw-r--r--test/CodeGenCXX/debug-info-cxx0x.cpp10
-rw-r--r--test/CodeGenCXX/debug-info-determinism.cpp16
-rw-r--r--test/CodeGenCXX/debug-info-enum-class.cpp15
-rw-r--r--test/CodeGenCXX/debug-info-flex-member.cpp9
-rw-r--r--test/CodeGenCXX/debug-info-fwd-ref.cpp6
-rw-r--r--test/CodeGenCXX/debug-info-gline-tables-only.cpp21
-rw-r--r--test/CodeGenCXX/debug-info-globalinit.cpp30
-rw-r--r--test/CodeGenCXX/debug-info-nullptr.cpp2
-rw-r--r--test/CodeGenCXX/debug-info-rvalue-ref.cpp11
-rw-r--r--test/CodeGenCXX/debug-info-static-fns.cpp2
-rw-r--r--test/CodeGenCXX/debug-info-template-array.cpp14
-rw-r--r--test/CodeGenCXX/debug-info-template-limit.cpp5
-rw-r--r--test/CodeGenCXX/debug-info-template-member.cpp2
-rw-r--r--test/CodeGenCXX/debug-info-template-quals.cpp23
-rw-r--r--test/CodeGenCXX/debug-info-union.cpp16
-rw-r--r--test/CodeGenCXX/debug-info-user-def.cpp14
-rw-r--r--test/CodeGenCXX/debug-lambda-expressions.cpp50
-rw-r--r--test/CodeGenCXX/derived-to-base-conv.cpp111
-rw-r--r--test/CodeGenCXX/destructor-debug-info.cpp7
-rw-r--r--test/CodeGenCXX/destructor-exception-spec.cpp12
-rw-r--r--test/CodeGenCXX/destructors.cpp16
-rw-r--r--test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp139
-rw-r--r--test/CodeGenCXX/devirtualize-virtual-function-calls.cpp30
-rw-r--r--test/CodeGenCXX/dynamic-cast-always-null.cpp5
-rw-r--r--test/CodeGenCXX/exceptions.cpp36
-rw-r--r--test/CodeGenCXX/global-array-destruction.cpp33
-rw-r--r--test/CodeGenCXX/global-block-literal-helpers.cpp27
-rw-r--r--test/CodeGenCXX/global-init.cpp107
-rw-r--r--test/CodeGenCXX/inline-functions.cpp14
-rw-r--r--test/CodeGenCXX/lambda-expressions.cpp8
-rw-r--r--test/CodeGenCXX/mangle-lambdas.cpp35
-rw-r--r--test/CodeGenCXX/mangle-ms-abi-examples.cpp28
-rw-r--r--test/CodeGenCXX/mangle-ms-back-references-pr13207.cpp165
-rw-r--r--test/CodeGenCXX/mangle-ms-back-references.cpp63
-rw-r--r--test/CodeGenCXX/mangle-ms-cxx11.cpp11
-rw-r--r--test/CodeGenCXX/mangle-ms-return-qualifiers.cpp173
-rw-r--r--test/CodeGenCXX/mangle-ms-templates.cpp68
-rw-r--r--test/CodeGenCXX/mangle-ms.cpp73
-rw-r--r--test/CodeGenCXX/mangle-ref-qualifiers.cpp8
-rw-r--r--test/CodeGenCXX/member-data-pointers.cpp13
-rw-r--r--test/CodeGenCXX/member-function-pointers.cpp119
-rw-r--r--test/CodeGenCXX/member-init-anon-union.cpp6
-rw-r--r--test/CodeGenCXX/member-init-ctor.cpp14
-rw-r--r--test/CodeGenCXX/member-pointer-type-convert.cpp11
-rw-r--r--test/CodeGenCXX/microsoft-abi-array-cookies.cpp59
-rw-r--r--test/CodeGenCXX/microsoft-abi-constructors.cpp21
-rw-r--r--test/CodeGenCXX/microsoft-abi-methods.cpp89
-rw-r--r--test/CodeGenCXX/microsoft-abi-static-initializers.cpp52
-rw-r--r--test/CodeGenCXX/ms_wide_predefined_expr.cpp20
-rw-r--r--test/CodeGenCXX/pointers-to-data-members.cpp14
-rw-r--r--test/CodeGenCXX/pr13396.cpp26
-rw-r--r--test/CodeGenCXX/pragma-visibility.cpp28
-rw-r--r--test/CodeGenCXX/rvalue-references.cpp2
-rw-r--r--test/CodeGenCXX/template-instantiation.cpp4
-rw-r--r--test/CodeGenCXX/throw-expression-cleanup.cpp22
-rw-r--r--test/CodeGenCXX/virt-call-offsets.cpp8
-rw-r--r--test/CodeGenCXX/virt-template-vtable.cpp10
-rw-r--r--test/CodeGenCXX/virtual-destructor-calls.cpp11
-rw-r--r--test/CodeGenCXX/visibility-inlines-hidden.cpp18
-rw-r--r--test/CodeGenCXX/visibility.cpp511
-rw-r--r--test/CodeGenObjC/2008-11-12-Metadata.m2
-rw-r--r--test/CodeGenObjC/2008-11-24-ConstCFStrings.m2
-rw-r--r--test/CodeGenObjC/2010-03-17-StructRef.m2
-rw-r--r--test/CodeGenObjC/arc-blocks.m17
-rw-r--r--test/CodeGenObjC/arc.m3
-rw-r--r--test/CodeGenObjC/assign.m2
-rw-r--r--test/CodeGenObjC/autorelease.m4
-rw-r--r--test/CodeGenObjC/bitfield-1.m6
-rw-r--r--test/CodeGenObjC/bitfield-access.m4
-rw-r--r--test/CodeGenObjC/bitfield-gnu.m2
-rw-r--r--test/CodeGenObjC/bitfield_encoding.m4
-rw-r--r--test/CodeGenObjC/block-6.m2
-rw-r--r--test/CodeGenObjC/block-var-layout.m2
-rw-r--r--test/CodeGenObjC/blocks-1.m4
-rw-r--r--test/CodeGenObjC/blocks-2.m6
-rw-r--r--test/CodeGenObjC/blocks-3.m2
-rw-r--r--test/CodeGenObjC/blocks-4.m2
-rw-r--r--test/CodeGenObjC/blocks-5.m2
-rw-r--r--test/CodeGenObjC/blocks.m6
-rw-r--r--test/CodeGenObjC/boxing.m95
-rw-r--r--test/CodeGenObjC/builtins.m2
-rw-r--r--test/CodeGenObjC/category-class.m2
-rw-r--r--test/CodeGenObjC/class-type.m6
-rw-r--r--test/CodeGenObjC/constant-string-class.m2
-rw-r--r--test/CodeGenObjC/constant-strings.m4
-rw-r--r--test/CodeGenObjC/deadcode_strip_used_var.m4
-rw-r--r--test/CodeGenObjC/debug-info-block-helper.m4
-rw-r--r--test/CodeGenObjC/debug-info-crash.m2
-rw-r--r--test/CodeGenObjC/debug-info-static-var.m2
-rw-r--r--test/CodeGenObjC/debug-info-synthesis.m2
-rw-r--r--test/CodeGenObjC/encode-cstyle-method.m2
-rw-r--r--test/CodeGenObjC/encode-test-6.m19
-rw-r--r--test/CodeGenObjC/encode-test.m2
-rw-r--r--test/CodeGenObjC/exceptions.m2
-rw-r--r--test/CodeGenObjC/fp2ret.m6
-rw-r--r--test/CodeGenObjC/fpret.m6
-rw-r--r--test/CodeGenObjC/getter-property-mismatch.m20
-rw-r--r--test/CodeGenObjC/getter-property-type-mismatch.m35
-rw-r--r--test/CodeGenObjC/gnu-exceptions.m2
-rw-r--r--test/CodeGenObjC/id-isa-codegen.m4
-rw-r--r--test/CodeGenObjC/image-info.m2
-rw-r--r--test/CodeGenObjC/implicit-objc_msgSend.m2
-rw-r--r--test/CodeGenObjC/interface-layout-64.m47
-rw-r--r--test/CodeGenObjC/interface.m2
-rw-r--r--test/CodeGenObjC/ivar-layout-64-bitfields.m4
-rw-r--r--test/CodeGenObjC/ivar-layout-array0-struct.m2
-rw-r--r--test/CodeGenObjC/ivar-layout-no-optimize.m4
-rw-r--r--test/CodeGenObjC/ivars.m4
-rw-r--r--test/CodeGenObjC/layout-bitfield-crash.m35
-rw-r--r--test/CodeGenObjC/link-errors.m4
-rw-r--r--test/CodeGenObjC/local-static-block.m2
-rw-r--r--test/CodeGenObjC/messages-2.m2
-rw-r--r--test/CodeGenObjC/messages.m6
-rw-r--r--test/CodeGenObjC/metadata-symbols-32.m2
-rw-r--r--test/CodeGenObjC/misc-atomic-property.m2
-rw-r--r--test/CodeGenObjC/mrr-autorelease.m2
-rw-r--r--test/CodeGenObjC/nested-rethrow.m2
-rw-r--r--test/CodeGenObjC/next-objc-dispatch.m2
-rw-r--r--test/CodeGenObjC/no-category-class.m2
-rw-r--r--test/CodeGenObjC/nonlazy-msgSend.m2
-rw-r--r--test/CodeGenObjC/ns-constant-strings.m2
-rw-r--r--test/CodeGenObjC/objc-align.m2
-rw-r--r--test/CodeGenObjC/objc-assign-ivar.m2
-rw-r--r--test/CodeGenObjC/objc-gc-aggr-assign.m4
-rw-r--r--test/CodeGenObjC/objc-literal-debugger-test.m39
-rw-r--r--test/CodeGenObjC/objc-read-weak-byref.m4
-rw-r--r--test/CodeGenObjC/objc2-assign-global.m2
-rw-r--r--test/CodeGenObjC/objc2-new-gc-api-strongcast.m4
-rw-r--r--test/CodeGenObjC/objc2-no-write-barrier.m4
-rw-r--r--test/CodeGenObjC/objc2-retain-codegen.m4
-rw-r--r--test/CodeGenObjC/objc2-strong-cast-1.m4
-rw-r--r--test/CodeGenObjC/objc2-weak-assign.m4
-rw-r--r--test/CodeGenObjC/objc2-weak-block-call.m4
-rw-r--r--test/CodeGenObjC/objc2-weak-compare.m4
-rw-r--r--test/CodeGenObjC/objc2-weak-ivar-debug.m8
-rw-r--r--test/CodeGenObjC/objc2-weak-ivar.m4
-rw-r--r--test/CodeGenObjC/objc2-write-barrier-2.m4
-rw-r--r--test/CodeGenObjC/objc2-write-barrier-4.m4
-rw-r--r--test/CodeGenObjC/objc2-write-barrier-5.m4
-rw-r--r--test/CodeGenObjC/objc2-write-barrier.m4
-rw-r--r--test/CodeGenObjC/object-incr-decr-1.m2
-rw-r--r--test/CodeGenObjC/objfw.m15
-rw-r--r--test/CodeGenObjC/predefined-expr.m2
-rw-r--r--test/CodeGenObjC/property-complex.m4
-rw-r--r--test/CodeGenObjC/property-ref-cast-to-void.m4
-rw-r--r--test/CodeGenObjC/protocol-in-extended-class.m2
-rw-r--r--test/CodeGenObjC/protocols-lazy.m2
-rw-r--r--test/CodeGenObjC/rdr-6732143-dangling-block-reference.m2
-rw-r--r--test/CodeGenObjC/super-message-fragileabi.m2
-rw-r--r--test/CodeGenObjC/synchronized.m2
-rw-r--r--test/CodeGenObjC/terminate.m4
-rw-r--r--test/CodeGenObjC/undefined-protocol.m2
-rw-r--r--test/CodeGenObjC/variadic-sends.m4
-rw-r--r--test/CodeGenObjC/x86_64-struct-return-gc.m2
-rw-r--r--test/CodeGenObjCXX/arc.mm16
-rw-r--r--test/CodeGenObjCXX/block-in-template-inst.mm2
-rw-r--r--test/CodeGenObjCXX/block-var-layout.mm2
-rw-r--r--test/CodeGenObjCXX/blocks.mm21
-rw-r--r--test/CodeGenObjCXX/catch-id-type.mm2
-rw-r--r--test/CodeGenObjCXX/copy.mm2
-rw-r--r--test/CodeGenObjCXX/copyable-property-object.mm2
-rw-r--r--test/CodeGenObjCXX/encode.mm58
-rw-r--r--test/CodeGenObjCXX/gc.mm2
-rw-r--r--test/CodeGenObjCXX/implicit-copy-assign-operator.mm2
-rw-r--r--test/CodeGenObjCXX/implicit-copy-constructor.mm2
-rw-r--r--test/CodeGenObjCXX/lambda-expressions.mm10
-rw-r--r--test/CodeGenObjCXX/mangle-blocks.mm18
-rw-r--r--test/CodeGenObjCXX/message-reference.mm2
-rw-r--r--test/CodeGenObjCXX/nrvo.mm4
-rw-r--r--test/CodeGenObjCXX/property-derived-to-base-conv.mm2
-rw-r--r--test/CodeGenObjCXX/property-object-conditional-exp.mm2
-rw-r--r--test/CodeGenObjCXX/property-object-reference-2.mm2
-rw-r--r--test/CodeGenObjCXX/property-object-reference.mm2
-rw-r--r--test/CodeGenObjCXX/property-reference.mm2
-rw-r--r--test/CodeGenObjCXX/refence-assign-write-barrier.mm2
-rw-r--r--test/CodeGenObjCXX/selector-expr-lvalue.mm2
-rw-r--r--test/CodeGenObjCXX/write-barrier-global-assign.mm2
-rw-r--r--test/CodeGenOpenCL/kernel-arg-info.cl7
-rw-r--r--test/CodeGenOpenCL/kernel-attributes.cl12
-rw-r--r--test/CodeGenOpenCL/ptx-calls.cl2
-rw-r--r--test/CodeGenOpenCL/ptx-kernels.cl2
-rw-r--r--test/Coverage/codegen-gnu.m2
-rw-r--r--test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_dynamic.o0
-rw-r--r--test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_so.o0
-rw-r--r--test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_static.o0
-rw-r--r--test/Driver/Inputs/basic_android_tree/usr/lib/crtend_android.o0
-rw-r--r--test/Driver/Inputs/basic_android_tree/usr/lib/crtend_so.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/lib/mips-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/lib/mipsel-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mips-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mipsel-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/include/mips-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/include/mipsel-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/64/crtbegin.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/crtbegin.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/n32/crtbegin.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/64/crtbegin.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/crtbegin.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/n32/crtbegin.o0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/mips-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/debian_multiarch_tree/usr/lib/mipsel-linux-gnu/.keep0
-rw-r--r--test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/crtbegin.o1
-rw-r--r--test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/libgcc.a1
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabi/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabihf/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabi/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabihf/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/.keep0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crt1.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crti.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crtn.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crt1.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crti.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crtn.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o0
-rw-r--r--test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o0
-rw-r--r--test/Driver/altivec.cpp3
-rw-r--r--test/Driver/apple-kext-i386.cpp5
-rw-r--r--test/Driver/arc.c4
-rw-r--r--test/Driver/arclite-link.c11
-rw-r--r--test/Driver/asan-ld.c31
-rw-r--r--test/Driver/bounds-checking.c7
-rw-r--r--test/Driver/ccc-as-cpp.c6
-rw-r--r--test/Driver/clang-translation.c30
-rw-r--r--test/Driver/clang_f_opts.c9
-rw-r--r--test/Driver/clang_f_opts.h2
-rw-r--r--test/Driver/constructors.c14
-rw-r--r--test/Driver/crash-report.c27
-rw-r--r--test/Driver/darwin-ld.c5
-rw-r--r--test/Driver/darwin-objc-defaults.m18
-rw-r--r--test/Driver/darwin-objc-options.m15
-rw-r--r--test/Driver/debug-options-as.c6
-rw-r--r--test/Driver/debug-options.c49
-rw-r--r--test/Driver/debug-unsupported.c13
-rw-r--r--test/Driver/fast-math.c24
-rw-r--r--test/Driver/flags.c2
-rw-r--r--test/Driver/fpack-struct.c10
-rw-r--r--test/Driver/freebsd.c16
-rw-r--r--test/Driver/gnu-runtime.m5
-rw-r--r--test/Driver/ios-simulator-arcruntime.c8
-rw-r--r--test/Driver/linker-opts.c2
-rw-r--r--test/Driver/linux-as.c37
-rw-r--r--test/Driver/linux-ld.c130
-rw-r--r--test/Driver/mips-as.c19
-rw-r--r--test/Driver/mips-features.c39
-rw-r--r--test/Driver/mips-float.c1
-rw-r--r--test/Driver/montavista-gcc-toolchain.c9
-rw-r--r--test/Driver/msvc_forward.c5
-rw-r--r--test/Driver/nodefaultlib.c8
-rw-r--r--test/Driver/rewrite-legacy-objc.m2
-rw-r--r--test/Driver/rewrite-objc.m2
-rw-r--r--test/Driver/target-triple-deployment.c33
-rw-r--r--test/Driver/warning-options.cpp10
-rw-r--r--test/Driver/warning-options_pedantic.cpp7
-rw-r--r--test/FixIt/fixit-autoreleasepool.m9
-rw-r--r--test/FixIt/fixit-cxx0x.cpp14
-rw-r--r--test/FixIt/fixit-include.c13
-rw-r--r--test/FixIt/fixit-include.h1
-rw-r--r--test/FixIt/fixit-interface-as-param.m11
-rw-r--r--test/FixIt/fixit-objc-message-comma-separator.m17
-rw-r--r--test/FixIt/fixit-unicode.c33
-rw-r--r--test/FixIt/fixit-vexing-parse.cpp51
-rw-r--r--test/FixIt/fixit.c8
-rw-r--r--test/FixIt/fixit.cpp90
-rw-r--r--test/FixIt/format-no-fixit.m30
-rw-r--r--test/FixIt/format.m95
-rw-r--r--test/FixIt/messages.cpp13
-rw-r--r--test/FixIt/objc-literals.m16
-rw-r--r--test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/all.h1
-rw-r--r--test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/warn.h2
-rw-r--r--test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/all.h1
-rw-r--r--test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/warn.h2
-rw-r--r--test/Frontend/Inputs/SystemHeaderPrefix/src/all.h6
-rw-r--r--test/Frontend/Inputs/SystemHeaderPrefix/src/warn.h2
-rw-r--r--test/Frontend/Inputs/rewrite-includes1.h3
-rw-r--r--test/Frontend/Inputs/rewrite-includes2.h1
-rw-r--r--test/Frontend/Inputs/rewrite-includes3.h1
-rw-r--r--test/Frontend/Inputs/rewrite-includes4.h1
-rw-r--r--test/Frontend/Inputs/rewrite-includes5.h1
-rw-r--r--test/Frontend/Inputs/rewrite-includes6.h2
-rw-r--r--test/Frontend/Inputs/rewrite-includes7.h4
-rw-r--r--test/Frontend/rewrite-includes-missing.c7
-rw-r--r--test/Frontend/rewrite-includes.c145
-rw-r--r--test/Frontend/system-header-prefix.c11
-rw-r--r--test/Frontend/verify-directive.h2
-rw-r--r--test/Frontend/verify-fatal.c12
-rw-r--r--test/Frontend/verify.c125
-rw-r--r--test/Frontend/verify2.c19
-rw-r--r--test/Frontend/verify2.h5
-rw-r--r--test/Headers/ms-null-ms-header-vs-stddef.cpp16
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-01.xml7
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-02.xml9
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-03.xml11
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-04.xml11
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-05.xml10
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-06.xml5
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-07.xml13
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-08.xml13
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-09.xml13
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-10.xml11
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-11.xml13
-rw-r--r--test/Index/Inputs/CommentXML/invalid-function-12.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-class-01.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-class-02.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-class-03.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-class-04.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-enum-01.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-01.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-02.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-03.xml12
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-04.xml13
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-05.xml8
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-06.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-07.xml30
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-08.xml17
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-09.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-function-10.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-namespace-01.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-other-01.xml5
-rw-r--r--test/Index/Inputs/CommentXML/valid-typedef-01.xml6
-rw-r--r--test/Index/Inputs/CommentXML/valid-typedef-02.xml25
-rw-r--r--test/Index/Inputs/CommentXML/valid-variable-01.xml6
-rw-r--r--test/Index/Inputs/annotate-comments-preprocessor.h2
-rw-r--r--test/Index/annotate-comments-preprocessor.c45
-rw-r--r--test/Index/annotate-comments-unterminated.c13
-rw-r--r--test/Index/annotate-comments.cpp926
-rw-r--r--test/Index/availability.c10
-rw-r--r--test/Index/boxed-exprs.h10
-rw-r--r--test/Index/boxed-exprs.m19
-rw-r--r--test/Index/c-index-api-loadTU-test.m2
-rw-r--r--test/Index/comment-xml-schema.c43
-rw-r--r--test/Index/compile_commands.json27
-rw-r--r--test/Index/complete-documentation.cpp51
-rw-r--r--test/Index/complete-exprs.m10
-rw-r--r--test/Index/complete-lambdas.mm51
-rw-r--r--test/Index/complete-method-decls.m2
-rw-r--r--test/Index/complete-properties.m14
-rw-r--r--test/Index/create-tu-fail.c10
-rw-r--r--test/Index/cursor-dynamic-call.mm59
-rw-r--r--test/Index/get-cursor.c6
-rw-r--r--test/Index/get-cursor.cpp10
-rw-r--r--test/Index/get-cursor.m33
-rw-r--r--test/Index/index-decls.m17
-rw-r--r--test/Index/index-kernel-invocation.cpp4
-rw-r--r--test/Index/index-many-call-ops.cpp47
-rw-r--r--test/Index/index-refs.m18
-rw-r--r--test/Index/overrides.m58
-rw-r--r--test/Index/pch-with-errors.c4
-rw-r--r--test/Index/print-typekind.m6
-rw-r--r--test/Index/recursive-cxx-member-calls.cpp5
-rw-r--r--test/Lexer/bcpl-escaped-newline.c3
-rw-r--r--test/Lexer/c90.c7
-rw-r--r--test/Lexer/char-literal.cpp3
-rw-r--r--test/Lexer/hexfloat.cpp8
-rw-r--r--test/Lexer/newline-eof.c4
-rw-r--r--test/Lexer/pragma-operators.cpp1
-rw-r--r--test/Lexer/wchar-signedness.c8
-rw-r--r--test/Misc/ast-dump-wchar.cpp13
-rw-r--r--test/Misc/diag-aka-types.cpp23
-rw-r--r--test/Misc/diag-template-diffing-color.cpp19
-rw-r--r--test/Misc/diag-template-diffing.cpp433
-rw-r--r--test/Misc/diag-trailing-null-bytes.cpp10
-rw-r--r--test/Misc/emit-html.c8
-rw-r--r--test/Misc/integer-literal-printing.cpp4
-rw-r--r--test/Misc/serialized-diags-frontend.c8
-rw-r--r--test/Misc/show-diag-options.c2
-rw-r--r--test/Misc/tabstop.c9
-rw-r--r--test/Misc/warning-flags-enabled.c27
-rw-r--r--test/Misc/warning-flags-tree.c56
-rw-r--r--test/Misc/warning-flags.c103
-rw-r--r--test/Misc/wnull-character.cppbin143 -> 0 bytes
-rw-r--r--test/Modules/Inputs/Module.framework/Headers/Module.h6
-rw-r--r--test/Modules/Inputs/category_right.h2
-rw-r--r--test/Modules/Inputs/import-decl.h1
-rw-r--r--test/Modules/Inputs/module.map3
-rw-r--r--test/Modules/cstd.m2
-rw-r--r--test/Modules/import-decl.cpp10
-rw-r--r--test/Modules/lookup.cpp2
-rw-r--r--test/Modules/objc-categories.m1
-rw-r--r--test/Modules/on-demand-build.m5
-rw-r--r--test/PCH/Inputs/__va_list_tag.h5
-rw-r--r--test/PCH/__va_list_tag.c12
-rw-r--r--test/PCH/attrs.c3
-rw-r--r--test/PCH/badpch-dir.h.gch/.keep0
-rw-r--r--test/PCH/badpch-empty.h.gch0
-rw-r--r--test/PCH/badpch.c13
-rw-r--r--test/PCH/chain-staticvar-instantiation.cpp45
-rw-r--r--test/PCH/chain-trivial.c2
-rw-r--r--test/PCH/cxx-friends.cpp8
-rw-r--r--test/PCH/cxx-friends.h12
-rw-r--r--test/PCH/cxx-member-init.cpp4
-rw-r--r--test/PCH/cxx-static_assert.cpp3
-rw-r--r--test/PCH/cxx-trailing-return.cpp6
-rw-r--r--test/PCH/cxx0x-default-delete.cpp12
-rw-r--r--test/PCH/cxx0x-delegating-ctors.cpp12
-rw-r--r--test/PCH/cxx11-constexpr.cpp6
-rw-r--r--test/PCH/cxx11-enum-template.cpp4
-rw-r--r--test/PCH/cxx11-user-defined-literals.cpp3
-rw-r--r--test/PCH/empty-with-headers.c27
-rw-r--r--test/PCH/exprs.h2
-rw-r--r--test/PCH/format-strings.c18
-rw-r--r--test/PCH/ms-if-exists.cpp3
-rw-r--r--test/PCH/objc_methods.h30
-rw-r--r--test/PCH/objc_methods.m2
-rw-r--r--test/PCH/objc_stmts.m4
-rw-r--r--test/PCH/pch__VA_ARGS__.c6
-rw-r--r--test/PCH/pch__VA_ARGS__.h2
-rw-r--r--test/PCH/remap-file-from-pch.cpp10
-rw-r--r--test/PCH/remap-file-from-pch.cpp.h2
-rw-r--r--test/PCH/remap-file-from-pch.cpp.remap.h4
-rw-r--r--test/PCH/replaced-decl.m3
-rw-r--r--test/PCH/typo2.cpp3
-rw-r--r--test/PCH/variables.c12
-rw-r--r--test/Parser/DelayedTemplateParsing.cpp13
-rw-r--r--test/Parser/MicrosoftExtensions.c43
-rw-r--r--test/Parser/MicrosoftExtensions.cpp60
-rw-r--r--test/Parser/altivec.c10
-rw-r--r--test/Parser/c1x-alignas.c8
-rw-r--r--test/Parser/completely-empty-header-file.h0
-rw-r--r--test/Parser/cuda-kernel-call.cu7
-rw-r--r--test/Parser/cxx-class.cpp8
-rw-r--r--test/Parser/cxx-decl.cpp6
-rw-r--r--test/Parser/cxx-extra-semi.cpp41
-rw-r--r--test/Parser/cxx-template-argument.cpp15
-rw-r--r--test/Parser/cxx-throw.cpp2
-rw-r--r--test/Parser/cxx-undeclared-identifier.cpp12
-rw-r--r--test/Parser/cxx-using-declaration.cpp16
-rw-r--r--test/Parser/cxx-using-directive.cpp5
-rw-r--r--test/Parser/cxx0x-ambig.cpp6
-rw-r--r--test/Parser/cxx0x-attributes.cpp135
-rw-r--r--test/Parser/cxx0x-decl.cpp16
-rw-r--r--test/Parser/cxx0x-lambda-expressions.cpp7
-rw-r--r--test/Parser/cxx11-type-specifier.cpp4
-rw-r--r--test/Parser/declarators.c12
-rw-r--r--test/Parser/empty-translation-unit.c10
-rw-r--r--test/Parser/missing-selector-name.mm52
-rw-r--r--test/Parser/ms-inline-asm.c33
-rw-r--r--test/Parser/objc-boxing.m26
-rw-r--r--test/Parser/objc-diag-width.mm8
-rw-r--r--test/Parser/objc-forcollection-neg.m4
-rw-r--r--test/Parser/objc-init.m4
-rw-r--r--test/Parser/objc-recover.mm64
-rw-r--r--test/Parser/objcxx11-attributes.mm3
-rw-r--r--test/Parser/opencl-pragma.cl2
-rw-r--r--test/Parser/recovery.c5
-rw-r--r--test/Parser/recovery.cpp12
-rw-r--r--test/Preprocessor/_Pragma-location.c49
-rw-r--r--test/Preprocessor/comment_save.c14
-rw-r--r--test/Preprocessor/disabled-cond-diags2.c27
-rw-r--r--test/Preprocessor/has_attribute.c10
-rw-r--r--test/Preprocessor/has_include.c48
-rw-r--r--test/Preprocessor/init.c440
-rw-r--r--test/Preprocessor/line-directive.c14
-rw-r--r--test/Preprocessor/macro_fn.c18
-rw-r--r--test/Preprocessor/macro_paste_c_block_comment.c4
-rw-r--r--test/Preprocessor/macro_paste_identifier_error.c7
-rw-r--r--test/Preprocessor/mmx.c3
-rw-r--r--test/Preprocessor/non_fragile_feature1.m2
-rw-r--r--test/Preprocessor/pp-record.c11
-rw-r--r--test/Preprocessor/predefined-arch-macros.c205
-rw-r--r--test/Preprocessor/predefined-macros.c13
-rw-r--r--test/Preprocessor/undef-error.c2
-rw-r--r--test/Preprocessor/warning_tests.c8
-rw-r--r--test/Rewriter/blockcast3.mm2
-rw-r--r--test/Rewriter/blockstruct.m2
-rw-r--r--test/Rewriter/crash.m2
-rw-r--r--test/Rewriter/finally.m2
-rw-r--r--test/Rewriter/func-in-impl.m2
-rw-r--r--test/Rewriter/id-test-3.m2
-rw-r--r--test/Rewriter/inner-block-helper-funcs.mm2
-rw-r--r--test/Rewriter/instancetype-test.mm2
-rw-r--r--test/Rewriter/ivar-encoding-1.m2
-rw-r--r--test/Rewriter/ivar-encoding-2.m2
-rw-r--r--test/Rewriter/metadata-test-1.m2
-rw-r--r--test/Rewriter/metadata-test-2.m2
-rw-r--r--test/Rewriter/method-encoding-1.m2
-rw-r--r--test/Rewriter/objc-bool-literal-modern-1.mm3
-rw-r--r--test/Rewriter/objc-encoding-bug-1.m2
-rw-r--r--test/Rewriter/objc-ivar-receiver-1.m4
-rw-r--r--test/Rewriter/objc-modern-StretAPI.mm45
-rw-r--r--test/Rewriter/objc-modern-boxing.mm72
-rw-r--r--test/Rewriter/objc-modern-container-subscript.mm3
-rw-r--r--test/Rewriter/objc-modern-getclass-proto.mm10
-rw-r--r--test/Rewriter/objc-modern-property-attributes.mm10
-rw-r--r--test/Rewriter/objc-string-concat-1.m2
-rw-r--r--test/Rewriter/objc-super-test.m2
-rw-r--r--test/Rewriter/objc-synchronized-1.m2
-rw-r--r--test/Rewriter/properties.m2
-rw-r--r--test/Rewriter/property-dot-syntax.mm2
-rw-r--r--test/Rewriter/protocol-rewrite-1.m2
-rw-r--r--test/Rewriter/protocol-rewrite-2.m2
-rw-r--r--test/Rewriter/rewrite-anonymous-union.m2
-rw-r--r--test/Rewriter/rewrite-api-bug.m2
-rw-r--r--test/Rewriter/rewrite-block-argument.m2
-rw-r--r--test/Rewriter/rewrite-block-consts.mm2
-rw-r--r--test/Rewriter/rewrite-block-ivar-call.mm2
-rw-r--r--test/Rewriter/rewrite-block-literal-1.mm5
-rw-r--r--test/Rewriter/rewrite-block-literal.mm5
-rw-r--r--test/Rewriter/rewrite-block-pointer.mm5
-rw-r--r--test/Rewriter/rewrite-block-property.m2
-rw-r--r--test/Rewriter/rewrite-byref-in-nested-blocks.mm9
-rw-r--r--test/Rewriter/rewrite-byref-vars.mm4
-rw-r--r--test/Rewriter/rewrite-cast-ivar-access.mm2
-rw-r--r--test/Rewriter/rewrite-cast-to-bool.mm2
-rw-r--r--test/Rewriter/rewrite-category-property.mm2
-rw-r--r--test/Rewriter/rewrite-constructor-init.mm2
-rw-r--r--test/Rewriter/rewrite-eh.m2
-rw-r--r--test/Rewriter/rewrite-elaborated-type.mm2
-rw-r--r--test/Rewriter/rewrite-extern-c.mm2
-rw-r--r--test/Rewriter/rewrite-foreach-1.m2
-rw-r--r--test/Rewriter/rewrite-foreach-2.m2
-rw-r--r--test/Rewriter/rewrite-foreach-3.m2
-rw-r--r--test/Rewriter/rewrite-foreach-4.m2
-rw-r--r--test/Rewriter/rewrite-foreach-5.m2
-rw-r--r--test/Rewriter/rewrite-foreach-6.m2
-rw-r--r--test/Rewriter/rewrite-foreach-7.m2
-rw-r--r--test/Rewriter/rewrite-foreach-in-block.mm4
-rw-r--r--test/Rewriter/rewrite-foreach-protocol-id.m2
-rw-r--r--test/Rewriter/rewrite-forward-class.m2
-rw-r--r--test/Rewriter/rewrite-forward-class.mm2
-rw-r--r--test/Rewriter/rewrite-function-decl.mm2
-rw-r--r--test/Rewriter/rewrite-implementation.mm2
-rw-r--r--test/Rewriter/rewrite-ivar-use.m2
-rw-r--r--test/Rewriter/rewrite-local-externs-in-block.mm2
-rw-r--r--test/Rewriter/rewrite-local-static-id.mm2
-rw-r--r--test/Rewriter/rewrite-message-expr.mm2
-rw-r--r--test/Rewriter/rewrite-modern-atautoreleasepool.mm32
-rw-r--r--test/Rewriter/rewrite-modern-block-consts.mm22
-rw-r--r--test/Rewriter/rewrite-modern-block.mm44
-rw-r--r--test/Rewriter/rewrite-modern-captured-nested-bvar.mm35
-rw-r--r--test/Rewriter/rewrite-modern-default-property-synthesis.mm80
-rw-r--r--test/Rewriter/rewrite-modern-extern-c-func-decl.mm49
-rw-r--r--test/Rewriter/rewrite-modern-ivar-access.mm98
-rw-r--r--test/Rewriter/rewrite-modern-ivars-1.mm37
-rw-r--r--test/Rewriter/rewrite-modern-private-ivars.mm53
-rw-r--r--test/Rewriter/rewrite-modern-struct-ivar-1.mm48
-rw-r--r--test/Rewriter/rewrite-modern-struct-ivar.mm32
-rw-r--r--test/Rewriter/rewrite-modern-typeof.mm1
-rw-r--r--test/Rewriter/rewrite-nest.m2
-rw-r--r--test/Rewriter/rewrite-nested-blocks-1.mm3
-rw-r--r--test/Rewriter/rewrite-nested-blocks-2.mm3
-rw-r--r--test/Rewriter/rewrite-nested-blocks.mm3
-rw-r--r--test/Rewriter/rewrite-nested-ivar.mm2
-rwxr-xr-xtest/Rewriter/rewrite-nested-property-in-blocks.mm3
-rw-r--r--test/Rewriter/rewrite-no-nextline.mm2
-rw-r--r--test/Rewriter/rewrite-property-attributes.mm2
-rw-r--r--test/Rewriter/rewrite-property-set-cfstring.mm2
-rw-r--r--test/Rewriter/rewrite-protocol-property.mm2
-rw-r--r--test/Rewriter/rewrite-protocol-qualified.mm2
-rw-r--r--test/Rewriter/rewrite-protocol-type-1.m2
-rw-r--r--test/Rewriter/rewrite-qualified-id.mm2
-rw-r--r--test/Rewriter/rewrite-rewritten-initializer.mm7
-rw-r--r--test/Rewriter/rewrite-static-block.mm2
-rw-r--r--test/Rewriter/rewrite-super-message.mm2
-rw-r--r--test/Rewriter/rewrite-trivial-constructor.mm2
-rw-r--r--test/Rewriter/rewrite-try-catch.m2
-rw-r--r--test/Rewriter/rewrite-typeof.mm2
-rw-r--r--test/Rewriter/rewrite-unique-block-api.mm3
-rw-r--r--test/Rewriter/rewrite-user-defined-accessors.mm2
-rw-r--r--test/Rewriter/rewrite-vararg.m2
-rw-r--r--test/Rewriter/rewrite-weak-attr.m2
-rw-r--r--test/Rewriter/static-type-protocol-1.m2
-rw-r--r--test/Rewriter/undecl-objc-h.m2
-rw-r--r--test/Rewriter/undeclared-method-1.m2
-rw-r--r--test/Rewriter/undef-field-reference-1.m2
-rw-r--r--test/Rewriter/va-method.m2
-rw-r--r--test/Rewriter/weak_byref_objects.m2
-rw-r--r--test/Sema/128bitint.c9
-rw-r--r--test/Sema/Inputs/format-unused-system-args.h8
-rw-r--r--test/Sema/MicrosoftCompatibility.c7
-rw-r--r--test/Sema/MicrosoftExtensions.c8
-rw-r--r--test/Sema/alignas.c13
-rw-r--r--test/Sema/alloc_size.c26
-rw-r--r--test/Sema/annotate.c5
-rw-r--r--test/Sema/array-bounds-ptr-arith.c18
-rw-r--r--test/Sema/array-init.c2
-rw-r--r--test/Sema/attr-aligned.c2
-rw-r--r--test/Sema/attr-availability-ios.c8
-rw-r--r--test/Sema/attr-availability-macosx.c2
-rw-r--r--test/Sema/attr-availability.c21
-rw-r--r--test/Sema/attr-coldhot.c10
-rw-r--r--test/Sema/attr-decl-after-definition.c22
-rw-r--r--test/Sema/attr-deprecated-message.c8
-rw-r--r--test/Sema/attr-deprecated.c29
-rw-r--r--test/Sema/attr-nodebug.c7
-rw-r--r--test/Sema/attr-section.c4
-rw-r--r--test/Sema/attr-tls_model.c14
-rw-r--r--test/Sema/attr-unavailable-message.c4
-rw-r--r--test/Sema/attr-visibility.c15
-rw-r--r--test/Sema/c89-2.c2
-rw-r--r--test/Sema/compare.c7
-rw-r--r--test/Sema/conditional-expr.c2
-rw-r--r--test/Sema/const-eval.c3
-rw-r--r--test/Sema/dllimport-dllexport.c6
-rw-r--r--test/Sema/exprs.c15
-rw-r--r--test/Sema/format-strings-enum-fixed-type.cpp92
-rw-r--r--test/Sema/format-strings-enum.c36
-rw-r--r--test/Sema/format-strings-fixit.c34
-rw-r--r--test/Sema/format-strings-scanf.c55
-rw-r--r--test/Sema/format-strings-size_t.c13
-rw-r--r--test/Sema/format-strings.c117
-rw-r--r--test/Sema/fpack-struct.c4
-rw-r--r--test/Sema/implicit-builtin-decl.c2
-rw-r--r--test/Sema/inline.c72
-rw-r--r--test/Sema/invalid-decl.c9
-rw-r--r--test/Sema/knr-def-call.c4
-rw-r--r--test/Sema/ms_class_layout.cpp173
-rw-r--r--test/Sema/ms_wide_predefined_expr.cpp25
-rw-r--r--test/Sema/pragma-pack-6.c16
-rw-r--r--test/Sema/switch.c31
-rw-r--r--test/Sema/thread-specifier.c12
-rw-r--r--test/Sema/tls.c20
-rw-r--r--test/Sema/typeof-use-deprecated.c12
-rw-r--r--test/Sema/uninit-variables.c104
-rw-r--r--test/Sema/unused-expr.c3
-rw-r--r--test/Sema/vector-ops.c2
-rw-r--r--test/Sema/warn-documentation-almost-trailing.c14
-rw-r--r--test/Sema/warn-documentation-fixits.cpp27
-rw-r--r--test/Sema/warn-documentation.cpp670
-rw-r--r--test/Sema/warn-documentation.m93
-rw-r--r--test/Sema/warn-outof-range-assign-enum.c32
-rw-r--r--test/Sema/warn-self-assign-field.mm66
-rw-r--r--test/Sema/warn-strncat-size.c7
-rw-r--r--test/SemaCXX/MicrosoftCompatibility.cpp4
-rw-r--r--test/SemaCXX/MicrosoftCompatibilityNoExceptions.cpp8
-rw-r--r--test/SemaCXX/PR10243.cpp4
-rw-r--r--test/SemaCXX/abstract.cpp14
-rw-r--r--test/SemaCXX/address-of-temporary.cpp40
-rw-r--r--test/SemaCXX/alias-template.cpp27
-rw-r--r--test/SemaCXX/altivec.cpp7
-rw-r--r--test/SemaCXX/anonymous-union-cxx11.cpp13
-rw-r--r--test/SemaCXX/array-bound-merge.cpp2
-rw-r--r--test/SemaCXX/arrow-operator.cpp28
-rw-r--r--test/SemaCXX/attr-deprecated.cpp42
-rw-r--r--test/SemaCXX/attr-visibility.cpp20
-rw-r--r--test/SemaCXX/bool.cpp2
-rw-r--r--test/SemaCXX/condition.cpp2
-rw-r--r--test/SemaCXX/conditional-expr.cpp26
-rw-r--r--test/SemaCXX/constant-expression-cxx11.cpp130
-rw-r--r--test/SemaCXX/constant-expression.cpp9
-rw-r--r--test/SemaCXX/constexpr-many-arguments.cpp42
-rw-r--r--test/SemaCXX/constructor-initializer.cpp13
-rw-r--r--test/SemaCXX/conversion-function.cpp11
-rw-r--r--test/SemaCXX/conversion.cpp60
-rw-r--r--test/SemaCXX/crashes.cpp32
-rw-r--r--test/SemaCXX/cxx0x-cursory-default-delete.cpp21
-rw-r--r--test/SemaCXX/cxx0x-defaulted-functions.cpp110
-rw-r--r--test/SemaCXX/cxx0x-initializer-aggregates.cpp29
-rw-r--r--test/SemaCXX/cxx0x-initializer-constructor.cpp23
-rw-r--r--test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp12
-rw-r--r--test/SemaCXX/cxx98-compat-pedantic.cpp2
-rw-r--r--test/SemaCXX/cxx98-compat.cpp61
-rw-r--r--test/SemaCXX/dcl_ambig_res.cpp6
-rw-r--r--test/SemaCXX/dcl_init_aggr.cpp2
-rw-r--r--test/SemaCXX/decl-expr-ambiguity.cpp33
-rw-r--r--test/SemaCXX/default1.cpp9
-rw-r--r--test/SemaCXX/deleted-function.cpp10
-rw-r--r--test/SemaCXX/deleted-operator.cpp4
-rw-r--r--test/SemaCXX/elaborated-type-specifier.cpp7
-rw-r--r--test/SemaCXX/enum-scoped.cpp7
-rw-r--r--test/SemaCXX/expressions.cpp2
-rw-r--r--test/SemaCXX/format-strings-0x.cpp12
-rw-r--r--test/SemaCXX/function-extern-c.cpp2
-rw-r--r--test/SemaCXX/function-redecl.cpp28
-rw-r--r--test/SemaCXX/implicit-exception-spec.cpp17
-rw-r--r--test/SemaCXX/invalid-member-expr.cpp31
-rw-r--r--test/SemaCXX/lambda-expressions.cpp141
-rw-r--r--test/SemaCXX/literal-operators.cpp1
-rw-r--r--test/SemaCXX/long-virtual-inheritance-chain.cpp53
-rw-r--r--test/SemaCXX/member-expr.cpp10
-rw-r--r--test/SemaCXX/member-init.cpp4
-rw-r--r--test/SemaCXX/member-operator-expr.cpp5
-rw-r--r--test/SemaCXX/microsoft-cxx0x.cpp14
-rw-r--r--test/SemaCXX/neon-vector-types.cpp19
-rw-r--r--test/SemaCXX/nested-name-spec.cpp14
-rw-r--r--test/SemaCXX/no-rtti.cpp10
-rw-r--r--test/SemaCXX/nullptr.cpp4
-rw-r--r--test/SemaCXX/offsetof-0x.cpp20
-rw-r--r--test/SemaCXX/overload-call.cpp34
-rw-r--r--test/SemaCXX/overload-member-call.cpp13
-rw-r--r--test/SemaCXX/overloaded-builtin-operators.cpp33
-rw-r--r--test/SemaCXX/pr13353.cpp13
-rw-r--r--test/SemaCXX/pr13394-crash-on-invalid.cpp16
-rw-r--r--test/SemaCXX/printf-block.cpp18
-rw-r--r--test/SemaCXX/printf-cstr.cpp53
-rw-r--r--test/SemaCXX/qualified-id-lookup.cpp5
-rw-r--r--test/SemaCXX/reinterpret-cast.cpp2
-rw-r--r--test/SemaCXX/static-assert.cpp14
-rw-r--r--test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp119
-rw-r--r--test/SemaCXX/switch-implicit-fallthrough-per-method.cpp51
-rw-r--r--test/SemaCXX/switch-implicit-fallthrough.cpp197
-rw-r--r--test/SemaCXX/type-traits.cpp35
-rw-r--r--test/SemaCXX/typo-correction.cpp44
-rw-r--r--test/SemaCXX/uninit-variables.cpp6
-rw-r--r--test/SemaCXX/uninitialized.cpp165
-rw-r--r--test/SemaCXX/unknown-type-name.cpp56
-rw-r--r--test/SemaCXX/unused.cpp48
-rw-r--r--test/SemaCXX/user-defined-conversions.cpp2
-rw-r--r--test/SemaCXX/virtuals.cpp2
-rw-r--r--test/SemaCXX/warn-deprecated-header.cpp3
-rw-r--r--test/SemaCXX/warn-literal-conversion.cpp28
-rw-r--r--test/SemaCXX/warn-loop-analysis.cpp154
-rw-r--r--test/SemaCXX/warn-memset-bad-sizeof.cpp24
-rw-r--r--test/SemaCXX/warn-static-function-inheader.cpp12
-rw-r--r--test/SemaCXX/warn-static-function-inheader.h3
-rw-r--r--test/SemaCXX/warn-thread-safety-analysis.cpp1008
-rw-r--r--test/SemaCXX/warn-thread-safety-parsing.cpp857
-rw-r--r--test/SemaCXX/warn-unique-enum.cpp27
-rw-r--r--test/SemaCXX/warn-unused-private-field.cpp246
-rw-r--r--test/SemaCXX/warn-unused-value.cpp2
-rw-r--r--test/SemaObjC/arc-bridged-cast.m26
-rw-r--r--test/SemaObjC/arc-cf.m10
-rw-r--r--test/SemaObjC/arc-dict-bridged-cast.m46
-rw-r--r--test/SemaObjC/arc-no-runtime.m2
-rw-r--r--test/SemaObjC/arc-property-lifetime.m5
-rw-r--r--test/SemaObjC/arc-property.m10
-rw-r--r--test/SemaObjC/arc-unbridged-cast.m8
-rw-r--r--test/SemaObjC/arc.m23
-rw-r--r--test/SemaObjC/assign-rvalue-message.m2
-rw-r--r--test/SemaObjC/at-defs.m2
-rw-r--r--test/SemaObjC/attr-availability.m16
-rw-r--r--test/SemaObjC/attr-deprecated.m27
-rw-r--r--test/SemaObjC/blocks.m121
-rw-r--r--test/SemaObjC/boxing-illegal-types.m58
-rw-r--r--test/SemaObjC/category-1.m9
-rw-r--r--test/SemaObjC/class-bitfield.m2
-rw-r--r--test/SemaObjC/cocoa-api-usage.m8
-rw-r--r--test/SemaObjC/cocoa-api-usage.m.fixed8
-rw-r--r--test/SemaObjC/conflicting-ivar-test-1.m2
-rw-r--r--test/SemaObjC/continuation-class-err.m10
-rw-r--r--test/SemaObjC/continuation-class-property.m4
-rw-r--r--test/SemaObjC/dealloc.m25
-rw-r--r--test/SemaObjC/default-synthesize-1.m30
-rw-r--r--test/SemaObjC/default-synthesize-2.m19
-rw-r--r--test/SemaObjC/delay-parsing-cfunctions.m43
-rw-r--r--test/SemaObjC/direct-synthesized-ivar-access.m2
-rw-r--r--test/SemaObjC/duplicate-property-class-extension.m2
-rw-r--r--test/SemaObjC/error-implicit-property.m2
-rw-r--r--test/SemaObjC/format-strings-objc.m57
-rw-r--r--test/SemaObjC/format-strings-system.h10
-rw-r--r--test/SemaObjC/getter-setter-defined-in-category-of-parent.m26
-rw-r--r--test/SemaObjC/iboutlet.m23
-rw-r--r--test/SemaObjC/id.m12
-rw-r--r--test/SemaObjC/interface-1.m2
-rw-r--r--test/SemaObjC/interface-layout.m2
-rw-r--r--test/SemaObjC/ivar-in-class-extension-error.m2
-rw-r--r--test/SemaObjC/method-bad-param.m2
-rw-r--r--test/SemaObjC/method-prototype-scope.m4
-rw-r--r--test/SemaObjC/mismatched-undefined-method.m13
-rw-r--r--test/SemaObjC/narrow-property-type-in-cont-class.m2
-rw-r--r--test/SemaObjC/no-ivar-in-interface-block.m32
-rw-r--r--test/SemaObjC/nowarn-superclass-method-mismatch.m20
-rw-r--r--test/SemaObjC/nsobject-attribute.m12
-rw-r--r--test/SemaObjC/objc-container-subscripting-2.m4
-rw-r--r--test/SemaObjC/objc-container-subscripting-3.m2
-rw-r--r--test/SemaObjC/objc-container-subscripting.m8
-rw-r--r--test/SemaObjC/objc-cstyle-args-in-methods.m2
-rw-r--r--test/SemaObjC/objc-dictionary-literal.m2
-rw-r--r--test/SemaObjC/objc-literal-comparison.m96
-rw-r--r--test/SemaObjC/objc-literal-nsnumber.m4
-rw-r--r--test/SemaObjC/objc-literal-sig.m17
-rw-r--r--test/SemaObjC/property-10.m2
-rw-r--r--test/SemaObjC/property-12.m37
-rw-r--r--test/SemaObjC/property-impl-misuse.m17
-rw-r--r--test/SemaObjC/property-ivar-mismatch.m12
-rw-r--r--test/SemaObjC/property-typecheck-1.m8
-rw-r--r--test/SemaObjC/property-user-setter.m52
-rw-r--r--test/SemaObjC/property.m4
-rw-r--r--test/SemaObjC/protocol-attribute.m2
-rw-r--r--test/SemaObjC/protocols.m2
-rw-r--r--test/SemaObjC/provisional-ivar-lookup.m2
-rw-r--r--test/SemaObjC/related-result-type-inference.m8
-rw-r--r--test/SemaObjC/sizeof-interface.m18
-rw-r--r--test/SemaObjC/special-dep-unavail-warning.m2
-rw-r--r--test/SemaObjC/tentative-property-decl.m47
-rw-r--r--test/SemaObjC/unused.m2
-rw-r--r--test/SemaObjC/warn-deprecated-implementations.m2
-rw-r--r--test/SemaObjC/warn-direct-ivar-access.m56
-rw-r--r--test/SemaObjC/warn-isa-ref.m (renamed from test/SemaObjC/id-isa-ref.m)48
-rw-r--r--test/SemaObjC/warn-protocol-method-deprecated.m32
-rw-r--r--test/SemaObjC/warn-retain-block-property.m (renamed from test/SemaObjC/arc-retain-block-property.m)2
-rw-r--r--test/SemaObjC/weak-receiver-warn.m65
-rw-r--r--test/SemaObjCXX/arc-0x.mm32
-rw-r--r--test/SemaObjCXX/arc-templates.mm17
-rw-r--r--test/SemaObjCXX/arc-type-traits.mm126
-rw-r--r--test/SemaObjCXX/arc-unbridged-cast.mm8
-rw-r--r--test/SemaObjCXX/boxing-illegal-types.mm58
-rw-r--r--test/SemaObjCXX/crash.mm23
-rw-r--r--test/SemaObjCXX/delay-parsing-cfunctions.mm48
-rw-r--r--test/SemaObjCXX/delay-parsing-cplusfuncs.mm52
-rw-r--r--test/SemaObjCXX/delay-parsing-func-tryblock.mm66
-rw-r--r--test/SemaObjCXX/exceptions-fragile.mm2
-rw-r--r--test/SemaObjCXX/foreach.mm61
-rw-r--r--test/SemaObjCXX/fragile-abi-object-assign.m2
-rw-r--r--test/SemaObjCXX/instantiate-stmt.mm8
-rw-r--r--test/SemaObjCXX/message.mm2
-rw-r--r--test/SemaObjCXX/objc-container-subscripting.mm4
-rw-r--r--test/SemaObjCXX/property-synthesis-error.mm13
-rw-r--r--test/SemaObjCXX/protocol-lookup.mm2
-rw-r--r--test/SemaObjCXX/warn-missing-super.mm19
-rw-r--r--test/SemaOpenCL/storageclass.cl12
-rw-r--r--test/SemaOpenCL/warn-missing-prototypes.cl6
-rw-r--r--test/SemaTemplate/alias-templates.cpp76
-rw-r--r--test/SemaTemplate/class-template-decl.cpp42
-rw-r--r--test/SemaTemplate/delegating-constructors.cpp18
-rw-r--r--test/SemaTemplate/dependent-base-classes.cpp3
-rw-r--r--test/SemaTemplate/explicit-instantiation.cpp4
-rw-r--r--test/SemaTemplate/friend-template.cpp59
-rw-r--r--test/SemaTemplate/instantiate-exception-spec-cxx11.cpp14
-rw-r--r--test/SemaTemplate/instantiate-expr-1.cpp2
-rw-r--r--test/SemaTemplate/instantiate-init.cpp15
-rw-r--r--test/SemaTemplate/instantiate-member-class.cpp7
-rw-r--r--test/SemaTemplate/instantiate-method.cpp7
-rw-r--r--test/SemaTemplate/instantiate-objc-1.mm21
-rw-r--r--test/SemaTemplate/instantiation-backtrace.cpp19
-rw-r--r--test/SemaTemplate/instantiation-depth-defarg.cpp36
-rw-r--r--test/SemaTemplate/instantiation-depth-subst-2.cpp9
-rw-r--r--test/SemaTemplate/instantiation-depth-subst.cpp9
-rw-r--r--test/SemaTemplate/instantiation-depth.cpp20
-rw-r--r--test/SemaTemplate/member-template-access-expr.cpp2
-rw-r--r--test/SemaTemplate/ms-function-specialization-class-scope.cpp21
-rw-r--r--test/SemaTemplate/ms-lookup-template-base-classes.cpp29
-rw-r--r--test/SemaTemplate/overload-candidates.cpp26
-rw-r--r--test/SemaTemplate/recovery-crash.cpp3
-rw-r--r--test/SemaTemplate/typename-specifier.cpp41
-rw-r--r--test/Tooling/Inputs/lit.local.cfg1
-rw-r--r--test/Tooling/Inputs/pch-fail.h0
-rw-r--r--test/Tooling/Inputs/pch.cpp0
-rw-r--r--test/Tooling/Inputs/pch.h0
-rw-r--r--test/Tooling/auto-detect-from-source-parent-of-cwd.cpp10
-rw-r--r--test/Tooling/auto-detect-from-source-parent.cpp10
-rw-r--r--test/Tooling/auto-detect-from-source.cpp10
-rw-r--r--test/Tooling/clang-check-args.cpp7
-rw-r--r--test/Tooling/clang-check-ast-dump.cpp35
-rw-r--r--test/Tooling/clang-check-autodetect-dir.cpp11
-rw-r--r--test/Tooling/clang-check-builtin-headers.cpp15
-rw-r--r--test/Tooling/clang-check-chdir.cpp17
-rw-r--r--test/Tooling/clang-check-pwd.cpp8
-rw-r--r--test/Tooling/clang-check.cpp9
-rw-r--r--test/Tooling/multi-jobs.cpp7
-rw-r--r--test/Tooling/pch.cpp17
-rw-r--r--test/lit.cfg8
-rw-r--r--tools/CMakeLists.txt9
-rw-r--r--tools/Makefile6
-rw-r--r--tools/arcmt-test/CMakeLists.txt16
-rw-r--r--tools/arcmt-test/Makefile6
-rw-r--r--tools/c-arcmt-test/CMakeLists.txt6
-rw-r--r--tools/c-arcmt-test/Makefile7
-rw-r--r--tools/c-index-test/CMakeLists.txt13
-rw-r--r--tools/c-index-test/Makefile17
-rw-r--r--tools/c-index-test/c-index-test.c606
-rw-r--r--tools/clang-check/CMakeLists.txt15
-rw-r--r--tools/clang-check/ClangCheck.cpp104
-rw-r--r--tools/clang-check/Makefile4
-rwxr-xr-xtools/diag-build/diag-build.sh115
-rw-r--r--tools/diagtool/CMakeLists.txt28
-rw-r--r--tools/diagtool/DiagnosticNames.cpp78
-rw-r--r--tools/diagtool/DiagnosticNames.h128
-rw-r--r--tools/diagtool/ListWarnings.cpp37
-rw-r--r--tools/diagtool/Makefile10
-rw-r--r--tools/diagtool/ShowEnabledWarnings.cpp148
-rw-r--r--tools/diagtool/TreeView.cpp135
-rw-r--r--tools/diagtool/diagtool_main.cpp2
-rw-r--r--tools/driver/CMakeLists.txt40
-rw-r--r--tools/driver/cc1_main.cpp4
-rw-r--r--tools/driver/cc1as_main.cpp4
-rw-r--r--tools/driver/driver.cpp16
-rw-r--r--tools/libclang/CIndex.cpp221
-rw-r--r--tools/libclang/CIndexCodeCompletion.cpp25
-rw-r--r--tools/libclang/CIndexDiagnostic.cpp30
-rw-r--r--tools/libclang/CIndexUSRs.cpp2
-rw-r--r--tools/libclang/CMakeLists.txt76
-rw-r--r--tools/libclang/CXComment.cpp1230
-rw-r--r--tools/libclang/CXComment.h47
-rw-r--r--tools/libclang/CXCompilationDatabase.cpp129
-rw-r--r--tools/libclang/CXCursor.cpp244
-rw-r--r--tools/libclang/CXCursor.h11
-rw-r--r--tools/libclang/CXStoredDiagnostic.cpp7
-rw-r--r--tools/libclang/CXTranslationUnit.h1
-rw-r--r--tools/libclang/CXType.cpp18
-rw-r--r--tools/libclang/IndexBody.cpp16
-rw-r--r--tools/libclang/IndexDecl.cpp15
-rw-r--r--tools/libclang/IndexTypeSourceInfo.cpp4
-rw-r--r--tools/libclang/Indexing.cpp10
-rw-r--r--tools/libclang/IndexingContext.h6
-rw-r--r--tools/libclang/Makefile7
-rw-r--r--tools/libclang/RecursiveASTVisitor.h2185
-rw-r--r--tools/libclang/libclang.exports50
-rwxr-xr-xtools/scan-build/ccc-analyzer21
-rwxr-xr-xtools/scan-build/scan-build213
-rw-r--r--tools/scan-build/scan-build.1348
-rwxr-xr-xtools/scan-build/set-xcode-analyzer6
-rw-r--r--unittests/AST/CMakeLists.txt8
-rw-r--r--unittests/AST/CommentLexer.cpp1636
-rw-r--r--unittests/AST/CommentParser.cpp1383
-rw-r--r--unittests/AST/Makefile15
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.cpp2312
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.h128
-rw-r--r--unittests/ASTMatchers/CMakeLists.txt12
-rw-r--r--unittests/ASTMatchers/Makefile19
-rw-r--r--unittests/Basic/CMakeLists.txt9
-rw-r--r--unittests/Basic/SourceManagerTest.cpp48
-rw-r--r--unittests/CMakeLists.txt80
-rw-r--r--unittests/Frontend/CMakeLists.txt13
-rw-r--r--unittests/Frontend/FrontendActionTest.cpp1
-rw-r--r--unittests/Frontend/Makefile3
-rw-r--r--unittests/Lex/CMakeLists.txt8
-rw-r--r--unittests/Makefile2
-rw-r--r--unittests/Tooling/CMakeLists.txt22
-rw-r--r--unittests/Tooling/CommentHandlerTest.cpp221
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp58
-rw-r--r--unittests/Tooling/Makefile7
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTest.cpp388
-rw-r--r--unittests/Tooling/RefactoringCallbacksTest.cpp100
-rw-r--r--unittests/Tooling/RefactoringTest.cpp305
-rw-r--r--unittests/Tooling/RewriterTest.cpp37
-rw-r--r--unittests/Tooling/RewriterTestContext.h125
-rw-r--r--unittests/Tooling/TestVisitor.h144
-rw-r--r--unittests/Tooling/ToolingTest.cpp25
-rw-r--r--utils/TableGen/ClangASTNodesEmitter.cpp61
-rw-r--r--utils/TableGen/ClangASTNodesEmitter.h84
-rw-r--r--utils/TableGen/ClangAttrEmitter.cpp175
-rw-r--r--utils/TableGen/ClangAttrEmitter.h153
-rw-r--r--utils/TableGen/ClangDiagnosticsEmitter.cpp294
-rw-r--r--utils/TableGen/ClangDiagnosticsEmitter.h54
-rw-r--r--utils/TableGen/ClangSACheckersEmitter.cpp8
-rw-r--r--utils/TableGen/ClangSACheckersEmitter.h31
-rw-r--r--utils/TableGen/NeonEmitter.cpp233
-rw-r--r--utils/TableGen/NeonEmitter.h210
-rw-r--r--utils/TableGen/OptParserEmitter.cpp13
-rw-r--r--utils/TableGen/OptParserEmitter.h34
-rw-r--r--utils/TableGen/TableGen.cpp58
-rw-r--r--utils/TableGen/TableGenBackends.h56
-rwxr-xr-xutils/analyzer/CmpRuns.py135
-rw-r--r--utils/analyzer/SATestAdd.py2
-rw-r--r--utils/analyzer/SATestBuild.py33
-rw-r--r--utils/analyzer/SumTimerInfo.py75
-rwxr-xr-xutils/analyzer/reducer.pl65
-rw-r--r--utils/clang-completion-mode.el40
-rw-r--r--utils/clangVisualizers.txt313
-rw-r--r--www/analyzer/annotations.html3
-rw-r--r--www/analyzer/available_checks.html48
-rw-r--r--www/analyzer/content.css1
-rw-r--r--www/analyzer/faq.html120
-rw-r--r--www/analyzer/images/example_custom_assert.pngbin0 -> 23686 bytes
-rw-r--r--www/analyzer/images/example_null_pointer.pngbin0 -> 23580 bytes
-rw-r--r--www/analyzer/images/example_use_assert.pngbin0 -> 25513 bytes
-rw-r--r--www/analyzer/installation.html2
-rw-r--r--www/analyzer/latest_checker.html.incl2
-rw-r--r--www/analyzer/menu.html.incl5
-rw-r--r--www/analyzer/release_notes.html38
-rwxr-xr-xwww/builtins.py5
-rw-r--r--www/comparison.html2
-rw-r--r--www/content.css3
-rw-r--r--www/cxx_status.html28
-rw-r--r--www/diagnostics.html33
-rw-r--r--www/features.html16
-rw-r--r--www/get_started.html33
-rw-r--r--www/menu.html.incl1
1875 files changed, 112126 insertions, 30616 deletions
diff --git a/.gitignore b/.gitignore
index ddd6638..b906ab9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,9 @@
#==============================================================================#
cscope.files
cscope.out
+
+#==============================================================================#
+# Directories to ignore (do not add trailing '/'s, they skip symlinks).
+#==============================================================================#
+# Clang extra user tools, which is tracked independently (clang-tools-extra).
+tools/extra
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c828482..2f4fa1c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,6 +53,14 @@ if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/Debug/llvm-tblgen${CMAKE_EXECUTABLE_SUFFIX}")
endif()
+ # Define the default arguments to use with 'lit', and an option for the user
+ # to override.
+ set(LIT_ARGS_DEFAULT "-sv")
+ if (MSVC OR XCODE)
+ set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
+ endif()
+ set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
+
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
@@ -131,6 +139,10 @@ if (APPLE)
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress")
endif ()
+# libxml2 is an optional dependency, required only to run validation
+# tests on XML output.
+find_package(LibXml2)
+
configure_file(
${CLANG_SOURCE_DIR}/include/clang/Config/config.h.cmake
${CLANG_BINARY_DIR}/include/clang/Config/config.h)
@@ -192,20 +204,10 @@ macro(add_clang_library name)
add_dependencies( ${name} ${LLVM_COMMON_DEPENDS} )
endif( LLVM_COMMON_DEPENDS )
- target_link_libraries( ${name} ${LLVM_USED_LIBS} )
llvm_config( ${name} ${LLVM_LINK_COMPONENTS} )
target_link_libraries( ${name} ${LLVM_COMMON_LIBS} )
link_system_libs( ${name} )
- add_dependencies(${name} ClangDiagnosticCommon)
- if(MSVC)
- get_target_property(cflag ${name} COMPILE_FLAGS)
- if(NOT cflag)
- set(cflag "")
- endif(NOT cflag)
- set(cflag "${cflag} /Za")
- set_target_properties(${name} PROPERTIES COMPILE_FLAGS ${cflag})
- endif(MSVC)
install(TARGETS ${name}
LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
@@ -252,14 +254,14 @@ mark_as_advanced(CLANG_EXECUTABLE_VERSION LIBCLANG_LIBRARY_VERSION)
add_subdirectory(utils/TableGen)
-option(CLANG_BUILD_EXAMPLES "Build CLANG example programs by default." OFF)
-add_subdirectory(examples)
-
add_subdirectory(include)
add_subdirectory(lib)
add_subdirectory(tools)
add_subdirectory(runtime)
+option(CLANG_BUILD_EXAMPLES "Build CLANG example programs by default." OFF)
+add_subdirectory(examples)
+
# TODO: docs.
add_subdirectory(test)
@@ -280,3 +282,4 @@ endif()
set(BUG_REPORT_URL "http://llvm.org/bugs/" CACHE STRING
"Default URL where bug reports are to be submitted.")
+
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index 6f0d25f..fc0a2a1 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -65,6 +65,8 @@ call is efficient.
from ctypes import *
import collections
+import clang.enumerations
+
def get_cindex_library():
# FIXME: It's probably not the case that the library is actually found in
# this location. We need a better system of identifying and loading the
@@ -85,6 +87,49 @@ def get_cindex_library():
c_object_p = POINTER(c_void_p)
lib = get_cindex_library()
+callbacks = {}
+
+### Exception Classes ###
+
+class TranslationUnitLoadError(Exception):
+ """Represents an error that occurred when loading a TranslationUnit.
+
+ This is raised in the case where a TranslationUnit could not be
+ instantiated due to failure in the libclang library.
+
+ FIXME: Make libclang expose additional error information in this scenario.
+ """
+ pass
+
+class TranslationUnitSaveError(Exception):
+ """Represents an error that occurred when saving a TranslationUnit.
+
+ Each error has associated with it an enumerated value, accessible under
+ e.save_error. Consumers can compare the value with one of the ERROR_
+ constants in this class.
+ """
+
+ # Indicates that an unknown error occurred. This typically indicates that
+ # I/O failed during save.
+ ERROR_UNKNOWN = 1
+
+ # Indicates that errors during translation prevented saving. The errors
+ # should be available via the TranslationUnit's diagnostics.
+ ERROR_TRANSLATION_ERRORS = 2
+
+ # Indicates that the translation unit was somehow invalid.
+ ERROR_INVALID_TU = 3
+
+ def __init__(self, enumeration, message):
+ assert isinstance(enumeration, int)
+
+ if enumeration < 1 or enumeration > 3:
+ raise Exception("Encountered undefined TranslationUnit save error "
+ "constant: %d. Please file a bug to have this "
+ "value supported." % enumeration)
+
+ self.save_error = enumeration
+ Exception.__init__(self, 'Error %d: %s' % (enumeration, message))
### Structures and Utility Classes ###
@@ -94,12 +139,12 @@ class _CXString(Structure):
_fields_ = [("spelling", c_char_p), ("free", c_int)]
def __del__(self):
- _CXString_dispose(self)
+ lib.clang_disposeString(self)
@staticmethod
def from_result(res, fn, args):
assert isinstance(res, _CXString)
- return _CXString_getCString(res)
+ return lib.clang_getCString(res)
class SourceLocation(Structure):
"""
@@ -111,7 +156,8 @@ class SourceLocation(Structure):
def _get_instantiation(self):
if self._data is None:
f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint()
- SourceLocation_loc(self, byref(f), byref(l), byref(c), byref(o))
+ lib.clang_getInstantiationLocation(self, byref(f), byref(l),
+ byref(c), byref(o))
if f:
f = File(f)
else:
@@ -125,7 +171,17 @@ class SourceLocation(Structure):
Retrieve the source location associated with a given file/line/column in
a particular translation unit.
"""
- return SourceLocation_getLocation(tu, file, line, column)
+ return lib.clang_getLocation(tu, file, line, column)
+
+ @staticmethod
+ def from_offset(tu, file, offset):
+ """Retrieve a SourceLocation from a given character offset.
+
+ tu -- TranslationUnit file belongs to
+ file -- File instance to obtain offset from
+ offset -- Integer character offset within file
+ """
+ return lib.clang_getLocationForOffset(tu, file, offset)
@property
def file(self):
@@ -148,7 +204,7 @@ class SourceLocation(Structure):
return self._get_instantiation()[3]
def __eq__(self, other):
- return SourceLocation_equalLocations(self, other)
+ return lib.clang_equalLocations(self, other)
def __ne__(self, other):
return not self.__eq__(other)
@@ -175,7 +231,7 @@ class SourceRange(Structure):
# object.
@staticmethod
def from_locations(start, end):
- return SourceRange_getRange(start, end)
+ return lib.clang_getRange(start, end)
@property
def start(self):
@@ -183,7 +239,7 @@ class SourceRange(Structure):
Return a SourceLocation representing the first character within a
source range.
"""
- return SourceRange_start(self)
+ return lib.clang_getRangeStart(self)
@property
def end(self):
@@ -191,10 +247,10 @@ class SourceRange(Structure):
Return a SourceLocation representing the last character within a
source range.
"""
- return SourceRange_end(self)
+ return lib.clang_getRangeEnd(self)
def __eq__(self, other):
- return SourceRange_equalRanges(self, other)
+ return lib.clang_equalRanges(self, other)
def __ne__(self, other):
return not self.__eq__(other)
@@ -219,19 +275,19 @@ class Diagnostic(object):
self.ptr = ptr
def __del__(self):
- _clang_disposeDiagnostic(self)
+ lib.clang_disposeDiagnostic(self)
@property
def severity(self):
- return _clang_getDiagnosticSeverity(self)
+ return lib.clang_getDiagnosticSeverity(self)
@property
def location(self):
- return _clang_getDiagnosticLocation(self)
+ return lib.clang_getDiagnosticLocation(self)
@property
def spelling(self):
- return _clang_getDiagnosticSpelling(self)
+ return lib.clang_getDiagnosticSpelling(self)
@property
def ranges(self):
@@ -240,12 +296,12 @@ class Diagnostic(object):
self.diag = diag
def __len__(self):
- return int(_clang_getDiagnosticNumRanges(self.diag))
+ return int(lib.clang_getDiagnosticNumRanges(self.diag))
def __getitem__(self, key):
if (key >= len(self)):
raise IndexError
- return _clang_getDiagnosticRange(self.diag, key)
+ return lib.clang_getDiagnosticRange(self.diag, key)
return RangeIterator(self)
@@ -256,11 +312,12 @@ class Diagnostic(object):
self.diag = diag
def __len__(self):
- return int(_clang_getDiagnosticNumFixIts(self.diag))
+ return int(lib.clang_getDiagnosticNumFixIts(self.diag))
def __getitem__(self, key):
range = SourceRange()
- value = _clang_getDiagnosticFixIt(self.diag, key, byref(range))
+ value = lib.clang_getDiagnosticFixIt(self.diag, key,
+ byref(range))
if len(value) == 0:
raise IndexError
@@ -271,25 +328,25 @@ class Diagnostic(object):
@property
def category_number(self):
"""The category number for this diagnostic."""
- return _clang_getDiagnosticCategory(self)
+ return lib.clang_getDiagnosticCategory(self)
@property
def category_name(self):
"""The string name of the category for this diagnostic."""
- return _clang_getDiagnosticCategoryName(self.category_number)
+ return lib.clang_getDiagnosticCategoryName(self.category_number)
@property
def option(self):
"""The command-line option that enables this diagnostic."""
- return _clang_getDiagnosticOption(self, None)
+ return lib.clang_getDiagnosticOption(self, None)
@property
def disable_option(self):
"""The command-line option that disables this diagnostic."""
disable = _CXString()
- _clang_getDiagnosticOption(self, byref(disable))
+ lib.clang_getDiagnosticOption(self, byref(disable))
- return _CXString_getCString(disable)
+ return lib.clang_getCString(disable)
def __repr__(self):
return "<Diagnostic severity %r, location %r, spelling %r>" % (
@@ -312,6 +369,98 @@ class FixIt(object):
def __repr__(self):
return "<FixIt range %r, value %r>" % (self.range, self.value)
+class TokenGroup(object):
+ """Helper class to facilitate token management.
+
+ Tokens are allocated from libclang in chunks. They must be disposed of as a
+ collective group.
+
+ One purpose of this class is for instances to represent groups of allocated
+ tokens. Each token in a group contains a reference back to an instance of
+ this class. When all tokens from a group are garbage collected, it allows
+ this class to be garbage collected. When this class is garbage collected,
+ it calls the libclang destructor which invalidates all tokens in the group.
+
+ You should not instantiate this class outside of this module.
+ """
+ def __init__(self, tu, memory, count):
+ self._tu = tu
+ self._memory = memory
+ self._count = count
+
+ def __del__(self):
+ lib.clang_disposeTokens(self._tu, self._memory, self._count)
+
+ @staticmethod
+ def get_tokens(tu, extent):
+ """Helper method to return all tokens in an extent.
+
+ This functionality is needed multiple places in this module. We define
+ it here because it seems like a logical place.
+ """
+ tokens_memory = POINTER(Token)()
+ tokens_count = c_uint()
+
+ lib.clang_tokenize(tu, extent, byref(tokens_memory),
+ byref(tokens_count))
+
+ count = int(tokens_count.value)
+
+ # If we get no tokens, no memory was allocated. Be sure not to return
+ # anything and potentially call a destructor on nothing.
+ if count < 1:
+ return
+
+ tokens_array = cast(tokens_memory, POINTER(Token * count)).contents
+
+ token_group = TokenGroup(tu, tokens_memory, tokens_count)
+
+ for i in xrange(0, count):
+ token = Token()
+ token.int_data = tokens_array[i].int_data
+ token.ptr_data = tokens_array[i].ptr_data
+ token._tu = tu
+ token._group = token_group
+
+ yield token
+
+class TokenKind(object):
+ """Describes a specific type of a Token."""
+
+ _value_map = {} # int -> TokenKind
+
+ def __init__(self, value, name):
+ """Create a new TokenKind instance from a numeric value and a name."""
+ self.value = value
+ self.name = name
+
+ def __repr__(self):
+ return 'TokenKind.%s' % (self.name,)
+
+ @staticmethod
+ def from_value(value):
+ """Obtain a registered TokenKind instance from its value."""
+ result = TokenKind._value_map.get(value, None)
+
+ if result is None:
+ raise ValueError('Unknown TokenKind: %d' % value)
+
+ return result
+
+ @staticmethod
+ def register(value, name):
+ """Register a new TokenKind enumeration.
+
+ This should only be called at module load time by code within this
+ package.
+ """
+ if value in TokenKind._value_map:
+ raise ValueError('TokenKind already registered: %d' % value)
+
+ kind = TokenKind(value, name)
+ TokenKind._value_map[value] = kind
+ setattr(TokenKind, name, kind)
+
### Cursor Kinds ###
class CursorKind(object):
@@ -358,39 +507,39 @@ class CursorKind(object):
def is_declaration(self):
"""Test if this is a declaration kind."""
- return CursorKind_is_decl(self)
+ return lib.clang_isDeclaration(self)
def is_reference(self):
"""Test if this is a reference kind."""
- return CursorKind_is_ref(self)
+ return lib.clang_isReference(self)
def is_expression(self):
"""Test if this is an expression kind."""
- return CursorKind_is_expr(self)
+ return lib.clang_isExpression(self)
def is_statement(self):
"""Test if this is a statement kind."""
- return CursorKind_is_stmt(self)
+ return lib.clang_isStatement(self)
def is_attribute(self):
"""Test if this is an attribute kind."""
- return CursorKind_is_attribute(self)
+ return lib.clang_isAttribute(self)
def is_invalid(self):
"""Test if this is an invalid kind."""
- return CursorKind_is_inv(self)
+ return lib.clang_isInvalid(self)
def is_translation_unit(self):
"""Test if this is a translation unit kind."""
- return CursorKind_is_translation_unit(self)
+ return lib.clang_isTranslationUnit(self)
def is_preprocessing(self):
"""Test if this is a preprocessing kind."""
- return CursorKind_is_preprocessing(self)
+ return lib.clang_isPreprocessing(self)
def is_unexposed(self):
"""Test if this is an unexposed kind."""
- return CursorKind_is_unexposed(self)
+ return lib.clang_isUnexposed(self)
def __repr__(self):
return 'CursorKind.%s' % (self.name,)
@@ -635,7 +784,7 @@ CursorKind.BINARY_OPERATOR = CursorKind(114)
CursorKind.COMPOUND_ASSIGNMENT_OPERATOR = CursorKind(115)
# The ?: ternary operator.
-CursorKind.CONDITONAL_OPERATOR = CursorKind(116)
+CursorKind.CONDITIONAL_OPERATOR = CursorKind(116)
# An explicit cast in C (C99 6.5.4) or a C-style cast in C++
# (C++ [expr.cast]), which uses the syntax (Type)expr.
@@ -887,10 +1036,15 @@ class Cursor(Structure):
@staticmethod
def from_location(tu, location):
- return Cursor_get(tu, location)
+ # We store a reference to the TU in the instance so the TU won't get
+ # collected before the cursor.
+ cursor = lib.clang_getCursor(tu, location)
+ cursor._tu = tu
+
+ return cursor
def __eq__(self, other):
- return Cursor_eq(self, other)
+ return lib.clang_equalCursors(self, other)
def __ne__(self, other):
return not self.__eq__(other)
@@ -900,7 +1054,13 @@ class Cursor(Structure):
Returns true if the declaration pointed at by the cursor is also a
definition of that entity.
"""
- return Cursor_is_def(self)
+ return lib.clang_isCursorDefinition(self)
+
+ def is_static_method(self):
+ """Returns True if the cursor refers to a C++ member function or member
+ function template that is declared 'static'.
+ """
+ return lib.clang_CXXMethod_isStatic(self)
def get_definition(self):
"""
@@ -910,7 +1070,7 @@ class Cursor(Structure):
"""
# TODO: Should probably check that this is either a reference or
# declaration prior to issuing the lookup.
- return Cursor_def(self)
+ return lib.clang_getCursorDefinition(self)
def get_usr(self):
"""Return the Unified Symbol Resultion (USR) for the entity referenced
@@ -921,7 +1081,7 @@ class Cursor(Structure):
program. USRs can be compared across translation units to determine,
e.g., when references in one translation refer to an entity defined in
another translation unit."""
- return Cursor_usr(self)
+ return lib.clang_getCursorUSR(self)
@property
def kind(self):
@@ -936,7 +1096,8 @@ class Cursor(Structure):
# this, for consistency with clang_getCursorUSR.
return None
if not hasattr(self, '_spelling'):
- self._spelling = Cursor_spelling(self)
+ self._spelling = lib.clang_getCursorSpelling(self)
+
return self._spelling
@property
@@ -949,7 +1110,8 @@ class Cursor(Structure):
class template specialization.
"""
if not hasattr(self, '_displayname'):
- self._displayname = Cursor_displayname(self)
+ self._displayname = lib.clang_getCursorDisplayName(self)
+
return self._displayname
@property
@@ -959,7 +1121,8 @@ class Cursor(Structure):
pointed at by the cursor.
"""
if not hasattr(self, '_loc'):
- self._loc = Cursor_loc(self)
+ self._loc = lib.clang_getCursorLocation(self)
+
return self._loc
@property
@@ -969,7 +1132,8 @@ class Cursor(Structure):
pointed at by the cursor.
"""
if not hasattr(self, '_extent'):
- self._extent = Cursor_extent(self)
+ self._extent = lib.clang_getCursorExtent(self)
+
return self._extent
@property
@@ -978,10 +1142,33 @@ class Cursor(Structure):
Retrieve the Type (if any) of the entity pointed at by the cursor.
"""
if not hasattr(self, '_type'):
- self._type = Cursor_type(self)
+ self._type = lib.clang_getCursorType(self)
+
return self._type
@property
+ def canonical(self):
+ """Return the canonical Cursor corresponding to this Cursor.
+
+ The canonical cursor is the cursor which is representative for the
+ underlying entity. For example, if you have multiple forward
+ declarations for the same class, the canonical cursor for the forward
+ declarations will be identical.
+ """
+ if not hasattr(self, '_canonical'):
+ self._canonical = lib.clang_getCanonicalCursor(self)
+
+ return self._canonical
+
+ @property
+ def result_type(self):
+ """Retrieve the Type of the result for this Cursor."""
+ if not hasattr(self, '_result_type'):
+ self._result_type = lib.clang_getResultType(self.type)
+
+ return self._result_type
+
+ @property
def underlying_typedef_type(self):
"""Return the underlying type of a typedef declaration.
@@ -990,7 +1177,7 @@ class Cursor(Structure):
"""
if not hasattr(self, '_underlying_type'):
assert self.kind.is_declaration()
- self._underlying_type = Cursor_underlying_type(self)
+ self._underlying_type = lib.clang_getTypedefDeclUnderlyingType(self)
return self._underlying_type
@@ -1003,15 +1190,39 @@ class Cursor(Structure):
"""
if not hasattr(self, '_enum_type'):
assert self.kind == CursorKind.ENUM_DECL
- self._enum_type = Cursor_enum_type(self)
+ self._enum_type = lib.clang_getEnumDeclIntegerType(self)
return self._enum_type
@property
+ def enum_value(self):
+ """Return the value of an enum constant."""
+ if not hasattr(self, '_enum_value'):
+ assert self.kind == CursorKind.ENUM_CONSTANT_DECL
+ # Figure out the underlying type of the enum to know if it
+ # is a signed or unsigned quantity.
+ underlying_type = self.type
+ if underlying_type.kind == TypeKind.ENUM:
+ underlying_type = underlying_type.get_declaration().enum_type
+ if underlying_type.kind in (TypeKind.CHAR_U,
+ TypeKind.UCHAR,
+ TypeKind.CHAR16,
+ TypeKind.CHAR32,
+ TypeKind.USHORT,
+ TypeKind.UINT,
+ TypeKind.ULONG,
+ TypeKind.ULONGLONG,
+ TypeKind.UINT128):
+ self._enum_value = lib.clang_getEnumConstantDeclUnsignedValue(self)
+ else:
+ self._enum_value = lib.clang_getEnumConstantDeclValue(self)
+ return self._enum_value
+
+ @property
def objc_type_encoding(self):
"""Return the Objective-C type encoding as a str."""
if not hasattr(self, '_objc_type_encoding'):
- self._objc_type_encoding = Cursor_objc_type_encoding(self)
+ self._objc_type_encoding = lib.clang_getDeclObjCTypeEncoding(self)
return self._objc_type_encoding
@@ -1019,10 +1230,33 @@ class Cursor(Structure):
def hash(self):
"""Returns a hash of the cursor as an int."""
if not hasattr(self, '_hash'):
- self._hash = Cursor_hash(self)
+ self._hash = lib.clang_hashCursor(self)
return self._hash
+ @property
+ def semantic_parent(self):
+ """Return the semantic parent for this cursor."""
+ if not hasattr(self, '_semantic_parent'):
+ self._semantic_parent = lib.clang_getCursorSemanticParent(self)
+
+ return self._semantic_parent
+
+ @property
+ def lexical_parent(self):
+ """Return the lexical parent for this cursor."""
+ if not hasattr(self, '_lexical_parent'):
+ self._lexical_parent = lib.clang_getCursorLexicalParent(self)
+
+ return self._lexical_parent
+
+ @property
+ def translation_unit(self):
+ """Returns the TranslationUnit to which this Cursor belongs."""
+ # If this triggers an AttributeError, the instance was not properly
+ # created.
+ return self._tu
+
def get_children(self):
"""Return an iterator for accessing the children of this cursor."""
@@ -1030,21 +1264,57 @@ class Cursor(Structure):
def visitor(child, parent, children):
# FIXME: Document this assertion in API.
# FIXME: There should just be an isNull method.
- assert child != Cursor_null()
+ assert child != lib.clang_getNullCursor()
+
+ # Create reference to TU so it isn't GC'd before Cursor.
+ child._tu = self._tu
children.append(child)
return 1 # continue
children = []
- Cursor_visit(self, Cursor_visit_callback(visitor), children)
+ lib.clang_visitChildren(self, callbacks['cursor_visit'](visitor),
+ children)
return iter(children)
+ def get_tokens(self):
+ """Obtain Token instances formulating that compose this Cursor.
+
+ This is a generator for Token instances. It returns all tokens which
+ occupy the extent this cursor occupies.
+ """
+ return TokenGroup.get_tokens(self._tu, self.extent)
+
@staticmethod
def from_result(res, fn, args):
assert isinstance(res, Cursor)
# FIXME: There should just be an isNull method.
- if res == Cursor_null():
+ if res == lib.clang_getNullCursor():
return None
+
+ # Store a reference to the TU in the Python object so it won't get GC'd
+ # before the Cursor.
+ tu = None
+ for arg in args:
+ if isinstance(arg, TranslationUnit):
+ tu = arg
+ break
+
+ if hasattr(arg, 'translation_unit'):
+ tu = arg.translation_unit
+ break
+
+ assert tu is not None
+
+ res._tu = tu
return res
+ @staticmethod
+ def from_cursor_result(res, fn, args):
+ assert isinstance(res, Cursor)
+ if res == lib.clang_getNullCursor():
+ return None
+
+ res._tu = args[0]._tu
+ return res
### Type Kinds ###
@@ -1082,7 +1352,7 @@ class TypeKind(object):
@property
def spelling(self):
"""Retrieve the spelling of this TypeKind."""
- return TypeKind_spelling(self.value)
+ return lib.clang_getTypeKindSpelling(self.value)
@staticmethod
def from_id(id):
@@ -1093,12 +1363,6 @@ class TypeKind(object):
def __repr__(self):
return 'TypeKind.%s' % (self.name,)
-TypeKind_spelling = lib.clang_getTypeKindSpelling
-TypeKind_spelling.argtypes = [c_uint]
-TypeKind_spelling.restype = _CXString
-TypeKind_spelling.errcheck = _CXString.from_result
-
-
TypeKind.INVALID = TypeKind(0)
TypeKind.UNEXPOSED = TypeKind(1)
TypeKind.VOID = TypeKind(2)
@@ -1168,7 +1432,7 @@ class Type(Structure):
def __len__(self):
if self.length is None:
- self.length = Type_get_num_arg_types(self.parent)
+ self.length = lib.clang_getNumArgTypes(self.parent)
return self.length
@@ -1184,7 +1448,7 @@ class Type(Structure):
raise IndexError("Index greater than container length: "
"%d > %d" % ( key, len(self) ))
- result = Type_get_arg_type(self.parent, key)
+ result = lib.clang_getArgType(self.parent, key)
if result.kind == TypeKind.INVALID:
raise IndexError("Argument could not be retrieved.")
@@ -1200,7 +1464,7 @@ class Type(Structure):
If accessed on a type that is not an array, complex, or vector type, an
exception will be raised.
"""
- result = Type_get_element_type(self)
+ result = lib.clang_getElementType(self)
if result.kind == TypeKind.INVALID:
raise Exception('Element type not available on this type.')
@@ -1214,15 +1478,32 @@ class Type(Structure):
If the Type is not an array or vector, this raises.
"""
- result = Type_get_num_elements(self)
+ result = lib.clang_getNumElements(self)
if result < 0:
raise Exception('Type does not have elements.')
return result
+ @property
+ def translation_unit(self):
+ """The TranslationUnit to which this Type is associated."""
+ # If this triggers an AttributeError, the instance was not properly
+ # instantiated.
+ return self._tu
+
@staticmethod
def from_result(res, fn, args):
assert isinstance(res, Type)
+
+ tu = None
+ for arg in args:
+ if hasattr(arg, 'translation_unit'):
+ tu = arg.translation_unit
+ break
+
+ assert tu is not None
+ res._tu = tu
+
return res
def get_canonical(self):
@@ -1235,7 +1516,7 @@ class Type(Structure):
example, if 'T' is a typedef for 'int', the canonical type for
'T' would be 'int'.
"""
- return Type_get_canonical(self)
+ return lib.clang_getCanonicalType(self)
def is_const_qualified(self):
"""Determine whether a Type has the "const" qualifier set.
@@ -1243,7 +1524,7 @@ class Type(Structure):
This does not look through typedefs that may have added "const"
at a different level.
"""
- return Type_is_const_qualified(self)
+ return lib.clang_isConstQualifiedType(self)
def is_volatile_qualified(self):
"""Determine whether a Type has the "volatile" qualifier set.
@@ -1251,7 +1532,7 @@ class Type(Structure):
This does not look through typedefs that may have added "volatile"
at a different level.
"""
- return Type_is_volatile_qualified(self)
+ return lib.clang_isVolatileQualifiedType(self)
def is_restrict_qualified(self):
"""Determine whether a Type has the "restrict" qualifier set.
@@ -1259,53 +1540,53 @@ class Type(Structure):
This does not look through typedefs that may have added "restrict" at
a different level.
"""
- return Type_is_restrict_qualified(self)
+ return lib.clang_isRestrictQualifiedType(self)
def is_function_variadic(self):
"""Determine whether this function Type is a variadic function type."""
assert self.kind == TypeKind.FUNCTIONPROTO
- return Type_is_variadic(self)
+ return lib.clang_isFunctionTypeVariadic(self)
def is_pod(self):
"""Determine whether this Type represents plain old data (POD)."""
- return Type_is_pod(self)
+ return lib.clang_isPODType(self)
def get_pointee(self):
"""
For pointer types, returns the type of the pointee.
"""
- return Type_get_pointee(self)
+ return lib.clang_getPointeeType(self)
def get_declaration(self):
"""
Return the cursor for the declaration of the given type.
"""
- return Type_get_declaration(self)
+ return lib.clang_getTypeDeclaration(self)
def get_result(self):
"""
Retrieve the result type associated with a function type.
"""
- return Type_get_result(self)
+ return lib.clang_getResultType(self)
def get_array_element_type(self):
"""
Retrieve the type of the elements of the array type.
"""
- return Type_get_array_element(self)
+ return lib.clang_getArrayElementType(self)
def get_array_size(self):
"""
Retrieve the size of the constant array.
"""
- return Type_get_array_size(self)
+ return lib.clang_getArraySize(self)
def __eq__(self, other):
if type(other) != type(self):
return False
- return Type_equal(self, other)
+ return lib.clang_equalTypes(self, other)
def __ne__(self, other):
return not self.__eq__(other)
@@ -1333,65 +1614,6 @@ class _CXUnsavedFile(Structure):
"""Helper for passing unsaved file arguments."""
_fields_ = [("name", c_char_p), ("contents", c_char_p), ('length', c_ulong)]
-## Diagnostic Conversion ##
-
-_clang_getNumDiagnostics = lib.clang_getNumDiagnostics
-_clang_getNumDiagnostics.argtypes = [c_object_p]
-_clang_getNumDiagnostics.restype = c_uint
-
-_clang_getDiagnostic = lib.clang_getDiagnostic
-_clang_getDiagnostic.argtypes = [c_object_p, c_uint]
-_clang_getDiagnostic.restype = c_object_p
-
-_clang_disposeDiagnostic = lib.clang_disposeDiagnostic
-_clang_disposeDiagnostic.argtypes = [Diagnostic]
-
-_clang_getDiagnosticSeverity = lib.clang_getDiagnosticSeverity
-_clang_getDiagnosticSeverity.argtypes = [Diagnostic]
-_clang_getDiagnosticSeverity.restype = c_int
-
-_clang_getDiagnosticLocation = lib.clang_getDiagnosticLocation
-_clang_getDiagnosticLocation.argtypes = [Diagnostic]
-_clang_getDiagnosticLocation.restype = SourceLocation
-
-_clang_getDiagnosticSpelling = lib.clang_getDiagnosticSpelling
-_clang_getDiagnosticSpelling.argtypes = [Diagnostic]
-_clang_getDiagnosticSpelling.restype = _CXString
-_clang_getDiagnosticSpelling.errcheck = _CXString.from_result
-
-_clang_getDiagnosticNumRanges = lib.clang_getDiagnosticNumRanges
-_clang_getDiagnosticNumRanges.argtypes = [Diagnostic]
-_clang_getDiagnosticNumRanges.restype = c_uint
-
-_clang_getDiagnosticRange = lib.clang_getDiagnosticRange
-_clang_getDiagnosticRange.argtypes = [Diagnostic, c_uint]
-_clang_getDiagnosticRange.restype = SourceRange
-
-_clang_getDiagnosticNumFixIts = lib.clang_getDiagnosticNumFixIts
-_clang_getDiagnosticNumFixIts.argtypes = [Diagnostic]
-_clang_getDiagnosticNumFixIts.restype = c_uint
-
-_clang_getDiagnosticFixIt = lib.clang_getDiagnosticFixIt
-_clang_getDiagnosticFixIt.argtypes = [Diagnostic, c_uint, POINTER(SourceRange)]
-_clang_getDiagnosticFixIt.restype = _CXString
-_clang_getDiagnosticFixIt.errcheck = _CXString.from_result
-
-_clang_getDiagnosticCategory = lib.clang_getDiagnosticCategory
-_clang_getDiagnosticCategory.argtypes = [Diagnostic]
-_clang_getDiagnosticCategory.restype = c_uint
-
-_clang_getDiagnosticCategoryName = lib.clang_getDiagnosticCategoryName
-_clang_getDiagnosticCategoryName.argtypes = [c_uint]
-_clang_getDiagnosticCategoryName.restype = _CXString
-_clang_getDiagnosticCategoryName.errcheck = _CXString.from_result
-
-_clang_getDiagnosticOption = lib.clang_getDiagnosticOption
-_clang_getDiagnosticOption.argtypes = [Diagnostic, POINTER(_CXString)]
-_clang_getDiagnosticOption.restype = _CXString
-_clang_getDiagnosticOption.errcheck = _CXString.from_result
-
-###
-
class CompletionChunk:
class Kind:
def __init__(self, name):
@@ -1412,16 +1634,16 @@ class CompletionChunk:
@property
def spelling(self):
- return _clang_getCompletionChunkText(self.cs, self.key).spelling
+ return lib.clang_getCompletionChunkText(self.cs, self.key).spelling
@property
def kind(self):
- res = _clang_getCompletionChunkKind(self.cs, self.key)
+ res = lib.clang_getCompletionChunkKind(self.cs, self.key)
return completionChunkKindMap[res]
@property
def string(self):
- res = _clang_getCompletionChunkCompletionString(self.cs, self.key)
+ res = lib.clang_getCompletionChunkCompletionString(self.cs, self.key)
if (res):
return CompletionString(res)
@@ -1478,7 +1700,7 @@ class CompletionString(ClangObject):
return "<Availability: %s>" % self
def __len__(self):
- return _clang_getNumCompletionChunks(self.obj)
+ return lib.clang_getNumCompletionChunks(self.obj)
def __getitem__(self, key):
if len(self) <= key:
@@ -1487,11 +1709,11 @@ class CompletionString(ClangObject):
@property
def priority(self):
- return _clang_getCompletionPriority(self.obj)
+ return lib.clang_getCompletionPriority(self.obj)
@property
def availability(self):
- res = _clang_getCompletionAvailability(self.obj)
+ res = lib.clang_getCompletionAvailability(self.obj)
return availabilityKinds[res]
def __repr__(self):
@@ -1553,10 +1775,10 @@ class CodeCompletionResults(ClangObject):
self.ccr= ccr
def __len__(self):
- return int(_clang_codeCompleteGetNumDiagnostics(self.ccr))
+ return int(lib.clang_codeCompleteGetNumDiagnostics(self.ccr))
def __getitem__(self, key):
- return _clang_codeCompleteGetDiagnostic(self.ccr, key)
+ return lib.clang_codeCompleteGetDiagnostic(self.ccr, key)
return DiagnosticsItr(self)
@@ -1575,21 +1797,17 @@ class Index(ClangObject):
Parameters:
excludeDecls -- Exclude local declarations from translation units.
"""
- return Index(Index_create(excludeDecls, 0))
+ return Index(lib.clang_createIndex(excludeDecls, 0))
def __del__(self):
- Index_dispose(self)
+ lib.clang_disposeIndex(self)
def read(self, path):
- """Load the translation unit from the given AST file."""
- ptr = TranslationUnit_read(self, path)
- if ptr:
- return TranslationUnit(ptr)
- return None
+ """Load a TranslationUnit from the given AST file."""
+ return TranslationUnit.from_ast(path, self)
- def parse(self, path, args = [], unsaved_files = [], options = 0):
- """
- Load the translation unit from the given source code file by running
+ def parse(self, path, args=None, unsaved_files=None, options = 0):
+ """Load the translation unit from the given source code file by running
clang and generating the AST before loading. Additional command line
parameters can be passed to clang via the args parameter.
@@ -1597,53 +1815,167 @@ class Index(ClangObject):
to as unsaved_files, the first item should be the filenames to be mapped
and the second should be the contents to be substituted for the
file. The contents may be passed as strings or file objects.
- """
- arg_array = 0
- if len(args):
- arg_array = (c_char_p * len(args))(* args)
- unsaved_files_array = 0
- if len(unsaved_files):
- unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))()
- for i,(name,value) in enumerate(unsaved_files):
- if not isinstance(value, str):
- # FIXME: It would be great to support an efficient version
- # of this, one day.
- value = value.read()
- print value
- if not isinstance(value, str):
- raise TypeError,'Unexpected unsaved file contents.'
- unsaved_files_array[i].name = name
- unsaved_files_array[i].contents = value
- unsaved_files_array[i].length = len(value)
- ptr = TranslationUnit_parse(self, path, arg_array, len(args),
- unsaved_files_array, len(unsaved_files),
- options)
- if ptr:
- return TranslationUnit(ptr)
- return None
+ If an error was encountered during parsing, a TranslationUnitLoadError
+ will be raised.
+ """
+ return TranslationUnit.from_source(path, args, unsaved_files, options,
+ self)
class TranslationUnit(ClangObject):
- """
- The TranslationUnit class represents a source code translation unit and
- provides read-only access to its top-level declarations.
+ """Represents a source code translation unit.
+
+ This is one of the main types in the API. Any time you wish to interact
+ with Clang's representation of a source file, you typically start with a
+ translation unit.
"""
- def __init__(self, ptr):
+ # Default parsing mode.
+ PARSE_NONE = 0
+
+ # Instruct the parser to create a detailed processing record containing
+ # metadata not normally retained.
+ PARSE_DETAILED_PROCESSING_RECORD = 1
+
+ # Indicates that the translation unit is incomplete. This is typically used
+ # when parsing headers.
+ PARSE_INCOMPLETE = 2
+
+ # Instruct the parser to create a pre-compiled preamble for the translation
+ # unit. This caches the preamble (included files at top of source file).
+ # This is useful if the translation unit will be reparsed and you don't
+ # want to incur the overhead of reparsing the preamble.
+ PARSE_PRECOMPILED_PREAMBLE = 4
+
+ # Cache code completion information on parse. This adds time to parsing but
+ # speeds up code completion.
+ PARSE_CACHE_COMPLETION_RESULTS = 8
+
+ # Flags with values 16 and 32 are deprecated and intentionally omitted.
+
+ # Do not parse function bodies. This is useful if you only care about
+ # searching for declarations/definitions.
+ PARSE_SKIP_FUNCTION_BODIES = 64
+
+ @classmethod
+ def from_source(cls, filename, args=None, unsaved_files=None, options=0,
+ index=None):
+ """Create a TranslationUnit by parsing source.
+
+ This is capable of processing source code both from files on the
+ filesystem as well as in-memory contents.
+
+ Command-line arguments that would be passed to clang are specified as
+ a list via args. These can be used to specify include paths, warnings,
+ etc. e.g. ["-Wall", "-I/path/to/include"].
+
+ In-memory file content can be provided via unsaved_files. This is an
+ iterable of 2-tuples. The first element is the str filename. The
+ second element defines the content. Content can be provided as str
+ source code or as file objects (anything with a read() method). If
+ a file object is being used, content will be read until EOF and the
+ read cursor will not be reset to its original position.
+
+ options is a bitwise or of TranslationUnit.PARSE_XXX flags which will
+ control parsing behavior.
+
+ index is an Index instance to utilize. If not provided, a new Index
+ will be created for this TranslationUnit.
+
+ To parse source from the filesystem, the filename of the file to parse
+ is specified by the filename argument. Or, filename could be None and
+ the args list would contain the filename(s) to parse.
+
+ To parse source from an in-memory buffer, set filename to the virtual
+ filename you wish to associate with this source (e.g. "test.c"). The
+ contents of that file are then provided in unsaved_files.
+
+ If an error occurs, a TranslationUnitLoadError is raised.
+
+ Please note that a TranslationUnit with parser errors may be returned.
+ It is the caller's responsibility to check tu.diagnostics for errors.
+
+ Also note that Clang infers the source language from the extension of
+ the input filename. If you pass in source code containing a C++ class
+ declaration with the filename "test.c" parsing will fail.
+ """
+ if args is None:
+ args = []
+
+ if unsaved_files is None:
+ unsaved_files = []
+
+ if index is None:
+ index = Index.create()
+
+ args_array = None
+ if len(args) > 0:
+ args_array = (c_char_p * len(args))(* args)
+
+ unsaved_array = None
+ if len(unsaved_files) > 0:
+ unsaved_array = (_CXUnsavedFile * len(unsaved_files))()
+ for i, (name, contents) in enumerate(unsaved_files):
+ if hasattr(contents, "read"):
+ contents = contents.read()
+
+ unsaved_array[i].name = name
+ unsaved_array[i].contents = contents
+ unsaved_array[i].length = len(contents)
+
+ ptr = lib.clang_parseTranslationUnit(index, filename, args_array,
+ len(args), unsaved_array,
+ len(unsaved_files), options)
+
+ if ptr is None:
+ raise TranslationUnitLoadError("Error parsing translation unit.")
+
+ return cls(ptr, index=index)
+
+ @classmethod
+ def from_ast_file(cls, filename, index=None):
+ """Create a TranslationUnit instance from a saved AST file.
+
+ A previously-saved AST file (provided with -emit-ast or
+ TranslationUnit.save()) is loaded from the filename specified.
+
+ If the file cannot be loaded, a TranslationUnitLoadError will be
+ raised.
+
+ index is optional and is the Index instance to use. If not provided,
+ a default Index will be created.
+ """
+ if index is None:
+ index = Index.create()
+
+ ptr = lib.clang_createTranslationUnit(index, filename)
+ if ptr is None:
+ raise TranslationUnitLoadError(filename)
+
+ return cls(ptr=ptr, index=index)
+
+ def __init__(self, ptr, index):
+ """Create a TranslationUnit instance.
+
+ TranslationUnits should be created using one of the from_* @classmethod
+ functions above. __init__ is only called internally.
+ """
+ assert isinstance(index, Index)
+
ClangObject.__init__(self, ptr)
def __del__(self):
- TranslationUnit_dispose(self)
+ lib.clang_disposeTranslationUnit(self)
@property
def cursor(self):
"""Retrieve the cursor that represents the given translation unit."""
- return TranslationUnit_cursor(self)
+ return lib.clang_getTranslationUnitCursor(self)
@property
def spelling(self):
"""Get the original translation unit source file name."""
- return TranslationUnit_spelling(self)
+ return lib.clang_getTranslationUnitSpelling(self)
def get_includes(self):
"""
@@ -1660,11 +1992,72 @@ class TranslationUnit(ClangObject):
# Automatically adapt CIndex/ctype pointers to python objects
includes = []
- TranslationUnit_includes(self,
- TranslationUnit_includes_callback(visitor),
- includes)
+ lib.clang_getInclusions(self,
+ callbacks['translation_unit_includes'](visitor), includes)
+
return iter(includes)
+ def get_file(self, filename):
+ """Obtain a File from this translation unit."""
+
+ return File.from_name(self, filename)
+
+ def get_location(self, filename, position):
+ """Obtain a SourceLocation for a file in this translation unit.
+
+ The position can be specified by passing:
+
+ - Integer file offset. Initial file offset is 0.
+ - 2-tuple of (line number, column number). Initial file position is
+ (0, 0)
+ """
+ f = self.get_file(filename)
+
+ if isinstance(position, int):
+ return SourceLocation.from_offset(self, f, position)
+
+ return SourceLocation.from_position(self, f, position[0], position[1])
+
+ def get_extent(self, filename, locations):
+ """Obtain a SourceRange from this translation unit.
+
+ The bounds of the SourceRange must ultimately be defined by a start and
+ end SourceLocation. For the locations argument, you can pass:
+
+ - 2 SourceLocation instances in a 2-tuple or list.
+ - 2 int file offsets via a 2-tuple or list.
+ - 2 2-tuple or lists of (line, column) pairs in a 2-tuple or list.
+
+ e.g.
+
+ get_extent('foo.c', (5, 10))
+ get_extent('foo.c', ((1, 1), (1, 15)))
+ """
+ f = self.get_file(filename)
+
+ if len(locations) < 2:
+ raise Exception('Must pass object with at least 2 elements')
+
+ start_location, end_location = locations
+
+ if hasattr(start_location, '__len__'):
+ start_location = SourceLocation.from_position(self, f,
+ start_location[0], start_location[1])
+ elif isinstance(start_location, int):
+ start_location = SourceLocation.from_offset(self, f,
+ start_location)
+
+ if hasattr(end_location, '__len__'):
+ end_location = SourceLocation.from_position(self, f,
+ end_location[0], end_location[1])
+ elif isinstance(end_location, int):
+ end_location = SourceLocation.from_offset(self, f, end_location)
+
+ assert isinstance(start_location, SourceLocation)
+ assert isinstance(end_location, SourceLocation)
+
+ return SourceRange.from_locations(start_location, end_location)
+
@property
def diagnostics(self):
"""
@@ -1675,17 +2068,17 @@ class TranslationUnit(ClangObject):
self.tu = tu
def __len__(self):
- return int(_clang_getNumDiagnostics(self.tu))
+ return int(lib.clang_getNumDiagnostics(self.tu))
def __getitem__(self, key):
- diag = _clang_getDiagnostic(self.tu, key)
+ diag = lib.clang_getDiagnostic(self.tu, key)
if not diag:
raise IndexError
return Diagnostic(diag)
return DiagIterator(self)
- def reparse(self, unsaved_files = [], options = 0):
+ def reparse(self, unsaved_files=None, options=0):
"""
Reparse an already parsed translation unit.
@@ -1694,6 +2087,9 @@ class TranslationUnit(ClangObject):
and the second should be the contents to be substituted for the
file. The contents may be passed as strings or file objects.
"""
+ if unsaved_files is None:
+ unsaved_files = []
+
unsaved_files_array = 0
if len(unsaved_files):
unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))()
@@ -1708,10 +2104,31 @@ class TranslationUnit(ClangObject):
unsaved_files_array[i].name = name
unsaved_files_array[i].contents = value
unsaved_files_array[i].length = len(value)
- ptr = TranslationUnit_reparse(self, len(unsaved_files),
- unsaved_files_array,
- options)
- def codeComplete(self, path, line, column, unsaved_files = [], options = 0):
+ ptr = lib.clang_reparseTranslationUnit(self, len(unsaved_files),
+ unsaved_files_array, options)
+
+ def save(self, filename):
+ """Saves the TranslationUnit to a file.
+
+ This is equivalent to passing -emit-ast to the clang frontend. The
+ saved file can be loaded back into a TranslationUnit. Or, if it
+ corresponds to a header, it can be used as a pre-compiled header file.
+
+ If an error occurs while saving, a TranslationUnitSaveError is raised.
+ If the error was TranslationUnitSaveError.ERROR_INVALID_TU, this means
+ the constructed TranslationUnit was not valid at time of save. In this
+ case, the reason(s) why should be available via
+ TranslationUnit.diagnostics().
+
+ filename -- The path to save the translation unit to.
+ """
+ options = lib.clang_defaultSaveOptions(self)
+ result = int(lib.clang_saveTranslationUnit(self, filename, options))
+ if result != 0:
+ raise TranslationUnitSaveError(result,
+ 'Error saving TranslationUnit.')
+
+ def codeComplete(self, path, line, column, unsaved_files=None, options=0):
"""
Code complete in this translation unit.
@@ -1720,6 +2137,9 @@ class TranslationUnit(ClangObject):
and the second should be the contents to be substituted for the
file. The contents may be passed as strings or file objects.
"""
+ if unsaved_files is None:
+ unsaved_files = []
+
unsaved_files_array = 0
if len(unsaved_files):
unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))()
@@ -1734,15 +2154,25 @@ class TranslationUnit(ClangObject):
unsaved_files_array[i].name = name
unsaved_files_array[i].contents = value
unsaved_files_array[i].length = len(value)
- ptr = TranslationUnit_codeComplete(self, path,
- line, column,
- unsaved_files_array,
- len(unsaved_files),
- options)
+ ptr = lib.clang_codeCompleteAt(self, path, line, column,
+ unsaved_files_array, len(unsaved_files), options)
if ptr:
return CodeCompletionResults(ptr)
return None
+ def get_tokens(self, locations=None, extent=None):
+ """Obtain tokens in this translation unit.
+
+ This is a generator for Token instances. The caller specifies a range
+ of source code to obtain tokens for. The range can be specified as a
+ 2-tuple of SourceLocation or as a SourceRange. If both are defined,
+ behavior is undefined.
+ """
+ if locations is not None:
+ extent = SourceRange(start=locations[0], end=locations[1])
+
+ return TokenGroup.get_tokens(self, extent)
+
class File(ClangObject):
"""
The File class represents a particular source file that is part of a
@@ -1752,17 +2182,17 @@ class File(ClangObject):
@staticmethod
def from_name(translation_unit, file_name):
"""Retrieve a file handle within the given translation unit."""
- return File(File_getFile(translation_unit, file_name))
+ return File(lib.clang_getFile(translation_unit, file_name))
@property
def name(self):
"""Return the complete file and path name of the file."""
- return _CXString_getCString(File_name(self))
+ return lib.clang_getCString(lib.clang_getFileName(self))
@property
def time(self):
"""Return the last modification time of the file."""
- return File_time(self)
+ return lib.clang_getFileTime(self)
def __str__(self):
return self.name
@@ -1770,6 +2200,14 @@ class File(ClangObject):
def __repr__(self):
return "<File: %s>" % (self.name)
+ @staticmethod
+ def from_cursor_result(res, fn, args):
+ assert isinstance(res, File)
+
+ # Copy a reference to the TranslationUnit to prevent premature GC.
+ res._tu = args[0]._tu
+ return res
+
class FileInclusion(object):
"""
The FileInclusion class represents the inclusion of one source file by
@@ -1790,337 +2228,616 @@ class FileInclusion(object):
"""True if the included file is the input file."""
return self.depth == 0
-# Additional Functions and Types
+class CompilationDatabaseError(Exception):
+ """Represents an error that occurred when working with a CompilationDatabase
-# String Functions
-_CXString_dispose = lib.clang_disposeString
-_CXString_dispose.argtypes = [_CXString]
+ Each error is associated to an enumerated value, accessible under
+ e.cdb_error. Consumers can compare the value with one of the ERROR_
+ constants in this class.
+ """
-_CXString_getCString = lib.clang_getCString
-_CXString_getCString.argtypes = [_CXString]
-_CXString_getCString.restype = c_char_p
+ # An unknown error occured
+ ERROR_UNKNOWN = 0
-# Source Location Functions
-SourceLocation_loc = lib.clang_getInstantiationLocation
-SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p),
- POINTER(c_uint), POINTER(c_uint),
- POINTER(c_uint)]
-
-SourceLocation_getLocation = lib.clang_getLocation
-SourceLocation_getLocation.argtypes = [TranslationUnit, File, c_uint, c_uint]
-SourceLocation_getLocation.restype = SourceLocation
-
-SourceLocation_equalLocations = lib.clang_equalLocations
-SourceLocation_equalLocations.argtypes = [SourceLocation, SourceLocation]
-SourceLocation_equalLocations.restype = bool
-
-# Source Range Functions
-SourceRange_getRange = lib.clang_getRange
-SourceRange_getRange.argtypes = [SourceLocation, SourceLocation]
-SourceRange_getRange.restype = SourceRange
-
-SourceRange_start = lib.clang_getRangeStart
-SourceRange_start.argtypes = [SourceRange]
-SourceRange_start.restype = SourceLocation
-
-SourceRange_end = lib.clang_getRangeEnd
-SourceRange_end.argtypes = [SourceRange]
-SourceRange_end.restype = SourceLocation
-
-SourceRange_equalRanges = lib.clang_equalRanges
-SourceRange_equalRanges.argtypes = [SourceRange, SourceRange]
-SourceRange_equalRanges.restype = bool
-
-# CursorKind Functions
-CursorKind_is_decl = lib.clang_isDeclaration
-CursorKind_is_decl.argtypes = [CursorKind]
-CursorKind_is_decl.restype = bool
-
-CursorKind_is_ref = lib.clang_isReference
-CursorKind_is_ref.argtypes = [CursorKind]
-CursorKind_is_ref.restype = bool
-
-CursorKind_is_expr = lib.clang_isExpression
-CursorKind_is_expr.argtypes = [CursorKind]
-CursorKind_is_expr.restype = bool
-
-CursorKind_is_stmt = lib.clang_isStatement
-CursorKind_is_stmt.argtypes = [CursorKind]
-CursorKind_is_stmt.restype = bool
-
-CursorKind_is_attribute = lib.clang_isAttribute
-CursorKind_is_attribute.argtypes = [CursorKind]
-CursorKind_is_attribute.restype = bool
-
-CursorKind_is_inv = lib.clang_isInvalid
-CursorKind_is_inv.argtypes = [CursorKind]
-CursorKind_is_inv.restype = bool
-
-CursorKind_is_translation_unit = lib.clang_isTranslationUnit
-CursorKind_is_translation_unit.argtypes = [CursorKind]
-CursorKind_is_translation_unit.restype = bool
-
-CursorKind_is_preprocessing = lib.clang_isPreprocessing
-CursorKind_is_preprocessing.argtypes = [CursorKind]
-CursorKind_is_preprocessing.restype = bool
-
-CursorKind_is_unexposed = lib.clang_isUnexposed
-CursorKind_is_unexposed.argtypes = [CursorKind]
-CursorKind_is_unexposed.restype = bool
-
-# Cursor Functions
-# TODO: Implement this function
-Cursor_get = lib.clang_getCursor
-Cursor_get.argtypes = [TranslationUnit, SourceLocation]
-Cursor_get.restype = Cursor
-
-Cursor_null = lib.clang_getNullCursor
-Cursor_null.restype = Cursor
-
-Cursor_usr = lib.clang_getCursorUSR
-Cursor_usr.argtypes = [Cursor]
-Cursor_usr.restype = _CXString
-Cursor_usr.errcheck = _CXString.from_result
-
-Cursor_is_def = lib.clang_isCursorDefinition
-Cursor_is_def.argtypes = [Cursor]
-Cursor_is_def.restype = bool
-
-Cursor_def = lib.clang_getCursorDefinition
-Cursor_def.argtypes = [Cursor]
-Cursor_def.restype = Cursor
-Cursor_def.errcheck = Cursor.from_result
-
-Cursor_eq = lib.clang_equalCursors
-Cursor_eq.argtypes = [Cursor, Cursor]
-Cursor_eq.restype = bool
-
-Cursor_hash = lib.clang_hashCursor
-Cursor_hash.argtypes = [Cursor]
-Cursor_hash.restype = c_uint
-
-Cursor_spelling = lib.clang_getCursorSpelling
-Cursor_spelling.argtypes = [Cursor]
-Cursor_spelling.restype = _CXString
-Cursor_spelling.errcheck = _CXString.from_result
-
-Cursor_displayname = lib.clang_getCursorDisplayName
-Cursor_displayname.argtypes = [Cursor]
-Cursor_displayname.restype = _CXString
-Cursor_displayname.errcheck = _CXString.from_result
-
-Cursor_loc = lib.clang_getCursorLocation
-Cursor_loc.argtypes = [Cursor]
-Cursor_loc.restype = SourceLocation
-
-Cursor_extent = lib.clang_getCursorExtent
-Cursor_extent.argtypes = [Cursor]
-Cursor_extent.restype = SourceRange
-
-Cursor_ref = lib.clang_getCursorReferenced
-Cursor_ref.argtypes = [Cursor]
-Cursor_ref.restype = Cursor
-Cursor_ref.errcheck = Cursor.from_result
-
-Cursor_type = lib.clang_getCursorType
-Cursor_type.argtypes = [Cursor]
-Cursor_type.restype = Type
-Cursor_type.errcheck = Type.from_result
-
-Cursor_underlying_type = lib.clang_getTypedefDeclUnderlyingType
-Cursor_underlying_type.argtypes = [Cursor]
-Cursor_underlying_type.restype = Type
-Cursor_underlying_type.errcheck = Type.from_result
-
-Cursor_enum_type = lib.clang_getEnumDeclIntegerType
-Cursor_enum_type.argtypes = [Cursor]
-Cursor_enum_type.restype = Type
-Cursor_enum_type.errcheck = Type.from_result
-
-Cursor_objc_type_encoding = lib.clang_getDeclObjCTypeEncoding
-Cursor_objc_type_encoding.argtypes = [Cursor]
-Cursor_objc_type_encoding.restype = _CXString
-Cursor_objc_type_encoding.errcheck = _CXString.from_result
-
-Cursor_visit_callback = CFUNCTYPE(c_int, Cursor, Cursor, py_object)
-Cursor_visit = lib.clang_visitChildren
-Cursor_visit.argtypes = [Cursor, Cursor_visit_callback, py_object]
-Cursor_visit.restype = c_uint
-
-# Type Functions
-Type_get_canonical = lib.clang_getCanonicalType
-Type_get_canonical.argtypes = [Type]
-Type_get_canonical.restype = Type
-Type_get_canonical.errcheck = Type.from_result
-
-Type_is_const_qualified = lib.clang_isConstQualifiedType
-Type_is_const_qualified.argtypes = [Type]
-Type_is_const_qualified.restype = bool
-
-Type_is_volatile_qualified = lib.clang_isVolatileQualifiedType
-Type_is_volatile_qualified.argtypes = [Type]
-Type_is_volatile_qualified.restype = bool
-
-Type_is_restrict_qualified = lib.clang_isRestrictQualifiedType
-Type_is_restrict_qualified.argtypes = [Type]
-Type_is_restrict_qualified.restype = bool
-
-Type_is_pod = lib.clang_isPODType
-Type_is_pod.argtypes = [Type]
-Type_is_pod.restype = bool
-
-Type_is_variadic = lib.clang_isFunctionTypeVariadic
-Type_is_variadic.argtypes = [Type]
-Type_is_variadic.restype = bool
-
-Type_get_pointee = lib.clang_getPointeeType
-Type_get_pointee.argtypes = [Type]
-Type_get_pointee.restype = Type
-Type_get_pointee.errcheck = Type.from_result
-
-Type_get_declaration = lib.clang_getTypeDeclaration
-Type_get_declaration.argtypes = [Type]
-Type_get_declaration.restype = Cursor
-Type_get_declaration.errcheck = Cursor.from_result
-
-Type_get_result = lib.clang_getResultType
-Type_get_result.argtypes = [Type]
-Type_get_result.restype = Type
-Type_get_result.errcheck = Type.from_result
-
-Type_get_num_arg_types = lib.clang_getNumArgTypes
-Type_get_num_arg_types.argtypes = [Type]
-Type_get_num_arg_types.restype = c_uint
-
-Type_get_arg_type = lib.clang_getArgType
-Type_get_arg_type.argtypes = [Type, c_uint]
-Type_get_arg_type.restype = Type
-Type_get_arg_type.errcheck = Type.from_result
-Type_get_element_type = lib.clang_getElementType
-
-Type_get_element_type.argtypes = [Type]
-Type_get_element_type.restype = Type
-Type_get_element_type.errcheck = Type.from_result
-
-Type_get_num_elements = lib.clang_getNumElements
-Type_get_num_elements.argtypes = [Type]
-Type_get_num_elements.restype = c_longlong
-
-Type_get_array_element = lib.clang_getArrayElementType
-Type_get_array_element.argtypes = [Type]
-Type_get_array_element.restype = Type
-Type_get_array_element.errcheck = Type.from_result
-
-Type_get_array_size = lib.clang_getArraySize
-Type_get_array_size.argtype = [Type]
-Type_get_array_size.restype = c_longlong
-
-Type_equal = lib.clang_equalTypes
-Type_equal.argtypes = [Type, Type]
-Type_equal.restype = bool
-
-# Index Functions
-Index_create = lib.clang_createIndex
-Index_create.argtypes = [c_int, c_int]
-Index_create.restype = c_object_p
-
-Index_dispose = lib.clang_disposeIndex
-Index_dispose.argtypes = [Index]
-
-# Translation Unit Functions
-TranslationUnit_read = lib.clang_createTranslationUnit
-TranslationUnit_read.argtypes = [Index, c_char_p]
-TranslationUnit_read.restype = c_object_p
-
-TranslationUnit_parse = lib.clang_parseTranslationUnit
-TranslationUnit_parse.argtypes = [Index, c_char_p, c_void_p,
- c_int, c_void_p, c_int, c_int]
-TranslationUnit_parse.restype = c_object_p
-
-TranslationUnit_reparse = lib.clang_reparseTranslationUnit
-TranslationUnit_reparse.argtypes = [TranslationUnit, c_int, c_void_p, c_int]
-TranslationUnit_reparse.restype = c_int
-
-TranslationUnit_codeComplete = lib.clang_codeCompleteAt
-TranslationUnit_codeComplete.argtypes = [TranslationUnit, c_char_p, c_int,
- c_int, c_void_p, c_int, c_int]
-TranslationUnit_codeComplete.restype = POINTER(CCRStructure)
-
-TranslationUnit_cursor = lib.clang_getTranslationUnitCursor
-TranslationUnit_cursor.argtypes = [TranslationUnit]
-TranslationUnit_cursor.restype = Cursor
-TranslationUnit_cursor.errcheck = Cursor.from_result
-
-TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling
-TranslationUnit_spelling.argtypes = [TranslationUnit]
-TranslationUnit_spelling.restype = _CXString
-TranslationUnit_spelling.errcheck = _CXString.from_result
-
-TranslationUnit_dispose = lib.clang_disposeTranslationUnit
-TranslationUnit_dispose.argtypes = [TranslationUnit]
-
-TranslationUnit_includes_callback = CFUNCTYPE(None,
- c_object_p,
- POINTER(SourceLocation),
- c_uint, py_object)
-TranslationUnit_includes = lib.clang_getInclusions
-TranslationUnit_includes.argtypes = [TranslationUnit,
- TranslationUnit_includes_callback,
- py_object]
-
-# File Functions
-File_getFile = lib.clang_getFile
-File_getFile.argtypes = [TranslationUnit, c_char_p]
-File_getFile.restype = c_object_p
-
-File_name = lib.clang_getFileName
-File_name.argtypes = [File]
-File_name.restype = _CXString
-
-File_time = lib.clang_getFileTime
-File_time.argtypes = [File]
-File_time.restype = c_uint
-
-# Code completion
-
-CodeCompletionResults_dispose = lib.clang_disposeCodeCompleteResults
-CodeCompletionResults_dispose.argtypes = [CodeCompletionResults]
-
-_clang_codeCompleteGetNumDiagnostics = lib.clang_codeCompleteGetNumDiagnostics
-_clang_codeCompleteGetNumDiagnostics.argtypes = [CodeCompletionResults]
-_clang_codeCompleteGetNumDiagnostics.restype = c_int
-
-_clang_codeCompleteGetDiagnostic = lib.clang_codeCompleteGetDiagnostic
-_clang_codeCompleteGetDiagnostic.argtypes = [CodeCompletionResults, c_int]
-_clang_codeCompleteGetDiagnostic.restype = Diagnostic
-
-_clang_getCompletionChunkText = lib.clang_getCompletionChunkText
-_clang_getCompletionChunkText.argtypes = [c_void_p, c_int]
-_clang_getCompletionChunkText.restype = _CXString
+ # The database could not be loaded
+ ERROR_CANNOTLOADDATABASE = 1
-_clang_getCompletionChunkKind = lib.clang_getCompletionChunkKind
-_clang_getCompletionChunkKind.argtypes = [c_void_p, c_int]
-_clang_getCompletionChunkKind.restype = c_int
+ def __init__(self, enumeration, message):
+ assert isinstance(enumeration, int)
-_clang_getCompletionChunkCompletionString = lib.clang_getCompletionChunkCompletionString
-_clang_getCompletionChunkCompletionString.argtypes = [c_void_p, c_int]
-_clang_getCompletionChunkCompletionString.restype = c_object_p
+ if enumeration > 1:
+ raise Exception("Encountered undefined CompilationDatabase error "
+ "constant: %d. Please file a bug to have this "
+ "value supported." % enumeration)
-_clang_getNumCompletionChunks = lib.clang_getNumCompletionChunks
-_clang_getNumCompletionChunks.argtypes = [c_void_p]
-_clang_getNumCompletionChunks.restype = c_int
+ self.cdb_error = enumeration
+ Exception.__init__(self, 'Error %d: %s' % (enumeration, message))
-_clang_getCompletionAvailability = lib.clang_getCompletionAvailability
-_clang_getCompletionAvailability.argtypes = [c_void_p]
-_clang_getCompletionAvailability.restype = c_int
+class CompileCommand(object):
+ """Represents the compile command used to build a file"""
+ def __init__(self, cmd, ccmds):
+ self.cmd = cmd
+ # Keep a reference to the originating CompileCommands
+ # to prevent garbage collection
+ self.ccmds = ccmds
-_clang_getCompletionPriority = lib.clang_getCompletionPriority
-_clang_getCompletionPriority.argtypes = [c_void_p]
-_clang_getCompletionPriority.restype = c_int
+ @property
+ def directory(self):
+ """Get the working directory for this CompileCommand"""
+ return lib.clang_CompileCommand_getDirectory(self.cmd)
+ @property
+ def arguments(self):
+ """
+ Get an iterable object providing each argument in the
+ command line for the compiler invocation as a _CXString.
-###
+ Invariant : the first argument is the compiler executable
+ """
+ length = lib.clang_CompileCommand_getNumArgs(self.cmd)
+ for i in xrange(length):
+ yield lib.clang_CompileCommand_getArg(self.cmd, i)
+
+class CompileCommands(object):
+ """
+ CompileCommands is an iterable object containing all CompileCommand
+ that can be used for building a specific file.
+ """
+ def __init__(self, ccmds):
+ self.ccmds = ccmds
+
+ def __del__(self):
+ lib.clang_CompileCommands_dispose(self.ccmds)
+
+ def __len__(self):
+ return int(lib.clang_CompileCommands_getSize(self.ccmds))
+
+ def __getitem__(self, i):
+ cc = lib.clang_CompileCommands_getCommand(self.ccmds, i)
+ if not cc:
+ raise IndexError
+ return CompileCommand(cc, self)
+
+ @staticmethod
+ def from_result(res, fn, args):
+ if not res:
+ return None
+ return CompileCommands(res)
+
+class CompilationDatabase(ClangObject):
+ """
+ The CompilationDatabase is a wrapper class around
+ clang::tooling::CompilationDatabase
+
+ It enables querying how a specific source file can be built.
+ """
+
+ def __del__(self):
+ lib.clang_CompilationDatabase_dispose(self)
+
+ @staticmethod
+ def from_result(res, fn, args):
+ if not res:
+ raise CompilationDatabaseError(0,
+ "CompilationDatabase loading failed")
+ return CompilationDatabase(res)
+
+ @staticmethod
+ def fromDirectory(buildDir):
+ """Builds a CompilationDatabase from the database found in buildDir"""
+ errorCode = c_uint()
+ try:
+ cdb = lib.clang_CompilationDatabase_fromDirectory(buildDir,
+ byref(errorCode))
+ except CompilationDatabaseError as e:
+ raise CompilationDatabaseError(int(errorCode.value),
+ "CompilationDatabase loading failed")
+ return cdb
+
+ def getCompileCommands(self, filename):
+ """
+ Get an iterable object providing all the CompileCommands available to
+ build filename. Returns None if filename is not found in the database.
+ """
+ return lib.clang_CompilationDatabase_getCompileCommands(self, filename)
+
+class Token(Structure):
+ """Represents a single token from the preprocessor.
+
+ Tokens are effectively segments of source code. Source code is first parsed
+ into tokens before being converted into the AST and Cursors.
+
+ Tokens are obtained from parsed TranslationUnit instances. You currently
+ can't create tokens manually.
+ """
+ _fields_ = [
+ ('int_data', c_uint * 4),
+ ('ptr_data', c_void_p)
+ ]
+
+ @property
+ def spelling(self):
+ """The spelling of this token.
+
+ This is the textual representation of the token in source.
+ """
+ return lib.clang_getTokenSpelling(self._tu, self)
+
+ @property
+ def kind(self):
+ """Obtain the TokenKind of the current token."""
+ return TokenKind.from_value(lib.clang_getTokenKind(self))
+
+ @property
+ def location(self):
+ """The SourceLocation this Token occurs at."""
+ return lib.clang_getTokenLocation(self._tu, self)
+
+ @property
+ def extent(self):
+ """The SourceRange this Token occupies."""
+ return lib.clang_getTokenExtent(self._tu, self)
+
+ @property
+ def cursor(self):
+ """The Cursor this Token corresponds to."""
+ cursor = Cursor()
+
+ lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor))
+
+ return cursor
+
+# Now comes the plumbing to hook up the C library.
+
+# Register callback types in common container.
+callbacks['translation_unit_includes'] = CFUNCTYPE(None, c_object_p,
+ POINTER(SourceLocation), c_uint, py_object)
+callbacks['cursor_visit'] = CFUNCTYPE(c_int, Cursor, Cursor, py_object)
+
+def register_functions(lib):
+ """Register function prototypes with a libclang library instance.
+
+ This must be called as part of library instantiation so Python knows how
+ to call out to the shared library.
+ """
+ # Functions are registered in strictly alphabetical order.
+ lib.clang_annotateTokens.argtype = [TranslationUnit, POINTER(Token),
+ c_uint, POINTER(Cursor)]
+
+ lib.clang_CompilationDatabase_dispose.argtypes = [c_object_p]
+
+ lib.clang_CompilationDatabase_fromDirectory.argtypes = [c_char_p,
+ POINTER(c_uint)]
+ lib.clang_CompilationDatabase_fromDirectory.restype = c_object_p
+ lib.clang_CompilationDatabase_fromDirectory.errcheck = CompilationDatabase.from_result
+
+ lib.clang_CompilationDatabase_getCompileCommands.argtypes = [c_object_p, c_char_p]
+ lib.clang_CompilationDatabase_getCompileCommands.restype = c_object_p
+ lib.clang_CompilationDatabase_getCompileCommands.errcheck = CompileCommands.from_result
+
+ lib.clang_CompileCommands_dispose.argtypes = [c_object_p]
+
+ lib.clang_CompileCommands_getCommand.argtypes = [c_object_p, c_uint]
+ lib.clang_CompileCommands_getCommand.restype = c_object_p
+
+ lib.clang_CompileCommands_getSize.argtypes = [c_object_p]
+ lib.clang_CompileCommands_getSize.restype = c_uint
+
+ lib.clang_CompileCommand_getArg.argtypes = [c_object_p, c_uint]
+ lib.clang_CompileCommand_getArg.restype = _CXString
+ lib.clang_CompileCommand_getArg.errcheck = _CXString.from_result
+
+ lib.clang_CompileCommand_getDirectory.argtypes = [c_object_p]
+ lib.clang_CompileCommand_getDirectory.restype = _CXString
+ lib.clang_CompileCommand_getDirectory.errcheck = _CXString.from_result
+
+ lib.clang_CompileCommand_getNumArgs.argtypes = [c_object_p]
+ lib.clang_CompileCommand_getNumArgs.restype = c_uint
+
+ lib.clang_codeCompleteAt.argtypes = [TranslationUnit, c_char_p, c_int,
+ c_int, c_void_p, c_int, c_int]
+ lib.clang_codeCompleteAt.restype = POINTER(CCRStructure)
+
+ lib.clang_codeCompleteGetDiagnostic.argtypes = [CodeCompletionResults,
+ c_int]
+ lib.clang_codeCompleteGetDiagnostic.restype = Diagnostic
+
+ lib.clang_codeCompleteGetNumDiagnostics.argtypes = [CodeCompletionResults]
+ lib.clang_codeCompleteGetNumDiagnostics.restype = c_int
+
+ lib.clang_createIndex.argtypes = [c_int, c_int]
+ lib.clang_createIndex.restype = c_object_p
+
+ lib.clang_createTranslationUnit.argtypes = [Index, c_char_p]
+ lib.clang_createTranslationUnit.restype = c_object_p
+
+ lib.clang_CXXMethod_isStatic.argtypes = [Cursor]
+ lib.clang_CXXMethod_isStatic.restype = bool
+
+ lib.clang_CXXMethod_isVirtual.argtypes = [Cursor]
+ lib.clang_CXXMethod_isVirtual.restype = bool
+
+ lib.clang_defaultSaveOptions.argtypes = [TranslationUnit]
+ lib.clang_defaultSaveOptions.restype = c_uint
+
+ lib.clang_disposeCodeCompleteResults.argtypes = [CodeCompletionResults]
+
+ #lib.clang_disposeCXTUResourceUsage.argtypes = [CXTUResourceUsage]
+
+ lib.clang_disposeDiagnostic.argtypes = [Diagnostic]
+
+ lib.clang_disposeIndex.argtypes = [Index]
+
+ lib.clang_disposeString.argtypes = [_CXString]
+
+ lib.clang_disposeTokens.argtype = [TranslationUnit, POINTER(Token), c_uint]
+
+ lib.clang_disposeTranslationUnit.argtypes = [TranslationUnit]
+
+ lib.clang_equalCursors.argtypes = [Cursor, Cursor]
+ lib.clang_equalCursors.restype = bool
+
+ lib.clang_equalLocations.argtypes = [SourceLocation, SourceLocation]
+ lib.clang_equalLocations.restype = bool
+
+ lib.clang_equalRanges.argtypes = [SourceRange, SourceRange]
+ lib.clang_equalRanges.restype = bool
+
+ lib.clang_equalTypes.argtypes = [Type, Type]
+ lib.clang_equalTypes.restype = bool
+
+ lib.clang_getArgType.argtypes = [Type, c_uint]
+ lib.clang_getArgType.restype = Type
+ lib.clang_getArgType.errcheck = Type.from_result
+
+ lib.clang_getArrayElementType.argtypes = [Type]
+ lib.clang_getArrayElementType.restype = Type
+ lib.clang_getArrayElementType.errcheck = Type.from_result
+
+ lib.clang_getArraySize.argtypes = [Type]
+ lib.clang_getArraySize.restype = c_longlong
+
+ lib.clang_getCanonicalCursor.argtypes = [Cursor]
+ lib.clang_getCanonicalCursor.restype = Cursor
+ lib.clang_getCanonicalCursor.errcheck = Cursor.from_cursor_result
+
+ lib.clang_getCanonicalType.argtypes = [Type]
+ lib.clang_getCanonicalType.restype = Type
+ lib.clang_getCanonicalType.errcheck = Type.from_result
+
+ lib.clang_getCompletionAvailability.argtypes = [c_void_p]
+ lib.clang_getCompletionAvailability.restype = c_int
+
+ lib.clang_getCompletionChunkCompletionString.argtypes = [c_void_p, c_int]
+ lib.clang_getCompletionChunkCompletionString.restype = c_object_p
+
+ lib.clang_getCompletionChunkKind.argtypes = [c_void_p, c_int]
+ lib.clang_getCompletionChunkKind.restype = c_int
+
+ lib.clang_getCompletionChunkText.argtypes = [c_void_p, c_int]
+ lib.clang_getCompletionChunkText.restype = _CXString
+
+ lib.clang_getCompletionPriority.argtypes = [c_void_p]
+ lib.clang_getCompletionPriority.restype = c_int
+
+ lib.clang_getCString.argtypes = [_CXString]
+ lib.clang_getCString.restype = c_char_p
+
+ lib.clang_getCursor.argtypes = [TranslationUnit, SourceLocation]
+ lib.clang_getCursor.restype = Cursor
+
+ lib.clang_getCursorDefinition.argtypes = [Cursor]
+ lib.clang_getCursorDefinition.restype = Cursor
+ lib.clang_getCursorDefinition.errcheck = Cursor.from_result
+
+ lib.clang_getCursorDisplayName.argtypes = [Cursor]
+ lib.clang_getCursorDisplayName.restype = _CXString
+ lib.clang_getCursorDisplayName.errcheck = _CXString.from_result
+
+ lib.clang_getCursorExtent.argtypes = [Cursor]
+ lib.clang_getCursorExtent.restype = SourceRange
+
+ lib.clang_getCursorLexicalParent.argtypes = [Cursor]
+ lib.clang_getCursorLexicalParent.restype = Cursor
+ lib.clang_getCursorLexicalParent.errcheck = Cursor.from_cursor_result
+
+ lib.clang_getCursorLocation.argtypes = [Cursor]
+ lib.clang_getCursorLocation.restype = SourceLocation
+
+ lib.clang_getCursorReferenced.argtypes = [Cursor]
+ lib.clang_getCursorReferenced.restype = Cursor
+ lib.clang_getCursorReferenced.errcheck = Cursor.from_result
+
+ lib.clang_getCursorReferenceNameRange.argtypes = [Cursor, c_uint, c_uint]
+ lib.clang_getCursorReferenceNameRange.restype = SourceRange
+
+ lib.clang_getCursorSemanticParent.argtypes = [Cursor]
+ lib.clang_getCursorSemanticParent.restype = Cursor
+ lib.clang_getCursorSemanticParent.errcheck = Cursor.from_cursor_result
+
+ lib.clang_getCursorSpelling.argtypes = [Cursor]
+ lib.clang_getCursorSpelling.restype = _CXString
+ lib.clang_getCursorSpelling.errcheck = _CXString.from_result
+
+ lib.clang_getCursorType.argtypes = [Cursor]
+ lib.clang_getCursorType.restype = Type
+ lib.clang_getCursorType.errcheck = Type.from_result
+
+ lib.clang_getCursorUSR.argtypes = [Cursor]
+ lib.clang_getCursorUSR.restype = _CXString
+ lib.clang_getCursorUSR.errcheck = _CXString.from_result
+
+ #lib.clang_getCXTUResourceUsage.argtypes = [TranslationUnit]
+ #lib.clang_getCXTUResourceUsage.restype = CXTUResourceUsage
+
+ lib.clang_getCXXAccessSpecifier.argtypes = [Cursor]
+ lib.clang_getCXXAccessSpecifier.restype = c_uint
+
+ lib.clang_getDeclObjCTypeEncoding.argtypes = [Cursor]
+ lib.clang_getDeclObjCTypeEncoding.restype = _CXString
+ lib.clang_getDeclObjCTypeEncoding.errcheck = _CXString.from_result
+
+ lib.clang_getDiagnostic.argtypes = [c_object_p, c_uint]
+ lib.clang_getDiagnostic.restype = c_object_p
+
+ lib.clang_getDiagnosticCategory.argtypes = [Diagnostic]
+ lib.clang_getDiagnosticCategory.restype = c_uint
+
+ lib.clang_getDiagnosticCategoryName.argtypes = [c_uint]
+ lib.clang_getDiagnosticCategoryName.restype = _CXString
+ lib.clang_getDiagnosticCategoryName.errcheck = _CXString.from_result
+
+ lib.clang_getDiagnosticFixIt.argtypes = [Diagnostic, c_uint,
+ POINTER(SourceRange)]
+ lib.clang_getDiagnosticFixIt.restype = _CXString
+ lib.clang_getDiagnosticFixIt.errcheck = _CXString.from_result
+
+ lib.clang_getDiagnosticLocation.argtypes = [Diagnostic]
+ lib.clang_getDiagnosticLocation.restype = SourceLocation
+
+ lib.clang_getDiagnosticNumFixIts.argtypes = [Diagnostic]
+ lib.clang_getDiagnosticNumFixIts.restype = c_uint
+
+ lib.clang_getDiagnosticNumRanges.argtypes = [Diagnostic]
+ lib.clang_getDiagnosticNumRanges.restype = c_uint
+
+ lib.clang_getDiagnosticOption.argtypes = [Diagnostic, POINTER(_CXString)]
+ lib.clang_getDiagnosticOption.restype = _CXString
+ lib.clang_getDiagnosticOption.errcheck = _CXString.from_result
+
+ lib.clang_getDiagnosticRange.argtypes = [Diagnostic, c_uint]
+ lib.clang_getDiagnosticRange.restype = SourceRange
+
+ lib.clang_getDiagnosticSeverity.argtypes = [Diagnostic]
+ lib.clang_getDiagnosticSeverity.restype = c_int
+
+ lib.clang_getDiagnosticSpelling.argtypes = [Diagnostic]
+ lib.clang_getDiagnosticSpelling.restype = _CXString
+ lib.clang_getDiagnosticSpelling.errcheck = _CXString.from_result
+
+ lib.clang_getElementType.argtypes = [Type]
+ lib.clang_getElementType.restype = Type
+ lib.clang_getElementType.errcheck = Type.from_result
+
+ lib.clang_getEnumConstantDeclUnsignedValue.argtypes = [Cursor]
+ lib.clang_getEnumConstantDeclUnsignedValue.restype = c_ulonglong
+
+ lib.clang_getEnumConstantDeclValue.argtypes = [Cursor]
+ lib.clang_getEnumConstantDeclValue.restype = c_longlong
+
+ lib.clang_getEnumDeclIntegerType.argtypes = [Cursor]
+ lib.clang_getEnumDeclIntegerType.restype = Type
+ lib.clang_getEnumDeclIntegerType.errcheck = Type.from_result
+
+ lib.clang_getFile.argtypes = [TranslationUnit, c_char_p]
+ lib.clang_getFile.restype = c_object_p
+
+ lib.clang_getFileName.argtypes = [File]
+ lib.clang_getFileName.restype = _CXString
+ # TODO go through _CXString.from_result?
+
+ lib.clang_getFileTime.argtypes = [File]
+ lib.clang_getFileTime.restype = c_uint
+
+ lib.clang_getIBOutletCollectionType.argtypes = [Cursor]
+ lib.clang_getIBOutletCollectionType.restype = Type
+ lib.clang_getIBOutletCollectionType.errcheck = Type.from_result
+
+ lib.clang_getIncludedFile.argtypes = [Cursor]
+ lib.clang_getIncludedFile.restype = File
+ lib.clang_getIncludedFile.errcheck = File.from_cursor_result
+
+ lib.clang_getInclusions.argtypes = [TranslationUnit,
+ callbacks['translation_unit_includes'], py_object]
+
+ lib.clang_getInstantiationLocation.argtypes = [SourceLocation,
+ POINTER(c_object_p), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint)]
+
+ lib.clang_getLocation.argtypes = [TranslationUnit, File, c_uint, c_uint]
+ lib.clang_getLocation.restype = SourceLocation
+
+ lib.clang_getLocationForOffset.argtypes = [TranslationUnit, File, c_uint]
+ lib.clang_getLocationForOffset.restype = SourceLocation
+
+ lib.clang_getNullCursor.restype = Cursor
+
+ lib.clang_getNumArgTypes.argtypes = [Type]
+ lib.clang_getNumArgTypes.restype = c_uint
+
+ lib.clang_getNumCompletionChunks.argtypes = [c_void_p]
+ lib.clang_getNumCompletionChunks.restype = c_int
+
+ lib.clang_getNumDiagnostics.argtypes = [c_object_p]
+ lib.clang_getNumDiagnostics.restype = c_uint
+
+ lib.clang_getNumElements.argtypes = [Type]
+ lib.clang_getNumElements.restype = c_longlong
+
+ lib.clang_getNumOverloadedDecls.argtypes = [Cursor]
+ lib.clang_getNumOverloadedDecls.restyp = c_uint
+
+ lib.clang_getOverloadedDecl.argtypes = [Cursor, c_uint]
+ lib.clang_getOverloadedDecl.restype = Cursor
+ lib.clang_getOverloadedDecl.errcheck = Cursor.from_cursor_result
+
+ lib.clang_getPointeeType.argtypes = [Type]
+ lib.clang_getPointeeType.restype = Type
+ lib.clang_getPointeeType.errcheck = Type.from_result
+
+ lib.clang_getRange.argtypes = [SourceLocation, SourceLocation]
+ lib.clang_getRange.restype = SourceRange
+
+ lib.clang_getRangeEnd.argtypes = [SourceRange]
+ lib.clang_getRangeEnd.restype = SourceLocation
+
+ lib.clang_getRangeStart.argtypes = [SourceRange]
+ lib.clang_getRangeStart.restype = SourceLocation
+
+ lib.clang_getResultType.argtypes = [Type]
+ lib.clang_getResultType.restype = Type
+ lib.clang_getResultType.errcheck = Type.from_result
+
+ lib.clang_getSpecializedCursorTemplate.argtypes = [Cursor]
+ lib.clang_getSpecializedCursorTemplate.restype = Cursor
+ lib.clang_getSpecializedCursorTemplate.errcheck = Cursor.from_cursor_result
+
+ lib.clang_getTemplateCursorKind.argtypes = [Cursor]
+ lib.clang_getTemplateCursorKind.restype = c_uint
+
+ lib.clang_getTokenExtent.argtypes = [TranslationUnit, Token]
+ lib.clang_getTokenExtent.restype = SourceRange
+
+ lib.clang_getTokenKind.argtypes = [Token]
+ lib.clang_getTokenKind.restype = c_uint
+
+ lib.clang_getTokenLocation.argtype = [TranslationUnit, Token]
+ lib.clang_getTokenLocation.restype = SourceLocation
+
+ lib.clang_getTokenSpelling.argtype = [TranslationUnit, Token]
+ lib.clang_getTokenSpelling.restype = _CXString
+ lib.clang_getTokenSpelling.errcheck = _CXString.from_result
+
+ lib.clang_getTranslationUnitCursor.argtypes = [TranslationUnit]
+ lib.clang_getTranslationUnitCursor.restype = Cursor
+ lib.clang_getTranslationUnitCursor.errcheck = Cursor.from_result
+
+ lib.clang_getTranslationUnitSpelling.argtypes = [TranslationUnit]
+ lib.clang_getTranslationUnitSpelling.restype = _CXString
+ lib.clang_getTranslationUnitSpelling.errcheck = _CXString.from_result
+
+ lib.clang_getTUResourceUsageName.argtypes = [c_uint]
+ lib.clang_getTUResourceUsageName.restype = c_char_p
+
+ lib.clang_getTypeDeclaration.argtypes = [Type]
+ lib.clang_getTypeDeclaration.restype = Cursor
+ lib.clang_getTypeDeclaration.errcheck = Cursor.from_result
+
+ lib.clang_getTypedefDeclUnderlyingType.argtypes = [Cursor]
+ lib.clang_getTypedefDeclUnderlyingType.restype = Type
+ lib.clang_getTypedefDeclUnderlyingType.errcheck = Type.from_result
+
+ lib.clang_getTypeKindSpelling.argtypes = [c_uint]
+ lib.clang_getTypeKindSpelling.restype = _CXString
+ lib.clang_getTypeKindSpelling.errcheck = _CXString.from_result
+
+ lib.clang_hashCursor.argtypes = [Cursor]
+ lib.clang_hashCursor.restype = c_uint
+
+ lib.clang_isAttribute.argtypes = [CursorKind]
+ lib.clang_isAttribute.restype = bool
+
+ lib.clang_isConstQualifiedType.argtypes = [Type]
+ lib.clang_isConstQualifiedType.restype = bool
+
+ lib.clang_isCursorDefinition.argtypes = [Cursor]
+ lib.clang_isCursorDefinition.restype = bool
+
+ lib.clang_isDeclaration.argtypes = [CursorKind]
+ lib.clang_isDeclaration.restype = bool
+
+ lib.clang_isExpression.argtypes = [CursorKind]
+ lib.clang_isExpression.restype = bool
+
+ lib.clang_isFileMultipleIncludeGuarded.argtypes = [TranslationUnit, File]
+ lib.clang_isFileMultipleIncludeGuarded.restype = bool
+
+ lib.clang_isFunctionTypeVariadic.argtypes = [Type]
+ lib.clang_isFunctionTypeVariadic.restype = bool
+
+ lib.clang_isInvalid.argtypes = [CursorKind]
+ lib.clang_isInvalid.restype = bool
+
+ lib.clang_isPODType.argtypes = [Type]
+ lib.clang_isPODType.restype = bool
+
+ lib.clang_isPreprocessing.argtypes = [CursorKind]
+ lib.clang_isPreprocessing.restype = bool
+
+ lib.clang_isReference.argtypes = [CursorKind]
+ lib.clang_isReference.restype = bool
+
+ lib.clang_isRestrictQualifiedType.argtypes = [Type]
+ lib.clang_isRestrictQualifiedType.restype = bool
+
+ lib.clang_isStatement.argtypes = [CursorKind]
+ lib.clang_isStatement.restype = bool
+
+ lib.clang_isTranslationUnit.argtypes = [CursorKind]
+ lib.clang_isTranslationUnit.restype = bool
+
+ lib.clang_isUnexposed.argtypes = [CursorKind]
+ lib.clang_isUnexposed.restype = bool
+
+ lib.clang_isVirtualBase.argtypes = [Cursor]
+ lib.clang_isVirtualBase.restype = bool
+
+ lib.clang_isVolatileQualifiedType.argtypes = [Type]
+ lib.clang_isVolatileQualifiedType.restype = bool
+
+ lib.clang_parseTranslationUnit.argypes = [Index, c_char_p, c_void_p, c_int,
+ c_void_p, c_int, c_int]
+ lib.clang_parseTranslationUnit.restype = c_object_p
+
+ lib.clang_reparseTranslationUnit.argtypes = [TranslationUnit, c_int,
+ c_void_p, c_int]
+ lib.clang_reparseTranslationUnit.restype = c_int
+
+ lib.clang_saveTranslationUnit.argtypes = [TranslationUnit, c_char_p,
+ c_uint]
+ lib.clang_saveTranslationUnit.restype = c_int
+
+ lib.clang_tokenize.argtypes = [TranslationUnit, SourceRange,
+ POINTER(POINTER(Token)), POINTER(c_uint)]
+
+ lib.clang_visitChildren.argtypes = [Cursor, callbacks['cursor_visit'],
+ py_object]
+ lib.clang_visitChildren.restype = c_uint
+
+register_functions(lib)
+
+def register_enumerations():
+ for name, value in clang.enumerations.TokenKinds:
+ TokenKind.register(value, name)
+
+register_enumerations()
-__all__ = ['Index', 'TranslationUnit', 'Cursor', 'CursorKind', 'Type', 'TypeKind',
- 'Diagnostic', 'FixIt', 'CodeCompletionResults', 'SourceRange',
- 'SourceLocation', 'File']
+__all__ = [
+ 'CodeCompletionResults',
+ 'CompilationDatabase',
+ 'CompileCommands',
+ 'CompileCommand',
+ 'CursorKind',
+ 'Cursor',
+ 'Diagnostic',
+ 'File',
+ 'FixIt',
+ 'Index',
+ 'SourceLocation',
+ 'SourceRange',
+ 'TokenKind',
+ 'Token',
+ 'TranslationUnitLoadError',
+ 'TranslationUnit',
+ 'TypeKind',
+ 'Type',
+]
diff --git a/bindings/python/clang/enumerations.py b/bindings/python/clang/enumerations.py
new file mode 100644
index 0000000..a86a48a
--- /dev/null
+++ b/bindings/python/clang/enumerations.py
@@ -0,0 +1,34 @@
+#===- enumerations.py - Python Enumerations ------------------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+"""
+Clang Enumerations
+==================
+
+This module provides static definitions of enumerations that exist in libclang.
+
+Enumerations are typically defined as a list of tuples. The exported values are
+typically munged into other types or classes at module load time.
+
+All enumerations are centrally defined in this file so they are all grouped
+together and easier to audit. And, maybe even one day this file will be
+automatically generated by scanning the libclang headers!
+"""
+
+# Maps to CXTokenKind. Note that libclang maintains a separate set of token
+# enumerations from the C++ API.
+TokenKinds = [
+ ('PUNCTUATION', 0),
+ ('KEYWORD', 1),
+ ('IDENTIFIER', 2),
+ ('LITERAL', 3),
+ ('COMMENT', 4),
+]
+
+__all__ = ['TokenKinds']
diff --git a/bindings/python/tests/cindex/INPUTS/compile_commands.json b/bindings/python/tests/cindex/INPUTS/compile_commands.json
new file mode 100644
index 0000000..944150b
--- /dev/null
+++ b/bindings/python/tests/cindex/INPUTS/compile_commands.json
@@ -0,0 +1,17 @@
+[
+{
+ "directory": "/home/john.doe/MyProject",
+ "command": "clang++ -o project.o -c /home/john.doe/MyProject/project.cpp",
+ "file": "/home/john.doe/MyProject/project.cpp"
+},
+{
+ "directory": "/home/john.doe/MyProjectA",
+ "command": "clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp",
+ "file": "/home/john.doe/MyProject/project2.cpp"
+},
+{
+ "directory": "/home/john.doe/MyProjectB",
+ "command": "clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp",
+ "file": "/home/john.doe/MyProject/project2.cpp"
+}
+]
diff --git a/bindings/python/tests/cindex/test_cdb.py b/bindings/python/tests/cindex/test_cdb.py
new file mode 100644
index 0000000..d0f580e
--- /dev/null
+++ b/bindings/python/tests/cindex/test_cdb.py
@@ -0,0 +1,89 @@
+from clang.cindex import CompilationDatabase
+from clang.cindex import CompilationDatabaseError
+from clang.cindex import CompileCommands
+from clang.cindex import CompileCommand
+import os
+import gc
+
+kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
+
+def test_create_fail():
+ """Check we fail loading a database with an assertion"""
+ path = os.path.dirname(__file__)
+ try:
+ cdb = CompilationDatabase.fromDirectory(path)
+ except CompilationDatabaseError as e:
+ assert e.cdb_error == CompilationDatabaseError.ERROR_CANNOTLOADDATABASE
+ else:
+ assert False
+
+def test_create():
+ """Check we can load a compilation database"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+
+def test_lookup_fail():
+ """Check file lookup failure"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ assert cdb.getCompileCommands('file_do_not_exist.cpp') == None
+
+def test_lookup_succeed():
+ """Check we get some results if the file exists in the db"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+ assert len(cmds) != 0
+
+def test_1_compilecommand():
+ """Check file with single compile command"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+ assert len(cmds) == 1
+ assert cmds[0].directory == '/home/john.doe/MyProject'
+ expected = [ 'clang++', '-o', 'project.o', '-c',
+ '/home/john.doe/MyProject/project.cpp']
+ for arg, exp in zip(cmds[0].arguments, expected):
+ assert arg == exp
+
+def test_2_compilecommand():
+ """Check file with 2 compile commands"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp')
+ assert len(cmds) == 2
+ expected = [
+ { 'wd': '/home/john.doe/MyProjectA',
+ 'line': ['clang++', '-o', 'project2.o', '-c',
+ '/home/john.doe/MyProject/project2.cpp']},
+ { 'wd': '/home/john.doe/MyProjectB',
+ 'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
+ '/home/john.doe/MyProject/project2.cpp']}
+ ]
+ for i in range(len(cmds)):
+ assert cmds[i].directory == expected[i]['wd']
+ for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
+ assert arg == exp
+
+def test_compilecommand_iterator_stops():
+ """Check that iterator stops after the correct number of elements"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ count = 0
+ for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'):
+ count += 1
+ assert count <= 2
+
+def test_compilationDB_references():
+ """Ensure CompilationsCommands are independent of the database"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+ del cdb
+ gc.collect()
+ workingdir = cmds[0].directory
+
+def test_compilationCommands_references():
+ """Ensure CompilationsCommand keeps a reference to CompilationCommands"""
+ cdb = CompilationDatabase.fromDirectory(kInputsDir)
+ cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
+ del cdb
+ cmd0 = cmds[0]
+ del cmds
+ gc.collect()
+ workingdir = cmd0.directory
+
diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py
index 9f02bb2..51695e2 100644
--- a/bindings/python/tests/cindex/test_cursor.py
+++ b/bindings/python/tests/cindex/test_cursor.py
@@ -1,6 +1,10 @@
+import gc
+
from clang.cindex import CursorKind
+from clang.cindex import TranslationUnit
from clang.cindex import TypeKind
from .util import get_cursor
+from .util import get_cursors
from .util import get_tu
kInput = """\
@@ -37,6 +41,8 @@ def test_get_children():
tu_nodes = list(it)
assert len(tu_nodes) == 3
+ for cursor in tu_nodes:
+ assert cursor.translation_unit is not None
assert tu_nodes[0] != tu_nodes[1]
assert tu_nodes[0].kind == CursorKind.STRUCT_DECL
@@ -46,6 +52,7 @@ def test_get_children():
assert tu_nodes[0].location.line == 4
assert tu_nodes[0].location.column == 8
assert tu_nodes[0].hash > 0
+ assert tu_nodes[0].translation_unit is not None
s0_nodes = list(tu_nodes[0].get_children())
assert len(s0_nodes) == 2
@@ -66,6 +73,51 @@ def test_get_children():
assert tu_nodes[2].displayname == 'f0(int, int)'
assert tu_nodes[2].is_definition() == True
+def test_references():
+ """Ensure that references to TranslationUnit are kept."""
+ tu = get_tu('int x;')
+ cursors = list(tu.cursor.get_children())
+ assert len(cursors) > 0
+
+ cursor = cursors[0]
+ assert isinstance(cursor.translation_unit, TranslationUnit)
+
+ # Delete reference to TU and perform a full GC.
+ del tu
+ gc.collect()
+ assert isinstance(cursor.translation_unit, TranslationUnit)
+
+ # If the TU was destroyed, this should cause a segfault.
+ parent = cursor.semantic_parent
+
+def test_canonical():
+ source = 'struct X; struct X; struct X { int member; };'
+ tu = get_tu(source)
+
+ cursors = []
+ for cursor in tu.cursor.get_children():
+ if cursor.spelling == 'X':
+ cursors.append(cursor)
+
+ assert len(cursors) == 3
+ assert cursors[1].canonical == cursors[2].canonical
+
+def test_is_static_method():
+ """Ensure Cursor.is_static_method works."""
+
+ source = 'class X { static void foo(); void bar(); };'
+ tu = get_tu(source, lang='cpp')
+
+ cls = get_cursor(tu, 'X')
+ foo = get_cursor(tu, 'foo')
+ bar = get_cursor(tu, 'bar')
+ assert cls is not None
+ assert foo is not None
+ assert bar is not None
+
+ assert foo.is_static_method()
+ assert not bar.is_static_method()
+
def test_underlying_type():
tu = get_tu('typedef int foo;')
typedef = get_cursor(tu, 'foo')
@@ -75,6 +127,30 @@ def test_underlying_type():
underlying = typedef.underlying_typedef_type
assert underlying.kind == TypeKind.INT
+kParentTest = """\
+ class C {
+ void f();
+ }
+
+ void C::f() { }
+ """
+def test_semantic_parent():
+ tu = get_tu(kParentTest, 'cpp')
+ curs = get_cursors(tu, 'f')
+ decl = get_cursor(tu, 'C')
+ assert(len(curs) == 2)
+ assert(curs[0].semantic_parent == curs[1].semantic_parent)
+ assert(curs[0].semantic_parent == decl)
+
+def test_lexical_parent():
+ tu = get_tu(kParentTest, 'cpp')
+ curs = get_cursors(tu, 'f')
+ decl = get_cursor(tu, 'C')
+ assert(len(curs) == 2)
+ assert(curs[0].lexical_parent != curs[1].lexical_parent)
+ assert(curs[0].lexical_parent == decl)
+ assert(curs[1].lexical_parent == tu.cursor)
+
def test_enum_type():
tu = get_tu('enum TEST { FOO=1, BAR=2 };')
enum = get_cursor(tu, 'TEST')
@@ -84,9 +160,84 @@ def test_enum_type():
enum_type = enum.enum_type
assert enum_type.kind == TypeKind.UINT
+def test_enum_type_cpp():
+ tu = get_tu('enum TEST : long long { FOO=1, BAR=2 };', lang="cpp")
+ enum = get_cursor(tu, 'TEST')
+ assert enum is not None
+
+ assert enum.kind == CursorKind.ENUM_DECL
+ assert enum.enum_type.kind == TypeKind.LONGLONG
+
def test_objc_type_encoding():
tu = get_tu('int i;', lang='objc')
i = get_cursor(tu, 'i')
assert i is not None
assert i.objc_type_encoding == 'i'
+
+def test_enum_values():
+ tu = get_tu('enum TEST { SPAM=1, EGG, HAM = EGG * 20};')
+ enum = get_cursor(tu, 'TEST')
+ assert enum is not None
+
+ assert enum.kind == CursorKind.ENUM_DECL
+
+ enum_constants = list(enum.get_children())
+ assert len(enum_constants) == 3
+
+ spam, egg, ham = enum_constants
+
+ assert spam.kind == CursorKind.ENUM_CONSTANT_DECL
+ assert spam.enum_value == 1
+ assert egg.kind == CursorKind.ENUM_CONSTANT_DECL
+ assert egg.enum_value == 2
+ assert ham.kind == CursorKind.ENUM_CONSTANT_DECL
+ assert ham.enum_value == 40
+
+def test_enum_values_cpp():
+ tu = get_tu('enum TEST : long long { SPAM = -1, HAM = 0x10000000000};', lang="cpp")
+ enum = get_cursor(tu, 'TEST')
+ assert enum is not None
+
+ assert enum.kind == CursorKind.ENUM_DECL
+
+ enum_constants = list(enum.get_children())
+ assert len(enum_constants) == 2
+
+ spam, ham = enum_constants
+
+ assert spam.kind == CursorKind.ENUM_CONSTANT_DECL
+ assert spam.enum_value == -1
+ assert ham.kind == CursorKind.ENUM_CONSTANT_DECL
+ assert ham.enum_value == 0x10000000000
+
+def test_annotation_attribute():
+ tu = get_tu('int foo (void) __attribute__ ((annotate("here be annotation attribute")));')
+
+ foo = get_cursor(tu, 'foo')
+ assert foo is not None
+
+ for c in foo.get_children():
+ if c.kind == CursorKind.ANNOTATE_ATTR:
+ assert c.displayname == "here be annotation attribute"
+ break
+ else:
+ assert False, "Couldn't find annotation"
+
+def test_result_type():
+ tu = get_tu('int foo();')
+ foo = get_cursor(tu, 'foo')
+
+ assert foo is not None
+ t = foo.result_type
+ assert t.kind == TypeKind.INT
+
+def test_get_tokens():
+ """Ensure we can map cursors back to tokens."""
+ tu = get_tu('int foo(int i);')
+ foo = get_cursor(tu, 'foo')
+
+ tokens = list(foo.get_tokens())
+ assert len(tokens) == 7
+ assert tokens[0].spelling == 'int'
+ assert tokens[1].spelling == 'foo'
diff --git a/bindings/python/tests/cindex/test_location.py b/bindings/python/tests/cindex/test_location.py
index 528676e..9e9ef48 100644
--- a/bindings/python/tests/cindex/test_location.py
+++ b/bindings/python/tests/cindex/test_location.py
@@ -60,6 +60,15 @@ def test_location():
location3 = SourceLocation.from_position(tu, file, 1, 4)
assert location2 != location3
+ offset_location = SourceLocation.from_offset(tu, file, 5)
+ cursor = Cursor.from_location(tu, offset_location)
+ verified = False
+ for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']:
+ assert n == cursor
+ verified = True
+
+ assert verified
+
def test_extent():
tu = get_tu(baseInput)
one = get_cursor(tu, 'one')
diff --git a/bindings/python/tests/cindex/test_token_kind.py b/bindings/python/tests/cindex/test_token_kind.py
new file mode 100644
index 0000000..62ec63e
--- /dev/null
+++ b/bindings/python/tests/cindex/test_token_kind.py
@@ -0,0 +1,43 @@
+from clang.cindex import TokenKind
+from nose.tools import eq_
+from nose.tools import ok_
+from nose.tools import raises
+
+def test_constructor():
+ """Ensure TokenKind constructor works as expected."""
+
+ t = TokenKind(5, 'foo')
+
+ eq_(t.value, 5)
+ eq_(t.name, 'foo')
+
+@raises(ValueError)
+def test_bad_register():
+ """Ensure a duplicate value is rejected for registration."""
+
+ TokenKind.register(2, 'foo')
+
+@raises(ValueError)
+def test_unknown_value():
+ """Ensure trying to fetch an unknown value raises."""
+
+ TokenKind.from_value(-1)
+
+def test_registration():
+ """Ensure that items registered appear as class attributes."""
+ ok_(hasattr(TokenKind, 'LITERAL'))
+ literal = TokenKind.LITERAL
+
+ ok_(isinstance(literal, TokenKind))
+
+def test_from_value():
+ """Ensure registered values can be obtained from from_value()."""
+ t = TokenKind.from_value(3)
+ ok_(isinstance(t, TokenKind))
+ eq_(t, TokenKind.LITERAL)
+
+def test_repr():
+ """Ensure repr() works."""
+
+ r = repr(TokenKind.LITERAL)
+ eq_(r, 'TokenKind.LITERAL')
diff --git a/bindings/python/tests/cindex/test_tokens.py b/bindings/python/tests/cindex/test_tokens.py
new file mode 100644
index 0000000..7074842
--- /dev/null
+++ b/bindings/python/tests/cindex/test_tokens.py
@@ -0,0 +1,52 @@
+from clang.cindex import CursorKind
+from clang.cindex import Index
+from clang.cindex import SourceLocation
+from clang.cindex import SourceRange
+from clang.cindex import TokenKind
+from nose.tools import eq_
+from nose.tools import ok_
+
+from .util import get_tu
+
+def test_token_to_cursor():
+ """Ensure we can obtain a Cursor from a Token instance."""
+ tu = get_tu('int i = 5;')
+ r = tu.get_extent('t.c', (0, 9))
+ tokens = list(tu.get_tokens(extent=r))
+
+ assert len(tokens) == 5
+ assert tokens[1].spelling == 'i'
+ assert tokens[1].kind == TokenKind.IDENTIFIER
+
+ cursor = tokens[1].cursor
+ assert cursor.kind == CursorKind.VAR_DECL
+ assert tokens[1].cursor == tokens[2].cursor
+
+def test_token_location():
+ """Ensure Token.location works."""
+
+ tu = get_tu('int foo = 10;')
+ r = tu.get_extent('t.c', (0, 11))
+
+ tokens = list(tu.get_tokens(extent=r))
+ eq_(len(tokens), 4)
+
+ loc = tokens[1].location
+ ok_(isinstance(loc, SourceLocation))
+ eq_(loc.line, 1)
+ eq_(loc.column, 5)
+ eq_(loc.offset, 4)
+
+def test_token_extent():
+ """Ensure Token.extent works."""
+ tu = get_tu('int foo = 10;')
+ r = tu.get_extent('t.c', (0, 11))
+
+ tokens = list(tu.get_tokens(extent=r))
+ eq_(len(tokens), 4)
+
+ extent = tokens[1].extent
+ ok_(isinstance(extent, SourceRange))
+
+ eq_(extent.start.offset, 4)
+ eq_(extent.end.offset, 7)
diff --git a/bindings/python/tests/cindex/test_translation_unit.py b/bindings/python/tests/cindex/test_translation_unit.py
index 2e65d95..c91f126 100644
--- a/bindings/python/tests/cindex/test_translation_unit.py
+++ b/bindings/python/tests/cindex/test_translation_unit.py
@@ -1,42 +1,48 @@
-from clang.cindex import *
+import gc
import os
+from clang.cindex import CursorKind
+from clang.cindex import Cursor
+from clang.cindex import File
+from clang.cindex import Index
+from clang.cindex import SourceLocation
+from clang.cindex import SourceRange
+from clang.cindex import TranslationUnitSaveError
+from clang.cindex import TranslationUnit
+from .util import get_cursor
+from .util import get_tu
+
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
def test_spelling():
path = os.path.join(kInputsDir, 'hello.cpp')
- index = Index.create()
- tu = index.parse(path)
+ tu = TranslationUnit.from_source(path)
assert tu.spelling == path
def test_cursor():
path = os.path.join(kInputsDir, 'hello.cpp')
- index = Index.create()
- tu = index.parse(path)
+ tu = get_tu(path)
c = tu.cursor
assert isinstance(c, Cursor)
assert c.kind is CursorKind.TRANSLATION_UNIT
def test_parse_arguments():
path = os.path.join(kInputsDir, 'parse_arguments.c')
- index = Index.create()
- tu = index.parse(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
+ tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-2] == 'hello'
assert spellings[-1] == 'hi'
def test_reparse_arguments():
path = os.path.join(kInputsDir, 'parse_arguments.c')
- index = Index.create()
- tu = index.parse(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
+ tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
tu.reparse()
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-2] == 'hello'
assert spellings[-1] == 'hi'
def test_unsaved_files():
- index = Index.create()
- tu = index.parse('fake.c', ['-I./'], unsaved_files = [
+ tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
('fake.c', """
#include "fake.h"
int x;
@@ -52,8 +58,7 @@ int SOME_DEFINE;
def test_unsaved_files_2():
import StringIO
- index = Index.create()
- tu = index.parse('fake.c', unsaved_files = [
+ tu = TranslationUnit.from_source('fake.c', unsaved_files = [
('fake.c', StringIO.StringIO('int x;'))])
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-1] == 'x'
@@ -78,7 +83,159 @@ def test_includes():
h3 = os.path.join(kInputsDir, "header3.h")
inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
- index = Index.create()
- tu = index.parse(src)
+ tu = TranslationUnit.from_source(src)
for i in zip(inc, tu.get_includes()):
assert eq(i[0], i[1])
+
+def save_tu(tu):
+ """Convenience API to save a TranslationUnit to a file.
+
+ Returns the filename it was saved to.
+ """
+
+ # FIXME Generate a temp file path using system APIs.
+ base = 'TEMP_FOR_TRANSLATIONUNIT_SAVE.c'
+ path = os.path.join(kInputsDir, base)
+
+ # Just in case.
+ if os.path.exists(path):
+ os.unlink(path)
+
+ tu.save(path)
+
+ return path
+
+def test_save():
+ """Ensure TranslationUnit.save() works."""
+
+ tu = get_tu('int foo();')
+
+ path = save_tu(tu)
+ assert os.path.exists(path)
+ assert os.path.getsize(path) > 0
+ os.unlink(path)
+
+def test_save_translation_errors():
+ """Ensure that saving to an invalid directory raises."""
+
+ tu = get_tu('int foo();')
+
+ path = '/does/not/exist/llvm-test.ast'
+ assert not os.path.exists(os.path.dirname(path))
+
+ try:
+ tu.save(path)
+ assert False
+ except TranslationUnitSaveError as ex:
+ expected = TranslationUnitSaveError.ERROR_UNKNOWN
+ assert ex.save_error == expected
+
+def test_load():
+ """Ensure TranslationUnits can be constructed from saved files."""
+
+ tu = get_tu('int foo();')
+ assert len(tu.diagnostics) == 0
+ path = save_tu(tu)
+
+ assert os.path.exists(path)
+ assert os.path.getsize(path) > 0
+
+ tu2 = TranslationUnit.from_ast_file(filename=path)
+ assert len(tu2.diagnostics) == 0
+
+ foo = get_cursor(tu2, 'foo')
+ assert foo is not None
+
+ # Just in case there is an open file descriptor somewhere.
+ del tu2
+
+ os.unlink(path)
+
+def test_index_parse():
+ path = os.path.join(kInputsDir, 'hello.cpp')
+ index = Index.create()
+ tu = index.parse(path)
+ assert isinstance(tu, TranslationUnit)
+
+def test_get_file():
+ """Ensure tu.get_file() works appropriately."""
+
+ tu = get_tu('int foo();')
+
+ f = tu.get_file('t.c')
+ assert isinstance(f, File)
+ assert f.name == 't.c'
+
+ try:
+ f = tu.get_file('foobar.cpp')
+ except:
+ pass
+ else:
+ assert False
+
+def test_get_source_location():
+ """Ensure tu.get_source_location() works."""
+
+ tu = get_tu('int foo();')
+
+ location = tu.get_location('t.c', 2)
+ assert isinstance(location, SourceLocation)
+ assert location.offset == 2
+ assert location.file.name == 't.c'
+
+ location = tu.get_location('t.c', (1, 3))
+ assert isinstance(location, SourceLocation)
+ assert location.line == 1
+ assert location.column == 3
+ assert location.file.name == 't.c'
+
+def test_get_source_range():
+ """Ensure tu.get_source_range() works."""
+
+ tu = get_tu('int foo();')
+
+ r = tu.get_extent('t.c', (1,4))
+ assert isinstance(r, SourceRange)
+ assert r.start.offset == 1
+ assert r.end.offset == 4
+ assert r.start.file.name == 't.c'
+ assert r.end.file.name == 't.c'
+
+ r = tu.get_extent('t.c', ((1,2), (1,3)))
+ assert isinstance(r, SourceRange)
+ assert r.start.line == 1
+ assert r.start.column == 2
+ assert r.end.line == 1
+ assert r.end.column == 3
+ assert r.start.file.name == 't.c'
+ assert r.end.file.name == 't.c'
+
+ start = tu.get_location('t.c', 0)
+ end = tu.get_location('t.c', 5)
+
+ r = tu.get_extent('t.c', (start, end))
+ assert isinstance(r, SourceRange)
+ assert r.start.offset == 0
+ assert r.end.offset == 5
+ assert r.start.file.name == 't.c'
+ assert r.end.file.name == 't.c'
+
+def test_get_tokens_gc():
+ """Ensures get_tokens() works properly with garbage collection."""
+
+ tu = get_tu('int foo();')
+ r = tu.get_extent('t.c', (0, 10))
+ tokens = list(tu.get_tokens(extent=r))
+
+ assert tokens[0].spelling == 'int'
+ gc.collect()
+ assert tokens[0].spelling == 'int'
+
+ del tokens[1]
+ gc.collect()
+ assert tokens[0].spelling == 'int'
+
+ # May trigger segfault if we don't do our job properly.
+ del tokens
+ gc.collect()
+ gc.collect() # Just in case.
diff --git a/bindings/python/tests/cindex/test_type.py b/bindings/python/tests/cindex/test_type.py
index 03621f3..28e4411 100644
--- a/bindings/python/tests/cindex/test_type.py
+++ b/bindings/python/tests/cindex/test_type.py
@@ -1,4 +1,7 @@
+import gc
+
from clang.cindex import CursorKind
+from clang.cindex import TranslationUnit
from clang.cindex import TypeKind
from nose.tools import raises
from .util import get_cursor
@@ -28,6 +31,7 @@ def test_a_struct():
assert teststruct is not None, "Could not find teststruct."
fields = list(teststruct.get_children())
assert all(x.kind == CursorKind.FIELD_DECL for x in fields)
+ assert all(x.translation_unit is not None for x in fields)
assert fields[0].spelling == 'a'
assert not fields[0].type.is_const_qualified()
@@ -72,6 +76,26 @@ def test_a_struct():
assert fields[7].type.get_pointee().get_pointee().kind == TypeKind.POINTER
assert fields[7].type.get_pointee().get_pointee().get_pointee().kind == TypeKind.INT
+def test_references():
+ """Ensure that a Type maintains a reference to a TranslationUnit."""
+
+ tu = get_tu('int x;')
+ children = list(tu.cursor.get_children())
+ assert len(children) > 0
+
+ cursor = children[0]
+ t = cursor.type
+
+ assert isinstance(t.translation_unit, TranslationUnit)
+
+ # Delete main TranslationUnit reference and force a GC.
+ del tu
+ gc.collect()
+ assert isinstance(t.translation_unit, TranslationUnit)
+
+ # If the TU was destroyed, this should cause a segfault.
+ decl = t.get_declaration()
+
constarrayInput="""
struct teststruct {
void *A[2];
@@ -263,7 +287,7 @@ def test_is_volatile_qualified():
def test_is_restrict_qualified():
"""Ensure Type.is_restrict_qualified works."""
- tu = get_tu('struct s { void * restrict i; void * j };')
+ tu = get_tu('struct s { void * restrict i; void * j; };')
i = get_cursor(tu, 'i')
j = get_cursor(tu, 'j')
diff --git a/bindings/python/tests/cindex/util.py b/bindings/python/tests/cindex/util.py
index 388b269..2323839 100644
--- a/bindings/python/tests/cindex/util.py
+++ b/bindings/python/tests/cindex/util.py
@@ -1,7 +1,7 @@
# This file provides common utility functions for the test suite.
from clang.cindex import Cursor
-from clang.cindex import Index
+from clang.cindex import TranslationUnit
def get_tu(source, lang='c', all_warnings=False):
"""Obtain a translation unit from source and language.
@@ -15,21 +15,20 @@ def get_tu(source, lang='c', all_warnings=False):
all_warnings is a convenience argument to enable all compiler warnings.
"""
name = 't.c'
+ args = []
if lang == 'cpp':
name = 't.cpp'
+ args.append('-std=c++11')
elif lang == 'objc':
name = 't.m'
elif lang != 'c':
raise Exception('Unknown language: %s' % lang)
- args = []
if all_warnings:
- args = ['-Wall', '-Wextra']
+ args += ['-Wall', '-Wextra']
- index = Index.create()
- tu = index.parse(name, args=args, unsaved_files=[(name, source)])
- assert tu is not None
- return tu
+ return TranslationUnit.from_source(name, args, unsaved_files=[(name,
+ source)])
def get_cursor(source, spelling):
"""Obtain a cursor from a source object.
@@ -57,9 +56,38 @@ def get_cursor(source, spelling):
return result
return None
+
+def get_cursors(source, spelling):
+ """Obtain all cursors from a source object with a specific spelling.
+
+ This provides a convenient search mechanism to find all cursors with specific
+ spelling within a source. The first argument can be either a
+ TranslationUnit or Cursor instance.
+
+ If no cursors are found, an empty list is returned.
+ """
+ cursors = []
+ children = []
+ if isinstance(source, Cursor):
+ children = source.get_children()
+ else:
+ # Assume TU
+ children = source.cursor.get_children()
+
+ for cursor in children:
+ if cursor.spelling == spelling:
+ cursors.append(cursor)
+
+ # Recurse into children.
+ cursors.extend(get_cursors(cursor, spelling))
+
+ return cursors
+
+
__all__ = [
'get_cursor',
+ 'get_cursors',
'get_tu',
]
diff --git a/bindings/xml/comment-xml-schema.rng b/bindings/xml/comment-xml-schema.rng
new file mode 100644
index 0000000..a0329f8
--- /dev/null
+++ b/bindings/xml/comment-xml-schema.rng
@@ -0,0 +1,434 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <choice>
+ <!-- Everything else not explicitly mentioned below. -->
+ <ref name="Other" />
+
+ <ref name="Function" />
+ <ref name="Class" />
+ <ref name="Variable" />
+ <ref name="Namespace" />
+ <ref name="Typedef" />
+ <ref name="Enum" />
+ </choice>
+ </start>
+
+ <define name="Other">
+ <element name="Other">
+ <ref name="attrSourceLocation" />
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="Function">
+ <element name="Function">
+ <optional>
+ <attribute name="templateKind">
+ <choice>
+ <value>template</value>
+ <value>specialization</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="attrSourceLocation" />
+
+ <optional>
+ <attribute name="isInstanceMethod">
+ <data type="boolean" />
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="isClassMethod">
+ <data type="boolean" />
+ </attribute>
+ </optional>
+
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <!-- TODO: Add exception specification. -->
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="Class">
+ <element name="Class">
+ <optional>
+ <attribute name="templateKind">
+ <choice>
+ <value>template</value>
+ <value>specialization</value>
+ <value>partialSpecialization</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="attrSourceLocation" />
+
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+
+ <!-- Parameters and results don't make sense for classes, but the user
+ can specify \param or \returns in a comment anyway. -->
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="Variable">
+ <element name="Variable">
+ <ref name="attrSourceLocation" />
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+
+ <!-- Template parameters, parameters and results don't make sense for
+ variables, but the user can specify \tparam \param or \returns
+ in a comment anyway. -->
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="Namespace">
+ <element name="Namespace">
+ <ref name="attrSourceLocation" />
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+
+ <!-- Template parameters, parameters and results don't make sense for
+ namespaces, but the user can specify \tparam, \param or \returns
+ in a comment anyway. -->
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="Typedef">
+ <element name="Typedef">
+ <ref name="attrSourceLocation" />
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+
+ <!-- Parameters and results might make sense for typedefs if the type is
+ a function pointer type. -->
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="Enum">
+ <element name="Enum">
+ <ref name="attrSourceLocation" />
+ <ref name="Name" />
+ <optional>
+ <ref name="USR" />
+ </optional>
+ <optional>
+ <ref name="Abstract" />
+ </optional>
+
+ <!-- Template parameters, parameters and results don't make sense for
+ enums, but the user can specify \tparam \param or \returns in a
+ comment anyway. -->
+ <optional>
+ <ref name="TemplateParameters" />
+ </optional>
+ <optional>
+ <ref name="Parameters" />
+ </optional>
+ <optional>
+ <ref name="ResultDiscussion" />
+ </optional>
+
+ <optional>
+ <ref name="Discussion" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="attrSourceLocation">
+ <optional>
+ <attribute name="file">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="line">
+ <data type="positiveInteger" />
+ </attribute>
+ <attribute name="column">
+ <data type="positiveInteger" />
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="Name">
+ <element name="Name">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ </define>
+
+ <define name="USR">
+ <element name="USR">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ </define>
+
+ <define name="Abstract">
+ <element name="Abstract">
+ <zeroOrMore>
+ <ref name="TextBlockContent" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="Discussion">
+ <element name="Discussion">
+ <oneOrMore>
+ <ref name="TextBlockContent" />
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="TemplateParameters">
+ <element name="TemplateParameters">
+ <!-- Parameter elements should be sorted according to position. -->
+ <oneOrMore>
+ <element name="Parameter">
+ <element name="Name">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ <optional>
+ <!-- This is index at depth 0. libclang API can return more
+ information about position, but we expose only essential
+ information here, since "Parameter" elements are already
+ sorted.
+
+ "Position" element could be added in future if needed. -->
+ <element name="Index">
+ <data type="nonNegativeInteger" />
+ </element>
+ </optional>
+ <!-- In general, template parameters with whitespace discussion
+ should not be emitted. Schema might be more strict here. -->
+ <element name="Discussion">
+ <ref name="TextBlockContent" />
+ </element>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="Parameters">
+ <element name="Parameters">
+ <!-- Parameter elements should be sorted according to index. -->
+ <oneOrMore>
+ <element name="Parameter">
+ <element name="Name">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ <optional>
+ <element name="Index">
+ <data type="nonNegativeInteger" />
+ </element>
+ </optional>
+ <element name="Direction">
+ <attribute name="isExplicit">
+ <data type="boolean" />
+ </attribute>
+ <choice>
+ <value>in</value>
+ <value>out</value>
+ <value>in,out</value>
+ </choice>
+ </element>
+ <!-- In general, template parameters with whitespace discussion
+ should not be emitted, unless direction is explicitly specified.
+ Schema might be more strict here. -->
+ <element name="Discussion">
+ <ref name="TextBlockContent" />
+ </element>
+ </element>
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="ResultDiscussion">
+ <element name="ResultDiscussion">
+ <zeroOrMore>
+ <ref name="TextBlockContent" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="TextBlockContent">
+ <choice>
+ <element name="Para">
+ <zeroOrMore>
+ <ref name="TextInlineContent" />
+ </zeroOrMore>
+ </element>
+ <element name="Verbatim">
+ <attribute name="xml:space">
+ <value>preserve</value>
+ </attribute>
+ <attribute name="kind">
+ <!-- TODO: add all Doxygen verbatim kinds -->
+ <choice>
+ <value>code</value>
+ <value>verbatim</value>
+ </choice>
+ </attribute>
+ <text />
+ </element>
+ </choice>
+ </define>
+
+ <define name="TextInlineContent">
+ <choice>
+ <text />
+ <element name="bold">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ <element name="monospaced">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ <element name="emphasized">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ <element name="rawHTML">
+ <!-- Non-empty text content. -->
+ <data type="string">
+ <param name="pattern">.*\S.*</param>
+ </data>
+ </element>
+ </choice>
+ </define>
+
+</grammar>
+
diff --git a/docs/AddressSanitizer.html b/docs/AddressSanitizer.html
index c1dc91b..98ea934 100644
--- a/docs/AddressSanitizer.html
+++ b/docs/AddressSanitizer.html
@@ -21,14 +21,16 @@
<h1>AddressSanitizer</h1>
<ul>
- <li> <a href="intro">Introduction</a>
- <li> <a href="howtobuild">How to Build</a>
- <li> <a href="usage">Usage</a>
- <ul><li> <a href="has_feature">__has_feature(address_sanitizer)</a></ul>
- <li> <a href="platforms">Supported Platforms</a>
- <li> <a href="limitations">Limitations</a>
- <li> <a href="status">Current Status</a>
- <li> <a href="moreinfo">More Information</a>
+ <li> <a href="#intro">Introduction</a>
+ <li> <a href="#howtobuild">How to Build</a>
+ <li> <a href="#usage">Usage</a>
+ <ul><li> <a href="#has_feature">__has_feature(address_sanitizer)</a></ul>
+ <ul><li> <a href="#no_address_safety_analysis">
+ __attribute__((no_address_safety_analysis))</a></ul>
+ <li> <a href="#platforms">Supported Platforms</a>
+ <li> <a href="#limitations">Limitations</a>
+ <li> <a href="#status">Current Status</a>
+ <li> <a href="#moreinfo">More Information</a>
</ul>
<h2 id="intro">Introduction</h2>
@@ -97,36 +99,47 @@ AddressSanitizer is enabled.
<a href="LanguageExtensions.html#__has_feature_extension">__has_feature</a>
can be used for this purpose.
<pre>
-#if defined(__has_feature) &amp;&amp; __has_feature(address_sanitizer)
- code that runs only under AddressSanitizer
-#else
- code that does not run under AddressSanitizer
+#if defined(__has_feature)
+# if __has_feature(address_sanitizer)
+ code that builds only under AddressSanitizer
+# endif
#endif
</pre>
+<h3 id="no_address_safety_analysis">__attribute__((no_address_safety_analysis))</h3>
+Some code should not be instrumentated by AddressSanitizer.
+One may use the function attribute
+<a href="LanguageExtensions.html#address_sanitizer">
+ <tt>no_address_safety_analysis</tt></a>
+to disable instrumentation of a particular function.
+This attribute may not be supported by other compilers, so we suggest to
+use it together with <tt>__has_feature(address_sanitizer)</tt>.
+Note: currently, this attribute will be lost if the function is inlined.
+
<h2 id="platforms">Supported Platforms</h2>
AddressSanitizer is supported on
<ul><li>Linux x86_64 (tested on Ubuntu 10.04).
-<li>MacOS 10.6 i386/x86_64.
+<li>MacOS 10.6 and 10.7 (i386/x86_64).
</ul>
-Support for Linux i386/ARM and MacOS 10.7 is in progress
+Support for Linux i386/ARM is in progress
(it may work, but is not guaranteed too).
<h2 id="limitations">Limitations</h2>
<ul>
- <li> AddressSanitizer uses more real memory than a native run.
- How much -- depends on the allocations sizes. The smaller the
- allocations you make the bigger the overhead.
- <li> On 64-bit platforms AddressSanitizer maps (but not reserves)
- 16+ Terabytes of virtual address space.
- This means that tools like <tt>ulimit</tt> may not work as usually expected.
- <li> Static linking is not supported.
+<li> AddressSanitizer uses more real memory than a native run.
+How much -- depends on the allocations sizes. The smaller the
+allocations you make the bigger the overhead.
+<li> AddressSanitizer uses more stack memory. We have seen up to 3x increase.
+<li> On 64-bit platforms AddressSanitizer maps (but not reserves)
+16+ Terabytes of virtual address space.
+This means that tools like <tt>ulimit</tt> may not work as usually expected.
+<li> Static linking is not supported.
</ul>
<h2 id="status">Current Status</h2>
-AddressSanitizer is fully functional on supported platforms in LLVM head.
+AddressSanitizer is fully functional on supported platforms starting from LLVM 3.1.
However, the test suite is not fully integrated yet and we lack the testing
process (buildbots).
diff --git a/docs/AutomaticReferenceCounting.html b/docs/AutomaticReferenceCounting.html
index 1416df5..3f1ccaf 100644
--- a/docs/AutomaticReferenceCounting.html
+++ b/docs/AutomaticReferenceCounting.html
@@ -313,7 +313,7 @@ interoperation:</p>
<ul>
<li>The type system must reliably identify which objects are to be
managed. An <tt>int*</tt> might be a pointer to a <tt>malloc</tt>'ed
-array, or it might be a interior pointer to such an array, or it might
+array, or it might be an interior pointer to such an array, or it might
point to some field or local variable. In contrast, values of the
retainable object pointer types are never interior.</li>
<li>The type system must reliably indicate how to
@@ -1863,7 +1863,7 @@ of these annotations. A program is ill-formed if it annotates the
same function with both <tt>cf_audited_transfer</tt>
and <tt>cf_unknown_transfer</tt>.</p>
-<p>A pragma is provided to faciliate the mass annotation of interfaces:</p>
+<p>A pragma is provided to facilitate the mass annotation of interfaces:</p>
<pre>#pragma arc_cf_code_audited begin
...
diff --git a/docs/ClangPlugins.html b/docs/ClangPlugins.html
new file mode 100644
index 0000000..ed560fe
--- /dev/null
+++ b/docs/ClangPlugins.html
@@ -0,0 +1,170 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Clang Plugins</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>Clang Plugins</h1>
+<p>Clang Plugins make it possible to run extra user defined actions during
+a compilation. This document will provide a basic walkthrough of how to write
+and run a Clang Plugin.</p>
+
+<!-- ======================================================================= -->
+<h2 id="intro">Introduction</h2>
+<!-- ======================================================================= -->
+
+<p>Clang Plugins run FrontendActions over code. See the
+<a href="RAVFrontendAction.html">FrontendAction tutorial</a> on how to write a
+FrontendAction using the RecursiveASTVisitor. In this tutorial, we'll
+demonstrate how to write a simple clang plugin.
+</p>
+
+<!-- ======================================================================= -->
+<h2 id="pluginactions">Writing a PluginASTAction</h2>
+<!-- ======================================================================= -->
+
+<p>The main difference from writing normal FrontendActions is that you can
+handle plugin command line options. The
+PluginASTAction base class declares a ParseArgs method which you have to
+implement in your plugin.
+</p>
+<pre>
+ bool ParseArgs(const CompilerInstance &amp;CI,
+ const std::vector&lt;std::string>&amp; args) {
+ for (unsigned i = 0, e = args.size(); i != e; ++i) {
+ if (args[i] == "-some-arg") {
+ // Handle the command line argument.
+ }
+ }
+ return true;
+ }
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="registerplugin">Registering a plugin</h2>
+<!-- ======================================================================= -->
+
+<p>A plugin is loaded from a dynamic library at runtime by the compiler. To register
+a plugin in a library, use FrontendPluginRegistry::Add:</p>
+<pre>
+ static FrontendPluginRegistry::Add&lt;MyPlugin> X("my-plugin-name", "my plugin description");
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="example">Putting it all together</h2>
+<!-- ======================================================================= -->
+
+<p>Let's look at an example plugin that prints top-level function names.
+This example is also checked into the clang repository; please also take a look
+at the latest <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/PrintFunctionNames.cpp?view=markup">checked in version of PrintFunctionNames.cpp</a>.</p>
+<pre>
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/AST.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace clang;
+
+namespace {
+
+class PrintFunctionsConsumer : public ASTConsumer {
+public:
+ virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
+ for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; ++i) {
+ const Decl *D = *i;
+ if (const NamedDecl *ND = dyn_cast&lt;NamedDecl>(D))
+ llvm::errs() &lt;&lt; "top-level-decl: \"" &lt;&lt; ND->getNameAsString() &lt;&lt; "\"\n";
+ }
+
+ return true;
+ }
+};
+
+class PrintFunctionNamesAction : public PluginASTAction {
+protected:
+ ASTConsumer *CreateASTConsumer(CompilerInstance &amp;CI, llvm::StringRef) {
+ return new PrintFunctionsConsumer();
+ }
+
+ bool ParseArgs(const CompilerInstance &amp;CI,
+ const std::vector&lt;std::string>&amp; args) {
+ for (unsigned i = 0, e = args.size(); i != e; ++i) {
+ llvm::errs() &lt;&lt; "PrintFunctionNames arg = " &lt;&lt; args[i] &lt;&lt; "\n";
+
+ // Example error handling.
+ if (args[i] == "-an-error") {
+ DiagnosticsEngine &amp;D = CI.getDiagnostics();
+ unsigned DiagID = D.getCustomDiagID(
+ DiagnosticsEngine::Error, "invalid argument '" + args[i] + "'");
+ D.Report(DiagID);
+ return false;
+ }
+ }
+ if (args.size() &amp;&amp; args[0] == "help")
+ PrintHelp(llvm::errs());
+
+ return true;
+ }
+ void PrintHelp(llvm::raw_ostream&amp; ros) {
+ ros &lt;&lt; "Help for PrintFunctionNames plugin goes here\n";
+ }
+
+};
+
+}
+
+static FrontendPluginRegistry::Add&lt;PrintFunctionNamesAction>
+X("print-fns", "print function names");
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="running">Running the plugin</h2>
+<!-- ======================================================================= -->
+
+<p>To run a plugin, the dynamic library containing the plugin registry must be
+loaded via the -load command line option. This will load all plugins that are
+registered, and you can select the plugins to run by specifying the -plugin
+option. Additional parameters for the plugins can be passed with -plugin-arg-&lt;plugin-name>.</p>
+
+<p>Note that those options must reach clang's cc1 process. There are two
+ways to do so:</p>
+<ul>
+<li>
+Directly call the parsing process by using the -cc1 option; this has the
+downside of not configuring the default header search paths, so you'll need to
+specify the full system path configuration on the command line.
+</li>
+<li>
+Use clang as usual, but prefix all arguments to the cc1 process with -Xclang.
+</li>
+</ul>
+<p>For example, to run the print-function-names plugin over a source file in clang,
+first build the plugin, and then call clang with the plugin from the source tree:</p>
+<pre>
+ $ export BD=/path/to/build/directory
+ $ (cd $BD &amp;&amp; make PrintFunctionNames )
+ $ clang++ -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \
+ -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE \
+ -I$BD/tools/clang/include -Itools/clang/include -I$BD/include -Iinclude \
+ tools/clang/tools/clang-check/ClangCheck.cpp -fsyntax-only \
+ -Xclang -load -Xclang $BD/lib/PrintFunctionNames.so -Xclang \
+ -plugin -Xclang print-fns
+</pre>
+
+<p>Also see the print-function-name plugin example's
+<a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/README.txt?view=markup">README</a></p>
+
+
+
+</div>
+</body>
+</html>
+
diff --git a/docs/ClangTools.html b/docs/ClangTools.html
new file mode 100644
index 0000000..0dfdc6a
--- /dev/null
+++ b/docs/ClangTools.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Clang Tools</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>Clang Tools</h1>
+<p>Clang Tools are standalone command line (and potentially GUI) tools design
+for use by C++ developers who are already using and enjoying Clang as their
+compiler. These tools provide developer-oriented functionality such as fast
+syntax checking, automatic formatting, refactoring, etc.</p>
+
+<p>Only a couple of the most basic and fundamental tools are kept in the primary
+Clang Subversion project. The rest of the tools are kept in a side-project so
+that developers who don't want or need to build them don't. If you want to get
+access to the extra Clang Tools repository, simply check it out into the tools
+tree of your Clang checkout and follow the usual process for building and
+working with a combined LLVM/Clang checkout:</p>
+<ul>
+ <li>With Subversion:
+ <ul>
+ <li><tt>cd llvm/tools/clang/tools</tt></li>
+ <li><tt>svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk
+ extra</tt></li>
+ </ul>
+ </li>
+ <li>Or with Git:
+ <ul>
+ <li><tt>cd llvm/tools/clang/tools</tt></li>
+ <li><tt>git clone http://llvm.org/git/clang-tools-extra.git extra</tt></li>
+ </ul>
+ </li>
+</ul>
+
+<p>This document describes a high-level overview of the organization of Clang
+Tools within the project as well as giving an introduction to some of the more
+important tools. However, it should be noted that this document is currently
+focused on Clang and Clang Tool developers, not on end users of these tools.</p>
+
+<!-- ======================================================================= -->
+<h2 id="org">Clang Tools Organization</h2>
+<!-- ======================================================================= -->
+
+<p>Clang Tools are CLI or GUI programs that are intended to be directly used by
+C++ developers. That is they are <em>not</em> primarily for use by Clang
+developers, although they are hopefully useful to C++ developers who happen to
+work on Clang, and we try to actively dogfood their functionality. They are
+developed in three components: the underlying infrastructure for building
+a standalone tool based on Clang, core shared logic used by many different tools
+in the form of refactoring and rewriting libraries, and the tools
+themselves.</p>
+
+<p>The underlying infrastructure for Clang Tools is the
+<a href="LibTooling.html">LibTooling</a> platform. See its documentation for
+much more detailed information about how this infrastructure works. The common
+refactoring and rewriting toolkit-style library is also part of LibTooling
+organizationally.</p>
+
+<p>A few Clang Tools are developed along side the core Clang libraries as
+examples and test cases of fundamental functionality. However, most of the tools
+are developed in a side repository to provide easy separation from the core
+libraries. We intentionally do not support public libraries in the side
+repository, as we want to carefully review and find good APIs for libraries as
+they are lifted out of a few tools and into the core Clang library set.</p>
+
+<p>Regardless of which repository Clang Tools' code resides in, the development
+process and practices for all Clang Tools are exactly those of Clang itself.
+They are entirely within the Clang <em>project</em>, regardless of the version
+control scheme.</p>
+
+
+<!-- ======================================================================= -->
+<h2 id="coretools">Core Clang Tools</h2>
+<!-- ======================================================================= -->
+
+<p>The core set of Clang tools that are within the main repository are tools
+that very specifically compliment, and allow use and testing of <em>Clang</em>
+specific functionality.</p>
+
+<h3 id="clang-check"><tt>clang-check</tt></h3>
+<p>This tool combines the LibTooling framework for running a Clang tool with the
+basic Clang diagnostics by syntax checking specific files in a fast, command line
+interface. It can also accept flags to re-display the diagnostics in different
+formats with different flags, suitable for use driving an IDE or editor.</p>
+
+<p>FIXME: Link to user-oriented clang-check documentation.</p>
+
+<h3 id="clang-fixit"><tt>clang-fixit</tt> (Not yet implemented!)</h3>
+<p>A tool which specifically applies the Clang fix-it hint diagnostic technology
+on top of a dedicated tool. It is designed to explore alternative interfaces for
+applying fix-it hints, including automatic application, prompting users with
+options for different fixes, etc.</p>
+
+<p><b>NB:</b> The clang-fixit tool is planned, but not yet implemented.</p>
+
+<p>FIXME: Link to user-oriented clang-fixit documentation.</p>
+
+<!-- ======================================================================= -->
+<h2 id="registerplugin">Extra Clang Tools</h2>
+<!-- ======================================================================= -->
+
+<p>As various categories of Clang Tools are added to the extra repository,
+they'll be tracked here. The focus of this documentation is on the scope and
+features of the tools for other tool developers; each tool should provide its
+own user-focused documentation.</p>
+
+</div>
+</body>
+</html>
+
diff --git a/docs/HowToSetupToolingForLLVM.html b/docs/HowToSetupToolingForLLVM.html
new file mode 100644
index 0000000..493c882
--- /dev/null
+++ b/docs/HowToSetupToolingForLLVM.html
@@ -0,0 +1,186 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>How To Setup Clang Tooling For LLVM</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>How To Setup Clang Tooling For LLVM</h1>
+<p>Clang Tooling provides infrastructure to write tools that need syntactic and
+semantic infomation about a program. This term also relates to a set of specific
+tools using this infrastructure (e.g. <code>clang-check</code>). This document
+provides information on how to set up and use Clang Tooling for the LLVM source
+code.</p>
+
+
+<!-- ======================================================================= -->
+<h2><a name="introduction">Introduction</a></h2>
+<!-- ======================================================================= -->
+
+<p>Clang Tooling needs a compilation database to figure out specific build
+options for each file. Currently it can create a compilation database from the
+<code>compilation_commands.json</code> file, generated by CMake. When invoking
+clang tools, you can either specify a path to a build directory using a command
+line parameter <code>-p</code> or let Clang Tooling find this file in your
+source tree. In either case you need to configure your build using CMake to use
+clang tools.</p>
+
+<!-- ======================================================================= -->
+<h2><a name="using-make">Setup Clang Tooling Using CMake and Make</a></h2>
+<!-- ======================================================================= -->
+
+<p>If you intend to use make to build LLVM, you should have CMake 2.8.6 or later
+installed (can be found <a href="http://cmake.org">here</a>).</p>
+<p>First, you need to generate Makefiles for LLVM with CMake. You need to make
+a build directory and run CMake from it:</p>
+<pre>
+ mkdir your/build/directory
+ cd your/build/directory
+ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON path/to/llvm/sources
+</pre>
+
+<p>If you want to use clang instead of GCC, you can add
+<code>-DCMAKE_C_COMPILER=/path/to/clang
+ -DCMAKE_CXX_COMPILER=/path/to/clang++</code>.
+You can also use ccmake, which provides a curses interface to configure CMake
+variables for lazy people.</p>
+
+<p>As a result, the new <code>compile_commands.json</code> file should appear in
+the current directory. You should link it to the LLVM source tree so that Clang
+Tooling is able to use it:</p>
+<pre>
+ ln -s $PWD/compile_commands.json path/to/llvm/source/
+</pre>
+
+<p>Now you are ready to build and test LLVM using make:</p>
+<pre>
+ make check-all
+</pre>
+
+<!-- ======================================================================= -->
+<h2><a name="using-tools">Using Clang Tools</a></h2>
+<!-- ======================================================================= -->
+
+<p>After you completed the previous steps, you are ready to run clang tools. If
+you have a recent clang installed, you should have <code>clang-check</code> in
+$PATH. Try to run it on any .cpp file inside the LLVM source tree:</p>
+<pre>
+ clang-check tools/clang/lib/Tooling/CompilationDatabase.cpp
+</pre>
+<p>If you're using vim, it's convenient to have clang-check integrated. Put this
+into your .vimrc:</p>
+<pre>
+ set makeprg=clang-check\ %
+ map &lt;F5&gt; :make&lt;CR&gt;&lt;CR&gt;
+</pre>
+
+<p>When editing C++ code, hit F5 to reparse the current buffer. The output will
+go into the error window, which you can enable with <code>:cope</code>.</p>
+
+<p>Other <code>clang-check</code> options that can be useful when working with
+clang AST:</p>
+<ul>
+ <li><code>-ast-print</code> - Build ASTs and then pretty-print them.</li>
+ <li><code>-ast-dump</code> - Build ASTs and then debug dump them.</li>
+ <li><code>-ast-dump-filter=&lt;string&gt;</code> - Use with
+ <code>-ast-dump</code> or <code>-ast-print</code> to dump/print
+ only AST declaration nodes having a certain substring in a qualified name.
+ Use <code>-ast-list</code> to list all filterable declaration node
+ names.</li>
+ <li><code>-ast-list</code> - Build ASTs and print the list of declaration
+ node qualified names.</li>
+</ul>
+<p>Examples:</p>
+<pre>
+<b>$ clang-check tools/clang/tools/clang-check/ClangCheck.cpp -ast-dump -ast-dump-filter ActionFactory::newASTConsumer</b>
+Processing: tools/clang/tools/clang-check/ClangCheck.cpp.
+Dumping <anonymous namespace>::ActionFactory::newASTConsumer:
+clang::ASTConsumer *newASTConsumer() (CompoundStmt 0x44da290 &lt;/home/alexfh/local/llvm/tools/clang/tools/clang-check/ClangCheck.cpp:64:40, line:72:3&gt;
+ (IfStmt 0x44d97c8 &lt;line:65:5, line:66:45&gt;
+ &lt;&lt;&lt;NULL&gt;&gt;&gt;
+ (ImplicitCastExpr 0x44d96d0 &lt;line:65:9&gt; '_Bool':'_Bool' &lt;UserDefinedConversion&gt;
+...
+<b>$ clang-check tools/clang/tools/clang-check/ClangCheck.cpp -ast-print -ast-dump-filter ActionFactory::newASTConsumer</b>
+Processing: tools/clang/tools/clang-check/ClangCheck.cpp.
+Printing &lt;anonymous namespace&gt;::ActionFactory::newASTConsumer:
+clang::ASTConsumer *newASTConsumer() {
+ if (this-&gt;ASTList.operator _Bool())
+ return clang::CreateASTDeclNodeLister();
+ if (this-&gt;ASTDump.operator _Bool())
+ return clang::CreateASTDumper(this-&gt;ASTDumpFilter);
+ if (this-&gt;ASTPrint.operator _Bool())
+ return clang::CreateASTPrinter(&amp;llvm::outs(), this-&gt;ASTDumpFilter);
+ return new clang::ASTConsumer();
+}
+</pre>
+
+<!-- ======================================================================= -->
+<h2><a name="using-ninja">(Experimental) Using Ninja Build System</a></h2>
+<!-- ======================================================================= -->
+
+<p>Optionally you can use the <a
+ href="https://github.com/martine/ninja">Ninja</a> build system instead of
+make. It is aimed at making your builds faster. Currently this step will require
+building Ninja from sources and using a development version of CMake.</p>
+<p>To take advantage of using Clang Tools along with Ninja build you need at
+least CMake 2.8.9. At the moment CMake 2.8.9 is still under development, so you
+can get latest development sources and build it yourself:</p>
+<pre>
+ git clone git://cmake.org/cmake.git
+ cd cmake
+ ./bootstrap
+ make
+ sudo make install
+</pre>
+
+<p>Having the correct version of CMake, you can clone the Ninja git repository
+and build Ninja from sources:</p>
+<pre>
+ git clone git://github.com/martine/ninja.git
+ cd ninja/
+ ./bootstrap.py
+</pre>
+<p>This will result in a single binary <code>ninja</code> in the current
+directory. It doesn't require installation and can just be copied to any
+location inside <code>$PATH</code>, say <code>/usr/local/bin/</code>:</p>
+<pre>
+ sudo cp ninja /usr/local/bin/
+ sudo chmod a+rx /usr/local/bin/ninja
+</pre>
+<p>After doing all of this, you'll need to generate Ninja build files for LLVM
+with CMake. You need to make a build directory and run CMake from it:</p>
+<pre>
+ mkdir your/build/directory
+ cd your/build/directory
+ cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON path/to/llvm/sources
+</pre>
+
+<p>If you want to use clang instead of GCC, you can add
+<code>-DCMAKE_C_COMPILER=/path/to/clang
+ -DCMAKE_CXX_COMPILER=/path/to/clang++</code>.
+You can also use ccmake, which provides a curses interface to configure CMake
+variables in an interactive manner.</p>
+
+<p>As a result, the new <code>compile_commands.json</code> file should appear in
+the current directory. You should link it to the LLVM source tree so that Clang
+Tooling is able to use it:</p>
+<pre>
+ ln -s $PWD/compile_commands.json path/to/llvm/source/
+</pre>
+
+<p>Now you are ready to build and test LLVM using Ninja:</p>
+<pre>
+ ninja check-all
+</pre>
+<p>Other target names can be used in the same way as with make.</p>
+</div>
+</body>
+</html>
+
diff --git a/docs/InternalsManual.html b/docs/InternalsManual.html
index bd6af8d..3f3e124 100644
--- a/docs/InternalsManual.html
+++ b/docs/InternalsManual.html
@@ -357,6 +357,12 @@ Clang:</p>
<tr><td>Example:</td><td><tt>"candidate found by name lookup is %q0"</tt></td></tr>
<tr><td>Class:</td><td>NamedDecl*</td></tr>
<tr><td>Description</td><td><p>This formatter indicates that the fully-qualified name of the declaration should be printed, e.g., "std::vector" rather than "vector".</p></td></tr>
+
+<tr><td colspan="2"><b>"diff" format</b></td></tr>
+<tr><td>Example:</td><td><tt>"no known conversion %diff{from | to | }1,2"</tt></td></tr>
+<tr><td>Class:</td><td>QualType</td></tr>
+<tr><td>Description</td><td><p>This formatter takes two QualTypes and attempts to print a template difference between the two. If tree printing is off, the text inside the braces before the pipe is printed, with the formatted text replacing the $. If tree printing is on, the text after the pipe is printed and a type tree is printed after the diagnostic message.
+</p></td></tr>
</table>
@@ -441,6 +447,18 @@ href="#DiagnosticClient">Other diagnostic clients</a> might choose
to render the code differently (e.g., as markup inline) or even give
the user the ability to automatically fix the problem.</p>
+<p>Fix-it hints on errors and warnings need to obey these rules:</p>
+
+<ul>
+<li>Since they are automatically applied if <code>-Xclang -fixit</code>
+is passed to the driver, they should only be used when it's very likely they
+match the user's intent.</li>
+<li>Clang must recover from errors as if the fix-it had been applied.</li>
+</ul>
+
+<p>If a fix-it can't obey these rules, put the fix-it on a note. Fix-its on
+notes are not applied automatically.</p>
+
<p>All fix-it hints are described by the <code>FixItHint</code> class,
instances of which should be attached to the diagnostic using the
&lt;&lt; operator in the same way that highlighted source ranges and
@@ -1686,7 +1704,7 @@ interacts with constant evaluation:</p>
<li><b><tt>__extension__</tt></b>: The expression form of this extension causes
any evaluatable subexpression to be accepted as an integer constant
expression.</li>
-<li><b><tt>__builtin_constant_p</tt></b>: This returns true (as a integer
+<li><b><tt>__builtin_constant_p</tt></b>: This returns true (as an integer
constant expression) if the operand evaluates to either a numeric value
(that is, not a pointer cast to integral type) of integral, enumeration,
floating or complex type, or if it evaluates to the address of the first
@@ -1751,18 +1769,6 @@ then <tt>__attribute__((myattribute("Hello", 3)))</tt> will be a valid use.</p>
<h4>Boilerplate</h4>
-<p>Add an element to the <tt>AttributeList::Kind</tt> enum in <a
-href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?view=markup">include/clang/Sema/AttributeList.h</a>
-named <tt>AT_lower_with_underscores</tt>. That is, a CamelCased
-<tt>AttributeName</tt> in <tt>Attr.td</tt> name should become
-<tt>AT_attribute_name</tt>.</p>
-
-<p>Add a case to the <tt>StringSwitch</tt> in <tt>AttributeList::getKind()</tt>
-in <a
-href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AttributeList.cpp?view=markup">lib/Sema/AttributeList.cpp</a>
-for each spelling of your attribute. Less common attributes should come toward
-the end of that list.</p>
-
<p>Write a new <tt>HandleYourAttr()</tt> function in <a
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?view=markup">lib/Sema/SemaDeclAttr.cpp</a>,
and add a case to the switch in <tt>ProcessNonInheritableDeclAttr()</tt> or
diff --git a/docs/IntroductionToTheClangAST.html b/docs/IntroductionToTheClangAST.html
new file mode 100644
index 0000000..28175dd
--- /dev/null
+++ b/docs/IntroductionToTheClangAST.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Introduction to the Clang AST</title>
+<link type="text/css" rel="stylesheet" href="../menu.css" />
+<link type="text/css" rel="stylesheet" href="../content.css" />
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>Introduction to the Clang AST</h1>
+<p>This document gives a gentle introduction to the mysteries of the Clang AST.
+It is targeted at developers who either want to contribute to Clang, or use
+tools that work based on Clang's AST, like the AST matchers.</p>
+<!-- FIXME: Add link once we have an AST matcher document -->
+
+<!-- ======================================================================= -->
+<h2 id="intro">Introduction</h2>
+<!-- ======================================================================= -->
+
+<p>Clang's AST is different from ASTs produced by some other compilers in that it closely
+resembles both the written C++ code and the C++ standard. For example,
+parenthesis expressions and compile time constants are available in an unreduced
+form in the AST. This makes Clang's AST a good fit for refactoring tools.</p>
+
+<p>Documentation for all Clang AST nodes is available via the generated
+<a href="http://clang.llvm.org/doxygen">Doxygen</a>. The doxygen online
+documentation is also indexed by your favorite search engine, which will make
+a search for clang and the AST node's class name usually turn up the doxygen
+of the class you're looking for (for example, search for: clang ParenExpr).</p>
+
+<!-- ======================================================================= -->
+<h2 id="examine">Examining the AST</h2>
+<!-- ======================================================================= -->
+
+<p>A good way to familarize yourself with the Clang AST is to actually look
+at it on some simple example code. Clang has a builtin AST-dump modes, which
+can be enabled with the flags -ast-dump and -ast-dump-xml. Note that -ast-dump-xml
+currently only works with debug-builds of clang.</p>
+
+<p>Let's look at a simple example AST:</p>
+<pre>
+# cat test.cc
+int f(int x) {
+ int result = (x / 42);
+ return result;
+}
+
+# Clang by default is a frontend for many tools; -cc1 tells it to directly
+# use the C++ compiler mode. -undef leaves out some internal declarations.
+$ clang -cc1 -undef -ast-dump-xml test.cc
+... cutting out internal declarations of clang ...
+&lt;TranslationUnit ptr="0x4871160">
+ &lt;Function ptr="0x48a5800" name="f" prototype="true">
+ &lt;FunctionProtoType ptr="0x4871de0" canonical="0x4871de0">
+ &lt;BuiltinType ptr="0x4871250" canonical="0x4871250"/>
+ &lt;parameters>
+ &lt;BuiltinType ptr="0x4871250" canonical="0x4871250"/>
+ &lt;/parameters>
+ &lt;/FunctionProtoType>
+ &lt;ParmVar ptr="0x4871d80" name="x" initstyle="c">
+ &lt;BuiltinType ptr="0x4871250" canonical="0x4871250"/>
+ &lt;/ParmVar>
+ &lt;Stmt>
+(CompoundStmt 0x48a5a38 &lt;t2.cc:1:14, line:4:1>
+ (DeclStmt 0x48a59c0 &lt;line:2:3, col:24>
+ 0x48a58c0 "int result =
+ (ParenExpr 0x48a59a0 &lt;col:16, col:23> 'int'
+ (BinaryOperator 0x48a5978 &lt;col:17, col:21> 'int' '/'
+ (ImplicitCastExpr 0x48a5960 &lt;col:17> 'int' &lt;LValueToRValue>
+ (DeclRefExpr 0x48a5918 &lt;col:17> 'int' lvalue ParmVar 0x4871d80 'x' 'int'))
+ (IntegerLiteral 0x48a5940 &lt;col:21> 'int' 42)))")
+ (ReturnStmt 0x48a5a18 &lt;line:3:3, col:10>
+ (ImplicitCastExpr 0x48a5a00 &lt;col:10> 'int' &lt;LValueToRValue>
+ (DeclRefExpr 0x48a59d8 &lt;col:10> 'int' lvalue Var 0x48a58c0 'result' 'int'))))
+
+ &lt;/Stmt>
+ &lt;/Function>
+&lt;/TranslationUnit>
+</pre>
+<p>In general, -ast-dump-xml dumps declarations in an XML-style format and
+statements in an S-expression-style format.
+The toplevel declaration in a translation unit is always the
+<a href="http://clang.llvm.org/doxygen/classclang_1_1TranslationUnitDecl.html">translation unit declaration</a>.
+In this example, our first user written declaration is the
+<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">function declaration</a>
+of 'f'. The body of 'f' is a <a href="http://clang.llvm.org/doxygen/classclang_1_1CompoundStmt.html">compound statement</a>,
+whose child nodes are a <a href="http://clang.llvm.org/doxygen/classclang_1_1DeclStmt.html">declaration statement</a>
+that declares our result variable, and the
+<a href="http://clang.llvm.org/doxygen/classclang_1_1ReturnStmt.html">return statement</a>.</p>
+
+<!-- ======================================================================= -->
+<h2 id="context">AST Context</h2>
+<!-- ======================================================================= -->
+
+<p>All information about the AST for a translation unit is bundled up in the class
+<a href="http://clang.llvm.org/doxygen/classclang_1_1ASTContext.html">ASTContext</a>.
+It allows traversal of the whole translation unit starting from
+<a href="http://clang.llvm.org/doxygen/classclang_1_1ASTContext.html#abd909fb01ef10cfd0244832a67b1dd64">getTranslationUnitDecl</a>,
+or to access Clang's <a href="http://clang.llvm.org/doxygen/classclang_1_1ASTContext.html#a4f95adb9958e22fbe55212ae6482feb4">table of identifiers</a>
+for the parsed translation unit.</p>
+
+<!-- ======================================================================= -->
+<h2 id="nodes">AST Nodes</h2>
+<!-- ======================================================================= -->
+
+<p>Clang's AST nodes are modeled on a class hierarchy that does not have a common
+ancestor. Instead, there are multiple larger hierarchies for basic node types like
+<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a> and
+<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>. Many
+important AST nodes derive from <a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>,
+<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>,
+<a href="http://clang.llvm.org/doxygen/classclang_1_1DeclContext.html">DeclContext</a> or
+<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>,
+with some classes deriving from both Decl and DeclContext.</p>
+<p>There are also a multitude of nodes in the AST that are not part of a
+larger hierarchy, and are only reachable from specific other nodes,
+like <a href="http://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>.
+</p>
+
+<p>Thus, to traverse the full AST, one starts from the <a href="http://clang.llvm.org/doxygen/classclang_1_1TranslationUnitDecl.html">TranslationUnitDecl</a>
+and then recursively traverses everything that can be reached from that node
+- this information has to be encoded for each specific node type. This algorithm
+is encoded in the <a href="http://clang.llvm.org/doxygen/classclang_1_1RecursiveASTVisitor.html">RecursiveASTVisitor</a>.
+See the <a href="http://clang.llvm.org/docs/RAVFrontendAction.html">RecursiveASTVisitor tutorial</a>.</p>
+
+<p>The two most basic nodes in the Clang AST are statements (<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>)
+and declarations (<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>).
+Note that expressions (<a href="http://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>)
+are also statements in Clang's AST.</p>
+
+</div>
+</body>
+</html>
+
diff --git a/docs/JSONCompilationDatabase.html b/docs/JSONCompilationDatabase.html
new file mode 100644
index 0000000..2907194
--- /dev/null
+++ b/docs/JSONCompilationDatabase.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>JSON Compilation Database Format Specification</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>JSON Compilation Database Format Specification</h1>
+<p>This document describes a format for specifying how to replay
+single compilations independently of the build system.</p>
+
+<h2>Background</h2>
+<p>Tools based on the C++ Abstract Syntax Tree need full information how to
+parse a translation unit. Usually this information is implicitly
+available in the build system, but running tools as part of
+the build system is not necessarily the best solution:
+<ul>
+<li>Build systems are inherently change driven, so running multiple
+tools over the same code base without changing the code does not fit
+into the architecture of many build systems.</li>
+<li>Figuring out whether things have changed is often an IO bound
+process; this makes it hard to build low latency end user tools based
+on the build system.</li>
+<li>Build systems are inherently sequential in the build graph, for example
+due to generated source code. While tools that run independently of the
+build still need the generated source code to exist, running tools multiple
+times over unchanging source does not require serialization of the runs
+according to the build dependency graph.</li>
+</ul>
+</p>
+
+<h2>Supported Systems</h2>
+<p>Currently <a href="http://cmake.org">CMake</a> (since 2.8.5) supports generation of compilation
+databases for Unix Makefile builds (Ninja builds in the works) with the option
+CMAKE_EXPORT_COMPILE_COMMANDS.</p>
+<p>Clang's tooling interface supports reading compilation databases; see
+the <a href="LibTooling.html">LibTooling documentation</a>. libclang and its
+python bindings also support this (since clang 3.2); see
+<a href="/doxygen/group__COMPILATIONDB.html">CXCompilationDatabase.h</a>.</p>
+
+<h2>Format</h2>
+<p>A compilation database is a JSON file, which consist of an array of
+"command objects", where each command object specifies one way a translation unit
+is compiled in the project.</p>
+<p>Each command object contains the translation unit's main file, the working
+directory of the compile run and the actual compile command.</p>
+<p>Example:
+<pre>
+[
+ { "directory": "/home/user/llvm/build",
+ "command": "/usr/bin/clang++ -Irelative -DSOMEDEF='\"With spaces and quotes.\"' -c -o file.o file.cc",
+ "file": "file.cc" },
+ ...
+]
+</pre>
+The contracts for each field in the command object are:
+<ul>
+<li><b>directory:</b> The working directory of the compilation. All paths specified
+in the <b>command</b> or <b>file</b> fields must be either absolute or relative to
+this directory.</li>
+<li><b>file:</b> The main translation unit source processed by this compilation step.
+This is used by tools as the key into the compilation database. There can be multiple
+command objects for the same file, for example if the same source file is
+compiled with different configurations.</li>
+<li><b>command:</b> The compile command executed. After JSON unescaping, this must
+be a valid command to rerun the exact compilation step for the translation unit in
+the environment the build system uses. Parameters use shell quoting and shell escaping
+of quotes, with '"' and '\' being the only special characters. Shell expansion is
+not supported.</li>
+</ul>
+</p>
+
+<h2>Build System Integration</h2>
+<p>The convention is to name the file compile_commands.json and put it at the top
+of the build directory. Clang tools are pointed to the top of the build directory
+to detect the file and use the compilation database to parse C++ code in the source
+tree.</p>
+
+</div>
+</body>
+</html>
+
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html
index 68f0afc..eac3c69 100644
--- a/docs/LanguageExtensions.html
+++ b/docs/LanguageExtensions.html
@@ -91,18 +91,24 @@
<li><a href="#objc_arc">Automatic reference counting</a></li>
<li><a href="#objc_fixed_enum">Enumerations with a fixed underlying type</a></li>
<li><a href="#objc_lambdas">Interoperability with C++11 lambdas</a></li>
- <li><a href="#object-literals-subscripting">Object Literals and Subscripting</a></li>
+ <li><a href="#objc_object_literals_subscripting">Object Literals and Subscripting</a></li>
</ul>
</li>
<li><a href="#overloading-in-c">Function Overloading in C</a></li>
<li><a href="#complex-list-init">Initializer lists for complex numbers in C</a></li>
<li><a href="#builtins">Builtin Functions</a>
<ul>
+ <li><a href="#__builtin_readcyclecounter">__builtin_readcyclecounter</a></li>
<li><a href="#__builtin_shufflevector">__builtin_shufflevector</a></li>
<li><a href="#__builtin_unreachable">__builtin_unreachable</a></li>
<li><a href="#__sync_swap">__sync_swap</a></li>
</ul>
</li>
+<li><a href="#non-standard-attributes">Non-standard C++11 Attributes</a>
+<ul>
+ <li><a href="#clang__fallthrough">The <tt>clang::fallthrough</tt> attribute</a></li>
+</ul>
+</li>
<li><a href="#targetspecific">Target-Specific Extensions</a>
<ul>
<li><a href="#x86-specific">X86/X86-64 Language Extensions</a></li>
@@ -655,7 +661,7 @@ framework search path. For consistency, we recommend that such files never be
included in installed versions of the framework.</p>
<!-- ======================================================================= -->
-<h2 id="availability">Availability attribute</h2
+<h2 id="availability">Availability attribute</h2>
<!-- ======================================================================= -->
<p>Clang introduces the <code>availability</code> attribute, which can
@@ -937,8 +943,8 @@ is enabled.</p>
<p>Use <tt>__has_feature(c_atomic)</tt> or <tt>__has_extension(c_atomic)</tt>
to determine if support for atomic types using <tt>_Atomic</tt> is enabled.
Clang also provides <a href="#__c11_atomic">a set of builtins</a> which can be
-used to implement the <tt>&lt;stdatomic.h&gt;</tt> operations on _Atomic
-types.</p>
+used to implement the <tt>&lt;stdatomic.h&gt;</tt> operations on
+<tt>_Atomic</tt> types.</p>
<h4 id="c_generic_selections">C11 generic selections</h4>
@@ -1183,10 +1189,28 @@ in Objective-C++, and not in C++ with blocks, due to its use of
Objective-C memory management (autorelease).</p>
<!-- ======================================================================= -->
-<h2 id="object-literals-subscripting">Object Literals and Subscripting</h2>
+<h2 id="objc_object_literals_subscripting">Object Literals and Subscripting</h2>
+<!-- ======================================================================= -->
+
+<p>Clang provides support for <a href="ObjectiveCLiterals.html">Object Literals
+and Subscripting</a> in Objective-C, which simplifies common Objective-C
+programming patterns, makes programs more concise, and improves the safety of
+container creation. There are several feature macros associated with object
+literals and subscripting: <code>__has_feature(objc_array_literals)</code>
+tests the availability of array literals;
+<code>__has_feature(objc_dictionary_literals)</code> tests the availability of
+dictionary literals; <code>__has_feature(objc_subscripting)</code> tests the
+availability of object subscripting.</p>
+
+<!-- ======================================================================= -->
+<h2 id="objc_default_synthesize_properties">Objective-C Autosynthesis of Properties</h2>
<!-- ======================================================================= -->
-<p>Clang provides support for <a href="ObjectiveCLiterals.html">Object Literals and Subscripting</a> in Objective-C, which simplifies common Objective-C programming patterns, makes programs more concise, and improves the safety of container creation. There are several feature macros associated with object literals and subscripting: <code>__has_feature(objc_array_literals)</code> tests the availability of array literals; <code>__has_feature(objc_dictionary_literals)</code> tests the availability of dictionary literals; <code>__has_feature(objc_subscripting)</code> tests the availability of object subscripting.</p>
+<p> Clang provides support for autosynthesis of declared properties. Using this
+feature, clang provides default synthesis of those properties not declared @dynamic
+and not having user provided backing getter and setter methods.
+<code>__has_feature(objc_default_synthesize_properties)</code> checks for availability
+of this feature in version of clang being used.</p>
<!-- ======================================================================= -->
<h2 id="overloading-in-c">Function Overloading in C</h2>
@@ -1344,6 +1368,42 @@ vector support</a> instead of builtins, in order to reduce the number of
builtins that we need to implement.</p>
<!-- ======================================================================= -->
+<h3><a name="__builtin_readcyclecounter">__builtin_readcyclecounter</a></h3>
+<!-- ======================================================================= -->
+
+<p><tt>__builtin_readcyclecounter</tt> is used to access the cycle counter
+register (or a similar low-latency, high-accuracy clock) on those targets that
+support it.
+</p>
+
+<p><b>Syntax:</b></p>
+
+<pre>
+__builtin_readcyclecounter()
+</pre>
+
+<p><b>Example of Use:</b></p>
+
+<pre>
+unsigned long long t0 = __builtin_readcyclecounter();
+do_something();
+unsigned long long t1 = __builtin_readcyclecounter();
+unsigned long long cycles_to_do_something = t1 - t0; // assuming no overflow
+</pre>
+
+<p><b>Description:</b></p>
+
+<p>The __builtin_readcyclecounter() builtin returns the cycle counter value,
+which may be either global or process/thread-specific depending on the target.
+As the backing counters often overflow quickly (on the order of
+seconds) this should only be used for timing small intervals. When not
+supported by the target, the return value is always zero. This builtin
+takes no arguments and produces an unsigned long long result.
+</p>
+
+<p>Query for this feature with __has_builtin(__builtin_readcyclecounter).</p>
+
+<!-- ======================================================================= -->
<h3><a name="__builtin_shufflevector">__builtin_shufflevector</a></h3>
<!-- ======================================================================= -->
@@ -1489,6 +1549,54 @@ with a <tt>__c11_</tt> prefix. The supported operations are:</p>
<li><tt>__c11_atomic_fetch_xor</tt></li>
</ul>
+<!-- ======================================================================= -->
+<h2 id="non-standard-attributes">Non-standard C++11 Attributes</h2>
+<!-- ======================================================================= -->
+
+<p>Clang supports one non-standard C++11 attribute. It resides in the
+<tt>clang</tt> attribute namespace.</p>
+
+<!-- ======================================================================= -->
+<h3 id="clang__fallthrough">The <tt>clang::fallthrough</tt> attribute</h3>
+<!-- ======================================================================= -->
+
+<p>The <tt>clang::fallthrough</tt> attribute is used along with the
+<tt>-Wimplicit-fallthrough</tt> argument to annotate intentional fall-through
+between switch labels. It can only be applied to a null statement placed at a
+point of execution between any statement and the next switch label. It is common
+to mark these places with a specific comment, but this attribute is meant to
+replace comments with a more strict annotation, which can be checked by the
+compiler. This attribute doesn't change semantics of the code and can be used
+wherever an intended fall-through occurs. It is designed to mimic
+control-flow statements like <tt>break;</tt>, so it can be placed in most places
+where <tt>break;</tt> can, but only if there are no statements on the execution
+path between it and the next switch label.</p>
+<p>Here is an example:</p>
+<pre>
+// compile with -Wimplicit-fallthrough
+switch (n) {
+case 33:
+ f();
+case 44: // warning: unannotated fall-through
+ g();
+ <b>[[clang::fallthrough]];</b>
+case 55: // no warning
+ if (x) {
+ h();
+ break;
+ }
+ else {
+ i();
+ <b>[[clang::fallthrough]];</b>
+ }
+case 66: // no warning
+ p();
+ <b>[[clang::fallthrough]];</b> // warning: fallthrough annotation does not directly precede case label
+ q();
+case 77: // warning: unannotated fall-through
+ r();
+}
+</pre>
<!-- ======================================================================= -->
<h2 id="targetspecific">Target-Specific Extensions</h2>
diff --git a/docs/LibTooling.html b/docs/LibTooling.html
new file mode 100644
index 0000000..ed0ad45
--- /dev/null
+++ b/docs/LibTooling.html
@@ -0,0 +1,229 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>LibTooling</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>LibTooling</h1>
+<p>LibTooling is a library to support writing standalone tools based on
+Clang. This document will provide a basic walkthrough of how to write
+a tool using LibTooling.</p>
+
+<!-- ======================================================================= -->
+<h2 id="intro">Introduction</h2>
+<!-- ======================================================================= -->
+
+<p>Tools built with LibTooling, like Clang Plugins, run FrontendActions over
+code. <!-- See FIXME for a tutorial on how to write FrontendActions. -->
+In this tutorial, we'll demonstrate the different ways of running clang's
+SyntaxOnlyAction, which runs a quick syntax check, over a bunch of
+code.</p>
+
+<!-- ======================================================================= -->
+<h2 id="runoncode">Parsing a code snippet in memory.</h2>
+<!-- ======================================================================= -->
+
+<p>If you ever wanted to run a FrontendAction over some sample code, for example
+to unit test parts of the Clang AST, runToolOnCode is what you looked for. Let
+me give you an example:
+<pre>
+ #include "clang/Tooling/Tooling.h"
+
+ TEST(runToolOnCode, CanSyntaxCheckCode) {
+ // runToolOnCode returns whether the action was correctly run over the
+ // given code.
+ EXPECT_TRUE(runToolOnCode(new clang::SyntaxOnlyAction, "class X {};"));
+ }
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="standalonetool">Writing a standalone tool.</h2>
+<!-- ======================================================================= -->
+
+<p>Once you unit tested your FrontendAction to the point where it cannot
+possibly break, it's time to create a standalone tool. For a standalone tool
+to run clang, it first needs to figure out what command line arguments to use
+for a specified file. To that end we create a CompilationDatabase.</p>
+
+<h3 id="compilationdb">Creating a compilation database.</h3>
+<p>CompilationDatabase provides static factory functions to help with parsing
+compile commands from a build directory or the command line. The following code
+allows for both explicit specification of a compile command line, as well as
+retrieving the compile commands lines from a database.
+<pre>
+int main(int argc, const char **argv) {
+ // First, try to create a fixed compile command database from the command line
+ // arguments.
+ llvm::OwningPtr&lt;CompilationDatabase> Compilations(
+ FixedCompilationDatabase::loadFromCommandLine(argc, argv));
+
+ // Next, use normal llvm command line parsing to get the tool specific
+ // parameters.
+ cl::ParseCommandLineOptions(argc, argv);
+
+ if (!Compilations) {
+ // In case the user did not specify the compile command line via positional
+ // command line arguments after "--", try to load the compile commands from
+ // a database in the specified build directory or auto-detect it from a
+ // source file.
+ std::string ErrorMessage;
+ if (!BuildPath.empty()) {
+ Compilations.reset(
+ CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage));
+ } else {
+ Compilations.reset(CompilationDatabase::autoDetectFromSource(
+ SourcePaths[0], ErrorMessage));
+ }
+ // If there is still no valid compile command database, we don't know how
+ // to run the tool.
+ if (!Compilations)
+ llvm::report_fatal_error(ErrorMessage);
+ }
+}
+</pre>
+</p>
+
+<h3 id="tool">Creating and running a ClangTool.</h3>
+<p>Once we have a CompilationDatabase, we can create a ClangTool and run our
+FrontendAction over some code. For example, to run the SyntaxOnlyAction over
+the files "a.cc" and "b.cc" one would write:
+<pre>
+ // A clang tool can run over a number of sources in the same process...
+ std::vector&lt;std::string> Sources;
+ Sources.push_back("a.cc");
+ Sources.push_back("b.cc");
+
+ // We hand the CompilationDatabase we created and the sources to run over into
+ // the tool constructor.
+ ClangTool Tool(*Compilations, Sources);
+
+ // The ClangTool needs a new FrontendAction for each translation unit we run
+ // on. Thus, it takes a FrontendActionFactory as parameter. To create a
+ // FrontendActionFactory from a given FrontendAction type, we call
+ // newFrontendActionFactory&lt;clang::SyntaxOnlyAction>().
+ int result = Tool.run(newFrontendActionFactory&lt;clang::SyntaxOnlyAction>());
+</pre>
+</p>
+
+<h3 id="main">Putting it together - the first tool.</h3>
+<p>Now we combine the two previous steps into our first real tool. This example
+tool is also checked into the clang tree at tools/clang-check/ClangCheck.cpp.
+<pre>
+#include "llvm/Support/CommandLine.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+
+using namespace clang::tooling;
+using namespace llvm;
+
+cl::opt&lt;std::string> BuildPath(
+ "p",
+ cl::desc("&lt;build-path>"),
+ cl::Optional);
+
+cl::list&lt;std::string> SourcePaths(
+ cl::Positional,
+ cl::desc("&lt;source0> [... &lt;sourceN>]"),
+ cl::OneOrMore);
+
+int main(int argc, const char **argv) {
+ llvm::OwningPtr&lt;CompilationDatabase> Compilations(
+ FixedCompilationDatabase::loadFromCommandLine(argc, argv));
+ cl::ParseCommandLineOptions(argc, argv);
+ if (!Compilations) {
+ std::string ErrorMessage;
+ Compilations.reset(
+ !BuildPath.empty() ?
+ CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
+ CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
+ );
+ if (!Compilations)
+ llvm::report_fatal_error(ErrorMessage);
+ }
+ ClangTool Tool(*Compilations, SourcePaths);
+ return Tool.run(newFrontendActionFactory&lt;clang::SyntaxOnlyAction>());
+}
+</pre>
+</p>
+
+<h3 id="running">Running the tool on some code.</h3>
+<p>When you check out and build clang, clang-check is already built and
+available to you in bin/clang-check inside your build directory.</p>
+<p>You can run clang-check on a file in the llvm repository by specifying
+all the needed parameters after a "--" separator:
+<pre>
+ $ cd /path/to/source/llvm
+ $ export BD=/path/to/build/llvm
+ $ $BD/bin/clang-check tools/clang/tools/clang-check/ClangCheck.cpp -- \
+ clang++ -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS \
+ -Itools/clang/include -I$BD/include -Iinclude -Itools/clang/lib/Headers -c
+</pre>
+</p>
+
+<p>As an alternative, you can also configure cmake to output a compile command
+database into its build directory:
+<pre>
+ # Alternatively to calling cmake, use ccmake, toggle to advanced mode and
+ # set the parameter CMAKE_EXPORT_COMPILE_COMMANDS from the UI.
+ $ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
+</pre>
+</p>
+<p>
+This creates a file called compile_commands.json in the build directory. Now
+you can run clang-check over files in the project by specifying the build path
+as first argument and some source files as further positional arguments:
+<pre>
+ $ cd /path/to/source/llvm
+ $ export BD=/path/to/build/llvm
+ $ $BD/bin/clang-check -p $BD tools/clang/tools/clang-check/ClangCheck.cpp
+</pre>
+</p>
+
+<h3 id="builtin">Builtin includes.</h3>
+<p>Clang tools need their builtin headers and search for them the same way clang
+does. Thus, the default location to look for builtin headers is in a path
+$(dirname /path/to/tool)/../lib/clang/3.2/include relative to the tool
+binary. This works out-of-the-box for tools running from llvm's toplevel
+binary directory after building clang-headers, or if the tool is running
+from the binary directory of a clang install next to the clang binary.</p>
+
+<p>Tips: if your tool fails to find stddef.h or similar headers, call
+the tool with -v and look at the search paths it looks through.</p>
+
+<h3 id="linking">Linking.</h3>
+<p>Please note that this presents the linking requirements at the time of this
+writing. For the most up-to-date information, look at one of the tools'
+Makefiles (for example
+<a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-check/Makefile?view=markup">clang-check/Makefile</a>).
+</p>
+
+<p>To link a binary using the tooling infrastructure, link in the following
+libraries:
+<ul>
+<li>Tooling</li>
+<li>Frontend</li>
+<li>Driver</li>
+<li>Serialization</li>
+<li>Parse</li>
+<li>Sema</li>
+<li>Analysis</li>
+<li>Edit</li>
+<li>AST</li>
+<li>Lex</li>
+<li>Basic</li>
+</ul>
+</p>
+
+</div>
+</body>
+</html>
+
diff --git a/docs/ObjectiveCLiterals.html b/docs/ObjectiveCLiterals.html
index 63b523c..11751a6 100644
--- a/docs/ObjectiveCLiterals.html
+++ b/docs/ObjectiveCLiterals.html
@@ -4,7 +4,7 @@
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF8">
- <title>Clang Language Extensions</title>
+ <title>Objective-C Literals</title>
<link type="text/css" rel="stylesheet" href="../menu.css">
<link type="text/css" rel="stylesheet" href="../content.css">
<style type="text/css">
@@ -24,21 +24,21 @@
<h2>Introduction</h2>
-Three new features were introduced into clang at the same time: <i>NSNumber Literals</i> provide a syntax for creating <code>NSNumber</code> from scalar literal expressions; <i>Collection Literals</i> provide a short-hand for creating arrays and dictionaries; <i>Object Subscripting</i> provides a way to use subscripting with Objective-C objects. Users of Apple compiler releases can use these features starting with the Apple LLVM Compiler 4.0. Users of open-source LLVM.org compiler releases can use these features starting with clang v3.1.<p>
+<p>Three new features were introduced into clang at the same time: <i>NSNumber Literals</i> provide a syntax for creating <code>NSNumber</code> from scalar literal expressions; <i>Collection Literals</i> provide a short-hand for creating arrays and dictionaries; <i>Object Subscripting</i> provides a way to use subscripting with Objective-C objects. Users of Apple compiler releases can use these features starting with the Apple LLVM Compiler 4.0. Users of open-source LLVM.org compiler releases can use these features starting with clang v3.1.</p>
-These language additions simplify common Objective-C programming patterns, make programs more concise, and improve the safety of container creation.<p>
+<p>These language additions simplify common Objective-C programming patterns, make programs more concise, and improve the safety of container creation.</p>
-This document describes how the features are implemented in clang, and how to use them in your own programs.<p>
+<p>This document describes how the features are implemented in clang, and how to use them in your own programs.</p>
<h2>NSNumber Literals</h2>
-The framework class <code>NSNumber</code> is used to wrap scalar values inside objects: signed and unsigned integers (<code>char</code>, <code>short</code>, <code>int</code>, <code>long</code>, <code>long long</code>), floating point numbers (<code>float</code>, <code>double</code>), and boolean values (<code>BOOL</code>, C++ <code>bool</code>). Scalar values wrapped in objects are also known as <i>boxed</i> values.<p>
+<p>The framework class <code>NSNumber</code> is used to wrap scalar values inside objects: signed and unsigned integers (<code>char</code>, <code>short</code>, <code>int</code>, <code>long</code>, <code>long long</code>), floating point numbers (<code>float</code>, <code>double</code>), and boolean values (<code>BOOL</code>, C++ <code>bool</code>). Scalar values wrapped in objects are also known as <i>boxed</i> values.</p>
-In Objective-C, any character, numeric or boolean literal prefixed with the <code>'@'</code> character will evaluate to a pointer to an <code>NSNumber</code> object initialized with that value. C's type suffixes may be used to control the size of numeric literals.
+<p>In Objective-C, any character, numeric or boolean literal prefixed with the <code>'@'</code> character will evaluate to a pointer to an <code>NSNumber</code> object initialized with that value. C's type suffixes may be used to control the size of numeric literals.</p>
<h3>Examples</h3>
-The following program illustrates the rules for <code>NSNumber</code> literals:<p>
+<p>The following program illustrates the rules for <code>NSNumber</code> literals:</p>
<pre>
void main(int argc, const char *argv[]) {
@@ -68,18 +68,19 @@ void main(int argc, const char *argv[]) {
<h3>Discussion</h3>
-NSNumber literals only support literal scalar values after the '@'. Consequently, @INT_MAX works, but @INT_MIN does not, because they are defined like this:<p>
+<p>NSNumber literals only support literal scalar values after the <code>'@'</code>. Consequently, <code>@INT_MAX</code> works, but <code>@INT_MIN</code> does not, because they are defined like this:</p>
<pre>
#define INT_MAX 2147483647 /* max value for an int */
#define INT_MIN (-2147483647-1) /* min value for an int */
</pre>
-The definition of INT_MIN is not a simple literal, but a parenthesized expression. This is by design, but may be improved in subsequent compiler releases.<p>
+<p>The definition of <code>INT_MIN</code> is not a simple literal, but a parenthesized expression. Parenthesized
+expressions are supported using the <a href="#objc_boxed_expressions">boxed expression</a> syntax, which is described in the next section.</p>
-Because <code>NSNumber</code> does not currently support wrapping <code>long double</code> values, the use of a <code>long double NSNumber</code> literal (e.g. <code>@123.23L</code>) will be rejected by the compiler.<p>
+<p>Because <code>NSNumber</code> does not currently support wrapping <code>long double</code> values, the use of a <code>long double NSNumber</code> literal (e.g. <code>@123.23L</code>) will be rejected by the compiler.</p>
-Previously, the <code>BOOL</code> type was simply a typedef for <code>signed char</code>, and <code>YES</code> and <code>NO</code> were macros that expand to <code>(BOOL)1</code> and <code>(BOOL)0</code> respectively. To support <code>@YES</code> and <code>@NO</code> expressions, these macros are now defined using new language keywords in <code>&LT;objc/objc.h&GT;</code>:<p>
+<p>Previously, the <code>BOOL</code> type was simply a typedef for <code>signed char</code>, and <code>YES</code> and <code>NO</code> were macros that expand to <code>(BOOL)1</code> and <code>(BOOL)0</code> respectively. To support <code>@YES</code> and <code>@NO</code> expressions, these macros are now defined using new language keywords in <code>&LT;objc/objc.h&GT;</code>:</p>
<pre>
#if __has_feature(objc_bool)
@@ -91,26 +92,126 @@ Previously, the <code>BOOL</code> type was simply a typedef for <code>signed cha
#endif
</pre>
-The compiler implicitly converts <code>__objc_yes</code> and <code>__objc_no</code> to <code>(BOOL)1</code> and <code>(BOOL)0</code>. The keywords are used to disambiguate <code>BOOL</code> and integer literals.<p>
+<p>The compiler implicitly converts <code>__objc_yes</code> and <code>__objc_no</code> to <code>(BOOL)1</code> and <code>(BOOL)0</code>. The keywords are used to disambiguate <code>BOOL</code> and integer literals.</p>
-Objective-C++ also supports <code>@true</code> and <code>@false</code> expressions, which are equivalent to <code>@YES</code> and <code>@NO</code>.
+<p>Objective-C++ also supports <code>@true</code> and <code>@false</code> expressions, which are equivalent to <code>@YES</code> and <code>@NO</code>.</p>
+<!-- ======================================================================= -->
+<h2 id="objc_boxed_expressions">Boxed Expressions</h2>
+<!-- ======================================================================= -->
+
+<p>Objective-C provides a new syntax for boxing C expressions:</p>
+
+<pre>
+<code>@( <em>expression</em> )</code>
+</pre>
+
+<p>Expressions of scalar (numeric, enumerated, BOOL) and C string pointer types
+are supported:</p>
+
+<pre>
+// numbers.
+NSNumber *smallestInt = @(-INT_MAX - 1); // [NSNumber numberWithInt:(-INT_MAX - 1)]
+NSNumber *piOverTwo = @(M_PI / 2); // [NSNumber numberWithDouble:(M_PI / 2)]
+
+// enumerated types.
+typedef enum { Red, Green, Blue } Color;
+NSNumber *favoriteColor = @(Green); // [NSNumber numberWithInt:((int)Green)]
+
+// strings.
+NSString *path = @(getenv("PATH")); // [NSString stringWithUTF8String:(getenv("PATH"))]
+NSArray *pathComponents = [path componentsSeparatedByString:@":"];
+</pre>
+
+<h3>Boxed Enums</h3>
+
+<p>
+Cocoa frameworks frequently define constant values using <em>enums.</em> Although enum values are integral, they may not be used directly as boxed literals (this avoids conflicts with future <code>'@'</code>-prefixed Objective-C keywords). Instead, an enum value must be placed inside a boxed expression. The following example demonstrates configuring an <code>AVAudioRecorder</code> using a dictionary that contains a boxed enumeration value:
+</p>
+
+<pre>
+enum {
+ AVAudioQualityMin = 0,
+ AVAudioQualityLow = 0x20,
+ AVAudioQualityMedium = 0x40,
+ AVAudioQualityHigh = 0x60,
+ AVAudioQualityMax = 0x7F
+};
+
+- (AVAudioRecorder *)recordToFile:(NSURL *)fileURL {
+ NSDictionary *settings = @{ AVEncoderAudioQualityKey : @(AVAudioQualityMax) };
+ return [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:NULL];
+}
+</pre>
+
+<p>
+The expression <code>@(AVAudioQualityMax)</code> converts <code>AVAudioQualityMax</code> to an integer type, and boxes the value accordingly. If the enum has a <a href="http://clang.llvm.org/docs/LanguageExtensions.html#objc_fixed_enum">fixed underlying type</a> as in:
+</p>
+
+<pre>
+typedef enum : unsigned char { Red, Green, Blue } Color;
+NSNumber *red = @(Red), *green = @(Green), *blue = @(Blue); // => [NSNumber numberWithUnsignedChar:]
+</pre>
+
+<p>
+then the fixed underlying type will be used to select the correct <code>NSNumber</code> creation method.
+</p>
+
+<p>
+Boxing a value of enum type will result in a <code>NSNumber</code> pointer with a creation method according to the underlying type of the enum,
+which can be a <a href="http://clang.llvm.org/docs/LanguageExtensions.html#objc_fixed_enum">fixed underlying type</a> or a compiler-defined
+integer type capable of representing the values of all the members of the enumeration:
+</p>
+
+<pre>
+typedef enum : unsigned char { Red, Green, Blue } Color;
+Color col = Red;
+NSNumber *nsCol = @(col); // => [NSNumber numberWithUnsignedChar:]
+</pre>
+
+<h3>Boxed C Strings</h3>
+
+<p>
+A C string literal prefixed by the <code>'@'</code> token denotes an <code>NSString</code> literal in the same way a numeric literal prefixed by the <code>'@'</code> token denotes an <code>NSNumber</code> literal. When the type of the parenthesized expression is <code>(char *)</code> or <code>(const char *)</code>, the result of the boxed expression is a pointer to an <code>NSString</code> object containing equivalent character data, which is assumed to be '\0'-terminated and UTF-8 encoded. The following example converts C-style command line arguments into <code>NSString</code> objects.
+</p>
+
+<pre>
+// Partition command line arguments into positional and option arguments.
+NSMutableArray *args = [NSMutableArray new];
+NSMutableDictionary *options = [NSMutableArray new];
+while (--argc) {
+ const char *arg = *++argv;
+ if (strncmp(arg, "--", 2) == 0) {
+ options[@(arg + 2)] = @(*++argv); // --key value
+ } else {
+ [args addObject:@(arg)]; // positional argument
+ }
+}
+</pre>
+
+<p>
+As with all C pointers, character pointer expressions can involve arbitrary pointer arithmetic, therefore programmers must ensure that the character data is valid. Passing <code>NULL</code> as the character pointer will raise an exception at runtime. When possible, the compiler will reject <code>NULL</code> character pointers used in boxed expressions.
+</p>
+
+<h3>Availability</h3>
+
+<p>Boxed expressions will be available in clang 3.2. It is not currently available in any Apple compiler.</p>
<h2>Container Literals</h2>
-Objective-C now supports a new expression syntax for creating immutable array and dictionary container objects.
+<p>Objective-C now supports a new expression syntax for creating immutable array and dictionary container objects.</p>
<h3>Examples</h3>
-Immutable array expression:<p>
+<p>Immutable array expression:</p>
- <pre>
+<pre>
NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ];
</pre>
-This creates an <code>NSArray</code> with 3 elements. The comma-separated sub-expressions of an array literal can be any Objective-C object pointer typed expression.<p>
+<p>This creates an <code>NSArray</code> with 3 elements. The comma-separated sub-expressions of an array literal can be any Objective-C object pointer typed expression.</p>
-Immutable dictionary expression:<p>
+<p>Immutable dictionary expression:</p>
<pre>
NSDictionary *dictionary = @{
@@ -120,21 +221,21 @@ NSDictionary *dictionary = @{
};
</pre>
-This creates an <code>NSDictionary</code> with 3 key/value pairs. Value sub-expressions of a dictionary literal must be Objective-C object pointer typed, as in array literals. Key sub-expressions must be of an Objective-C object pointer type that implements the <code>&LT;NSCopying&GT;</code> protocol.<p>
+<p>This creates an <code>NSDictionary</code> with 3 key/value pairs. Value sub-expressions of a dictionary literal must be Objective-C object pointer typed, as in array literals. Key sub-expressions must be of an Objective-C object pointer type that implements the <code>&LT;NSCopying&GT;</code> protocol.</p>
<h3>Discussion</h3>
-Neither keys nor values can have the value <code>nil</code> in containers. If the compiler can prove that a key or value is <code>nil</code> at compile time, then a warning will be emitted. Otherwise, a runtime error will occur.<p>
+<p>Neither keys nor values can have the value <code>nil</code> in containers. If the compiler can prove that a key or value is <code>nil</code> at compile time, then a warning will be emitted. Otherwise, a runtime error will occur.</p>
-Using array and dictionary literals is safer than the variadic creation forms commonly in use today. Array literal expressions expand to calls to <code>+[NSArray arrayWithObjects:count:]</code>, which validates that all objects are non-<code>nil</code>. The variadic form, <code>+[NSArray arrayWithObjects:]</code> uses <code>nil</code> as an argument list terminator, which can lead to malformed array objects. Dictionary literals are similarly created with <code>+[NSDictionary dictionaryWithObjects:forKeys:count:]</code> which validates all objects and keys, unlike <code>+[NSDictionary dictionaryWithObjectsAndKeys:]</code> which also uses a <code>nil</code> parameter as an argument list terminator.<p>
+<p>Using array and dictionary literals is safer than the variadic creation forms commonly in use today. Array literal expressions expand to calls to <code>+[NSArray arrayWithObjects:count:]</code>, which validates that all objects are non-<code>nil</code>. The variadic form, <code>+[NSArray arrayWithObjects:]</code> uses <code>nil</code> as an argument list terminator, which can lead to malformed array objects. Dictionary literals are similarly created with <code>+[NSDictionary dictionaryWithObjects:forKeys:count:]</code> which validates all objects and keys, unlike <code>+[NSDictionary dictionaryWithObjectsAndKeys:]</code> which also uses a <code>nil</code> parameter as an argument list terminator.</p>
<h2>Object Subscripting</h2>
-Objective-C object pointer values can now be used with C's subscripting operator.<p>
+<p>Objective-C object pointer values can now be used with C's subscripting operator.</p>
<h3>Examples</h3>
-The following code demonstrates the use of object subscripting syntax with <code>NSMutableArray</code> and <code>NSMutableDictionary</code> objects:<p>
+<p>The following code demonstrates the use of object subscripting syntax with <code>NSMutableArray</code> and <code>NSMutableDictionary</code> objects:</p>
<pre>
NSMutableArray *array = ...;
@@ -149,87 +250,93 @@ oldObject = dictionary[key];
dictionary[key] = newObject; // replace oldObject with newObject
</pre>
-The next section explains how subscripting expressions map to accessor methods.<p>
+<p>The next section explains how subscripting expressions map to accessor methods.</p>
<h3>Subscripting Methods</h3>
-Objective-C supports two kinds of subscript expressions: <i>array-style</i> subscript expressions use integer typed subscripts; <i>dictionary-style</i> subscript expressions use Objective-C object pointer typed subscripts. Each type of subscript expression is mapped to a message send using a predefined selector. The advantage of this design is flexibility: class designers are free to introduce subscripting by declaring methods or by adopting protocols. Moreover, because the method names are selected by the type of the subscript, an object can be subscripted using both array and dictionary styles.
+<p>Objective-C supports two kinds of subscript expressions: <i>array-style</i> subscript expressions use integer typed subscripts; <i>dictionary-style</i> subscript expressions use Objective-C object pointer typed subscripts. Each type of subscript expression is mapped to a message send using a predefined selector. The advantage of this design is flexibility: class designers are free to introduce subscripting by declaring methods or by adopting protocols. Moreover, because the method names are selected by the type of the subscript, an object can be subscripted using both array and dictionary styles.</p>
<h4>Array-Style Subscripting</h4>
-When the subscript operand has an integral type, the expression is rewritten to use one of two different selectors, depending on whether the element is being read or written. When an expression reads an element using an integral index, as in the following example:<p>
+<p>When the subscript operand has an integral type, the expression is rewritten to use one of two different selectors, depending on whether the element is being read or written. When an expression reads an element using an integral index, as in the following example:</p>
<pre>
NSUInteger idx = ...;
id value = object[idx];
</pre>
-it is translated into a call to <code>objectAtIndexedSubscript:</code><p>
+<p>it is translated into a call to <code>objectAtIndexedSubscript:</code></p>
<pre>
id value = [object objectAtIndexedSubscript:idx];
</pre>
-When an expression writes an element using an integral index:<p>
+<p>When an expression writes an element using an integral index:</p>
<pre>
object[idx] = newValue;
</pre>
-it is translated to a call to <code>setObject:atIndexedSubscript:</code><p>
+<p>it is translated to a call to <code>setObject:atIndexedSubscript:</code></p>
<pre>
[object setObject:newValue atIndexedSubscript:idx];
</pre>
-These message sends are then type-checked and performed just like explicit message sends. The method used for objectAtIndexedSubscript: must be declared with an argument of integral type and a return value of some Objective-C object pointer type. The method used for setObject:atIndexedSubscript: must be declared with its first argument having some Objective-C pointer type and its second argument having integral type.<p>
+<p>These message sends are then type-checked and performed just like explicit message sends. The method used for objectAtIndexedSubscript: must be declared with an argument of integral type and a return value of some Objective-C object pointer type. The method used for setObject:atIndexedSubscript: must be declared with its first argument having some Objective-C pointer type and its second argument having integral type.</p>
-The meaning of indexes is left up to the declaring class. The compiler will coerce the index to the appropriate argument type of the method it uses for type-checking. For an instance of <code>NSArray</code>, reading an element using an index outside the range <code>[0, array.count)</code> will raise an exception. For an instance of <code>NSMutableArray</code>, assigning to an element using an index within this range will replace that element, but assigning to an element using an index outside this range will raise an exception; no syntax is provided for inserting, appending, or removing elements for mutable arrays.<p>
+<p>The meaning of indexes is left up to the declaring class. The compiler will coerce the index to the appropriate argument type of the method it uses for type-checking. For an instance of <code>NSArray</code>, reading an element using an index outside the range <code>[0, array.count)</code> will raise an exception. For an instance of <code>NSMutableArray</code>, assigning to an element using an index within this range will replace that element, but assigning to an element using an index outside this range will raise an exception; no syntax is provided for inserting, appending, or removing elements for mutable arrays.</p>
-A class need not declare both methods in order to take advantage of this language feature. For example, the class <code>NSArray</code> declares only <code>objectAtIndexedSubscript:</code>, so that assignments to elements will fail to type-check; moreover, its subclass <code>NSMutableArray</code> declares <code>setObject:atIndexedSubscript:</code>.
+<p>A class need not declare both methods in order to take advantage of this language feature. For example, the class <code>NSArray</code> declares only <code>objectAtIndexedSubscript:</code>, so that assignments to elements will fail to type-check; moreover, its subclass <code>NSMutableArray</code> declares <code>setObject:atIndexedSubscript:</code>.</p>
<h4>Dictionary-Style Subscripting</h4>
-When the subscript operand has an Objective-C object pointer type, the expression is rewritten to use one of two different selectors, depending on whether the element is being read from or written to. When an expression reads an element using an Objective-C object pointer subscript operand, as in the following example:<p>
+<p>When the subscript operand has an Objective-C object pointer type, the expression is rewritten to use one of two different selectors, depending on whether the element is being read from or written to. When an expression reads an element using an Objective-C object pointer subscript operand, as in the following example:</p>
<pre>
id key = ...;
id value = object[key];
</pre>
-it is translated into a call to the <code>objectForKeyedSubscript:</code> method:<p>
+<p>it is translated into a call to the <code>objectForKeyedSubscript:</code> method:</p>
<pre>
id value = [object objectForKeyedSubscript:key];
</pre>
-When an expression writes an element using an Objective-C object pointer subscript:<p>
+<p>When an expression writes an element using an Objective-C object pointer subscript:</p>
<pre>
object[key] = newValue;
</pre>
-it is translated to a call to <code>setObject:forKeyedSubscript:</code>
+<p>it is translated to a call to <code>setObject:forKeyedSubscript:</code></p>
<pre>
[object setObject:newValue forKeyedSubscript:key];
</pre>
-The behavior of <code>setObject:forKeyedSubscript:</code> is class-specific; but in general it should replace an existing value if one is already associated with a key, otherwise it should add a new value for the key. No syntax is provided for removing elements from mutable dictionaries.<p>
+<p>The behavior of <code>setObject:forKeyedSubscript:</code> is class-specific; but in general it should replace an existing value if one is already associated with a key, otherwise it should add a new value for the key. No syntax is provided for removing elements from mutable dictionaries.</p>
<h3>Discussion</h3>
-An Objective-C subscript expression occurs when the base operand of the C subscript operator has an Objective-C object pointer type. Since this potentially collides with pointer arithmetic on the value, these expressions are only supported under the modern Objective-C runtime, which categorically forbids such arithmetic.<p>
+<p>An Objective-C subscript expression occurs when the base operand of the C subscript operator has an Objective-C object pointer type. Since this potentially collides with pointer arithmetic on the value, these expressions are only supported under the modern Objective-C runtime, which categorically forbids such arithmetic.</p>
-Currently, only subscripts of integral or Objective-C object pointer type are supported. In C++, a class type can be used if it has a single conversion function to an integral or Objective-C pointer type, in which case that conversion is applied and analysis continues as appropriate. Otherwise, the expression is ill-formed.<p>
+<p>Currently, only subscripts of integral or Objective-C object pointer type are supported. In C++, a class type can be used if it has a single conversion function to an integral or Objective-C pointer type, in which case that conversion is applied and analysis continues as appropriate. Otherwise, the expression is ill-formed.</p>
-An Objective-C object subscript expression is always an l-value. If the expression appears on the left-hand side of a simple assignment operator (=), the element is written as described below. If the expression appears on the left-hand side of a compound assignment operator (e.g. +=), the program is ill-formed, because the result of reading an element is always an Objective-C object pointer and no binary operators are legal on such pointers. If the expression appears in any other position, the element is read as described below. It is an error to take the address of a subscript expression, or (in C++) to bind a reference to it.<p>
+<p>An Objective-C object subscript expression is always an l-value. If the expression appears on the left-hand side of a simple assignment operator (=), the element is written as described below. If the expression appears on the left-hand side of a compound assignment operator (e.g. +=), the program is ill-formed, because the result of reading an element is always an Objective-C object pointer and no binary operators are legal on such pointers. If the expression appears in any other position, the element is read as described below. It is an error to take the address of a subscript expression, or (in C++) to bind a reference to it.</p>
+
+<p>Programs can use object subscripting with Objective-C object pointers of type <code>id</code>. Normal dynamic message send rules apply; the compiler must see <i>some</i> declaration of the subscripting methods, and will pick the declaration seen first.</p>
+
+<h2>Caveats</h2>
-Programs can use object subscripting with Objective-C object pointers of type <code>id</code>. Normal dynamic message send rules apply; the compiler must see <i>some</i> declaration of the subscripting methods, and will pick the declaration seen first.<p>
+<p>Objects created using the literal or boxed expression syntax are not guaranteed to be uniqued by the runtime, but nor are they guaranteed to be newly-allocated. As such, the result of performing direct comparisons against the location of an object literal (using <code>==</code>, <code>!=</code>, <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, or <code>&gt;=</code>) is not well-defined. This is usually a simple mistake in code that intended to call the <code>isEqual:</code> method (or the <code>compare:</code> method).</p>
+
+<p>This caveat applies to compile-time string literals as well. Historically, string literals (using the <code>@"..."</code> syntax) have been uniqued across translation units during linking. This is an implementation detail of the compiler and should not be relied upon. If you are using such code, please use global string constants instead (<code>NSString * const MyConst = @"..."</code>) or use <code>isEqual:</code>.</p>
<h2>Grammar Additions</h2>
-To support the new syntax described above, the Objective-C <code>@</code>-expression grammar has the following new productions:<p>
+<p>To support the new syntax described above, the Objective-C <code>@</code>-expression grammar has the following new productions:</p>
<pre>
objc-at-expression : '@' (string-literal | encode-literal | selector-literal | protocol-literal | object-literal)
@@ -263,11 +370,11 @@ key-value-pair : assignment-expression ':' assignment-expression
;
</pre>
-Note: <code>@true</code> and <code>@false</code> are only supported in Objective-C++.<p>
+<p>Note: <code>@true</code> and <code>@false</code> are only supported in Objective-C++.</p>
<h2>Availability Checks</h2>
-Programs test for the new features by using clang's __has_feature checks. Here are examples of their use:<p>
+<p>Programs test for the new features by using clang's __has_feature checks. Here are examples of their use:</p>
<pre>
#if __has_feature(objc_array_literals)
@@ -307,7 +414,9 @@ Programs test for the new features by using clang's __has_feature checks. Here a
#endif
</pre>
-Code can use also <code>__has_feature(objc_bool)</code> to check for the availability of numeric literals support. This checks for the new <code>__objc_yes / __objc_no</code> keywords, which enable the use of <code>@YES / @NO</code> literals.<p>
+<p>Code can use also <code>__has_feature(objc_bool)</code> to check for the availability of numeric literals support. This checks for the new <code>__objc_yes / __objc_no</code> keywords, which enable the use of <code>@YES / @NO</code> literals.</p>
+
+<p>To check whether boxed expressions are supported, use <code>__has_feature(objc_boxed_expressions)</code> feature macro.</p>
</div>
</body>
diff --git a/docs/RAVFrontendAction.html b/docs/RAVFrontendAction.html
new file mode 100644
index 0000000..b30cd57
--- /dev/null
+++ b/docs/RAVFrontendAction.html
@@ -0,0 +1,224 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>How to write RecursiveASTVisitor based ASTFrontendActions.</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>How to write RecursiveASTVisitor based ASTFrontendActions.</h1>
+
+<!-- ======================================================================= -->
+<h2 id="intro">Introduction</h2>
+<!-- ======================================================================= -->
+
+In this tutorial you will learn how to create a FrontendAction that uses
+a RecursiveASTVisitor to find CXXRecordDecl AST nodes with a specified name.
+
+<!-- ======================================================================= -->
+<h2 id="action">Creating a FrontendAction</h2>
+<!-- ======================================================================= -->
+
+<p>When writing a clang based tool like a Clang Plugin or a standalone tool
+based on LibTooling, the common entry point is the FrontendAction.
+FrontendAction is an interface that allows execution of user specific actions
+as part of the compilation. To run tools over the AST clang provides the
+convenience interface ASTFrontendAction, which takes care of executing the
+action. The only part left is to implement the CreateASTConsumer method that
+returns an ASTConsumer per translation unit.</p>
+<pre>
+ class FindNamedClassAction : public clang::ASTFrontendAction {
+ public:
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
+ return new FindNamedClassConsumer;
+ }
+ };
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="consumer">Creating an ASTConsumer</h2>
+<!-- ======================================================================= -->
+
+<p>ASTConsumer is an interface used to write generic actions on an AST,
+regardless of how the AST was produced. ASTConsumer provides many different
+entry points, but for our use case the only one needed is HandleTranslationUnit,
+which is called with the ASTContext for the translation unit.</p>
+<pre>
+ class FindNamedClassConsumer : public clang::ASTConsumer {
+ public:
+ virtual void HandleTranslationUnit(clang::ASTContext &amp;Context) {
+ // Traversing the translation unit decl via a RecursiveASTVisitor
+ // will visit all nodes in the AST.
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+ }
+ private:
+ // A RecursiveASTVisitor implementation.
+ FindNamedClassVisitor Visitor;
+ };
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="rav">Using the RecursiveASTVisitor</h2>
+<!-- ======================================================================= -->
+
+<p>Now that everything is hooked up, the next step is to implement a
+RecursiveASTVisitor to extract the relevant information from the AST.</p>
+<p>The RecursiveASTVisitor provides hooks of the form
+bool VisitNodeType(NodeType *) for most AST nodes; the exception are TypeLoc
+nodes, which are passed by-value. We only need to implement the methods for the
+relevant node types.
+</p>
+<p>Let's start by writing a RecursiveASTVisitor that visits all CXXRecordDecl's.
+<pre>
+ class FindNamedClassVisitor
+ : public RecursiveASTVisitor&lt;FindNamedClassVisitor> {
+ public:
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ // For debugging, dumping the AST nodes will show which nodes are already
+ // being visited.
+ Declaration->dump();
+
+ // The return value indicates whether we want the visitation to proceed.
+ // Return false to stop the traversal of the AST.
+ return true;
+ }
+ };
+</pre>
+</p>
+<p>In the methods of our RecursiveASTVisitor we can now use the full power of
+the Clang AST to drill through to the parts that are interesting for us. For
+example, to find all class declaration with a certain name, we can check for a
+specific qualified name:
+<pre>
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ if (Declaration->getQualifiedNameAsString() == "n::m::C")
+ Declaration->dump();
+ return true;
+ }
+</pre>
+</p>
+
+<!-- ======================================================================= -->
+<h2 id="context">Accessing the SourceManager and ASTContext</h2>
+<!-- ======================================================================= -->
+
+<p>Some of the information about the AST, like source locations and global
+identifier information, are not stored in the AST nodes themselves, but in
+the ASTContext and its associated source manager. To retrieve them we need to
+hand the ASTContext into our RecursiveASTVisitor implementation.</p>
+<p>The ASTContext is available from the CompilerInstance during the call
+to CreateASTConsumer. We can thus extract it there and hand it into our
+freshly created FindNamedClassConsumer:</p>
+<pre>
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
+ return new FindNamedClassConsumer(<b>&amp;Compiler.getASTContext()</b>);
+ }
+</pre>
+
+<p>Now that the ASTContext is available in the RecursiveASTVisitor, we can do
+more interesting things with AST nodes, like looking up their source
+locations:</p>
+<pre>
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ if (Declaration->getQualifiedNameAsString() == "n::m::C") {
+ // getFullLoc uses the ASTContext's SourceManager to resolve the source
+ // location and break it up into its line and column parts.
+ FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
+ if (FullLocation.isValid())
+ llvm::outs() &lt;&lt; "Found declaration at "
+ &lt;&lt; FullLocation.getSpellingLineNumber() &lt;&lt; ":"
+ &lt;&lt; FullLocation.getSpellingColumnNumber() &lt;&lt; "\n";
+ }
+ return true;
+ }
+</pre>
+
+<!-- ======================================================================= -->
+<h2 id="full">Putting it all together</h2>
+<!-- ======================================================================= -->
+
+<p>Now we can combine all of the above into a small example program:</p>
+<pre>
+ #include "clang/AST/ASTConsumer.h"
+ #include "clang/AST/RecursiveASTVisitor.h"
+ #include "clang/Frontend/CompilerInstance.h"
+ #include "clang/Frontend/FrontendAction.h"
+ #include "clang/Tooling/Tooling.h"
+
+ using namespace clang;
+
+ class FindNamedClassVisitor
+ : public RecursiveASTVisitor&lt;FindNamedClassVisitor> {
+ public:
+ explicit FindNamedClassVisitor(ASTContext *Context)
+ : Context(Context) {}
+
+ bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
+ if (Declaration->getQualifiedNameAsString() == "n::m::C") {
+ FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getLocStart());
+ if (FullLocation.isValid())
+ llvm::outs() &lt;&lt; "Found declaration at "
+ &lt;&lt; FullLocation.getSpellingLineNumber() &lt;&lt; ":"
+ &lt;&lt; FullLocation.getSpellingColumnNumber() &lt;&lt; "\n";
+ }
+ return true;
+ }
+
+ private:
+ ASTContext *Context;
+ };
+
+ class FindNamedClassConsumer : public clang::ASTConsumer {
+ public:
+ explicit FindNamedClassConsumer(ASTContext *Context)
+ : Visitor(Context) {}
+
+ virtual void HandleTranslationUnit(clang::ASTContext &amp;Context) {
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+ }
+ private:
+ FindNamedClassVisitor Visitor;
+ };
+
+ class FindNamedClassAction : public clang::ASTFrontendAction {
+ public:
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &amp;Compiler, llvm::StringRef InFile) {
+ return new FindNamedClassConsumer(&amp;Compiler.getASTContext());
+ }
+ };
+
+ int main(int argc, char **argv) {
+ if (argc > 1) {
+ clang::tooling::runToolOnCode(new FindNamedClassAction, argv[1]);
+ }
+ }
+</pre>
+
+<p>We store this into a file called FindClassDecls.cpp and create the following
+CMakeLists.txt to link it:</p>
+<pre>
+set(LLVM_USED_LIBS clangTooling)
+
+add_clang_executable(find-class-decls FindClassDecls.cpp)
+</pre>
+
+<p>When running this tool over a small code snippet it will output all
+declarations of a class n::m::C it found:</p>
+<pre>
+ $ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
+ Found declaration at 1:29
+</pre>
+
+</div>
+</body>
+</html>
+
diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html
index 8367b8a..2108909 100644
--- a/docs/ReleaseNotes.html
+++ b/docs/ReleaseNotes.html
@@ -2,7 +2,7 @@
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
-<title>Clang 3.1 Release Notes</title>
+<title>Clang 3.2 Release Notes</title>
<link type="text/css" rel="stylesheet" href="../menu.css">
<link type="text/css" rel="stylesheet" href="../content.css">
<style type="text/css">
@@ -17,21 +17,25 @@ td {
<div id="content">
-<h1>Clang 3.1 Release Notes</h1>
+<h1>Clang 3.2 Release Notes</h1>
<img style="float:right" src="http://llvm.org/img/DragonSmall.png"
width="136" height="136" alt="LLVM Dragon Logo">
<ul>
<li><a href="#intro">Introduction</a></li>
- <li><a href="#whatsnew">What's New in Clang 3.1?</a>
+ <li><a href="#whatsnew">What's New in Clang 3.2?</a>
<ul>
+ <li><a href="#majorfeatures">Major New Features</a></li>
+ <li><a href="#newflags">New Compiler Flags</a></li>
<li><a href="#cchanges">C Language Changes</a></li>
<li><a href="#cxxchanges">C++ Language Changes</a></li>
<li><a href="#objcchanges">Objective-C Language Changes</a></li>
+ <li><a href="#apichanges">Internal API Changes</a></li>
<li><a href="#pythonchanges">Python Binding Changes</a></li>
</ul>
</li>
+ <li><a href="#knownproblems">Known Problems</a></li>
<li><a href="#additionalinfo">Additional Information</a></li>
</ul>
@@ -39,36 +43,166 @@ td {
<p>Written by the <a href="http://llvm.org/">LLVM Team</a></p>
</div>
+<h1 style="color:red">These are in-progress notes for the upcoming Clang 3.2
+release.<br>
+You may prefer the
+<a href="http://llvm.org/releases/3.1/docs/ClangReleaseNotes.html">Clang 3.1
+Release Notes</a>.</h1>
+
<!-- ======================================================================= -->
<h2 id="intro">Introduction</h2>
<!-- ======================================================================= -->
<p>This document contains the release notes for the Clang C/C++/Objective-C
-frontend, part of the LLVM Compiler Infrastructure, release 3.1. Here we
-describe the status of Clang in some detail, including major improvements from
-the previous release and new feature work. For the general LLVM release notes,
-see <a href="http://llvm.org/docs/ReleaseNotes.html">the LLVM
- documentation</a>. All LLVM releases may be downloaded from the
-<a href="http://llvm.org/releases/">LLVM releases web site</a>.</p>
+ frontend, part of the LLVM Compiler Infrastructure, release 3.2. Here we
+ describe the status of Clang in some detail, including major improvements
+ from the previous release and new feature work. For the general LLVM release
+ notes, see <a href="http://llvm.org/docs/ReleaseNotes.html">the LLVM
+ documentation</a>. All LLVM releases may be downloaded from the
+ <a href="http://llvm.org/releases/">LLVM releases web site</a>.</p>
<p>For more information about Clang or LLVM, including information about the
-latest release, please check out the main please see the
-<a href="http://clang.llvm.org">Clang Web Site</a> or the
-<a href="http://llvm.org">LLVM Web Site</a>.
+ latest release, please check out the main please see the
+ <a href="http://clang.llvm.org">Clang Web Site</a> or the
+ <a href="http://llvm.org">LLVM Web Site</a>.
<p>Note that if you are reading this file from a Subversion checkout or the main
-Clang web page, this document applies to the <i>next</i> release, not the
-current one. To see the release notes for a specific release, please see the
-<a href="http://llvm.org/releases/">releases page</a>.</p>
+ Clang web page, this document applies to the <i>next</i> release, not the
+ current one. To see the release notes for a specific release, please see the
+ <a href="http://llvm.org/releases/">releases page</a>.</p>
<!-- ======================================================================= -->
-<h2 id="whatsnew">What's New in Clang 3.1?</h2>
+<h2 id="whatsnew">What's New in Clang 3.2?</h2>
<!-- ======================================================================= -->
<p>Some of the major new features and improvements to Clang are listed here.
-Generic improvements to Clang as a whole or two its underlying infrastructure
-are described first, followed by language-specific sections with improvements to
-Clang's support for those languages.</p>
+ Generic improvements to Clang as a whole or to its underlying infrastructure
+ are described first, followed by language-specific sections with improvements
+ to Clang's support for those languages.</p>
+
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+<h3 id="majorfeatures">Major New Features</h3>
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+
+<h4 id="diagnostics">Improvements to Clang's diagnostics</h4>
+
+<p>Clang's diagnostics are constantly being improved to catch more issues,
+explain them more clearly, and provide more accurate source information about
+them. The improvements since the 3.1 release include:</p>
+
+<ul>
+ <li><tt>-Wuninitialized</tt> has been taught to recognise uninitialized uses
+ which always occur when an explicitly-written non-constant condition is either
+ <tt>true</tt> or <tt>false</tt>. For example:
+
+<pre>
+int f(bool b) {
+ int n;
+ if (b)
+ n = 1;
+ return n;
+}
+
+<b>sometimes-uninit.cpp:3:7: <span class="warning">warning:</span> variable 'n' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]</b>
+ if (b)
+ <span class="caret">^</span>
+<b>sometimes-uninit.cpp:5:10: <span class="note">note:</span></b> uninitialized use occurs here
+ return n;
+ <span class="caret">^</span>
+<b>sometimes-uninit.cpp:3:3: <span class="note">note:</span></b> remove the 'if' if its condition is always true
+ if (b)
+ <span class="caret">^~~~~~</span>
+<b>sometimes-uninit.cpp:2:8: <span class="note">note:</span></b> initialize the variable 'n' to silence this warning
+ int n;
+ <span class="caret">^</span>
+ <span class="caret"> = 0</span>
+</pre>
+
+ This functionality can be enabled or disabled separately from
+ <tt>-Wuninitialized</tt> with the <tt>-Wsometimes-uninitialized</tt> warning
+ flag.</li>
+
+ <li>Template type diffing improves the display of diagnostics with templated
+ types in them.
+
+<pre>
+int f(vector&lt;map&lt;int, double&gt;&gt;);
+int x = f(vector&lt;map&lt;int, float&gt;&gt;());
+</pre>
+ The error message is the same, but the note is different based on the options selected.
+<pre>
+<b>template-diff.cpp:5:9: <span class="error">error:</span> no matching function for call to 'f'</b>
+int x = f(vector&lt;map&lt;int, float&gt;&gt;());
+ <span class="caret">^</span>
+</pre>
+ Templated type diffing with type elision (default):
+<pre>
+<b>template-diff.cpp:4:5: <span class="note">note:</span></b> candidate function not viable: no known conversion from 'vector&lt;map&lt;[...], <span class="template-highlight">float</span>&gt;&gt;' to 'vector&lt;map&lt;[...], <span class="template-highlight">double</span>&gt;&gt;' for 1st argument;
+int f(vector&lt;map&lt;int, double&gt;&gt;);
+ <span class="caret">^</span>
+</pre>
+ Templated type diffing without type elision (-fno-elide-type):
+<pre>
+<b>template-diff.cpp:4:5: <span class="note">note:</span></b> candidate function not viable: no known conversion from 'vector&lt;map&lt;int, <span class="template-highlight">float</span>&gt;&gt;' to 'vector&lt;map&lt;int, <span class="template-highlight">double</span>&gt;&gt;' for 1st argument;
+int f(vector&lt;map&lt;int, double&gt;&gt;);
+ <span class="caret">^</span>
+</pre>
+ Templated tree printing with type elision (-fdiagnostics-show-template-tree):
+<pre>
+<b>template-diff.cpp:4:5: <span class="note">note:</span></b> candidate function not viable: no known conversion for 1st argument;
+ vector&lt;
+ map&lt;
+ [...],
+ [<span class="template-highlight">float</span> != <span class="template-highlight">double</span>]&gt;&gt;
+int f(vector&lt;map&lt;int, double&gt;&gt;);
+ <span class="caret">^</span>
+</pre>
+ Templated tree printing without type elision (-fdiagnostics-show-template-tree -fno-elide-type):
+<pre>
+<b>template-diff.cpp:4:5: <span class="note">note:</span></b> candidate function not viable: no known conversion for 1st argument;
+ vector&lt;
+ map&lt;
+ int,
+ [<span class="template-highlight">float</span> != <span class="template-highlight">double</span>]&gt;&gt;
+int f(vector&lt;map&lt;int, double&gt;&gt;);
+ <span class="caret">^</span>
+</pre>
+
+ </li>
+
+</ul>
+
+<h4 id="tlsmodel">Support for <code>tls_model</code> attribute</h4>
+
+<p>Clang now supports the <code>tls_model</code> attribute, allowing code that
+uses thread-local storage to explicitly select which model to use. The available
+models are <code>"global-dynamic"</code>, <code>"local-dynamic"</code>,
+<code>"initial-exec"</code> and <code>"local-exec"</code>. See
+<a href="http://www.akkadia.org/drepper/tls.pdf">ELF Handling For Thread-Local
+ Storage</a> for more information.</p>
+
+<p>The compiler is free to choose a different model if the specified model is not
+supported by the target, or if the compiler determines that a more specific
+model can be used.
+</p>
+
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+<h3 id="newflags">New Compiler Flags</h3>
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+<ul>
+ <li><tt>-gline-tables-only</tt> controls the
+ <a href="http://clang.llvm.org/docs/UsersManual.html#debuginfosize">size of debug information</a>.
+ This flag tells Clang to emit debug info which is just enough to obtain stack traces with
+ function names, file names and line numbers (by such tools as gdb or addr2line).
+ Debug info for variables or function parameters is not produced, which reduces
+ the size of the resulting binary.
+
+ <li><tt>-ftls-model</tt> controls which TLS model to use for thread-local
+ variables. This can be overridden per variable using the
+ <a href="#tlsmodel"><tt>tls_model</tt> attribute</a> mentioned above.
+ For more details, see the <a href="UsersManual.html#opt_ftls-model">User's
+ Manual</a>.</li>
+</ul>
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
<h3 id="cchanges">C Language Changes in Clang</h3>
@@ -76,51 +210,33 @@ Clang's support for those languages.</p>
<h4 id="c11changes">C11 Feature Support</h4>
-<p>Clang 3.1 adds support for anonymous structs and anonymous unions, added in
-the latest ISO C standard. Use <code>-std=c11</code> or <code>-std=gnu11</code>
-to enable support for the new language standard. The new C11 features are
-backwards-compatible and are available as an extension in all language
-modes.</p>
-
-<p>All warning and language selection flags which previously accepted
-<code>c1x</code> have been updated to accept <code>c11</code>. The old
-<code>c1x</code> forms have been removed.
+<p>...</p>
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
<h3 id="cxxchanges">C++ Language Changes in Clang</h3>
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
<h4 id="cxx11changes">C++11 Feature Support</h4>
-<p>Clang 3.1 supports
-<a href="http://clang.llvm.org/cxx_status.html#cxx11">most of the language
-features</a> added in the latest ISO C++ standard,
-<a href="http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=50372">C++ 2011</a>.
-Use <code>-std=c++11</code> or <code>-std=gnu++11</code> to enable support for
-these features. In addition to the features supported by Clang 3.0, the
-following are now considered to be of production quality:
-<ul>
- <li>Generalized constant expressions</li>
- <li>Lambda expressions</li>
- <li>Generalized initializers</li>
- <li>Unrestricted unions</li>
- <li>User-defined literals</li>
- <li>Forward-declared enumerations</li>
- <li>Atomics (both libc++'s and libstdc++4.7's <tt>&lt;atomic&gt;</tt> are
- supported)</li>
-</ul>
+
+<p>...</p>
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
<h3 id="objcchanges">Objective-C Language Changes in Clang</h3>
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
-Clang 3.1 introduces several new Objective-C language features and improvements.
-<h4 id="literals-subscripting">Objective-C literals and subscripting</h3>
+<p>...</p>
-<p>Objective-C now provides additional literal expressions, including numeric, array, and dictionary literals. Additionally, array and dictionary elements can be accesses via the subscripting operator. For more information about the new literals, see the <a href="ObjectiveCLiterals.html">documentation for Objective-C literals and subscripting</a>.
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+<h3 id="apichanges">Internal API Changes</h3>
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+
+<p>These are major API changes that have happened since the 3.1 release of
+ Clang. If upgrading an external codebase that uses Clang as a library, this
+ section should help get you past the largest hurdles of upgrading.</p>
-<h4 id="objcwformat">Format string checking for NSString literals</h4>
+<h4 id="api1">API change 1</h4>
-<code>-Wformat</code> now checks <code>@"NSString literals"</code>.
+<p>...</p>
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
<h3 id="pythonchanges">Python Binding Changes</h3>
@@ -128,49 +244,40 @@ Clang 3.1 introduces several new Objective-C language features and improvements.
The following methods have been added:
<ul>
- <li>SourceLocation.from_position (static)</li>
- <li>SourceLocation.__eq__ and SourceLocation.__ne__</li>
- <li>SourceRange.__eq__ and SourceRange.__ne__</li>
- <li>Diagnostic.category_number (property)</li>
- <li>Diagnostic.category_name (property)</li>
- <li>Diagnostic.option (property)</li>
- <li>Diagnostic.disable_option (property)</li>
- <li>CursorKind.is_translation_unit</li>
- <li>CursorKind.is_preprocessing</li>
- <li>CursorKind.is_unexposed</li>
- <li>Cursor.from_location (static)</li>
- <li>Cursor.underlying_typedef_type (property)</li>
- <li>Cursor.enum_type (property)</li>
- <li>Cursor.objc_type_encoding (property)</li>
- <li>Cursor.hash</li>
- <li>TypeKind.spelling</li>
- <li>Type.argument_types</li>
- <li>Type.element_type (property)</li>
- <li>Type.element_count (property)</li>
- <li>Type.is_function_variadic</li>
- <li>Type.is_pod</li>
- <li>Type.get_array_element_type</li>
- <li>Type.get_array_size</li>
- <li>Type.__eq__ and Type.__ne__</li>
- <li>File.from_name (static)</li>
- <li>File.__str__ and File.__repr__</li>
+ <li>...</li>
</ul>
<!-- ======================================================================= -->
+<h2 id="knownproblems">Significant Known Problems</h2>
+<!-- ======================================================================= -->
+
+<!-- ======================================================================= -->
<h2 id="additionalinfo">Additional Information</h2>
<!-- ======================================================================= -->
<p>A wide variety of additional information is available on the
-<a href="http://clang.llvm.org/">Clang web page</a>. The web page contains
-versions of the API documentation which are up-to-date with the Subversion
-version of the source code. You can access versions of these documents specific
-to this release by going into the "<tt>clang/doc/</tt>" directory in the Clang
-tree.</p>
+ <a href="http://clang.llvm.org/">Clang web page</a>. The web page contains
+ versions of the API documentation which are up-to-date with the Subversion
+ version of the source code. You can access versions of these documents
+ specific to this release by going into the "<tt>clang/doc/</tt>" directory in
+ the Clang tree.</p>
<p>If you have any questions or comments about Clang, please feel free to
-contact us via the <a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev">
-mailing list</a>.</p>
+ contact us via
+ the <a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev"> mailing
+ list</a>.</p>
+
+<!-- ======================================================================= -->
+<!-- Likely 3.1 release notes -->
+<!-- ======================================================================= -->
+<!--
+This is just a section to hold things that have already gotten started and
+should likely pick up proper release notes in 3.1.
+
+- C1X and C++11 atomics infrastructure and support
+- CUDA support?
+-->
</div>
</body>
diff --git a/docs/ThreadSanitizer.html b/docs/ThreadSanitizer.html
new file mode 100644
index 0000000..7a1d075
--- /dev/null
+++ b/docs/ThreadSanitizer.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<!-- Material used from: HTML 4.01 specs: http://www.w3.org/TR/html401/ -->
+<html>
+<head>
+ <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <title>ThreadSanitizer, a race detector</title>
+ <link type="text/css" rel="stylesheet" href="../menu.css">
+ <link type="text/css" rel="stylesheet" href="../content.css">
+ <style type="text/css">
+ td {
+ vertical-align: top;
+ }
+ </style>
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>ThreadSanitizer</h1>
+<ul>
+ <li> <a href="#intro">Introduction</a>
+ <li> <a href="#howtobuild">How to Build</a>
+ <li> <a href="#platforms">Supported Platforms</a>
+ <li> <a href="#usage">Usage</a>
+ <li> <a href="#limitations">Limitations</a>
+ <li> <a href="#status">Current Status</a>
+ <li> <a href="#moreinfo">More Information</a>
+</ul>
+
+<h2 id="intro">Introduction</h2>
+ThreadSanitizer is a tool that detects data races. <BR>
+It consists of a compiler instrumentation module and a run-time library. <BR>
+Typical slowdown introduced by ThreadSanitizer is <b>5x-15x</b> (TODO: these numbers are
+approximate so far).
+
+<h2 id="howtobuild">How to build</h2>
+Follow the <a href="../get_started.html">clang build instructions</a>. <BR>
+Note: CMake build does not work yet.
+See <a href="http://llvm.org/bugs/show_bug.cgi?id=12272">bug 12272</a>.
+
+<h2 id="platforms">Supported Platforms</h2>
+ThreadSanitizer is supported on Linux x86_64 (tested on Ubuntu 10.04). <BR>
+Support for MacOS 10.7 (64-bit only) is planned for late 2012. <BR>
+Support for 32-bit platforms is problematic and not yet planned.
+
+
+
+<h2 id="usage">Usage</h2>
+Simply compile your program with <tt>-fthread-sanitizer -fPIE</tt> and link it
+with <tt>-fthread-sanitizer -pie</tt>.<BR>
+To get a reasonable performance add <tt>-O1</tt> or higher. <BR>
+Use <tt>-g</tt> to get file names and line numbers in the warning messages. <BR>
+
+Example:
+<pre>
+% cat projects/compiler-rt/lib/tsan/output_tests/tiny_race.c
+#include <pthread.h>
+int Global;
+void *Thread1(void *x) {
+ Global = 42;
+ return x;
+}
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Global = 43;
+ pthread_join(t, NULL);
+ return Global;
+}
+</pre>
+
+<pre>
+% clang -fthread-sanitizer -g -O1 tiny_race.c -fPIE -pie
+</pre>
+
+If a bug is detected, the program will print an error message to stderr.
+Currently, ThreadSanitizer symbolizes its output using an external
+<tt>addr2line</tt>
+process (this will be fixed in future).
+<pre>
+% TSAN_OPTIONS=strip_path_prefix=`pwd`/ # Don't print full paths.
+% ./a.out 2> log
+% cat log
+WARNING: ThreadSanitizer: data race (pid=19219)
+ Write of size 4 at 0x7fcf47b21bc0 by thread 1:
+ #0 Thread1 tiny_race.c:4 (exe+0x00000000a360)
+ Previous write of size 4 at 0x7fcf47b21bc0 by main thread:
+ #0 main tiny_race.c:10 (exe+0x00000000a3b4)
+ Thread 1 (running) created at:
+ #0 pthread_create ??:0 (exe+0x00000000c790)
+ #1 main tiny_race.c:9 (exe+0x00000000a3a4)
+</pre>
+
+
+<h2 id="limitations">Limitations</h2>
+<ul>
+<li> ThreadSanitizer uses more real memory than a native run.
+At the default settings the memory overhead is 9x plus 9Mb per each thread.
+Settings with 5x and 3x overhead (but less accurate analysis) are also available.
+<li> ThreadSanitizer maps (but does not reserve) a lot of virtual address space.
+This means that tools like <tt>ulimit</tt> may not work as usually expected.
+<li> Static linking is not supported.
+<li> ThreadSanitizer requires <tt>-fPIE -pie</tt>
+</ul>
+
+
+<h2 id="status">Current Status</h2>
+ThreadSanitizer is in alpha stage.
+It is known to work on large C++ programs using pthreads, but we do not promise
+anything (yet). <BR>
+C++11 threading is not yet supported.
+
+We are actively working on enhancing the tool -- stay tuned.
+Any help, especially in the form of minimized standalone tests is more than welcome.
+
+<h2 id="moreinfo">More Information</h2>
+<a href="http://code.google.com/p/thread-sanitizer/">http://code.google.com/p/thread-sanitizer</a>.
+
+
+</div>
+</body>
+</html>
diff --git a/docs/Tooling.html b/docs/Tooling.html
new file mode 100644
index 0000000..74837f4
--- /dev/null
+++ b/docs/Tooling.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Writing Clang Tools</title>
+<link type="text/css" rel="stylesheet" href="../menu.css">
+<link type="text/css" rel="stylesheet" href="../content.css">
+</head>
+<body>
+
+<!--#include virtual="../menu.html.incl"-->
+
+<div id="content">
+
+<h1>Writing Clang Tools</h1>
+<p>Clang provides infrastructure to write tools that need syntactic and semantic
+information about a program. This document will give a short introduction of the
+different ways to write clang tools, and their pros and cons.</p>
+
+<!-- ======================================================================= -->
+<h2 id="libclang"><a href="http://clang.llvm.org/doxygen/group__CINDEX.html">LibClang</a></h2>
+<!-- ======================================================================= -->
+
+<p>LibClang is a stable high level C interface to clang. When in doubt LibClang
+is probably the interface you want to use. Consider the other interfaces only
+when you have a good reason not to use LibClang.</p>
+<p>Canonical examples of when to use LibClang:</p>
+<ul>
+ <li>Xcode</li>
+ <li>Clang Python Bindings</li>
+</ul>
+<p>Use LibClang when you...</p>
+<ul>
+ <li>want to interface with clang from other languages than C++</li>
+ <li>need a stable interface that takes care to be backwards compatible</li>
+ <li>want powerful high-level abstractions, like iterating through an AST
+with a cursor, and don't want to learn all the nitty gritty details of Clang's
+AST.</li>
+</ul>
+<p>Do not use LibClang when you...</p>
+<ul>
+ <li>want full control over the Clang AST</li>
+</ul>
+
+<!-- ======================================================================= -->
+<h2 id="clang-plugins"><a href="ClangPlugins.html">Clang Plugins</a></h2>
+<!-- ======================================================================= -->
+
+<p>Clang Plugins allow you to run additional actions on the AST as part of
+a compilation. Plugins are dynamic libraries that are loaded at runtime by
+the compiler, and they're easy to integrate into your build environment.</p>
+<p>Canonical examples of when to use Clang Plugins:</p>
+<ul>
+ <li>special lint-style warnings or errors for your project</li>
+ <li>creating additional build artifacts from a single compile step</li>
+</ul>
+<p>Use Clang Plugins when you...</p>
+<ul>
+ <li>need your tool to rerun if any of the dependencies change</li>
+ <li>want your tool to make or break a build</li>
+ <li>need full control over the Clang AST</li>
+</ul>
+<p>Do not use Clang Plugins when you...</p>
+<ul>
+ <li>want to run tools outside of your build environment</li>
+ <li>want full control on how Clang is set up, including mapping of in-memory
+ virtual files</li>
+ <li>need to run over a specific subset of files in your project which is not
+ necessarily related to any changes which would trigger rebuilds</li>
+</ul>
+
+<!-- ======================================================================= -->
+<h2 id="libtooling"><a href="LibTooling.html">LibTooling</a></h2>
+<!-- ======================================================================= -->
+
+<p>LibTooling is a C++ interface aimed at writing standalone tools, as well as
+integrating into services that run clang tools.</p>
+<p>Canonical examples of when to use LibTooling:</p>
+<ul>
+ <li>a simple syntax checker</li>
+ <li>refactoring tools</li>
+</ul>
+<p>Use LibTooling when you...</p>
+<ul>
+ <li>want to run tools over a single file, or a specific subset of files,
+ independently of the build system</li>
+ <li>want full control over the Clang AST</li>
+ <li>want to share code with Clang Plugins</li>
+</ul>
+<p>Do not use LibTooling when you...</p>
+<ul>
+ <li>want to run as part of the build triggered by dependency changes</li>
+ <li>want a stable interface so you don't need to change your code when the
+ AST API changes</li>
+ <li>want high level abstractions like cursors and code completion out of the
+ box</li>
+ <li>do not want to write your tools in C++</li>
+</ul>
+
+<!-- ======================================================================= -->
+<h2 id="clang-tools"><a href="ClangTools.html">Clang Tools</a></h2>
+<!-- ======================================================================= -->
+
+<p>These are a collection of specific developer tools built on top of the
+LibTooling infrastructure as part of the Clang project. They are targeted at
+automating and improving core development activities of C/C++ developers.</p>
+<p>Examples of tools we are building or planning as part of the Clang
+project:</p>
+<ul>
+ <li>Syntax checking (clang-check)</li>
+ <li>Automatic fixing of compile errors (clangc-fixit)</li>
+ <li>Automatic code formatting</li>
+ <li>Migration tools for new features in new language standards</li>
+ <li>Core refactoring tools</li>
+</ul>
+
+</div>
+</body>
+</html>
+
diff --git a/docs/UsersManual.html b/docs/UsersManual.html
index b33ed61..69f916c 100644
--- a/docs/UsersManual.html
+++ b/docs/UsersManual.html
@@ -43,12 +43,14 @@ td {
<li><a href="#diagnostics_categories">Diagnostic Categories</a></li>
<li><a href="#diagnostics_commandline">Controlling Diagnostics via Command Line Flags</a></li>
<li><a href="#diagnostics_pragmas">Controlling Diagnostics via Pragmas</a></li>
+ <li><a href="#diagnostics_systemheader">Controlling Diagnostics in System Headers</a></li>
<li><a href="#diagnostics_enable_everything">Enabling All Warnings</a></li>
<li><a href="#analyzer_diagnositics">Controlling Static Analyzer Diagnostics</a></li>
</ul>
</li>
<li><a href="#precompiledheaders">Precompiled Headers</a></li>
<li><a href="#codegen">Controlling Code Generation</a></li>
+ <li><a href="#debuginfosize">Controlling Size of Debug Information</a></li>
</ul>
</li>
<li><a href="#c">C Language Features</a>
@@ -185,9 +187,10 @@ introduces the language selection and other high level options like -c, -g, etc.
<p><b>-Werror=foo</b>: Turn warning "foo" into an error.</p>
<p><b>-Wno-error=foo</b>: Turn warning "foo" into an warning even if -Werror is
specified.</p>
-<p><b>-Wfoo</b>: Enable warning foo</p>
-<p><b>-Wno-foo</b>: Disable warning foo</p>
+<p><b>-Wfoo</b>: Enable warning "foo".</p>
+<p><b>-Wno-foo</b>: Disable warning "foo".</p>
<p><b>-w</b>: Disable all warnings.</p>
+<p><b>-Weverything</b>: <a href="#diagnostics_enable_everything">Enable <b>all</b> warnings.</a></p>
<p><b>-pedantic</b>: Warn on language extensions.</p>
<p><b>-pedantic-errors</b>: Error on language extensions.</p>
<p><b>-Wsystem-headers</b>: Enable warnings from system headers.</p>
@@ -227,6 +230,9 @@ print something like:
<p>When this is disabled, Clang will print "test.c:28: warning..." with no
column number.</p>
+
+<p>The printed column numbers count bytes from the beginning of the line; take
+care if your source contains multibyte characters.</p>
</dd>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
@@ -393,6 +399,9 @@ exprs.c:47:15:{47:8-47:14}{47:17-47:24}: error: invalid operands to binary expre
</pre>
<p>The {}'s are generated by -fdiagnostics-print-source-range-info.</p>
+
+<p>The printed column numbers count bytes from the beginning of the line; take
+care if your source contains multibyte characters.</p>
</dd>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
@@ -413,6 +422,51 @@ respectively). Both the file name and the insertion string escape backslash (as
&quot;\\&quot;), tabs (as &quot;\t&quot;), newlines (as &quot;\n&quot;), double
quotes(as &quot;\&quot;&quot;) and non-printable characters (as octal
&quot;\xxx&quot;).</p>
+
+<p>The printed column numbers count bytes from the beginning of the line; take
+care if your source contains multibyte characters.</p>
+</dd>
+
+<dt id="opt_fno-elide-type">
+<b>-fno-elide-type</b>:
+Turns off elision in template type printing.</dt>
+<dd><p>The default for template type printing is to elide as many template
+arguments as possible, removing those which are the same in both template types,
+leaving only the differences. Adding this flag will print all the template
+arguments. If supported by the terminal, highlighting will still appear on
+differing arguments.</p>
+
+Default:
+<pre>
+t.cc:4:5: <span class="note">note</span>: candidate function not viable: no known conversion from 'vector&lt;map&lt;[...], map&lt;<span class="template-highlight">float</span>, [...]&gt;&gt;&gt;' to 'vector&lt;map&lt;[...], map&lt;<span class="template-highlight">double</span>, [...]&gt;&gt;&gt;' for 1st argument;
+</pre>
+-fno-elide-type:
+<pre>
+t.cc:4:5: <span class="note">note</span>: candidate function not viable: no known conversion from 'vector&lt;map&lt;int, map&lt;<span class="template-highlight">float</span>, int&gt;&gt;&gt;' to 'vector&lt;map&lt;int, map&lt;<span class="template-highlight">double</span>, int&gt;&gt;&gt;' for 1st argument;
+</pre>
+</dd>
+
+<dt id="opt_fdiagnostics-show-template-tree">
+<b>-fdiagnostics-show-template-tree</b>:
+Template type diffing prints a text tree.</dt>
+<dd><p>For diffing large templated types, this option will cause Clang to
+display the templates as an indented text tree, one argument per line, with
+differences marked inline. This is compatible with -fno-elide-type.</p>
+
+Default:
+<pre>
+t.cc:4:5: <span class="note">note</span>: candidate function not viable: no known conversion from 'vector&lt;map&lt;[...], map&lt;<span class="template-highlight">float</span>, [...]&gt;&gt;&gt;' to 'vector&lt;map&lt;[...], map&lt;<span class="template-highlight">double</span>, [...]&gt;&gt;&gt;' for 1st argument;
+</pre>
+-fdiagnostics-show-template-tree
+<pre>
+t.cc:4:5: <span class="note">note</span>: candidate function not viable: no known conversion for 1st argument;
+ vector&lt;
+ map&lt;
+ [...],
+ map&lt;
+ [<span class="template-highlight">float</span> != <span class="template-highlight">float</span>],
+ [...]&gt;&gt;&gt;
+</pre>
</dd>
</dl>
@@ -443,9 +497,6 @@ the end of preprocessor directives. For example:
<p>These extra tokens are not strictly conforming, and are usually best handled
by commenting them out.</p>
-
-<p>This option is also enabled by <a href="">-Wfoo</a>, <a href="">-Wbar</a>,
- and <a href="">-Wbaz</a>.</p>
</dd>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
@@ -609,7 +660,7 @@ mapping of category names to category id's can be obtained by running '<tt>clang
<h4 id="diagnostics_commandline">Controlling Diagnostics via Command Line
Flags</h4>
-<p>-W flags, -pedantic, etc</p>
+<p>TODO: -W flags, -pedantic, etc</p>
<h4 id="diagnostics_pragmas">Controlling Diagnostics via Pragmas</h4>
@@ -653,6 +704,45 @@ GCC do not support the exact same set of warnings, so even when using GCC
compatible #pragmas there is no guarantee that they will have identical behaviour
on both compilers. </p>
+<h4 id="diagnostics_systemheader">Controlling Diagnostics in System Headers</h4>
+
+<p>Warnings are suppressed when they occur in system headers. By default, an
+included file is treated as a system header if it is found in an include path
+specified by <tt>-isystem</tt>, but this can be overridden in several ways.</p>
+
+<p>The <tt>system_header</tt> pragma can be used to mark the current file as
+being a system header. No warnings will be produced from the location of the
+pragma onwards within the same file.</p>
+
+<pre>
+char a = 'xy'; // warning
+
+#pragma clang system_header
+
+char b = 'ab'; // no warning
+</pre>
+
+<p>The <tt>-isystem-prefix</tt> and <tt>-ino-system-prefix</tt> command-line
+arguments can be used to override whether subsets of an include path are treated
+as system headers. When the name in a <tt>#include</tt> directive is found
+within a header search path and starts with a system prefix, the header is
+treated as a system header. The last prefix on the command-line which matches
+the specified header name takes precedence. For instance:</p>
+
+<pre>
+clang -Ifoo -isystem bar -isystem-prefix x/ -ino-system-prefix x/y/
+</pre>
+
+<p>Here, <tt>#include "x/a.h"</tt> is treated as including a system header, even
+if the header is found in <tt>foo</tt>, and <tt>#include "x/y/b.h"</tt> is
+treated as not including a system header, even if the header is found in
+<tt>bar</tt>.
+</p>
+
+<p>A <tt>#include</tt> directive which finds a file relative to the current
+directory is treated as including a system header if the including file is
+treated as a system header.</p>
+
<h4 id="diagnostics_enable_everything">Enabling All Warnings</h4>
<p>In addition to the traditional <tt>-W</tt> flags, one can enable <b>all</b>
@@ -667,37 +757,11 @@ on both compilers. </p>
<p>While not strictly part of the compiler, the diagnostics from Clang's <a
href="http://clang-analyzer.llvm.org">static analyzer</a> can also be influenced
-by the user via changes to the source code. This can be done in two ways:
-
-<ul>
-
-<li id="analyzer_annotations"><b>Annotations</b>: The static analyzer recognizes various GCC-style
-attributes (e.g., <tt>__attribute__((nonnull)))</tt>) that can either suppress
-static analyzer warnings or teach the analyzer about code invariants which
-enable it to find more bugs. While many of these attributes are standard GCC
-attributes, additional ones have been added to Clang to specifically support the
-static analyzer. Detailed information on these annotations can be found in the
-<a href="http://clang-analyzer.llvm.org/annotations.html">analyzer's
-documentation</a>.</li>
-
-<li><b><tt>__clang_analyzer__</tt></b>: When the static analyzer is using Clang
-to parse source files, it implicitly defines the preprocessor macro
-<tt>__clang_analyzer__</tt>. While discouraged, code can use this macro to
-selectively exclude code the analyzer examines. Here is an example:
-
-<pre>
-#ifndef __clang_analyzer__
-// Code not to be analyzed
-#endif
-</pre>
-
-In general, this usage is discouraged. Instead, we prefer that users file bugs
-against the analyzer when it flags false positives. There is also active
-discussion of allowing users in the future to selectively silence specific
-analyzer warnings (some of which can already be done using <a
-href="#analyzer_annotations">annotations</a>).</li>
-
-</ul>
+by the user via changes to the source code. See the available
+<a href = "http://clang-analyzer.llvm.org/annotations.html" >annotations</a> and
+the analyzer's
+<a href= "http://clang-analyzer.llvm.org/faq.html#exclude_code" >FAQ page</a> for
+more information.
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
<h3 id="precompiledheaders">Precompiled Headers</h3>
@@ -855,6 +919,39 @@ generator will always lower the builtin to a call to the specified function
regardless of whether the target ISA has a trap instruction. This option is
useful for environments (e.g. deeply embedded) where a trap cannot be properly
handled, or when some custom behavior is desired.</dd>
+
+<dt id="opt_ftls-model"><b>-ftls-model=[model]</b>: Select which TLS model to
+use.</dt>
+<dd>Valid values are: <tt>global-dynamic</tt>, <tt>local-dynamic</tt>,
+<tt>initial-exec</tt> and <tt>local-exec</tt>. The default value is
+<tt>global-dynamic</tt>. The compiler may use a different model if the selected
+model is not supported by the target, or if a more efficient model can be used.
+The TLS model can be overridden per variable using the <tt>tls_model</tt>
+attribute.
+</dd>
+</dl>
+
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+<h3 id="debuginfosize">Controlling Size of Debug Information</h3>
+<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
+
+<p>Debug info kind generated by Clang can be set by one of the flags listed
+below. If multiple flags are present, the last one is used.</p>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+<dl>
+<dt id="opt_g0"><b>-g0</b>: Don't generate any debug info (default).
+
+<dt id="opt_gline-tables-only"><b>-gline-tables-only</b>:
+Generate line number tables only.
+<dd>
+This kind of debug info allows to obtain stack traces with function
+names, file names and line numbers (by such tools as
+gdb or addr2line). It doesn't contain any other data (e.g.
+description of local variables or function parameters).
+</dd>
+
+<dt id="opt_g"><b>-g</b>: Generate complete debug info.
</dl>
<!-- ======================================================================= -->
@@ -937,9 +1034,18 @@ interest in these features yet, so it's hard to say when they will be
implemented.</li>
<li>clang does not support nested functions; this is a complex feature which
-is infrequently used, so it is unlikely to be implemented anytime soon.</li>
+is infrequently used, so it is unlikely to be implemented anytime soon. In C++11
+it can be emulated by assigning lambda functions to local variables, e.g:
+<pre>
+ auto const local_function = [&](int parameter) {
+ // Do something
+ };
+ ...
+ local_function(1);
+</pre>
+</li>
-<li>clang does not support global register variables, this is unlikely
+<li>clang does not support global register variables; this is unlikely
to be implemented soon because it requires additional LLVM backend support.
</li>
@@ -955,7 +1061,7 @@ in 4.3, the glibc headers will not try to use this extension with clang at
the moment.</li>
<li>clang does not support the gcc extension for forward-declaring function
-parameters; this has not showed up in any real-world code yet, though, so it
+parameters; this has not shown up in any real-world code yet, though, so it
might never be implemented.</li>
</ul>
@@ -1001,6 +1107,14 @@ support is incomplete; enabling Microsoft extensions will silently drop
certain constructs (including __declspec and Microsoft-style asm statements).
</p>
+<p>clang has a -fms-compatibility flag that makes clang accept enough
+invalid C++ to be able to parse most Microsoft headers. This flag is enabled by
+default for Windows targets.</p>
+
+<p>-fdelayed-template-parsing lets clang delay all template instantiation until
+the end of a translation unit. This flag is enabled by default for Windows
+targets.</p>
+
<ul>
<li>clang allows setting _MSC_VER with -fmsc-version=. It defaults to 1300 which
is the same as Visual C/C++ 2003. Any number is supported and can greatly affect
@@ -1015,6 +1129,8 @@ record members can be declared using user defined typedefs.</li>
controlling record layout. GCC also contains support for this feature,
however where MSVC and GCC are incompatible clang follows the MSVC
definition.</li>
+
+<li>clang defaults to C++11 for Windows targets.</li>
</ul>
<!-- ======================================================================= -->
@@ -1092,7 +1208,7 @@ Generating assembly requires a suitable LLVM backend.
<h4 id="target_os_darwin">Darwin (Mac OS/X)</h4>
<!-- ======================================= -->
-<p>No __thread support, 64-bit ObjC support requires SL tools.</p>
+<p>None</p>
<!-- ======================================= -->
<h4 id="target_os_win32">Windows</h4>
@@ -1100,6 +1216,8 @@ Generating assembly requires a suitable LLVM backend.
<p>Experimental supports are on Cygming.</p>
+<p>See also <a href="#c_ms">Microsoft Extensions</a>.</p>
+
<h5>Cygwin</h5>
<p>Clang works on Cygwin-1.7.</p>
@@ -1135,7 +1253,7 @@ Clang assumes directories as below;</p>
<li><tt>some_directory/bin/../include</tt></li>
</ul>
-<p>This directory layout is standard for any toolchain you will find on the official <a href="mingw-w64.sourceforge.net">MinGW-w64 website</a>.
+<p>This directory layout is standard for any toolchain you will find on the official <a href="http://mingw-w64.sourceforge.net">MinGW-w64 website</a>.
<p>Clang expects the GCC executable &quot;gcc.exe&quot; compiled for i686-w64-mingw32 (or x86_64-w64-mingw32) to be present on PATH.</p>
diff --git a/docs/tools/clang.pod b/docs/tools/clang.pod
index 8f61568..425a91e 100644
--- a/docs/tools/clang.pod
+++ b/docs/tools/clang.pod
@@ -303,6 +303,14 @@ This flag sets the default visibility level.
This flag specifies that variables without initializers get common linkage. It
can be disabled with B<-fno-common>.
+=item B<-ftls-model>
+
+Set the default thread-local storage (TLS) model to use for thread-local
+variables. Valid values are: "global-dynamic", "local-dynamic", "initial-exec"
+and "local-exec". The default is "global-dynamic". The default model can be
+overridden with the tls_model attribute. The compiler will try to choose a more
+efficient model if possible.
+
=item B<-flto> B<-emit-llvm>
Generate output files in LLVM formats, suitable for link time optimization. When
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 19d8869..84a5d2c 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,5 +1,5 @@
if(NOT CLANG_BUILD_EXAMPLES)
- set(EXCLUDE_FROM_ALL ON)
+ set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
endif()
add_subdirectory(analyzer-plugin)
diff --git a/examples/PrintFunctionNames/CMakeLists.txt b/examples/PrintFunctionNames/CMakeLists.txt
index 86793ce..ba6a350 100644
--- a/examples/PrintFunctionNames/CMakeLists.txt
+++ b/examples/PrintFunctionNames/CMakeLists.txt
@@ -1,14 +1,23 @@
set(MODULE TRUE)
-set( LLVM_USED_LIBS
- clangFrontend
- clangAST
- )
-
set( LLVM_LINK_COMPONENTS support mc)
add_clang_library(PrintFunctionNames PrintFunctionNames.cpp)
+add_dependencies(PrintFunctionNames
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangStmtNodes
+ )
+
+target_link_libraries(PrintFunctionNames
+ clangFrontend
+ clangAST
+ )
+
set_target_properties(PrintFunctionNames
PROPERTIES
LINKER_LANGUAGE CXX
diff --git a/examples/analyzer-plugin/CMakeLists.txt b/examples/analyzer-plugin/CMakeLists.txt
index 2b9d825..ba73030 100644
--- a/examples/analyzer-plugin/CMakeLists.txt
+++ b/examples/analyzer-plugin/CMakeLists.txt
@@ -1,13 +1,22 @@
set(MODULE TRUE)
-set( LLVM_USED_LIBS
- clangStaticAnalyzerCore
- )
-
set( LLVM_LINK_COMPONENTS support mc)
add_clang_library(SampleAnalyzerPlugin MainCallChecker.cpp)
+add_dependencies(SampleAnalyzerPlugin
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangStmtNodes
+ )
+
+target_link_libraries(SampleAnalyzerPlugin
+ clangStaticAnalyzerCore
+ )
+
set_target_properties(SampleAnalyzerPlugin
PROPERTIES
LINKER_LANGUAGE CXX
diff --git a/examples/clang-interpreter/CMakeLists.txt b/examples/clang-interpreter/CMakeLists.txt
index 809e324..9abefc2 100644
--- a/examples/clang-interpreter/CMakeLists.txt
+++ b/examples/clang-interpreter/CMakeLists.txt
@@ -1,34 +1,37 @@
-set(LLVM_USED_LIBS
- clangFrontend
- clangSerialization
- clangDriver
- clangCodeGen
- clangSema
- clangStaticAnalyzerFrontend
- clangStaticAnalyzerCheckers
- clangStaticAnalyzerCore
- clangAnalysis
- clangRewrite
- clangAST
- clangParse
- clangLex
- clangBasic
- )
-
set(LLVM_LINK_COMPONENTS
- jit
- interpreter
- nativecodegen
- asmparser
- bitreader
- bitwriter
- codegen
- ipo
- linker
- selectiondag
+ jit
+ interpreter
+ nativecodegen
+ asmparser
+ bitreader
+ bitwriter
+ codegen
+ ipo
+ linker
+ selectiondag
)
add_clang_executable(clang-interpreter
main.cpp
)
-add_dependencies(clang-interpreter clang-headers)
+
+add_dependencies(clang-interpreter
+ clang-headers
+ )
+
+target_link_libraries(clang-interpreter
+ clangFrontend
+ clangSerialization
+ clangDriver
+ clangCodeGen
+ clangSema
+ clangStaticAnalyzerFrontend
+ clangStaticAnalyzerCheckers
+ clangStaticAnalyzerCore
+ clangAnalysis
+ clangRewrite
+ clangAST
+ clangParse
+ clangLex
+ clangBasic
+ )
diff --git a/include/clang-c/CXCompilationDatabase.h b/include/clang-c/CXCompilationDatabase.h
new file mode 100644
index 0000000..d11133c
--- /dev/null
+++ b/include/clang-c/CXCompilationDatabase.h
@@ -0,0 +1,146 @@
+/*===-- clang-c/CXCompilationDatabase.h - Compilation database ---*- C -*-===*\
+|* *|
+|* The LLVM Compiler Infrastructure *|
+|* *|
+|* This file is distributed under the University of Illinois Open Source *|
+|* License. See LICENSE.TXT for details. *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This header provides a public inferface to use CompilationDatabase without *|
+|* the full Clang C++ API. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef CLANG_CXCOMPILATIONDATABASE_H
+#define CLANG_CXCOMPILATIONDATABASE_H
+
+#include "clang-c/Platform.h"
+#include "clang-c/CXString.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \defgroup COMPILATIONDB CompilationDatabase functions
+ * \ingroup CINDEX
+ *
+ * @{
+ */
+
+/**
+ * A compilation database holds all information used to compile files in a
+ * project. For each file in the database, it can be queried for the working
+ * directory or the command line used for the compiler invocation.
+ *
+ * Must be freed by \c clang_CompilationDatabase_dispose
+ */
+typedef void * CXCompilationDatabase;
+
+/**
+ * \brief Contains the results of a search in the compilation database
+ *
+ * When searching for the compile command for a file, the compilation db can
+ * return several commands, as the file may have been compiled with
+ * different options in different places of the project. This choice of compile
+ * commands is wrapped in this opaque data structure. It must be freed by
+ * \c clang_CompileCommands_dispose.
+ */
+typedef void * CXCompileCommands;
+
+/**
+ * \brief Represents the command line invocation to compile a specific file.
+ */
+typedef void * CXCompileCommand;
+
+/**
+ * \brief Error codes for Compilation Database
+ */
+typedef enum {
+ /*
+ * \brief No error occured
+ */
+ CXCompilationDatabase_NoError = 0,
+
+ /*
+ * \brief Database can not be loaded
+ */
+ CXCompilationDatabase_CanNotLoadDatabase = 1
+
+} CXCompilationDatabase_Error;
+
+/**
+ * \brief Creates a compilation database from the database found in directory
+ * buildDir. For example, CMake can output a compile_commands.json which can
+ * be used to build the database.
+ *
+ * It must be freed by \c clang_CompilationDatabase_dispose.
+ */
+CINDEX_LINKAGE CXCompilationDatabase
+clang_CompilationDatabase_fromDirectory(const char *BuildDir,
+ CXCompilationDatabase_Error *ErrorCode);
+
+/**
+ * \brief Free the given compilation database
+ */
+CINDEX_LINKAGE void
+clang_CompilationDatabase_dispose(CXCompilationDatabase);
+
+/**
+ * \brief Find the compile commands used for a file. The compile commands
+ * must be freed by \c clang_CompileCommands_dispose.
+ */
+CINDEX_LINKAGE CXCompileCommands
+clang_CompilationDatabase_getCompileCommands(CXCompilationDatabase,
+ const char *CompleteFileName);
+
+/**
+ * \brief Free the given CompileCommands
+ */
+CINDEX_LINKAGE void clang_CompileCommands_dispose(CXCompileCommands);
+
+/**
+ * \brief Get the number of CompileCommand we have for a file
+ */
+CINDEX_LINKAGE unsigned
+clang_CompileCommands_getSize(CXCompileCommands);
+
+/**
+ * \brief Get the I'th CompileCommand for a file
+ *
+ * Note : 0 <= i < clang_CompileCommands_getSize(CXCompileCommands)
+ */
+CINDEX_LINKAGE CXCompileCommand
+clang_CompileCommands_getCommand(CXCompileCommands, unsigned I);
+
+/**
+ * \brief Get the working directory where the CompileCommand was executed from
+ */
+CINDEX_LINKAGE CXString
+clang_CompileCommand_getDirectory(CXCompileCommand);
+
+/**
+ * \brief Get the number of arguments in the compiler invocation.
+ *
+ */
+CINDEX_LINKAGE unsigned
+clang_CompileCommand_getNumArgs(CXCompileCommand);
+
+/**
+ * \brief Get the I'th argument value in the compiler invocations
+ *
+ * Invariant :
+ * - argument 0 is the compiler executable
+ */
+CINDEX_LINKAGE CXString
+clang_CompileCommand_getArg(CXCompileCommand, unsigned I);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/include/clang-c/CXString.h b/include/clang-c/CXString.h
new file mode 100644
index 0000000..74c3166
--- /dev/null
+++ b/include/clang-c/CXString.h
@@ -0,0 +1,61 @@
+/*===-- clang-c/CXString.h - C Index strings --------------------*- C -*-===*\
+|* *|
+|* The LLVM Compiler Infrastructure *|
+|* *|
+|* This file is distributed under the University of Illinois Open Source *|
+|* License. See LICENSE.TXT for details. *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This header provides the interface to C Index strings. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef CLANG_CXSTRING_H
+#define CLANG_CXSTRING_H
+
+#include "clang-c/Platform.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup CINDEX_STRING String manipulation routines
+ * \ingroup CINDEX
+ *
+ * @{
+ */
+
+/**
+ * \brief A character string.
+ *
+ * The \c CXString type is used to return strings from the interface when
+ * the ownership of that string might different from one call to the next.
+ * Use \c clang_getCString() to retrieve the string data and, once finished
+ * with the string data, call \c clang_disposeString() to free the string.
+ */
+typedef struct {
+ void *data;
+ unsigned private_flags;
+} CXString;
+
+/**
+ * \brief Retrieve the character data associated with the given string.
+ */
+CINDEX_LINKAGE const char *clang_getCString(CXString string);
+
+/**
+ * \brief Free the given string,
+ */
+CINDEX_LINKAGE void clang_disposeString(CXString string);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index 13ba6ba..edd3cbb 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -20,31 +20,13 @@
#include <time.h>
#include <stdio.h>
+#include "clang-c/Platform.h"
+#include "clang-c/CXString.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-/* MSVC DLL import/export. */
-#ifdef _MSC_VER
- #ifdef _CINDEX_LIB_
- #define CINDEX_LINKAGE __declspec(dllexport)
- #else
- #define CINDEX_LINKAGE __declspec(dllimport)
- #endif
-#else
- #define CINDEX_LINKAGE
-#endif
-
-#ifdef __GNUC__
- #define CINDEX_DEPRECATED __attribute__((deprecated))
-#else
- #ifdef _MSC_VER
- #define CINDEX_DEPRECATED __declspec(deprecated)
- #else
- #define CINDEX_DEPRECATED
- #endif
-#endif
-
/** \defgroup CINDEX libclang: C Interface to Clang
*
* The C Interface to Clang provides a relatively small API that exposes
@@ -132,43 +114,34 @@ enum CXAvailabilityKind {
*/
CXAvailability_NotAccessible
};
-
-/**
- * \defgroup CINDEX_STRING String manipulation routines
- *
- * @{
- */
-
-/**
- * \brief A character string.
- *
- * The \c CXString type is used to return strings from the interface when
- * the ownership of that string might different from one call to the next.
- * Use \c clang_getCString() to retrieve the string data and, once finished
- * with the string data, call \c clang_disposeString() to free the string.
- */
-typedef struct {
- void *data;
- unsigned private_flags;
-} CXString;
-
-/**
- * \brief Retrieve the character data associated with the given string.
- */
-CINDEX_LINKAGE const char *clang_getCString(CXString string);
-
-/**
- * \brief Free the given string,
- */
-CINDEX_LINKAGE void clang_disposeString(CXString string);
/**
- * @}
+ * \brief Describes a version number of the form major.minor.subminor.
*/
-
+typedef struct CXVersion {
+ /**
+ * \brief The major version number, e.g., the '10' in '10.7.3'. A negative
+ * value indicates that there is no version number at all.
+ */
+ int Major;
+ /**
+ * \brief The minor version number, e.g., the '7' in '10.7.3'. This value
+ * will be negative if no minor version number was provided, e.g., for
+ * version '10'.
+ */
+ int Minor;
+ /**
+ * \brief The subminor version number, e.g., the '3' in '10.7.3'. This value
+ * will be negative if no minor or subminor version number was provided,
+ * e.g., in version '10' or '10.7'.
+ */
+ int Subminor;
+} CXVersion;
+
/**
- * \brief clang_createIndex() provides a shared context for creating
- * translation units. It provides two options:
+ * \brief Provides a shared context for creating translation units.
+ *
+ * It provides two options:
*
* - excludeDeclarationsFromPCH: When non-zero, allows enumeration of "local"
* declarations (when loading any new translation units). A "local" declaration
@@ -178,6 +151,7 @@ CINDEX_LINKAGE void clang_disposeString(CXString string);
*
* Here is an example:
*
+ * \code
* // excludeDeclsFromPCH = 1, displayDiagnostics=1
* Idx = clang_createIndex(1, 1);
*
@@ -198,6 +172,7 @@ CINDEX_LINKAGE void clang_disposeString(CXString string);
* clang_visitChildren(clang_getTranslationUnitCursor(TU),
* TranslationUnitVisitor, 0);
* clang_disposeTranslationUnit(TU);
+ * \endcode
*
* This process of creating the 'pch', loading it separately, and using it (via
* -include-pch) allows 'excludeDeclsFromPCH' to remove redundant callbacks
@@ -223,16 +198,18 @@ typedef enum {
/**
* \brief Used to indicate that threads that libclang creates for indexing
* purposes should use background priority.
- * Affects \see clang_indexSourceFile, \see clang_indexTranslationUnit,
- * \see clang_parseTranslationUnit, \see clang_saveTranslationUnit.
+ *
+ * Affects #clang_indexSourceFile, #clang_indexTranslationUnit,
+ * #clang_parseTranslationUnit, #clang_saveTranslationUnit.
*/
CXGlobalOpt_ThreadBackgroundPriorityForIndexing = 0x1,
/**
* \brief Used to indicate that threads that libclang creates for editing
* purposes should use background priority.
- * Affects \see clang_reparseTranslationUnit, \see clang_codeCompleteAt,
- * \see clang_annotateTokens
+ *
+ * Affects #clang_reparseTranslationUnit, #clang_codeCompleteAt,
+ * #clang_annotateTokens
*/
CXGlobalOpt_ThreadBackgroundPriorityForEditing = 0x2,
@@ -247,7 +224,7 @@ typedef enum {
} CXGlobalOptFlags;
/**
- * \brief Sets general options associated with a CXIndex.
+ * \brief Sets general options associated with a CXIndex.
*
* For example:
* \code
@@ -294,7 +271,7 @@ CINDEX_LINKAGE time_t clang_getFileTime(CXFile SFile);
/**
* \brief Determine whether the given header is guarded against
* multiple inclusions, either with the conventional
- * #ifndef/#define/#endif macro guards or with #pragma once.
+ * \#ifndef/\#define/\#endif macro guards or with \#pragma once.
*/
CINDEX_LINKAGE unsigned
clang_isFileMultipleIncludeGuarded(CXTranslationUnit tu, CXFile file);
@@ -359,7 +336,7 @@ typedef struct {
CINDEX_LINKAGE CXSourceLocation clang_getNullLocation();
/**
- * \determine Determine whether two source locations, which must refer into
+ * \brief Determine whether two source locations, which must refer into
* the same translation unit, refer to exactly the same point in the source
* code.
*
@@ -444,12 +421,14 @@ CINDEX_LINKAGE void clang_getExpansionLocation(CXSourceLocation location,
*
* Example: given the following source code in a file somefile.c
*
+ * \code
* #123 "dummy.c" 1
*
* static int func(void)
* {
* return 0;
* }
+ * \endcode
*
* the location information returned by this function would be
*
@@ -486,7 +465,7 @@ CINDEX_LINKAGE void clang_getPresumedLocation(CXSourceLocation location,
* by the given source location.
*
* This interface has been replaced by the newer interface
- * \see clang_getExpansionLocation(). See that interface's documentation for
+ * #clang_getExpansionLocation(). See that interface's documentation for
* details.
*/
CINDEX_LINKAGE void clang_getInstantiationLocation(CXSourceLocation location,
@@ -599,7 +578,7 @@ CINDEX_LINKAGE unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags);
/**
* \brief Retrieve a diagnostic associated with the given CXDiagnosticSet.
*
- * \param Unit the CXDiagnosticSet to query.
+ * \param Diags the CXDiagnosticSet to query.
* \param Index the zero-based diagnostic number to retrieve.
*
* \returns the requested diagnostic. This diagnostic must be freed
@@ -633,23 +612,23 @@ enum CXLoadDiag_Error {
/**
* \brief Indicates that the serialized diagnostics file is invalid or
- * corrupt.
+ * corrupt.
*/
CXLoadDiag_InvalidFile = 3
};
/**
* \brief Deserialize a set of diagnostics from a Clang diagnostics bitcode
- * file.
+ * file.
*
- * \param The name of the file to deserialize.
- * \param A pointer to a enum value recording if there was a problem
+ * \param file The name of the file to deserialize.
+ * \param error A pointer to a enum value recording if there was a problem
* deserializing the diagnostics.
- * \param A pointer to a CXString for recording the error string
+ * \param errorString A pointer to a CXString for recording the error string
* if the file was not successfully loaded.
*
* \returns A loaded CXDiagnosticSet if successful, and NULL otherwise. These
- * diagnostics should be released using clang_disposeDiagnosticSet().
+ * diagnostics should be released using clang_disposeDiagnosticSet().
*/
CINDEX_LINKAGE CXDiagnosticSet clang_loadDiagnostics(const char *file,
enum CXLoadDiag_Error *error,
@@ -661,8 +640,10 @@ CINDEX_LINKAGE CXDiagnosticSet clang_loadDiagnostics(const char *file,
CINDEX_LINKAGE void clang_disposeDiagnosticSet(CXDiagnosticSet Diags);
/**
- * \brief Retrieve the child diagnostics of a CXDiagnostic. This
- * CXDiagnosticSet does not need to be released by clang_diposeDiagnosticSet.
+ * \brief Retrieve the child diagnostics of a CXDiagnostic.
+ *
+ * This CXDiagnosticSet does not need to be released by
+ * clang_diposeDiagnosticSet.
*/
CINDEX_LINKAGE CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D);
@@ -855,7 +836,6 @@ CXString clang_getDiagnosticCategoryName(unsigned Category);
/**
* \brief Retrieve the diagnostic category text for a given diagnostic.
*
- *
* \returns The text of the given diagnostic category.
*/
CINDEX_LINKAGE CXString clang_getDiagnosticCategoryText(CXDiagnostic);
@@ -951,12 +931,12 @@ clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit);
* '-c'
* '-emit-ast'
* '-fsyntax-only'
- * '-o <output file>' (both '-o' and '<output file>' are ignored)
+ * '-o \<output file>' (both '-o' and '\<output file>' are ignored)
*
* \param CIdx The index object with which the translation unit will be
* associated.
*
- * \param source_filename - The name of the source file to load, or NULL if the
+ * \param source_filename The name of the source file to load, or NULL if the
* source file is included in \p clang_command_line_args.
*
* \param num_clang_command_line_args The number of command-line arguments in
@@ -966,7 +946,7 @@ clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit);
* passed to the \c clang executable if it were being invoked out-of-process.
* These command-line options will be parsed and will affect how the translation
* unit is parsed. Note that the following options are ignored: '-c',
- * '-emit-ast', '-fsyntex-only' (which is the default), and '-o <output file>'.
+ * '-emit-ast', '-fsyntax-only' (which is the default), and '-o \<output file>'.
*
* \param num_unsaved_files the number of unsaved file entries in \p
* unsaved_files.
@@ -1078,7 +1058,14 @@ enum CXTranslationUnit_Flags {
* This option can be used to search for declarations/definitions while
* ignoring the usages.
*/
- CXTranslationUnit_SkipFunctionBodies = 0x40
+ CXTranslationUnit_SkipFunctionBodies = 0x40,
+
+ /**
+ * \brief Used to indicate that brief documentation comments should be
+ * included into the set of code completions returned from this translation
+ * unit.
+ */
+ CXTranslationUnit_IncludeBriefCommentsInCodeCompletion = 0x80
};
/**
@@ -1115,7 +1102,7 @@ CINDEX_LINKAGE unsigned clang_defaultEditingTranslationUnitOptions(void);
* passed to the \c clang executable if it were being invoked out-of-process.
* These command-line options will be parsed and will affect how the translation
* unit is parsed. Note that the following options are ignored: '-c',
- * '-emit-ast', '-fsyntex-only' (which is the default), and '-o <output file>'.
+ * '-emit-ast', '-fsyntax-only' (which is the default), and '-o \<output file>'.
*
* \param num_command_line_args The number of command-line arguments in
* \p command_line_args.
@@ -1411,13 +1398,13 @@ enum CXCursorKind {
CXCursor_VarDecl = 9,
/** \brief A function or method parameter. */
CXCursor_ParmDecl = 10,
- /** \brief An Objective-C @interface. */
+ /** \brief An Objective-C \@interface. */
CXCursor_ObjCInterfaceDecl = 11,
- /** \brief An Objective-C @interface for a category. */
+ /** \brief An Objective-C \@interface for a category. */
CXCursor_ObjCCategoryDecl = 12,
- /** \brief An Objective-C @protocol declaration. */
+ /** \brief An Objective-C \@protocol declaration. */
CXCursor_ObjCProtocolDecl = 13,
- /** \brief An Objective-C @property declaration. */
+ /** \brief An Objective-C \@property declaration. */
CXCursor_ObjCPropertyDecl = 14,
/** \brief An Objective-C instance variable. */
CXCursor_ObjCIvarDecl = 15,
@@ -1425,9 +1412,9 @@ enum CXCursorKind {
CXCursor_ObjCInstanceMethodDecl = 16,
/** \brief An Objective-C class method. */
CXCursor_ObjCClassMethodDecl = 17,
- /** \brief An Objective-C @implementation. */
+ /** \brief An Objective-C \@implementation. */
CXCursor_ObjCImplementationDecl = 18,
- /** \brief An Objective-C @implementation for a category. */
+ /** \brief An Objective-C \@implementation for a category. */
CXCursor_ObjCCategoryImplDecl = 19,
/** \brief A typedef */
CXCursor_TypedefDecl = 20,
@@ -1463,9 +1450,9 @@ enum CXCursorKind {
CXCursor_UsingDeclaration = 35,
/** \brief A C++ alias declaration */
CXCursor_TypeAliasDecl = 36,
- /** \brief An Objective-C @synthesize definition. */
+ /** \brief An Objective-C \@synthesize definition. */
CXCursor_ObjCSynthesizeDecl = 37,
- /** \brief An Objective-C @dynamic definition. */
+ /** \brief An Objective-C \@dynamic definition. */
CXCursor_ObjCDynamicDecl = 38,
/** \brief An access specifier. */
CXCursor_CXXAccessSpecifier = 39,
@@ -1768,15 +1755,15 @@ enum CXCursorKind {
*/
CXCursor_ObjCStringLiteral = 137,
- /** \brief An Objective-C @encode expression.
+ /** \brief An Objective-C \@encode expression.
*/
CXCursor_ObjCEncodeExpr = 138,
- /** \brief An Objective-C @selector expression.
+ /** \brief An Objective-C \@selector expression.
*/
CXCursor_ObjCSelectorExpr = 139,
- /** \brief An Objective-C @protocol expression.
+ /** \brief An Objective-C \@protocol expression.
*/
CXCursor_ObjCProtocolExpr = 140,
@@ -1921,23 +1908,23 @@ enum CXCursorKind {
*/
CXCursor_AsmStmt = 215,
- /** \brief Objective-C's overall @try-@catch-@finally statement.
+ /** \brief Objective-C's overall \@try-\@catch-\@finally statement.
*/
CXCursor_ObjCAtTryStmt = 216,
- /** \brief Objective-C's @catch statement.
+ /** \brief Objective-C's \@catch statement.
*/
CXCursor_ObjCAtCatchStmt = 217,
- /** \brief Objective-C's @finally statement.
+ /** \brief Objective-C's \@finally statement.
*/
CXCursor_ObjCAtFinallyStmt = 218,
- /** \brief Objective-C's @throw statement.
+ /** \brief Objective-C's \@throw statement.
*/
CXCursor_ObjCAtThrowStmt = 219,
- /** \brief Objective-C's @synchronized statement.
+ /** \brief Objective-C's \@synchronized statement.
*/
CXCursor_ObjCAtSynchronizedStmt = 220,
@@ -1973,6 +1960,10 @@ enum CXCursorKind {
*/
CXCursor_SEHFinallyStmt = 228,
+ /** \brief A MS inline assembly statement extension.
+ */
+ CXCursor_MSAsmStmt = 229,
+
/** \brief The null satement ";": C99 6.8.3p3.
*
* This cursor kind is used to describe the null statement.
@@ -2046,6 +2037,13 @@ typedef struct {
} CXCursor;
/**
+ * \brief A comment AST node.
+ */
+typedef struct {
+ const void *Data;
+} CXComment;
+
+/**
* \defgroup CINDEX_CURSOR_MANIP Cursor manipulations
*
* @{
@@ -2165,7 +2163,8 @@ enum CXLinkageKind {
CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor);
/**
- * \brief Determine the availability of the entity that this cursor refers to.
+ * \brief Determine the availability of the entity that this cursor refers to,
+ * taking the current target platform into account.
*
* \param cursor The cursor to query.
*
@@ -2175,6 +2174,94 @@ CINDEX_LINKAGE enum CXAvailabilityKind
clang_getCursorAvailability(CXCursor cursor);
/**
+ * Describes the availability of a given entity on a particular platform, e.g.,
+ * a particular class might only be available on Mac OS 10.7 or newer.
+ */
+typedef struct CXPlatformAvailability {
+ /**
+ * \brief A string that describes the platform for which this structure
+ * provides availability information.
+ *
+ * Possible values are "ios" or "macosx".
+ */
+ CXString Platform;
+ /**
+ * \brief The version number in which this entity was introduced.
+ */
+ CXVersion Introduced;
+ /**
+ * \brief The version number in which this entity was deprecated (but is
+ * still available).
+ */
+ CXVersion Deprecated;
+ /**
+ * \brief The version number in which this entity was obsoleted, and therefore
+ * is no longer available.
+ */
+ CXVersion Obsoleted;
+ /**
+ * \brief Whether the entity is unconditionally unavailable on this platform.
+ */
+ int Unavailable;
+ /**
+ * \brief An optional message to provide to a user of this API, e.g., to
+ * suggest replacement APIs.
+ */
+ CXString Message;
+} CXPlatformAvailability;
+
+/**
+ * \brief Determine the availability of the entity that this cursor refers to
+ * on any platforms for which availability information is known.
+ *
+ * \param cursor The cursor to query.
+ *
+ * \param always_deprecated If non-NULL, will be set to indicate whether the
+ * entity is deprecated on all platforms.
+ *
+ * \param deprecated_message If non-NULL, will be set to the message text
+ * provided along with the unconditional deprecation of this entity. The client
+ * is responsible for deallocating this string.
+ *
+ * \param always_unavailable If non-NULL, will be set to indicate whether the
+ * entity is unavailable on all platforms.
+ *
+ * \param unavailable_message If non-NULL, will be set to the message text
+ * provided along with the unconditional unavailability of this entity. The
+ * client is responsible for deallocating this string.
+ *
+ * \param availability If non-NULL, an array of CXPlatformAvailability instances
+ * that will be populated with platform availability information, up to either
+ * the number of platforms for which availability information is available (as
+ * returned by this function) or \c availability_size, whichever is smaller.
+ *
+ * \param availability_size The number of elements available in the
+ * \c availability array.
+ *
+ * \returns The number of platforms (N) for which availability information is
+ * available (which is unrelated to \c availability_size).
+ *
+ * Note that the client is responsible for calling
+ * \c clang_disposeCXPlatformAvailability to free each of the
+ * platform-availability structures returned. There are
+ * \c min(N, availability_size) such structures.
+ */
+CINDEX_LINKAGE int
+clang_getCursorPlatformAvailability(CXCursor cursor,
+ int *always_deprecated,
+ CXString *deprecated_message,
+ int *always_unavailable,
+ CXString *unavailable_message,
+ CXPlatformAvailability *availability,
+ int availability_size);
+
+/**
+ * \brief Free the memory associated with a \c CXPlatformAvailability structure.
+ */
+CINDEX_LINKAGE void
+clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability);
+
+/**
* \brief Describe the "language" of the entity referred to by a cursor.
*/
CINDEX_LINKAGE enum CXLanguageKind {
@@ -2571,10 +2658,10 @@ CINDEX_LINKAGE int clang_Cursor_getNumArguments(CXCursor C);
CINDEX_LINKAGE CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i);
/**
- * \determine Determine whether two CXTypes represent the same type.
+ * \brief Determine whether two CXTypes represent the same type.
*
- * \returns non-zero if the CXTypes represent the same type and
- zero otherwise.
+ * \returns non-zero if the CXTypes represent the same type and
+ * zero otherwise.
*/
CINDEX_LINKAGE unsigned clang_equalTypes(CXType A, CXType B);
@@ -2589,26 +2676,28 @@ CINDEX_LINKAGE unsigned clang_equalTypes(CXType A, CXType B);
CINDEX_LINKAGE CXType clang_getCanonicalType(CXType T);
/**
- * \determine Determine whether a CXType has the "const" qualifier set,
- * without looking through typedefs that may have added "const" at a different level.
+ * \brief Determine whether a CXType has the "const" qualifier set,
+ * without looking through typedefs that may have added "const" at a
+ * different level.
*/
CINDEX_LINKAGE unsigned clang_isConstQualifiedType(CXType T);
/**
- * \determine Determine whether a CXType has the "volatile" qualifier set,
- * without looking through typedefs that may have added "volatile" at a different level.
+ * \brief Determine whether a CXType has the "volatile" qualifier set,
+ * without looking through typedefs that may have added "volatile" at
+ * a different level.
*/
CINDEX_LINKAGE unsigned clang_isVolatileQualifiedType(CXType T);
/**
- * \determine Determine whether a CXType has the "restrict" qualifier set,
- * without looking through typedefs that may have added "restrict" at a different level.
+ * \brief Determine whether a CXType has the "restrict" qualifier set,
+ * without looking through typedefs that may have added "restrict" at a
+ * different level.
*/
CINDEX_LINKAGE unsigned clang_isRestrictQualifiedType(CXType T);
/**
* \brief For pointer types, returns the type of the pointee.
- *
*/
CINDEX_LINKAGE CXType clang_getPointeeType(CXType T);
@@ -2642,7 +2731,8 @@ CINDEX_LINKAGE enum CXCallingConv clang_getFunctionTypeCallingConv(CXType T);
CINDEX_LINKAGE CXType clang_getResultType(CXType T);
/**
- * \brief Retrieve the number of non-variadic arguments associated with a function type.
+ * \brief Retrieve the number of non-variadic arguments associated with a
+ * function type.
*
* If a non-function type is passed in, -1 is returned.
*/
@@ -2651,14 +2741,13 @@ CINDEX_LINKAGE int clang_getNumArgTypes(CXType T);
/**
* \brief Retrieve the type of an argument of a function type.
*
- * If a non-function type is passed in or the function does not have enough parameters,
- * an invalid type is returned.
+ * If a non-function type is passed in or the function does not have enough
+ * parameters, an invalid type is returned.
*/
CINDEX_LINKAGE CXType clang_getArgType(CXType T, unsigned i);
/**
* \brief Return 1 if the CXType is a variadic function type, and 0 otherwise.
- *
*/
CINDEX_LINKAGE unsigned clang_isFunctionTypeVariadic(CXType T);
@@ -2699,7 +2788,7 @@ CINDEX_LINKAGE long long clang_getNumElements(CXType T);
CINDEX_LINKAGE CXType clang_getArrayElementType(CXType T);
/**
- * \brief Return the the array size of a constant array.
+ * \brief Return the array size of a constant array.
*
* If a non-array type is passed in, -1 is returned.
*/
@@ -3052,7 +3141,7 @@ CINDEX_LINKAGE CXCursor clang_getCanonicalCursor(CXCursor);
* \brief If the cursor points to a selector identifier in a objc method or
* message expression, this returns the selector index.
*
- * After getting a cursor with \see clang_getCursor, this can be called to
+ * After getting a cursor with #clang_getCursor, this can be called to
* determine if the location points to a selector identifier.
*
* \returns The selector index if the cursor is an objc method or message
@@ -3062,6 +3151,557 @@ CINDEX_LINKAGE CXCursor clang_getCanonicalCursor(CXCursor);
CINDEX_LINKAGE int clang_Cursor_getObjCSelectorIndex(CXCursor);
/**
+ * \brief Given a cursor pointing to a C++ method call or an ObjC message,
+ * returns non-zero if the method/message is "dynamic", meaning:
+ *
+ * For a C++ method: the call is virtual.
+ * For an ObjC message: the receiver is an object instance, not 'super' or a
+ * specific class.
+ *
+ * If the method/message is "static" or the cursor does not point to a
+ * method/message, it will return zero.
+ */
+CINDEX_LINKAGE int clang_Cursor_isDynamicCall(CXCursor C);
+
+/**
+ * \brief Given a cursor that represents a declaration, return the associated
+ * comment's source range. The range may include multiple consecutive comments
+ * with whitespace in between.
+ */
+CINDEX_LINKAGE CXSourceRange clang_Cursor_getCommentRange(CXCursor C);
+
+/**
+ * \brief Given a cursor that represents a declaration, return the associated
+ * comment text, including comment markers.
+ */
+CINDEX_LINKAGE CXString clang_Cursor_getRawCommentText(CXCursor C);
+
+/**
+ * \brief Given a cursor that represents a documentable entity (e.g.,
+ * declaration), return the associated \\brief paragraph; otherwise return the
+ * first paragraph.
+ */
+CINDEX_LINKAGE CXString clang_Cursor_getBriefCommentText(CXCursor C);
+
+/**
+ * \brief Given a cursor that represents a documentable entity (e.g.,
+ * declaration), return the associated parsed comment as a
+ * \c CXComment_FullComment AST node.
+ */
+CINDEX_LINKAGE CXComment clang_Cursor_getParsedComment(CXCursor C);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_COMMENT Comment AST introspection
+ *
+ * The routines in this group provide access to information in the
+ * documentation comment ASTs.
+ *
+ * @{
+ */
+
+/**
+ * \brief Describes the type of the comment AST node (\c CXComment). A comment
+ * node can be considered block content (e. g., paragraph), inline content
+ * (plain text) or neither (the root AST node).
+ */
+enum CXCommentKind {
+ /**
+ * \brief Null comment. No AST node is constructed at the requested location
+ * because there is no text or a syntax error.
+ */
+ CXComment_Null = 0,
+
+ /**
+ * \brief Plain text. Inline content.
+ */
+ CXComment_Text = 1,
+
+ /**
+ * \brief A command with word-like arguments that is considered inline content.
+ *
+ * For example: \\c command.
+ */
+ CXComment_InlineCommand = 2,
+
+ /**
+ * \brief HTML start tag with attributes (name-value pairs). Considered
+ * inline content.
+ *
+ * For example:
+ * \verbatim
+ * <br> <br /> <a href="http://example.org/">
+ * \endverbatim
+ */
+ CXComment_HTMLStartTag = 3,
+
+ /**
+ * \brief HTML end tag. Considered inline content.
+ *
+ * For example:
+ * \verbatim
+ * </a>
+ * \endverbatim
+ */
+ CXComment_HTMLEndTag = 4,
+
+ /**
+ * \brief A paragraph, contains inline comment. The paragraph itself is
+ * block content.
+ */
+ CXComment_Paragraph = 5,
+
+ /**
+ * \brief A command that has zero or more word-like arguments (number of
+ * word-like arguments depends on command name) and a paragraph as an
+ * argument. Block command is block content.
+ *
+ * Paragraph argument is also a child of the block command.
+ *
+ * For example: \\brief has 0 word-like arguments and a paragraph argument.
+ *
+ * AST nodes of special kinds that parser knows about (e. g., \\param
+ * command) have their own node kinds.
+ */
+ CXComment_BlockCommand = 6,
+
+ /**
+ * \brief A \\param or \\arg command that describes the function parameter
+ * (name, passing direction, description).
+ *
+ * \brief For example: \\param [in] ParamName description.
+ */
+ CXComment_ParamCommand = 7,
+
+ /**
+ * \brief A \\tparam command that describes a template parameter (name and
+ * description).
+ *
+ * \brief For example: \\tparam T description.
+ */
+ CXComment_TParamCommand = 8,
+
+ /**
+ * \brief A verbatim block command (e. g., preformatted code). Verbatim
+ * block has an opening and a closing command and contains multiple lines of
+ * text (\c CXComment_VerbatimBlockLine child nodes).
+ *
+ * For example:
+ * \\verbatim
+ * aaa
+ * \\endverbatim
+ */
+ CXComment_VerbatimBlockCommand = 9,
+
+ /**
+ * \brief A line of text that is contained within a
+ * CXComment_VerbatimBlockCommand node.
+ */
+ CXComment_VerbatimBlockLine = 10,
+
+ /**
+ * \brief A verbatim line command. Verbatim line has an opening command,
+ * a single line of text (up to the newline after the opening command) and
+ * has no closing command.
+ */
+ CXComment_VerbatimLine = 11,
+
+ /**
+ * \brief A full comment attached to a declaration, contains block content.
+ */
+ CXComment_FullComment = 12
+};
+
+/**
+ * \brief The most appropriate rendering mode for an inline command, chosen on
+ * command semantics in Doxygen.
+ */
+enum CXCommentInlineCommandRenderKind {
+ /**
+ * \brief Command argument should be rendered in a normal font.
+ */
+ CXCommentInlineCommandRenderKind_Normal,
+
+ /**
+ * \brief Command argument should be rendered in a bold font.
+ */
+ CXCommentInlineCommandRenderKind_Bold,
+
+ /**
+ * \brief Command argument should be rendered in a monospaced font.
+ */
+ CXCommentInlineCommandRenderKind_Monospaced,
+
+ /**
+ * \brief Command argument should be rendered emphasized (typically italic
+ * font).
+ */
+ CXCommentInlineCommandRenderKind_Emphasized
+};
+
+/**
+ * \brief Describes parameter passing direction for \\param or \\arg command.
+ */
+enum CXCommentParamPassDirection {
+ /**
+ * \brief The parameter is an input parameter.
+ */
+ CXCommentParamPassDirection_In,
+
+ /**
+ * \brief The parameter is an output parameter.
+ */
+ CXCommentParamPassDirection_Out,
+
+ /**
+ * \brief The parameter is an input and output parameter.
+ */
+ CXCommentParamPassDirection_InOut
+};
+
+/**
+ * \param Comment AST node of any kind.
+ *
+ * \returns the type of the AST node.
+ */
+CINDEX_LINKAGE enum CXCommentKind clang_Comment_getKind(CXComment Comment);
+
+/**
+ * \param Comment AST node of any kind.
+ *
+ * \returns number of children of the AST node.
+ */
+CINDEX_LINKAGE unsigned clang_Comment_getNumChildren(CXComment Comment);
+
+/**
+ * \param Comment AST node of any kind.
+ *
+ * \param ArgIdx argument index (zero-based).
+ *
+ * \returns the specified child of the AST node.
+ */
+CINDEX_LINKAGE
+CXComment clang_Comment_getChild(CXComment Comment, unsigned ChildIdx);
+
+/**
+ * \brief A \c CXComment_Paragraph node is considered whitespace if it contains
+ * only \c CXComment_Text nodes that are empty or whitespace.
+ *
+ * Other AST nodes (except \c CXComment_Paragraph and \c CXComment_Text) are
+ * never considered whitespace.
+ *
+ * \returns non-zero if \c Comment is whitespace.
+ */
+CINDEX_LINKAGE unsigned clang_Comment_isWhitespace(CXComment Comment);
+
+/**
+ * \returns non-zero if \c Comment is inline content and has a newline
+ * immediately following it in the comment text. Newlines between paragraphs
+ * do not count.
+ */
+CINDEX_LINKAGE
+unsigned clang_InlineContentComment_hasTrailingNewline(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_Text AST node.
+ *
+ * \returns text contained in the AST node.
+ */
+CINDEX_LINKAGE CXString clang_TextComment_getText(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_InlineCommand AST node.
+ *
+ * \returns name of the inline command.
+ */
+CINDEX_LINKAGE
+CXString clang_InlineCommandComment_getCommandName(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_InlineCommand AST node.
+ *
+ * \returns the most appropriate rendering mode, chosen on command
+ * semantics in Doxygen.
+ */
+CINDEX_LINKAGE enum CXCommentInlineCommandRenderKind
+clang_InlineCommandComment_getRenderKind(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_InlineCommand AST node.
+ *
+ * \returns number of command arguments.
+ */
+CINDEX_LINKAGE
+unsigned clang_InlineCommandComment_getNumArgs(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_InlineCommand AST node.
+ *
+ * \param ArgIdx argument index (zero-based).
+ *
+ * \returns text of the specified argument.
+ */
+CINDEX_LINKAGE
+CXString clang_InlineCommandComment_getArgText(CXComment Comment,
+ unsigned ArgIdx);
+
+/**
+ * \param Comment a \c CXComment_HTMLStartTag or \c CXComment_HTMLEndTag AST
+ * node.
+ *
+ * \returns HTML tag name.
+ */
+CINDEX_LINKAGE CXString clang_HTMLTagComment_getTagName(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_HTMLStartTag AST node.
+ *
+ * \returns non-zero if tag is self-closing (for example, &lt;br /&gt;).
+ */
+CINDEX_LINKAGE
+unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_HTMLStartTag AST node.
+ *
+ * \returns number of attributes (name-value pairs) attached to the start tag.
+ */
+CINDEX_LINKAGE unsigned clang_HTMLStartTag_getNumAttrs(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_HTMLStartTag AST node.
+ *
+ * \param AttrIdx attribute index (zero-based).
+ *
+ * \returns name of the specified attribute.
+ */
+CINDEX_LINKAGE
+CXString clang_HTMLStartTag_getAttrName(CXComment Comment, unsigned AttrIdx);
+
+/**
+ * \param Comment a \c CXComment_HTMLStartTag AST node.
+ *
+ * \param AttrIdx attribute index (zero-based).
+ *
+ * \returns value of the specified attribute.
+ */
+CINDEX_LINKAGE
+CXString clang_HTMLStartTag_getAttrValue(CXComment Comment, unsigned AttrIdx);
+
+/**
+ * \param Comment a \c CXComment_BlockCommand AST node.
+ *
+ * \returns name of the block command.
+ */
+CINDEX_LINKAGE
+CXString clang_BlockCommandComment_getCommandName(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_BlockCommand AST node.
+ *
+ * \returns number of word-like arguments.
+ */
+CINDEX_LINKAGE
+unsigned clang_BlockCommandComment_getNumArgs(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_BlockCommand AST node.
+ *
+ * \param ArgIdx argument index (zero-based).
+ *
+ * \returns text of the specified word-like argument.
+ */
+CINDEX_LINKAGE
+CXString clang_BlockCommandComment_getArgText(CXComment Comment,
+ unsigned ArgIdx);
+
+/**
+ * \param Comment a \c CXComment_BlockCommand or
+ * \c CXComment_VerbatimBlockCommand AST node.
+ *
+ * \returns paragraph argument of the block command.
+ */
+CINDEX_LINKAGE
+CXComment clang_BlockCommandComment_getParagraph(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_ParamCommand AST node.
+ *
+ * \returns parameter name.
+ */
+CINDEX_LINKAGE
+CXString clang_ParamCommandComment_getParamName(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_ParamCommand AST node.
+ *
+ * \returns non-zero if the parameter that this AST node represents was found
+ * in the function prototype and \c clang_ParamCommandComment_getParamIndex
+ * function will return a meaningful value.
+ */
+CINDEX_LINKAGE
+unsigned clang_ParamCommandComment_isParamIndexValid(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_ParamCommand AST node.
+ *
+ * \returns zero-based parameter index in function prototype.
+ */
+CINDEX_LINKAGE
+unsigned clang_ParamCommandComment_getParamIndex(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_ParamCommand AST node.
+ *
+ * \returns non-zero if parameter passing direction was specified explicitly in
+ * the comment.
+ */
+CINDEX_LINKAGE
+unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_ParamCommand AST node.
+ *
+ * \returns parameter passing direction.
+ */
+CINDEX_LINKAGE
+enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
+ CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns template parameter name.
+ */
+CINDEX_LINKAGE
+CXString clang_TParamCommandComment_getParamName(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns non-zero if the parameter that this AST node represents was found
+ * in the template parameter list and
+ * \c clang_TParamCommandComment_getDepth and
+ * \c clang_TParamCommandComment_getIndex functions will return a meaningful
+ * value.
+ */
+CINDEX_LINKAGE
+unsigned clang_TParamCommandComment_isParamPositionValid(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns zero-based nesting depth of this parameter in the template parameter list.
+ *
+ * For example,
+ * \verbatim
+ * template<typename C, template<typename T> class TT>
+ * void test(TT<int> aaa);
+ * \endverbatim
+ * for C and TT nesting depth is 0,
+ * for T nesting depth is 1.
+ */
+CINDEX_LINKAGE
+unsigned clang_TParamCommandComment_getDepth(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns zero-based parameter index in the template parameter list at a
+ * given nesting depth.
+ *
+ * For example,
+ * \verbatim
+ * template<typename C, template<typename T> class TT>
+ * void test(TT<int> aaa);
+ * \endverbatim
+ * for C and TT nesting depth is 0, so we can ask for index at depth 0:
+ * at depth 0 C's index is 0, TT's index is 1.
+ *
+ * For T nesting depth is 1, so we can ask for index at depth 0 and 1:
+ * at depth 0 T's index is 1 (same as TT's),
+ * at depth 1 T's index is 0.
+ */
+CINDEX_LINKAGE
+unsigned clang_TParamCommandComment_getIndex(CXComment Comment, unsigned Depth);
+
+/**
+ * \param Comment a \c CXComment_VerbatimBlockLine AST node.
+ *
+ * \returns text contained in the AST node.
+ */
+CINDEX_LINKAGE
+CXString clang_VerbatimBlockLineComment_getText(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_VerbatimLine AST node.
+ *
+ * \returns text contained in the AST node.
+ */
+CINDEX_LINKAGE CXString clang_VerbatimLineComment_getText(CXComment Comment);
+
+/**
+ * \brief Convert an HTML tag AST node to string.
+ *
+ * \param Comment a \c CXComment_HTMLStartTag or \c CXComment_HTMLEndTag AST
+ * node.
+ *
+ * \returns string containing an HTML tag.
+ */
+CINDEX_LINKAGE CXString clang_HTMLTagComment_getAsString(CXComment Comment);
+
+/**
+ * \brief Convert a given full parsed comment to an HTML fragment.
+ *
+ * Specific details of HTML layout are subject to change. Don't try to parse
+ * this HTML back into an AST, use other APIs instead.
+ *
+ * Currently the following CSS classes are used:
+ * \li "para-brief" for \\brief paragraph and equivalent commands;
+ * \li "para-returns" for \\returns paragraph and equivalent commands;
+ * \li "word-returns" for the "Returns" word in \\returns paragraph.
+ *
+ * Function argument documentation is rendered as a \<dl\> list with arguments
+ * sorted in function prototype order. CSS classes used:
+ * \li "param-name-index-NUMBER" for parameter name (\<dt\>);
+ * \li "param-descr-index-NUMBER" for parameter description (\<dd\>);
+ * \li "param-name-index-invalid" and "param-descr-index-invalid" are used if
+ * parameter index is invalid.
+ *
+ * Template parameter documentation is rendered as a \<dl\> list with
+ * parameters sorted in template parameter list order. CSS classes used:
+ * \li "tparam-name-index-NUMBER" for parameter name (\<dt\>);
+ * \li "tparam-descr-index-NUMBER" for parameter description (\<dd\>);
+ * \li "tparam-name-index-other" and "tparam-descr-index-other" are used for
+ * names inside template template parameters;
+ * \li "tparam-name-index-invalid" and "tparam-descr-index-invalid" are used if
+ * parameter position is invalid.
+ *
+ * \param Comment a \c CXComment_FullComment AST node.
+ *
+ * \returns string containing an HTML fragment.
+ */
+CINDEX_LINKAGE CXString clang_FullComment_getAsHTML(CXComment Comment);
+
+/**
+ * \brief Convert a given full parsed comment to an XML document.
+ *
+ * A Relax NG schema for the XML can be found in comment-xml-schema.rng file
+ * inside clang source tree.
+ *
+ * \param TU the translation unit \c Comment belongs to.
+ *
+ * \param Comment a \c CXComment_FullComment AST node.
+ *
+ * \returns string containing an XML document.
+ */
+CINDEX_LINKAGE CXString clang_FullComment_getAsXML(CXTranslationUnit TU,
+ CXComment Comment);
+
+/**
* @}
*/
@@ -3148,7 +3788,7 @@ CINDEX_LINKAGE CXCursor clang_getSpecializedCursorTemplate(CXCursor C);
* \param PieceIndex For contiguous names or when passing the flag
* CXNameRange_WantSinglePiece, only one piece with index 0 is
* available. When the CXNameRange_WantSinglePiece flag is not passed for a
- * non-contiguous names, this index can be used to retreive the individual
+ * non-contiguous names, this index can be used to retrieve the individual
* pieces of the name. See also CXNameRange_WantSinglePiece.
*
* \returns The piece of the name pointed to by the given cursor. If there is no
@@ -3166,8 +3806,8 @@ enum CXNameRefFlags {
CXNameRange_WantQualifier = 0x1,
/**
- * \brief Include the explicit template arguments, e.g. <int> in x.f<int>, in
- * the range.
+ * \brief Include the explicit template arguments, e.g. \<int> in x.f<int>,
+ * in the range.
*/
CXNameRange_WantTemplateArgs = 0x2,
@@ -3686,12 +4326,20 @@ clang_getCompletionAnnotation(CXCompletionString completion_string,
* \param kind If non-NULL, will be set to the kind of the parent context,
* or CXCursor_NotImplemented if there is no context.
*
- * \param Returns the name of the completion parent, e.g., "NSObject" if
+ * \returns The name of the completion parent, e.g., "NSObject" if
* the completion string represents a method in the NSObject class.
*/
CINDEX_LINKAGE CXString
clang_getCompletionParent(CXCompletionString completion_string,
enum CXCursorKind *kind);
+
+/**
+ * \brief Retrieve the brief documentation comment attached to the declaration
+ * that corresponds to the given completion string.
+ */
+CINDEX_LINKAGE CXString
+clang_getCompletionBriefComment(CXCompletionString completion_string);
+
/**
* \brief Retrieve a completion string for an arbitrary declaration or macro
* definition cursor.
@@ -3742,7 +4390,13 @@ enum CXCodeComplete_Flags {
* \brief Whether to include code patterns for language constructs
* within the set of code completions, e.g., for loops.
*/
- CXCodeComplete_IncludeCodePatterns = 0x02
+ CXCodeComplete_IncludeCodePatterns = 0x02,
+
+ /**
+ * \brief Whether to include brief documentation within the set of code
+ * completions returned.
+ */
+ CXCodeComplete_IncludeBriefComments = 0x04
};
/**
@@ -3986,7 +4640,7 @@ unsigned clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *Results);
/**
* \brief Retrieve a diagnostic associated with the given code completion.
*
- * \param Result the code completion results to query.
+ * \param Results the code completion results to query.
* \param Index the zero-based diagnostic number to retrieve.
*
* \returns the requested diagnostic. This diagnostic must be freed
@@ -4078,8 +4732,8 @@ CINDEX_LINKAGE CXString clang_getClangVersion();
/**
* \brief Enable/disable crash recovery.
*
- * \param Flag to indicate if crash recovery is enabled. A non-zero value
- * enables crash recovery, while 0 disables it.
+ * \param isEnabled Flag to indicate if crash recovery is enabled. A non-zero
+ * value enables crash recovery, while 0 disables it.
*/
CINDEX_LINKAGE void clang_toggleCrashRecovery(unsigned isEnabled);
@@ -4088,7 +4742,7 @@ CINDEX_LINKAGE void clang_toggleCrashRecovery(unsigned isEnabled);
* (used with clang_getInclusions()).
*
* This visitor function will be invoked by clang_getInclusions() for each
- * file included (either at the top-level or by #include directives) within
+ * file included (either at the top-level or by \#include directives) within
* a translation unit. The first argument is the file being included, and
* the second and third arguments provide the inclusion stack. The
* array is sorted in order of immediate inclusion. For example,
@@ -4246,19 +4900,19 @@ typedef struct {
} CXIdxLoc;
/**
- * \brief Data for \see ppIncludedFile callback.
+ * \brief Data for ppIncludedFile callback.
*/
typedef struct {
/**
- * \brief Location of '#' in the #include/#import directive.
+ * \brief Location of '#' in the \#include/\#import directive.
*/
CXIdxLoc hashLoc;
/**
- * \brief Filename as written in the #include/#import directive.
+ * \brief Filename as written in the \#include/\#import directive.
*/
const char *filename;
/**
- * \brief The actual file that the #include/#import directive resolved to.
+ * \brief The actual file that the \#include/\#import directive resolved to.
*/
CXFile file;
int isImport;
@@ -4266,7 +4920,7 @@ typedef struct {
} CXIdxIncludedFileInfo;
/**
- * \brief Data for \see importedASTFile callback.
+ * \brief Data for IndexerCallbacks#importedASTFile.
*/
typedef struct {
CXFile file;
@@ -4380,7 +5034,7 @@ typedef struct {
CXIdxLoc loc;
const CXIdxContainerInfo *semanticContainer;
/**
- * \brief Generally same as \see semanticContainer but can be different in
+ * \brief Generally same as #semanticContainer but can be different in
* cases like out-of-line C++ member functions.
*/
const CXIdxContainerInfo *lexicalContainer;
@@ -4452,7 +5106,7 @@ typedef struct {
} CXIdxCXXClassDeclInfo;
/**
- * \brief Data for \see indexEntityReference callback.
+ * \brief Data for IndexerCallbacks#indexEntityReference.
*/
typedef enum {
/**
@@ -4467,7 +5121,7 @@ typedef enum {
} CXIdxEntityRefKind;
/**
- * \brief Data for \see indexEntityReference callback.
+ * \brief Data for IndexerCallbacks#indexEntityReference.
*/
typedef struct {
CXIdxEntityRefKind kind;
@@ -4498,6 +5152,10 @@ typedef struct {
const CXIdxContainerInfo *container;
} CXIdxEntityRefInfo;
+/**
+ * \brief A group of callbacks used by #clang_indexSourceFile and
+ * #clang_indexTranslationUnit.
+ */
typedef struct {
/**
* \brief Called periodically to check whether indexing should be aborted.
@@ -4512,10 +5170,10 @@ typedef struct {
CXDiagnosticSet, void *reserved);
CXIdxClientFile (*enteredMainFile)(CXClientData client_data,
- CXFile mainFile, void *reserved);
+ CXFile mainFile, void *reserved);
/**
- * \brief Called when a file gets #included/#imported.
+ * \brief Called when a file gets \#included/\#imported.
*/
CXIdxClientFile (*ppIncludedFile)(CXClientData client_data,
const CXIdxIncludedFileInfo *);
@@ -4628,9 +5286,9 @@ typedef enum {
CXIndexOpt_None = 0x0,
/**
- * \brief Used to indicate that \see indexEntityReference should be invoked
- * for only one reference of an entity per source file that does not also
- * include a declaration/definition of the entity.
+ * \brief Used to indicate that IndexerCallbacks#indexEntityReference should
+ * be invoked for only one reference of an entity per source file that does
+ * not also include a declaration/definition of the entity.
*/
CXIndexOpt_SuppressRedundantRefs = 0x1,
@@ -4654,7 +5312,7 @@ typedef enum {
/**
* \brief Index the given source file and the translation unit corresponding
- * to that file via callbacks implemented through \see IndexerCallbacks.
+ * to that file via callbacks implemented through #IndexerCallbacks.
*
* \param client_data pointer data supplied by the client, which will
* be passed to the invoked callbacks.
@@ -4662,7 +5320,7 @@ typedef enum {
* \param index_callbacks Pointer to indexing callbacks that the client
* implements.
*
- * \param index_callbacks_size Size of \see IndexerCallbacks structure that gets
+ * \param index_callbacks_size Size of #IndexerCallbacks structure that gets
* passed in index_callbacks.
*
* \param index_options A bitmask of options that affects how indexing is
@@ -4674,7 +5332,7 @@ typedef enum {
* \returns If there is a failure from which the there is no recovery, returns
* non-zero, otherwise returns 0.
*
- * The rest of the parameters are the same as \see clang_parseTranslationUnit.
+ * The rest of the parameters are the same as #clang_parseTranslationUnit.
*/
CINDEX_LINKAGE int clang_indexSourceFile(CXIndexAction,
CXClientData client_data,
@@ -4691,7 +5349,7 @@ CINDEX_LINKAGE int clang_indexSourceFile(CXIndexAction,
/**
* \brief Index the given translation unit via callbacks implemented through
- * \see IndexerCallbacks.
+ * #IndexerCallbacks.
*
* The order of callback invocations is not guaranteed to be the same as
* when indexing a source file. The high level order will be:
@@ -4700,7 +5358,7 @@ CINDEX_LINKAGE int clang_indexSourceFile(CXIndexAction,
* -Declaration/reference callbacks invocations
* -Diagnostic callback invocations
*
- * The parameters are the same as \see clang_indexSourceFile.
+ * The parameters are the same as #clang_indexSourceFile.
*
* \returns If there is a failure from which the there is no recovery, returns
* non-zero, otherwise returns 0.
diff --git a/include/clang-c/Platform.h b/include/clang-c/Platform.h
new file mode 100644
index 0000000..0f866c6
--- /dev/null
+++ b/include/clang-c/Platform.h
@@ -0,0 +1,45 @@
+/*===-- clang-c/Platform.h - C Index platform decls -------------*- C -*-===*\
+|* *|
+|* The LLVM Compiler Infrastructure *|
+|* *|
+|* This file is distributed under the University of Illinois Open Source *|
+|* License. See LICENSE.TXT for details. *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This header provides platform specific macros (dllimport, deprecated, ...) *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef CLANG_C_PLATFORM_H
+#define CLANG_C_PLATFORM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MSVC DLL import/export. */
+#ifdef _MSC_VER
+ #ifdef _CINDEX_LIB_
+ #define CINDEX_LINKAGE __declspec(dllexport)
+ #else
+ #define CINDEX_LINKAGE __declspec(dllimport)
+ #endif
+#else
+ #define CINDEX_LINKAGE
+#endif
+
+#ifdef __GNUC__
+ #define CINDEX_DEPRECATED __attribute__((deprecated))
+#else
+ #ifdef _MSC_VER
+ #define CINDEX_DEPRECATED __declspec(deprecated)
+ #else
+ #define CINDEX_DEPRECATED
+ #endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 96e41c5..8fd7d6e 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -27,6 +27,7 @@
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/CanonicalType.h"
+#include "clang/AST/RawCommentList.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -51,7 +52,6 @@ namespace clang {
class ASTMutationListener;
class IdentifierTable;
class SelectorTable;
- class SourceManager;
class TargetInfo;
class CXXABI;
// Decls
@@ -80,6 +80,10 @@ namespace clang {
namespace Builtin { class Context; }
+ namespace comments {
+ class FullComment;
+ }
+
/// ASTContext - This class holds long-lived AST nodes (such as types and
/// decls) that can be referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
@@ -198,10 +202,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// \brief The typedef for the __uint128_t type.
mutable TypedefDecl *UInt128Decl;
- /// BuiltinVaListType - built-in va list type.
- /// This is initially null and set by Sema::LazilyCreateBuiltin when
- /// a builtin that takes a valist is encountered.
- QualType BuiltinVaListType;
+ /// \brief The typedef for the target specific predefined
+ /// __builtin_va_list type.
+ mutable TypedefDecl *BuiltinVaListDecl;
/// \brief The typedef for the predefined 'id' type.
mutable TypedefDecl *ObjCIdDecl;
@@ -392,6 +395,11 @@ public:
SourceManager& getSourceManager() { return SourceMgr; }
const SourceManager& getSourceManager() const { return SourceMgr; }
+
+ llvm::BumpPtrAllocator &getAllocator() const {
+ return BumpAlloc;
+ }
+
void *Allocate(unsigned Size, unsigned Align = 8) const {
return BumpAlloc.Allocate(Size, Align);
}
@@ -419,6 +427,85 @@ public:
return FullSourceLoc(Loc,SourceMgr);
}
+ /// \brief All comments in this translation unit.
+ RawCommentList Comments;
+
+ /// \brief True if comments are already loaded from ExternalASTSource.
+ mutable bool CommentsLoaded;
+
+ class RawCommentAndCacheFlags {
+ public:
+ enum Kind {
+ /// We searched for a comment attached to the particular declaration, but
+ /// didn't find any.
+ ///
+ /// getRaw() == 0.
+ NoCommentInDecl = 0,
+
+ /// We have found a comment attached to this particular declaration.
+ ///
+ /// getRaw() != 0.
+ FromDecl,
+
+ /// This declaration does not have an attached comment, and we have
+ /// searched the redeclaration chain.
+ ///
+ /// If getRaw() == 0, the whole redeclaration chain does not have any
+ /// comments.
+ ///
+ /// If getRaw() != 0, it is a comment propagated from other
+ /// redeclaration.
+ FromRedecl
+ };
+
+ Kind getKind() const LLVM_READONLY {
+ return Data.getInt();
+ }
+
+ void setKind(Kind K) {
+ Data.setInt(K);
+ }
+
+ const RawComment *getRaw() const LLVM_READONLY {
+ return Data.getPointer();
+ }
+
+ void setRaw(const RawComment *RC) {
+ Data.setPointer(RC);
+ }
+
+ private:
+ llvm::PointerIntPair<const RawComment *, 2, Kind> Data;
+ };
+
+ /// \brief Mapping from declarations to comments attached to any
+ /// redeclaration.
+ ///
+ /// Raw comments are owned by Comments list. This mapping is populated
+ /// lazily.
+ mutable llvm::DenseMap<const Decl *, RawCommentAndCacheFlags> RedeclComments;
+
+ /// \brief Return the documentation comment attached to a given declaration,
+ /// without looking into cache.
+ RawComment *getRawCommentForDeclNoCache(const Decl *D) const;
+
+public:
+ RawCommentList &getRawCommentList() {
+ return Comments;
+ }
+
+ void addComment(const RawComment &RC) {
+ Comments.addComment(RC, BumpAlloc);
+ }
+
+ /// \brief Return the documentation comment attached to a given declaration.
+ /// Returns NULL if no comment is attached.
+ const RawComment *getRawCommentForAnyRedecl(const Decl *D) const;
+
+ /// Return parsed documentation comment attached to a given declaration.
+ /// Returns NULL if no comment is attached.
+ comments::FullComment *getCommentForDecl(const Decl *D) const;
+
/// \brief Retrieve the attributes for the given declaration.
AttrVec& getDeclAttrs(const Decl *D);
@@ -557,6 +644,7 @@ public:
CanQualType BoolTy;
CanQualType CharTy;
CanQualType WCharTy; // [C++ 3.9.1p5], integer type in C99.
+ CanQualType WIntTy; // [C99 7.24.1], integer type unchanged by default promotions.
CanQualType Char16Ty; // [C++0x 3.9.1p5], integer type in C99.
CanQualType Char32Ty; // [C++0x 3.9.1p5], integer type in C99.
CanQualType SignedCharTy, ShortTy, IntTy, LongTy, LongLongTy, Int128Ty;
@@ -575,6 +663,10 @@ public:
mutable QualType AutoDeductTy; // Deduction against 'auto'.
mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
+ // Type used to help define __builtin_va_list for some targets.
+ // The type is built when constructing 'BuiltinVaListDecl'.
+ mutable QualType VaListTagTy;
+
ASTContext(LangOptions& LOpts, SourceManager &SM, const TargetInfo *t,
IdentifierTable &idents, SelectorTable &sels,
Builtin::Context &builtins,
@@ -929,6 +1021,10 @@ public:
/// Used when in C++, as a GCC extension.
QualType getUnsignedWCharType() const;
+ /// getWIntType - In C99, this returns a type compatible with the type
+ /// defined in <stddef.h> as defined by the target.
+ QualType getWIntType() const { return WIntTy; }
+
/// getPointerDiffType - Return the unique type for "ptrdiff_t" (C99 7.17)
/// defined in <stddef.h>. Pointer - pointer requires this (C99 6.5.6p9).
QualType getPointerDiffType() const;
@@ -1148,8 +1244,19 @@ public:
return getObjCInterfaceType(getObjCProtocolDecl());
}
- void setBuiltinVaListType(QualType T);
- QualType getBuiltinVaListType() const { return BuiltinVaListType; }
+ /// \brief Retrieve the C type declaration corresponding to the predefined
+ /// __builtin_va_list type.
+ TypedefDecl *getBuiltinVaListDecl() const;
+
+ /// \brief Retrieve the type of the __builtin_va_list type.
+ QualType getBuiltinVaListType() const {
+ return getTypeDeclType(getBuiltinVaListDecl());
+ }
+
+ /// \brief Retrieve the C type declaration corresponding to the predefined
+ /// __va_list_tag type used to help define the __builtin_va_list type for
+ /// some targets.
+ QualType getVaListTagType() const;
/// getCVRQualifiedType - Returns a type with additional const,
/// volatile, or restrict qualifiers.
@@ -1210,10 +1317,10 @@ public:
const TemplateArgument &ArgPack) const;
enum GetBuiltinTypeError {
- GE_None, //< No error
- GE_Missing_stdio, //< Missing a type from <stdio.h>
- GE_Missing_setjmp, //< Missing a type from <setjmp.h>
- GE_Missing_ucontext //< Missing a type from <ucontext.h>
+ GE_None, ///< No error
+ GE_Missing_stdio, ///< Missing a type from <stdio.h>
+ GE_Missing_setjmp, ///< Missing a type from <setjmp.h>
+ GE_Missing_ucontext ///< Missing a type from <ucontext.h>
};
/// GetBuiltinType - Return the type for the specified builtin. If
@@ -1440,15 +1547,11 @@ public:
/// \brief Retrieves the default calling convention to use for
/// C++ instance methods.
- CallingConv getDefaultMethodCallConv();
+ CallingConv getDefaultCXXMethodCallConv(bool isVariadic);
/// \brief Retrieves the canonical representation of the given
/// calling convention.
- CallingConv getCanonicalCallConv(CallingConv CC) const {
- if (!LangOpts.MRTD && CC == CC_C)
- return CC_Default;
- return CC;
- }
+ CallingConv getCanonicalCallConv(CallingConv CC) const;
/// \brief Determines whether two calling conventions name the same
/// calling convention.
@@ -1463,7 +1566,7 @@ public:
/// be used to refer to a given template. For most templates, this
/// expression is just the template declaration itself. For example,
/// the template std::vector can be referred to via a variety of
- /// names---std::vector, ::std::vector, vector (if vector is in
+ /// names---std::vector, \::std::vector, vector (if vector is in
/// scope), etc.---but all of these names map down to the same
/// TemplateDecl, which is used to form the canonical template name.
///
@@ -1523,12 +1626,12 @@ public:
/// This routine adjusts the given parameter type @p T to the actual
/// parameter type used by semantic analysis (C99 6.7.5.3p[7,8],
/// C++ [dcl.fct]p3). The adjusted parameter type is returned.
- QualType getAdjustedParameterType(QualType T);
+ QualType getAdjustedParameterType(QualType T) const;
/// \brief Retrieve the parameter type as adjusted for use in the signature
/// of a function, decaying array and function types and removing top-level
/// cv-qualifiers.
- QualType getSignatureParameterType(QualType T);
+ QualType getSignatureParameterType(QualType T) const;
/// getArrayDecayedType - Return the properly qualified result of decaying the
/// specified array type to a pointer. This operation is non-trivial when
@@ -1700,7 +1803,7 @@ public:
/// \brief Get the implementation of ObjCCategoryDecl, or NULL if none exists.
ObjCCategoryImplDecl *getObjCImplementation(ObjCCategoryDecl *D);
- /// \brief returns true if there is at lease one @implementation in TU.
+ /// \brief returns true if there is at least one \@implementation in TU.
bool AnyObjCImplementation() {
return !ObjCImpls.empty();
}
@@ -1716,15 +1819,12 @@ public:
/// interface, or null if non exists.
const ObjCMethodDecl *getObjCMethodRedeclaration(
const ObjCMethodDecl *MD) const {
- llvm::DenseMap<const ObjCMethodDecl*, const ObjCMethodDecl*>::const_iterator
- I = ObjCMethodRedecls.find(MD);
- if (I == ObjCMethodRedecls.end())
- return 0;
- return I->second;
+ return ObjCMethodRedecls.lookup(MD);
}
void setObjCMethodRedeclaration(const ObjCMethodDecl *MD,
const ObjCMethodDecl *Redecl) {
+ assert(!getObjCMethodRedeclaration(MD) && "MD already has a redeclaration");
ObjCMethodRedecls[MD] = Redecl;
}
diff --git a/include/clang/AST/ASTImporter.h b/include/clang/AST/ASTImporter.h
index 7157efe..46a9881 100644
--- a/include/clang/AST/ASTImporter.h
+++ b/include/clang/AST/ASTImporter.h
@@ -271,7 +271,8 @@ namespace clang {
/// \brief Determine whether the given types are structurally
/// equivalent.
- bool IsStructurallyEquivalent(QualType From, QualType To);
+ bool IsStructurallyEquivalent(QualType From, QualType To,
+ bool Complain = true);
};
}
diff --git a/include/clang/AST/ASTVector.h b/include/clang/AST/ASTVector.h
index 217dfad..4ff5ea3 100644
--- a/include/clang/AST/ASTVector.h
+++ b/include/clang/AST/ASTVector.h
@@ -374,7 +374,7 @@ void ASTVector<T>::grow(ASTContext &C, size_t MinSize) {
NewCapacity = MinSize;
// Allocate the memory from the ASTContext.
- T *NewElts = new (C) T[NewCapacity];
+ T *NewElts = new (C, llvm::alignOf<T>()) T[NewCapacity];
// Copy the elements over.
if (llvm::is_class<T>::value) {
@@ -387,7 +387,7 @@ void ASTVector<T>::grow(ASTContext &C, size_t MinSize) {
memcpy(NewElts, Begin, CurSize * sizeof(T));
}
- C.Deallocate(Begin);
+ // ASTContext never frees any memory.
Begin = NewElts;
End = NewElts+CurSize;
Capacity = Begin+NewCapacity;
diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h
index ef1aa25..27b44d4 100644
--- a/include/clang/AST/Attr.h
+++ b/include/clang/AST/Attr.h
@@ -1,4 +1,4 @@
-//===--- Attr.h - Classes for representing expressions ----------*- C++ -*-===//
+//===--- Attr.h - Classes for representing attributes ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -147,14 +147,12 @@ public:
typedef SmallVector<Attr*, 2> AttrVec;
typedef SmallVector<const Attr*, 2> ConstAttrVec;
-/// DestroyAttrs - Destroy the contents of an AttrVec.
-inline void DestroyAttrs (AttrVec& V, ASTContext &C) {
-}
-
/// specific_attr_iterator - Iterates over a subrange of an AttrVec, only
/// providing attributes that are of a specifc type.
-template <typename SpecificAttr>
+template <typename SpecificAttr, typename Container = AttrVec>
class specific_attr_iterator {
+ typedef typename Container::const_iterator Iterator;
+
/// Current - The current, underlying iterator.
/// In order to ensure we don't dereference an invalid iterator unless
/// specifically requested, we don't necessarily advance this all the
@@ -162,14 +160,14 @@ class specific_attr_iterator {
/// operation is acting on what should be a past-the-end iterator,
/// then we offer no guarantees, but this way we do not dererence a
/// past-the-end iterator when we move to a past-the-end position.
- mutable AttrVec::const_iterator Current;
+ mutable Iterator Current;
void AdvanceToNext() const {
while (!isa<SpecificAttr>(*Current))
++Current;
}
- void AdvanceToNext(AttrVec::const_iterator I) const {
+ void AdvanceToNext(Iterator I) const {
while (Current != I && !isa<SpecificAttr>(*Current))
++Current;
}
@@ -182,7 +180,7 @@ public:
typedef std::ptrdiff_t difference_type;
specific_attr_iterator() : Current() { }
- explicit specific_attr_iterator(AttrVec::const_iterator i) : Current(i) { }
+ explicit specific_attr_iterator(Iterator i) : Current(i) { }
reference operator*() const {
AdvanceToNext();
@@ -217,23 +215,27 @@ public:
}
};
-template <typename T>
-inline specific_attr_iterator<T> specific_attr_begin(const AttrVec& vec) {
- return specific_attr_iterator<T>(vec.begin());
+template <typename SpecificAttr, typename Container>
+inline specific_attr_iterator<SpecificAttr, Container>
+ specific_attr_begin(const Container& container) {
+ return specific_attr_iterator<SpecificAttr, Container>(container.begin());
}
-template <typename T>
-inline specific_attr_iterator<T> specific_attr_end(const AttrVec& vec) {
- return specific_attr_iterator<T>(vec.end());
+template <typename SpecificAttr, typename Container>
+inline specific_attr_iterator<SpecificAttr, Container>
+ specific_attr_end(const Container& container) {
+ return specific_attr_iterator<SpecificAttr, Container>(container.end());
}
-template <typename T>
-inline bool hasSpecificAttr(const AttrVec& vec) {
- return specific_attr_begin<T>(vec) != specific_attr_end<T>(vec);
+template <typename SpecificAttr, typename Container>
+inline bool hasSpecificAttr(const Container& container) {
+ return specific_attr_begin<SpecificAttr>(container) !=
+ specific_attr_end<SpecificAttr>(container);
}
-template <typename T>
-inline T *getSpecificAttr(const AttrVec& vec) {
- specific_attr_iterator<T> i = specific_attr_begin<T>(vec);
- if (i != specific_attr_end<T>(vec))
+template <typename SpecificAttr, typename Container>
+inline SpecificAttr *getSpecificAttr(const Container& container) {
+ specific_attr_iterator<SpecificAttr, Container> i =
+ specific_attr_begin<SpecificAttr>(container);
+ if (i != specific_attr_end<SpecificAttr>(container))
return *i;
else
return 0;
diff --git a/include/clang/AST/BaseSubobject.h b/include/clang/AST/BaseSubobject.h
index 6a036bb..da538e3 100644
--- a/include/clang/AST/BaseSubobject.h
+++ b/include/clang/AST/BaseSubobject.h
@@ -66,9 +66,9 @@ template<> struct DenseMapInfo<clang::BaseSubobject> {
}
static unsigned getHashValue(const clang::BaseSubobject &Base) {
- return
- DenseMapInfo<const clang::CXXRecordDecl *>::getHashValue(Base.getBase()) ^
- DenseMapInfo<int64_t>::getHashValue(Base.getBaseOffset().getQuantity());
+ typedef std::pair<const clang::CXXRecordDecl *, clang::CharUnits> PairTy;
+ return DenseMapInfo<PairTy>::getHashValue(PairTy(Base.getBase(),
+ Base.getBaseOffset()));
}
static bool isEqual(const clang::BaseSubobject &LHS,
diff --git a/include/clang/AST/CMakeLists.txt b/include/clang/AST/CMakeLists.txt
index c10cda8..d7458aa 100644
--- a/include/clang/AST/CMakeLists.txt
+++ b/include/clang/AST/CMakeLists.txt
@@ -15,3 +15,8 @@ clang_tablegen(StmtNodes.inc -gen-clang-stmt-nodes
clang_tablegen(DeclNodes.inc -gen-clang-decl-nodes
SOURCE ../Basic/DeclNodes.td
TARGET ClangDeclNodes)
+
+clang_tablegen(CommentNodes.inc -gen-clang-comment-nodes
+ SOURCE ../Basic/CommentNodes.td
+ TARGET ClangCommentNodes)
+
diff --git a/include/clang/AST/CXXInheritance.h b/include/clang/AST/CXXInheritance.h
index 44c554b..ee6eba7 100644
--- a/include/clang/AST/CXXInheritance.h
+++ b/include/clang/AST/CXXInheritance.h
@@ -128,8 +128,7 @@ class CXXBasePaths {
/// while the element contains the number of non-virtual base
/// class subobjects for that class type. The key of the map is
/// the cv-unqualified canonical type of the base class subobject.
- std::map<QualType, std::pair<bool, unsigned>, QualTypeOrdering>
- ClassSubobjects;
+ llvm::SmallDenseMap<QualType, std::pair<bool, unsigned>, 8> ClassSubobjects;
/// FindAmbiguities - Whether Sema::IsDerivedFrom should try find
/// ambiguous paths while it is looking for a path from a derived
diff --git a/include/clang/AST/Comment.h b/include/clang/AST/Comment.h
new file mode 100644
index 0000000..01aaac3
--- /dev/null
+++ b/include/clang/AST/Comment.h
@@ -0,0 +1,1059 @@
+//===--- Comment.h - Comment AST nodes --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines comment AST nodes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_COMMENT_H
+#define LLVM_CLANG_AST_COMMENT_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+class Decl;
+class ParmVarDecl;
+class TemplateParameterList;
+
+namespace comments {
+
+/// Any part of the comment.
+/// Abstract class.
+class Comment {
+protected:
+ /// Preferred location to show caret.
+ SourceLocation Loc;
+
+ /// Source range of this AST node.
+ SourceRange Range;
+
+ class CommentBitfields {
+ friend class Comment;
+
+ /// Type of this AST node.
+ unsigned Kind : 8;
+ };
+ enum { NumCommentBits = 8 };
+
+ class InlineContentCommentBitfields {
+ friend class InlineContentComment;
+
+ unsigned : NumCommentBits;
+
+ /// True if there is a newline after this inline content node.
+ /// (There is no separate AST node for a newline.)
+ unsigned HasTrailingNewline : 1;
+ };
+ enum { NumInlineContentCommentBits = NumCommentBits + 1 };
+
+ class TextCommentBitfields {
+ friend class TextComment;
+
+ unsigned : NumInlineContentCommentBits;
+
+ /// True if \c IsWhitespace field contains a valid value.
+ mutable unsigned IsWhitespaceValid : 1;
+
+ /// True if this comment AST node contains only whitespace.
+ mutable unsigned IsWhitespace : 1;
+ };
+ enum { NumTextCommentBits = NumInlineContentCommentBits + 2 };
+
+ class InlineCommandCommentBitfields {
+ friend class InlineCommandComment;
+
+ unsigned : NumInlineContentCommentBits;
+
+ unsigned RenderKind : 2;
+ };
+ enum { NumInlineCommandCommentBits = NumInlineContentCommentBits + 1 };
+
+ class HTMLStartTagCommentBitfields {
+ friend class HTMLStartTagComment;
+
+ unsigned : NumInlineContentCommentBits;
+
+ /// True if this tag is self-closing (e. g., <br />). This is based on tag
+ /// spelling in comment (plain <br> would not set this flag).
+ unsigned IsSelfClosing : 1;
+ };
+ enum { NumHTMLStartTagCommentBits = NumInlineContentCommentBits + 1 };
+
+ class ParagraphCommentBitfields {
+ friend class ParagraphComment;
+
+ unsigned : NumCommentBits;
+
+ /// True if \c IsWhitespace field contains a valid value.
+ mutable unsigned IsWhitespaceValid : 1;
+
+ /// True if this comment AST node contains only whitespace.
+ mutable unsigned IsWhitespace : 1;
+ };
+ enum { NumParagraphCommentBits = NumCommentBits + 2 };
+
+ class ParamCommandCommentBitfields {
+ friend class ParamCommandComment;
+
+ unsigned : NumCommentBits;
+
+ /// Parameter passing direction, see ParamCommandComment::PassDirection.
+ unsigned Direction : 2;
+
+ /// True if direction was specified explicitly in the comment.
+ unsigned IsDirectionExplicit : 1;
+ };
+ enum { NumParamCommandCommentBits = 11 };
+
+ union {
+ CommentBitfields CommentBits;
+ InlineContentCommentBitfields InlineContentCommentBits;
+ TextCommentBitfields TextCommentBits;
+ InlineCommandCommentBitfields InlineCommandCommentBits;
+ HTMLStartTagCommentBitfields HTMLStartTagCommentBits;
+ ParagraphCommentBitfields ParagraphCommentBits;
+ ParamCommandCommentBitfields ParamCommandCommentBits;
+ };
+
+ void setSourceRange(SourceRange SR) {
+ Range = SR;
+ }
+
+ void setLocation(SourceLocation L) {
+ Loc = L;
+ }
+
+public:
+ enum CommentKind {
+ NoCommentKind = 0,
+#define COMMENT(CLASS, PARENT) CLASS##Kind,
+#define COMMENT_RANGE(BASE, FIRST, LAST) \
+ First##BASE##Constant=FIRST##Kind, Last##BASE##Constant=LAST##Kind,
+#define LAST_COMMENT_RANGE(BASE, FIRST, LAST) \
+ First##BASE##Constant=FIRST##Kind, Last##BASE##Constant=LAST##Kind
+#define ABSTRACT_COMMENT(COMMENT)
+#include "clang/AST/CommentNodes.inc"
+ };
+
+ Comment(CommentKind K,
+ SourceLocation LocBegin,
+ SourceLocation LocEnd) :
+ Loc(LocBegin), Range(SourceRange(LocBegin, LocEnd)) {
+ CommentBits.Kind = K;
+ }
+
+ CommentKind getCommentKind() const {
+ return static_cast<CommentKind>(CommentBits.Kind);
+ }
+
+ const char *getCommentKindName() const;
+
+ LLVM_ATTRIBUTE_USED void dump() const;
+ LLVM_ATTRIBUTE_USED void dump(SourceManager &SM) const;
+ void dump(llvm::raw_ostream &OS, SourceManager *SM) const;
+
+ static bool classof(const Comment *) { return true; }
+
+ SourceRange getSourceRange() const LLVM_READONLY { return Range; }
+
+ SourceLocation getLocStart() const LLVM_READONLY {
+ return Range.getBegin();
+ }
+
+ SourceLocation getLocEnd() const LLVM_READONLY {
+ return Range.getEnd();
+ }
+
+ SourceLocation getLocation() const LLVM_READONLY { return Loc; }
+
+ typedef Comment * const *child_iterator;
+
+ child_iterator child_begin() const;
+ child_iterator child_end() const;
+
+ // TODO: const child iterator
+
+ unsigned child_count() const {
+ return child_end() - child_begin();
+ }
+};
+
+/// Inline content (contained within a block).
+/// Abstract class.
+class InlineContentComment : public Comment {
+protected:
+ InlineContentComment(CommentKind K,
+ SourceLocation LocBegin,
+ SourceLocation LocEnd) :
+ Comment(K, LocBegin, LocEnd) {
+ InlineContentCommentBits.HasTrailingNewline = 0;
+ }
+
+public:
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() >= FirstInlineContentCommentConstant &&
+ C->getCommentKind() <= LastInlineContentCommentConstant;
+ }
+
+ static bool classof(const InlineContentComment *) { return true; }
+
+ void addTrailingNewline() {
+ InlineContentCommentBits.HasTrailingNewline = 1;
+ }
+
+ bool hasTrailingNewline() const {
+ return InlineContentCommentBits.HasTrailingNewline;
+ }
+};
+
+/// Plain text.
+class TextComment : public InlineContentComment {
+ StringRef Text;
+
+public:
+ TextComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Text) :
+ InlineContentComment(TextCommentKind, LocBegin, LocEnd),
+ Text(Text) {
+ TextCommentBits.IsWhitespaceValid = false;
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == TextCommentKind;
+ }
+
+ static bool classof(const TextComment *) { return true; }
+
+ child_iterator child_begin() const { return NULL; }
+
+ child_iterator child_end() const { return NULL; }
+
+ StringRef getText() const LLVM_READONLY { return Text; }
+
+ bool isWhitespace() const {
+ if (TextCommentBits.IsWhitespaceValid)
+ return TextCommentBits.IsWhitespace;
+
+ TextCommentBits.IsWhitespace = isWhitespaceNoCache();
+ TextCommentBits.IsWhitespaceValid = true;
+ return TextCommentBits.IsWhitespace;
+ }
+
+private:
+ bool isWhitespaceNoCache() const;
+};
+
+/// A command with word-like arguments that is considered inline content.
+class InlineCommandComment : public InlineContentComment {
+public:
+ struct Argument {
+ SourceRange Range;
+ StringRef Text;
+
+ Argument(SourceRange Range, StringRef Text) : Range(Range), Text(Text) { }
+ };
+
+ /// The most appropriate rendering mode for this command, chosen on command
+ /// semantics in Doxygen.
+ enum RenderKind {
+ RenderNormal,
+ RenderBold,
+ RenderMonospaced,
+ RenderEmphasized
+ };
+
+protected:
+ /// Command name.
+ StringRef Name;
+
+ /// Command arguments.
+ llvm::ArrayRef<Argument> Args;
+
+public:
+ InlineCommandComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name,
+ RenderKind RK,
+ llvm::ArrayRef<Argument> Args) :
+ InlineContentComment(InlineCommandCommentKind, LocBegin, LocEnd),
+ Name(Name), Args(Args) {
+ InlineCommandCommentBits.RenderKind = RK;
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == InlineCommandCommentKind;
+ }
+
+ static bool classof(const InlineCommandComment *) { return true; }
+
+ child_iterator child_begin() const { return NULL; }
+
+ child_iterator child_end() const { return NULL; }
+
+ StringRef getCommandName() const {
+ return Name;
+ }
+
+ SourceRange getCommandNameRange() const {
+ return SourceRange(getLocStart().getLocWithOffset(-1),
+ getLocEnd());
+ }
+
+ RenderKind getRenderKind() const {
+ return static_cast<RenderKind>(InlineCommandCommentBits.RenderKind);
+ }
+
+ unsigned getNumArgs() const {
+ return Args.size();
+ }
+
+ StringRef getArgText(unsigned Idx) const {
+ return Args[Idx].Text;
+ }
+
+ SourceRange getArgRange(unsigned Idx) const {
+ return Args[Idx].Range;
+ }
+};
+
+/// Abstract class for opening and closing HTML tags. HTML tags are always
+/// treated as inline content (regardless HTML semantics); opening and closing
+/// tags are not matched.
+class HTMLTagComment : public InlineContentComment {
+protected:
+ StringRef TagName;
+ SourceRange TagNameRange;
+
+ HTMLTagComment(CommentKind K,
+ SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef TagName,
+ SourceLocation TagNameBegin,
+ SourceLocation TagNameEnd) :
+ InlineContentComment(K, LocBegin, LocEnd),
+ TagName(TagName),
+ TagNameRange(TagNameBegin, TagNameEnd) {
+ setLocation(TagNameBegin);
+ }
+
+public:
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() >= FirstHTMLTagCommentConstant &&
+ C->getCommentKind() <= LastHTMLTagCommentConstant;
+ }
+
+ static bool classof(const HTMLTagComment *) { return true; }
+
+ StringRef getTagName() const LLVM_READONLY { return TagName; }
+
+ SourceRange getTagNameSourceRange() const LLVM_READONLY {
+ SourceLocation L = getLocation();
+ return SourceRange(L.getLocWithOffset(1),
+ L.getLocWithOffset(1 + TagName.size()));
+ }
+};
+
+/// An opening HTML tag with attributes.
+class HTMLStartTagComment : public HTMLTagComment {
+public:
+ class Attribute {
+ public:
+ SourceLocation NameLocBegin;
+ StringRef Name;
+
+ SourceLocation EqualsLoc;
+
+ SourceRange ValueRange;
+ StringRef Value;
+
+ Attribute() { }
+
+ Attribute(SourceLocation NameLocBegin, StringRef Name) :
+ NameLocBegin(NameLocBegin), Name(Name),
+ EqualsLoc(SourceLocation()),
+ ValueRange(SourceRange()), Value(StringRef())
+ { }
+
+ Attribute(SourceLocation NameLocBegin, StringRef Name,
+ SourceLocation EqualsLoc,
+ SourceRange ValueRange, StringRef Value) :
+ NameLocBegin(NameLocBegin), Name(Name),
+ EqualsLoc(EqualsLoc),
+ ValueRange(ValueRange), Value(Value)
+ { }
+
+ SourceLocation getNameLocEnd() const {
+ return NameLocBegin.getLocWithOffset(Name.size());
+ }
+
+ SourceRange getNameRange() const {
+ return SourceRange(NameLocBegin, getNameLocEnd());
+ }
+ };
+
+private:
+ ArrayRef<Attribute> Attributes;
+
+public:
+ HTMLStartTagComment(SourceLocation LocBegin,
+ StringRef TagName) :
+ HTMLTagComment(HTMLStartTagCommentKind,
+ LocBegin, LocBegin.getLocWithOffset(1 + TagName.size()),
+ TagName,
+ LocBegin.getLocWithOffset(1),
+ LocBegin.getLocWithOffset(1 + TagName.size())) {
+ HTMLStartTagCommentBits.IsSelfClosing = false;
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == HTMLStartTagCommentKind;
+ }
+
+ static bool classof(const HTMLStartTagComment *) { return true; }
+
+ child_iterator child_begin() const { return NULL; }
+
+ child_iterator child_end() const { return NULL; }
+
+ unsigned getNumAttrs() const {
+ return Attributes.size();
+ }
+
+ const Attribute &getAttr(unsigned Idx) const {
+ return Attributes[Idx];
+ }
+
+ void setAttrs(ArrayRef<Attribute> Attrs) {
+ Attributes = Attrs;
+ if (!Attrs.empty()) {
+ const Attribute &Attr = Attrs.back();
+ SourceLocation L = Attr.ValueRange.getEnd();
+ if (L.isValid())
+ Range.setEnd(L);
+ else {
+ Range.setEnd(Attr.getNameLocEnd());
+ }
+ }
+ }
+
+ void setGreaterLoc(SourceLocation GreaterLoc) {
+ Range.setEnd(GreaterLoc);
+ }
+
+ bool isSelfClosing() const {
+ return HTMLStartTagCommentBits.IsSelfClosing;
+ }
+
+ void setSelfClosing() {
+ HTMLStartTagCommentBits.IsSelfClosing = true;
+ }
+};
+
+/// A closing HTML tag.
+class HTMLEndTagComment : public HTMLTagComment {
+public:
+ HTMLEndTagComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef TagName) :
+ HTMLTagComment(HTMLEndTagCommentKind,
+ LocBegin, LocEnd,
+ TagName,
+ LocBegin.getLocWithOffset(2),
+ LocBegin.getLocWithOffset(2 + TagName.size()))
+ { }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == HTMLEndTagCommentKind;
+ }
+
+ static bool classof(const HTMLEndTagComment *) { return true; }
+
+ child_iterator child_begin() const { return NULL; }
+
+ child_iterator child_end() const { return NULL; }
+};
+
+/// Block content (contains inline content).
+/// Abstract class.
+class BlockContentComment : public Comment {
+protected:
+ BlockContentComment(CommentKind K,
+ SourceLocation LocBegin,
+ SourceLocation LocEnd) :
+ Comment(K, LocBegin, LocEnd)
+ { }
+
+public:
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() >= FirstBlockContentCommentConstant &&
+ C->getCommentKind() <= LastBlockContentCommentConstant;
+ }
+
+ static bool classof(const BlockContentComment *) { return true; }
+};
+
+/// A single paragraph that contains inline content.
+class ParagraphComment : public BlockContentComment {
+ llvm::ArrayRef<InlineContentComment *> Content;
+
+public:
+ ParagraphComment(llvm::ArrayRef<InlineContentComment *> Content) :
+ BlockContentComment(ParagraphCommentKind,
+ SourceLocation(),
+ SourceLocation()),
+ Content(Content) {
+ if (Content.empty()) {
+ ParagraphCommentBits.IsWhitespace = true;
+ ParagraphCommentBits.IsWhitespaceValid = true;
+ return;
+ }
+
+ ParagraphCommentBits.IsWhitespaceValid = false;
+
+ setSourceRange(SourceRange(Content.front()->getLocStart(),
+ Content.back()->getLocEnd()));
+ setLocation(Content.front()->getLocStart());
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == ParagraphCommentKind;
+ }
+
+ static bool classof(const ParagraphComment *) { return true; }
+
+ child_iterator child_begin() const {
+ return reinterpret_cast<child_iterator>(Content.begin());
+ }
+
+ child_iterator child_end() const {
+ return reinterpret_cast<child_iterator>(Content.end());
+ }
+
+ bool isWhitespace() const {
+ if (ParagraphCommentBits.IsWhitespaceValid)
+ return ParagraphCommentBits.IsWhitespace;
+
+ ParagraphCommentBits.IsWhitespace = isWhitespaceNoCache();
+ ParagraphCommentBits.IsWhitespaceValid = true;
+ return ParagraphCommentBits.IsWhitespace;
+ }
+
+private:
+ bool isWhitespaceNoCache() const;
+};
+
+/// A command that has zero or more word-like arguments (number of word-like
+/// arguments depends on command name) and a paragraph as an argument
+/// (e. g., \\brief).
+class BlockCommandComment : public BlockContentComment {
+public:
+ struct Argument {
+ SourceRange Range;
+ StringRef Text;
+
+ Argument() { }
+ Argument(SourceRange Range, StringRef Text) : Range(Range), Text(Text) { }
+ };
+
+protected:
+ /// Command name.
+ StringRef Name;
+
+ /// Word-like arguments.
+ llvm::ArrayRef<Argument> Args;
+
+ /// Paragraph argument.
+ ParagraphComment *Paragraph;
+
+ BlockCommandComment(CommentKind K,
+ SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) :
+ BlockContentComment(K, LocBegin, LocEnd),
+ Name(Name),
+ Paragraph(NULL) {
+ setLocation(getCommandNameRange().getBegin());
+ }
+
+public:
+ BlockCommandComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) :
+ BlockContentComment(BlockCommandCommentKind, LocBegin, LocEnd),
+ Name(Name),
+ Paragraph(NULL) {
+ setLocation(getCommandNameRange().getBegin());
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() >= FirstBlockCommandCommentConstant &&
+ C->getCommentKind() <= LastBlockCommandCommentConstant;
+ }
+
+ static bool classof(const BlockCommandComment *) { return true; }
+
+ child_iterator child_begin() const {
+ return reinterpret_cast<child_iterator>(&Paragraph);
+ }
+
+ child_iterator child_end() const {
+ return reinterpret_cast<child_iterator>(&Paragraph + 1);
+ }
+
+ StringRef getCommandName() const {
+ return Name;
+ }
+
+ SourceRange getCommandNameRange() const {
+ return SourceRange(getLocStart().getLocWithOffset(1),
+ getLocStart().getLocWithOffset(1 + Name.size()));
+ }
+
+ unsigned getNumArgs() const {
+ return Args.size();
+ }
+
+ StringRef getArgText(unsigned Idx) const {
+ return Args[Idx].Text;
+ }
+
+ SourceRange getArgRange(unsigned Idx) const {
+ return Args[Idx].Range;
+ }
+
+ void setArgs(llvm::ArrayRef<Argument> A) {
+ Args = A;
+ if (Args.size() > 0) {
+ SourceLocation NewLocEnd = Args.back().Range.getEnd();
+ if (NewLocEnd.isValid())
+ setSourceRange(SourceRange(getLocStart(), NewLocEnd));
+ }
+ }
+
+ ParagraphComment *getParagraph() const LLVM_READONLY {
+ return Paragraph;
+ }
+
+ bool hasNonWhitespaceParagraph() const {
+ return Paragraph && !Paragraph->isWhitespace();
+ }
+
+ void setParagraph(ParagraphComment *PC) {
+ Paragraph = PC;
+ SourceLocation NewLocEnd = PC->getLocEnd();
+ if (NewLocEnd.isValid())
+ setSourceRange(SourceRange(getLocStart(), NewLocEnd));
+ }
+};
+
+/// Doxygen \\param command.
+class ParamCommandComment : public BlockCommandComment {
+private:
+ /// Parameter index in the function declaration.
+ unsigned ParamIndex;
+
+public:
+ enum { InvalidParamIndex = ~0U };
+
+ ParamCommandComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) :
+ BlockCommandComment(ParamCommandCommentKind, LocBegin, LocEnd, Name),
+ ParamIndex(InvalidParamIndex) {
+ ParamCommandCommentBits.Direction = In;
+ ParamCommandCommentBits.IsDirectionExplicit = false;
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == ParamCommandCommentKind;
+ }
+
+ static bool classof(const ParamCommandComment *) { return true; }
+
+ enum PassDirection {
+ In,
+ Out,
+ InOut
+ };
+
+ static const char *getDirectionAsString(PassDirection D);
+
+ PassDirection getDirection() const LLVM_READONLY {
+ return static_cast<PassDirection>(ParamCommandCommentBits.Direction);
+ }
+
+ bool isDirectionExplicit() const LLVM_READONLY {
+ return ParamCommandCommentBits.IsDirectionExplicit;
+ }
+
+ void setDirection(PassDirection Direction, bool Explicit) {
+ ParamCommandCommentBits.Direction = Direction;
+ ParamCommandCommentBits.IsDirectionExplicit = Explicit;
+ }
+
+ bool hasParamName() const {
+ return getNumArgs() > 0;
+ }
+
+ StringRef getParamName() const {
+ return Args[0].Text;
+ }
+
+ SourceRange getParamNameRange() const {
+ return Args[0].Range;
+ }
+
+ bool isParamIndexValid() const LLVM_READONLY {
+ return ParamIndex != InvalidParamIndex;
+ }
+
+ unsigned getParamIndex() const LLVM_READONLY {
+ assert(isParamIndexValid());
+ return ParamIndex;
+ }
+
+ void setParamIndex(unsigned Index) {
+ ParamIndex = Index;
+ assert(isParamIndexValid());
+ }
+};
+
+/// Doxygen \\tparam command, describes a template parameter.
+class TParamCommandComment : public BlockCommandComment {
+private:
+ /// If this template parameter name was resolved (found in template parameter
+ /// list), then this stores a list of position indexes in all template
+ /// parameter lists.
+ ///
+ /// For example:
+ /// \verbatim
+ /// template<typename C, template<typename T> class TT>
+ /// void test(TT<int> aaa);
+ /// \endverbatim
+ /// For C: Position = { 0 }
+ /// For TT: Position = { 1 }
+ /// For T: Position = { 1, 0 }
+ llvm::ArrayRef<unsigned> Position;
+
+public:
+ TParamCommandComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) :
+ BlockCommandComment(TParamCommandCommentKind, LocBegin, LocEnd, Name)
+ { }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == TParamCommandCommentKind;
+ }
+
+ static bool classof(const TParamCommandComment *) { return true; }
+
+ bool hasParamName() const {
+ return getNumArgs() > 0;
+ }
+
+ StringRef getParamName() const {
+ return Args[0].Text;
+ }
+
+ SourceRange getParamNameRange() const {
+ return Args[0].Range;
+ }
+
+ bool isPositionValid() const LLVM_READONLY {
+ return !Position.empty();
+ }
+
+ unsigned getDepth() const {
+ assert(isPositionValid());
+ return Position.size();
+ }
+
+ unsigned getIndex(unsigned Depth) const {
+ assert(isPositionValid());
+ return Position[Depth];
+ }
+
+ void setPosition(ArrayRef<unsigned> NewPosition) {
+ Position = NewPosition;
+ assert(isPositionValid());
+ }
+};
+
+/// A line of text contained in a verbatim block.
+class VerbatimBlockLineComment : public Comment {
+ StringRef Text;
+
+public:
+ VerbatimBlockLineComment(SourceLocation LocBegin,
+ StringRef Text) :
+ Comment(VerbatimBlockLineCommentKind,
+ LocBegin,
+ LocBegin.getLocWithOffset(Text.size())),
+ Text(Text)
+ { }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == VerbatimBlockLineCommentKind;
+ }
+
+ static bool classof(const VerbatimBlockLineComment *) { return true; }
+
+ child_iterator child_begin() const { return NULL; }
+
+ child_iterator child_end() const { return NULL; }
+
+ StringRef getText() const LLVM_READONLY {
+ return Text;
+ }
+};
+
+/// A verbatim block command (e. g., preformatted code). Verbatim block has an
+/// opening and a closing command and contains multiple lines of text
+/// (VerbatimBlockLineComment nodes).
+class VerbatimBlockComment : public BlockCommandComment {
+protected:
+ StringRef CloseName;
+ SourceLocation CloseNameLocBegin;
+ llvm::ArrayRef<VerbatimBlockLineComment *> Lines;
+
+public:
+ VerbatimBlockComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) :
+ BlockCommandComment(VerbatimBlockCommentKind,
+ LocBegin, LocEnd, Name)
+ { }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == VerbatimBlockCommentKind;
+ }
+
+ static bool classof(const VerbatimBlockComment *) { return true; }
+
+ child_iterator child_begin() const {
+ return reinterpret_cast<child_iterator>(Lines.begin());
+ }
+
+ child_iterator child_end() const {
+ return reinterpret_cast<child_iterator>(Lines.end());
+ }
+
+ void setCloseName(StringRef Name, SourceLocation LocBegin) {
+ CloseName = Name;
+ CloseNameLocBegin = LocBegin;
+ }
+
+ void setLines(llvm::ArrayRef<VerbatimBlockLineComment *> L) {
+ Lines = L;
+ }
+
+ StringRef getCloseName() const {
+ return CloseName;
+ }
+
+ unsigned getNumLines() const {
+ return Lines.size();
+ }
+
+ StringRef getText(unsigned LineIdx) const {
+ return Lines[LineIdx]->getText();
+ }
+};
+
+/// A verbatim line command. Verbatim line has an opening command, a single
+/// line of text (up to the newline after the opening command) and has no
+/// closing command.
+class VerbatimLineComment : public BlockCommandComment {
+protected:
+ StringRef Text;
+ SourceLocation TextBegin;
+
+public:
+ VerbatimLineComment(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name,
+ SourceLocation TextBegin,
+ StringRef Text) :
+ BlockCommandComment(VerbatimLineCommentKind,
+ LocBegin, LocEnd,
+ Name),
+ Text(Text),
+ TextBegin(TextBegin)
+ { }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == VerbatimLineCommentKind;
+ }
+
+ static bool classof(const VerbatimLineComment *) { return true; }
+
+ child_iterator child_begin() const { return NULL; }
+
+ child_iterator child_end() const { return NULL; }
+
+ StringRef getText() const {
+ return Text;
+ }
+
+ SourceRange getTextRange() const {
+ return SourceRange(TextBegin, getLocEnd());
+ }
+};
+
+/// Information about the declaration, useful to clients of FullComment.
+struct DeclInfo {
+ /// Declaration the comment is attached to. Should not be NULL.
+ const Decl *ThisDecl;
+
+ /// Parameters that can be referenced by \\param if \c ThisDecl is something
+ /// that we consider a "function".
+ ArrayRef<const ParmVarDecl *> ParamVars;
+
+ /// Function result type if \c ThisDecl is something that we consider
+ /// a "function".
+ QualType ResultType;
+
+ /// Template parameters that can be referenced by \\tparam if \c ThisDecl is
+ /// a template (\c IsTemplateDecl or \c IsTemplatePartialSpecialization is
+ /// true).
+ const TemplateParameterList *TemplateParameters;
+
+ /// A simplified description of \c ThisDecl kind that should be good enough
+ /// for documentation rendering purposes.
+ enum DeclKind {
+ /// Everything else not explicitly mentioned below.
+ OtherKind,
+
+ /// Something that we consider a "function":
+ /// \li function,
+ /// \li function template,
+ /// \li function template specialization,
+ /// \li member function,
+ /// \li member function template,
+ /// \li member function template specialization,
+ /// \li ObjC method.
+ FunctionKind,
+
+ /// Something that we consider a "class":
+ /// \li class/struct,
+ /// \li class template,
+ /// \li class template (partial) specialization.
+ ClassKind,
+
+ /// Something that we consider a "variable":
+ /// \li namespace scope variables;
+ /// \li static and non-static class data members;
+ /// \li enumerators.
+ VariableKind,
+
+ /// A C++ namespace.
+ NamespaceKind,
+
+ /// A C++ typedef-name (a 'typedef' decl specifier or alias-declaration),
+ /// see \c TypedefNameDecl.
+ TypedefKind,
+
+ /// An enumeration or scoped enumeration.
+ EnumKind
+ };
+
+ /// What kind of template specialization \c ThisDecl is.
+ enum TemplateDeclKind {
+ NotTemplate,
+ Template,
+ TemplateSpecialization,
+ TemplatePartialSpecialization
+ };
+
+ /// If false, only \c ThisDecl is valid.
+ unsigned IsFilled : 1;
+
+ /// Simplified kind of \c ThisDecl, see\c DeclKind enum.
+ unsigned Kind : 3;
+
+ /// Is \c ThisDecl a template declaration.
+ unsigned TemplateKind : 2;
+
+ /// Is \c ThisDecl an ObjCMethodDecl.
+ unsigned IsObjCMethod : 1;
+
+ /// Is \c ThisDecl a non-static member function of C++ class or
+ /// instance method of ObjC class.
+ /// Can be true only if \c IsFunctionDecl is true.
+ unsigned IsInstanceMethod : 1;
+
+ /// Is \c ThisDecl a static member function of C++ class or
+ /// class method of ObjC class.
+ /// Can be true only if \c IsFunctionDecl is true.
+ unsigned IsClassMethod : 1;
+
+ void fill();
+
+ DeclKind getKind() const LLVM_READONLY {
+ return static_cast<DeclKind>(Kind);
+ }
+
+ TemplateDeclKind getTemplateKind() const LLVM_READONLY {
+ return static_cast<TemplateDeclKind>(TemplateKind);
+ }
+};
+
+/// A full comment attached to a declaration, contains block content.
+class FullComment : public Comment {
+ llvm::ArrayRef<BlockContentComment *> Blocks;
+
+ DeclInfo *ThisDeclInfo;
+
+public:
+ FullComment(llvm::ArrayRef<BlockContentComment *> Blocks, DeclInfo *D) :
+ Comment(FullCommentKind, SourceLocation(), SourceLocation()),
+ Blocks(Blocks), ThisDeclInfo(D) {
+ if (Blocks.empty())
+ return;
+
+ setSourceRange(SourceRange(Blocks.front()->getLocStart(),
+ Blocks.back()->getLocEnd()));
+ setLocation(Blocks.front()->getLocStart());
+ }
+
+ static bool classof(const Comment *C) {
+ return C->getCommentKind() == FullCommentKind;
+ }
+
+ static bool classof(const FullComment *) { return true; }
+
+ child_iterator child_begin() const {
+ return reinterpret_cast<child_iterator>(Blocks.begin());
+ }
+
+ child_iterator child_end() const {
+ return reinterpret_cast<child_iterator>(Blocks.end());
+ }
+
+ const Decl *getDecl() const LLVM_READONLY {
+ return ThisDeclInfo->ThisDecl;
+ }
+
+ const DeclInfo *getDeclInfo() const LLVM_READONLY {
+ if (!ThisDeclInfo->IsFilled)
+ ThisDeclInfo->fill();
+ return ThisDeclInfo;
+ }
+};
+
+} // end namespace comments
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentBriefParser.h b/include/clang/AST/CommentBriefParser.h
new file mode 100644
index 0000000..003c337
--- /dev/null
+++ b/include/clang/AST/CommentBriefParser.h
@@ -0,0 +1,56 @@
+//===--- CommentBriefParser.h - Dumb comment parser -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a very simple Doxygen comment parser.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LLVM_CLANG_AST_BRIEF_COMMENT_PARSER_H
+#define LLVM_CLANG_AST_BRIEF_COMMENT_PARSER_H
+
+#include "clang/AST/CommentLexer.h"
+
+namespace clang {
+namespace comments {
+
+/// A very simple comment parser that extracts "a brief description".
+///
+/// Due to a variety of comment styles, it considers the following as "a brief
+/// description", in order of priority:
+/// \li a \\brief or \\short command,
+/// \li the first paragraph,
+/// \li a \\result or \\return or \\returns paragraph.
+class BriefParser {
+ Lexer &L;
+
+ const CommandTraits &Traits;
+
+ /// Current lookahead token.
+ Token Tok;
+
+ SourceLocation ConsumeToken() {
+ SourceLocation Loc = Tok.getLocation();
+ L.lex(Tok);
+ return Loc;
+ }
+
+public:
+ BriefParser(Lexer &L, const CommandTraits &Traits);
+
+ /// Return \\brief paragraph, if it exists; otherwise return the first
+ /// paragraph.
+ std::string Parse();
+};
+
+} // end namespace comments
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentCommandTraits.h b/include/clang/AST/CommentCommandTraits.h
new file mode 100644
index 0000000..f188375
--- /dev/null
+++ b/include/clang/AST/CommentCommandTraits.h
@@ -0,0 +1,156 @@
+//===--- CommentCommandTraits.h - Comment command properties ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the class that provides information about comment
+// commands.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LLVM_CLANG_AST_COMMENT_COMMAND_TRAITS_H
+#define LLVM_CLANG_AST_COMMENT_COMMAND_TRAITS_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSwitch.h"
+
+namespace clang {
+namespace comments {
+
+/// This class provides informaiton about commands that can be used
+/// in comments.
+class CommandTraits {
+public:
+ CommandTraits() { }
+
+ /// \brief Check if a given command is a verbatim-like block command.
+ ///
+ /// A verbatim-like block command eats every character (except line starting
+ /// decorations) until matching end command is seen or comment end is hit.
+ ///
+ /// \param BeginName name of the command that starts the verbatim block.
+ /// \param [out] EndName name of the command that ends the verbatim block.
+ ///
+ /// \returns true if a given command is a verbatim block command.
+ bool isVerbatimBlockCommand(StringRef StartName, StringRef &EndName) const;
+
+ /// \brief Register a new verbatim block command.
+ void addVerbatimBlockCommand(StringRef BeginName, StringRef EndName);
+
+ /// \brief Check if a given command is a verbatim line command.
+ ///
+ /// A verbatim-like line command eats everything until a newline is seen or
+ /// comment end is hit.
+ bool isVerbatimLineCommand(StringRef Name) const;
+
+ /// \brief Check if a given command is a command that contains a declaration
+ /// for the entity being documented.
+ ///
+ /// For example:
+ /// \code
+ /// \fn void f(int a);
+ /// \endcode
+ bool isDeclarationCommand(StringRef Name) const;
+
+ /// \brief Register a new verbatim line command.
+ void addVerbatimLineCommand(StringRef Name);
+
+ /// \brief Check if a given command is a block command (of any kind).
+ bool isBlockCommand(StringRef Name) const;
+
+ /// \brief Check if a given command is introducing documentation for
+ /// a function parameter (\\param or an alias).
+ bool isParamCommand(StringRef Name) const;
+
+ /// \brief Check if a given command is introducing documentation for
+ /// a template parameter (\\tparam or an alias).
+ bool isTParamCommand(StringRef Name) const;
+
+ /// \brief Check if a given command is introducing a brief documentation
+ /// paragraph (\\brief or an alias).
+ bool isBriefCommand(StringRef Name) const;
+
+ /// \brief Check if a given command is \\brief or an alias.
+ bool isReturnsCommand(StringRef Name) const;
+
+ /// \returns the number of word-like arguments for a given block command,
+ /// except for \\param and \\tparam commands -- these have special argument
+ /// parsers.
+ unsigned getBlockCommandNumArgs(StringRef Name) const;
+
+ /// \brief Check if a given command is a inline command (of any kind).
+ bool isInlineCommand(StringRef Name) const;
+
+private:
+ struct VerbatimBlockCommand {
+ StringRef BeginName;
+ StringRef EndName;
+ };
+
+ typedef SmallVector<VerbatimBlockCommand, 4> VerbatimBlockCommandVector;
+
+ /// Registered additional verbatim-like block commands.
+ VerbatimBlockCommandVector VerbatimBlockCommands;
+
+ struct VerbatimLineCommand {
+ StringRef Name;
+ };
+
+ typedef SmallVector<VerbatimLineCommand, 4> VerbatimLineCommandVector;
+
+ /// Registered verbatim-like line commands.
+ VerbatimLineCommandVector VerbatimLineCommands;
+};
+
+inline bool CommandTraits::isBlockCommand(StringRef Name) const {
+ return isBriefCommand(Name) || isReturnsCommand(Name) ||
+ isParamCommand(Name) || isTParamCommand(Name) ||
+ llvm::StringSwitch<bool>(Name)
+ .Case("author", true)
+ .Case("authors", true)
+ .Case("pre", true)
+ .Case("post", true)
+ .Default(false);
+}
+
+inline bool CommandTraits::isParamCommand(StringRef Name) const {
+ return Name == "param";
+}
+
+inline bool CommandTraits::isTParamCommand(StringRef Name) const {
+ return Name == "tparam" || // Doxygen
+ Name == "templatefield"; // HeaderDoc
+}
+
+inline bool CommandTraits::isBriefCommand(StringRef Name) const {
+ return Name == "brief" || Name == "short";
+}
+
+inline bool CommandTraits::isReturnsCommand(StringRef Name) const {
+ return Name == "returns" || Name == "return" || Name == "result";
+}
+
+inline unsigned CommandTraits::getBlockCommandNumArgs(StringRef Name) const {
+ return 0;
+}
+
+inline bool CommandTraits::isInlineCommand(StringRef Name) const {
+ return llvm::StringSwitch<bool>(Name)
+ .Case("b", true)
+ .Cases("c", "p", true)
+ .Cases("a", "e", "em", true)
+ .Default(false);
+}
+
+} // end namespace comments
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentDiagnostic.h b/include/clang/AST/CommentDiagnostic.h
new file mode 100644
index 0000000..6e89410
--- /dev/null
+++ b/include/clang/AST/CommentDiagnostic.h
@@ -0,0 +1,29 @@
+//===--- CommentDiagnostic.h - Diagnostics for the AST library --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_COMMENTDIAGNOSTIC_H
+#define LLVM_CLANG_COMMENTDIAGNOSTIC_H
+
+#include "clang/Basic/Diagnostic.h"
+
+namespace clang {
+ namespace diag {
+ enum {
+#define DIAG(ENUM,FLAGS,DEFAULT_MAPPING,DESC,GROUP,\
+ SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER,CATEGORY) ENUM,
+#define COMMENTSTART
+#include "clang/Basic/DiagnosticCommentKinds.inc"
+#undef DIAG
+ NUM_BUILTIN_COMMENT_DIAGNOSTICS
+ };
+ } // end namespace diag
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentLexer.h b/include/clang/AST/CommentLexer.h
new file mode 100644
index 0000000..7a24b11
--- /dev/null
+++ b/include/clang/AST/CommentLexer.h
@@ -0,0 +1,353 @@
+//===--- CommentLexer.h - Lexer for structured comments ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines lexer for structured comments and supporting token class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_COMMENT_LEXER_H
+#define LLVM_CLANG_AST_COMMENT_LEXER_H
+
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace comments {
+
+class Lexer;
+class TextTokenRetokenizer;
+class CommandTraits;
+
+namespace tok {
+enum TokenKind {
+ eof,
+ newline,
+ text,
+ command,
+ verbatim_block_begin,
+ verbatim_block_line,
+ verbatim_block_end,
+ verbatim_line_name,
+ verbatim_line_text,
+ html_start_tag, // <tag
+ html_ident, // attr
+ html_equals, // =
+ html_quoted_string, // "blah\"blah" or 'blah\'blah'
+ html_greater, // >
+ html_slash_greater, // />
+ html_end_tag // </tag
+};
+} // end namespace tok
+
+class CommentOptions {
+public:
+ bool Markdown;
+};
+
+/// \brief Comment token.
+class Token {
+ friend class Lexer;
+ friend class TextTokenRetokenizer;
+
+ /// The location of the token.
+ SourceLocation Loc;
+
+ /// The actual kind of the token.
+ tok::TokenKind Kind;
+
+ /// Length of the token spelling in comment. Can be 0 for synthenized
+ /// tokens.
+ unsigned Length;
+
+ /// Contains text value associated with a token.
+ const char *TextPtr1;
+ unsigned TextLen1;
+
+public:
+ SourceLocation getLocation() const LLVM_READONLY { return Loc; }
+ void setLocation(SourceLocation SL) { Loc = SL; }
+
+ SourceLocation getEndLocation() const LLVM_READONLY {
+ if (Length == 0 || Length == 1)
+ return Loc;
+ return Loc.getLocWithOffset(Length - 1);
+ }
+
+ tok::TokenKind getKind() const LLVM_READONLY { return Kind; }
+ void setKind(tok::TokenKind K) { Kind = K; }
+
+ bool is(tok::TokenKind K) const LLVM_READONLY { return Kind == K; }
+ bool isNot(tok::TokenKind K) const LLVM_READONLY { return Kind != K; }
+
+ unsigned getLength() const LLVM_READONLY { return Length; }
+ void setLength(unsigned L) { Length = L; }
+
+ StringRef getText() const LLVM_READONLY {
+ assert(is(tok::text));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setText(StringRef Text) {
+ assert(is(tok::text));
+ TextPtr1 = Text.data();
+ TextLen1 = Text.size();
+ }
+
+ StringRef getCommandName() const LLVM_READONLY {
+ assert(is(tok::command));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setCommandName(StringRef Name) {
+ assert(is(tok::command));
+ TextPtr1 = Name.data();
+ TextLen1 = Name.size();
+ }
+
+ StringRef getVerbatimBlockName() const LLVM_READONLY {
+ assert(is(tok::verbatim_block_begin) || is(tok::verbatim_block_end));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setVerbatimBlockName(StringRef Name) {
+ assert(is(tok::verbatim_block_begin) || is(tok::verbatim_block_end));
+ TextPtr1 = Name.data();
+ TextLen1 = Name.size();
+ }
+
+ StringRef getVerbatimBlockText() const LLVM_READONLY {
+ assert(is(tok::verbatim_block_line));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setVerbatimBlockText(StringRef Text) {
+ assert(is(tok::verbatim_block_line));
+ TextPtr1 = Text.data();
+ TextLen1 = Text.size();
+ }
+
+ /// Returns the name of verbatim line command.
+ StringRef getVerbatimLineName() const LLVM_READONLY {
+ assert(is(tok::verbatim_line_name));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setVerbatimLineName(StringRef Name) {
+ assert(is(tok::verbatim_line_name));
+ TextPtr1 = Name.data();
+ TextLen1 = Name.size();
+ }
+
+ StringRef getVerbatimLineText() const LLVM_READONLY {
+ assert(is(tok::verbatim_line_text));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setVerbatimLineText(StringRef Text) {
+ assert(is(tok::verbatim_line_text));
+ TextPtr1 = Text.data();
+ TextLen1 = Text.size();
+ }
+
+ StringRef getHTMLTagStartName() const LLVM_READONLY {
+ assert(is(tok::html_start_tag));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setHTMLTagStartName(StringRef Name) {
+ assert(is(tok::html_start_tag));
+ TextPtr1 = Name.data();
+ TextLen1 = Name.size();
+ }
+
+ StringRef getHTMLIdent() const LLVM_READONLY {
+ assert(is(tok::html_ident));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setHTMLIdent(StringRef Name) {
+ assert(is(tok::html_ident));
+ TextPtr1 = Name.data();
+ TextLen1 = Name.size();
+ }
+
+ StringRef getHTMLQuotedString() const LLVM_READONLY {
+ assert(is(tok::html_quoted_string));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setHTMLQuotedString(StringRef Str) {
+ assert(is(tok::html_quoted_string));
+ TextPtr1 = Str.data();
+ TextLen1 = Str.size();
+ }
+
+ StringRef getHTMLTagEndName() const LLVM_READONLY {
+ assert(is(tok::html_end_tag));
+ return StringRef(TextPtr1, TextLen1);
+ }
+
+ void setHTMLTagEndName(StringRef Name) {
+ assert(is(tok::html_end_tag));
+ TextPtr1 = Name.data();
+ TextLen1 = Name.size();
+ }
+
+ void dump(const Lexer &L, const SourceManager &SM) const;
+};
+
+/// \brief Comment lexer.
+class Lexer {
+private:
+ Lexer(const Lexer&); // DO NOT IMPLEMENT
+ void operator=(const Lexer&); // DO NOT IMPLEMENT
+
+ /// Allocator for strings that are semantic values of tokens and have to be
+ /// computed (for example, resolved decimal character references).
+ llvm::BumpPtrAllocator &Allocator;
+
+ const CommandTraits &Traits;
+
+ const char *const BufferStart;
+ const char *const BufferEnd;
+ SourceLocation FileLoc;
+ CommentOptions CommOpts;
+
+ const char *BufferPtr;
+
+ /// One past end pointer for the current comment. For BCPL comments points
+ /// to newline or BufferEnd, for C comments points to star in '*/'.
+ const char *CommentEnd;
+
+ enum LexerCommentState {
+ LCS_BeforeComment,
+ LCS_InsideBCPLComment,
+ LCS_InsideCComment,
+ LCS_BetweenComments
+ };
+
+ /// Low-level lexer state, track if we are inside or outside of comment.
+ LexerCommentState CommentState;
+
+ enum LexerState {
+ /// Lexing normal comment text
+ LS_Normal,
+
+ /// Finished lexing verbatim block beginning command, will lex first body
+ /// line.
+ LS_VerbatimBlockFirstLine,
+
+ /// Lexing verbatim block body line-by-line, skipping line-starting
+ /// decorations.
+ LS_VerbatimBlockBody,
+
+ /// Finished lexing verbatim line beginning command, will lex text (one
+ /// line).
+ LS_VerbatimLineText,
+
+ /// Finished lexing \verbatim <TAG \endverbatim part, lexing tag attributes.
+ LS_HTMLStartTag,
+
+ /// Finished lexing \verbatim </TAG \endverbatim part, lexing '>'.
+ LS_HTMLEndTag
+ };
+
+ /// Current lexing mode.
+ LexerState State;
+
+ /// If State is LS_VerbatimBlock, contains the name of verbatim end
+ /// command, including command marker.
+ SmallString<16> VerbatimBlockEndCommandName;
+
+ /// Given a character reference name (e.g., "lt"), return the character that
+ /// it stands for (e.g., "<").
+ StringRef resolveHTMLNamedCharacterReference(StringRef Name) const;
+
+ /// Given a Unicode codepoint as base-10 integer, return the character.
+ StringRef resolveHTMLDecimalCharacterReference(StringRef Name) const;
+
+ /// Given a Unicode codepoint as base-16 integer, return the character.
+ StringRef resolveHTMLHexCharacterReference(StringRef Name) const;
+
+ void formTokenWithChars(Token &Result, const char *TokEnd,
+ tok::TokenKind Kind) {
+ const unsigned TokLen = TokEnd - BufferPtr;
+ Result.setLocation(getSourceLocation(BufferPtr));
+ Result.setKind(Kind);
+ Result.setLength(TokLen);
+#ifndef NDEBUG
+ Result.TextPtr1 = "<UNSET>";
+ Result.TextLen1 = 7;
+#endif
+ BufferPtr = TokEnd;
+ }
+
+ void formTextToken(Token &Result, const char *TokEnd) {
+ StringRef Text(BufferPtr, TokEnd - BufferPtr);
+ formTokenWithChars(Result, TokEnd, tok::text);
+ Result.setText(Text);
+ }
+
+ SourceLocation getSourceLocation(const char *Loc) const {
+ assert(Loc >= BufferStart && Loc <= BufferEnd &&
+ "Location out of range for this buffer!");
+
+ const unsigned CharNo = Loc - BufferStart;
+ return FileLoc.getLocWithOffset(CharNo);
+ }
+
+ /// Eat string matching regexp \code \s*\* \endcode.
+ void skipLineStartingDecorations();
+
+ /// Lex stuff inside comments. CommentEnd should be set correctly.
+ void lexCommentText(Token &T);
+
+ void setupAndLexVerbatimBlock(Token &T,
+ const char *TextBegin,
+ char Marker, StringRef EndName);
+
+ void lexVerbatimBlockFirstLine(Token &T);
+
+ void lexVerbatimBlockBody(Token &T);
+
+ void setupAndLexVerbatimLine(Token &T, const char *TextBegin);
+
+ void lexVerbatimLineText(Token &T);
+
+ void lexHTMLCharacterReference(Token &T);
+
+ void setupAndLexHTMLStartTag(Token &T);
+
+ void lexHTMLStartTag(Token &T);
+
+ void setupAndLexHTMLEndTag(Token &T);
+
+ void lexHTMLEndTag(Token &T);
+
+public:
+ Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits,
+ SourceLocation FileLoc, const CommentOptions &CommOpts,
+ const char *BufferStart, const char *BufferEnd);
+
+ void lex(Token &T);
+
+ StringRef getSpelling(const Token &Tok,
+ const SourceManager &SourceMgr,
+ bool *Invalid = NULL) const;
+};
+
+} // end namespace comments
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentParser.h b/include/clang/AST/CommentParser.h
new file mode 100644
index 0000000..0390799
--- /dev/null
+++ b/include/clang/AST/CommentParser.h
@@ -0,0 +1,124 @@
+//===--- CommentParser.h - Doxygen comment parser ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Doxygen comment parser.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_COMMENT_PARSER_H
+#define LLVM_CLANG_AST_COMMENT_PARSER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/CommentSema.h"
+#include "llvm/Support/Allocator.h"
+
+namespace clang {
+class SourceManager;
+
+namespace comments {
+class CommandTraits;
+
+/// Doxygen comment parser.
+class Parser {
+ Parser(const Parser&); // DO NOT IMPLEMENT
+ void operator=(const Parser&); // DO NOT IMPLEMENT
+
+ friend class TextTokenRetokenizer;
+
+ Lexer &L;
+
+ Sema &S;
+
+ /// Allocator for anything that goes into AST nodes.
+ llvm::BumpPtrAllocator &Allocator;
+
+ /// Source manager for the comment being parsed.
+ const SourceManager &SourceMgr;
+
+ DiagnosticsEngine &Diags;
+
+ DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
+ return Diags.Report(Loc, DiagID);
+ }
+
+ const CommandTraits &Traits;
+
+ /// Current lookahead token. We can safely assume that all tokens are from
+ /// a single source file.
+ Token Tok;
+
+ /// A stack of additional lookahead tokens.
+ SmallVector<Token, 8> MoreLATokens;
+
+ void consumeToken() {
+ if (MoreLATokens.empty())
+ L.lex(Tok);
+ else {
+ Tok = MoreLATokens.back();
+ MoreLATokens.pop_back();
+ }
+ }
+
+ void putBack(const Token &OldTok) {
+ MoreLATokens.push_back(Tok);
+ Tok = OldTok;
+ }
+
+ void putBack(ArrayRef<Token> Toks) {
+ if (Toks.empty())
+ return;
+
+ MoreLATokens.push_back(Tok);
+ for (const Token *I = &Toks.back(),
+ *B = &Toks.front();
+ I != B; --I) {
+ MoreLATokens.push_back(*I);
+ }
+
+ Tok = Toks[0];
+ }
+
+public:
+ Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
+ const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
+ const CommandTraits &Traits);
+
+ /// Parse arguments for \\param command.
+ void parseParamCommandArgs(ParamCommandComment *PC,
+ TextTokenRetokenizer &Retokenizer);
+
+ /// Parse arguments for \\tparam command.
+ void parseTParamCommandArgs(TParamCommandComment *TPC,
+ TextTokenRetokenizer &Retokenizer);
+
+ void parseBlockCommandArgs(BlockCommandComment *BC,
+ TextTokenRetokenizer &Retokenizer,
+ unsigned NumArgs);
+
+ BlockCommandComment *parseBlockCommand();
+ InlineCommandComment *parseInlineCommand();
+
+ HTMLStartTagComment *parseHTMLStartTag();
+ HTMLEndTagComment *parseHTMLEndTag();
+
+ BlockContentComment *parseParagraphOrBlockCommand();
+
+ VerbatimBlockComment *parseVerbatimBlock();
+ VerbatimLineComment *parseVerbatimLine();
+ BlockContentComment *parseBlockContent();
+ FullComment *parseFullComment();
+};
+
+} // end namespace comments
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentSema.h b/include/clang/AST/CommentSema.h
new file mode 100644
index 0000000..e1756ca
--- /dev/null
+++ b/include/clang/AST/CommentSema.h
@@ -0,0 +1,230 @@
+//===--- CommentSema.h - Doxygen comment semantic analysis ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the semantic analysis class for Doxygen comments.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_COMMENT_SEMA_H
+#define LLVM_CLANG_AST_COMMENT_SEMA_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/AST/Comment.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Allocator.h"
+
+namespace clang {
+class Decl;
+class SourceMgr;
+
+namespace comments {
+class CommandTraits;
+
+class Sema {
+ Sema(const Sema&); // DO NOT IMPLEMENT
+ void operator=(const Sema&); // DO NOT IMPLEMENT
+
+ /// Allocator for AST nodes.
+ llvm::BumpPtrAllocator &Allocator;
+
+ /// Source manager for the comment being parsed.
+ const SourceManager &SourceMgr;
+
+ DiagnosticsEngine &Diags;
+
+ const CommandTraits &Traits;
+
+ /// Information about the declaration this comment is attached to.
+ DeclInfo *ThisDeclInfo;
+
+ /// Comment AST nodes that correspond to \c ParamVars for which we have
+ /// found a \\param command or NULL if no documentation was found so far.
+ ///
+ /// Has correct size and contains valid values if \c DeclInfo->IsFilled is
+ /// true.
+ llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
+
+ /// Comment AST nodes that correspond to parameter names in
+ /// \c TemplateParameters.
+ ///
+ /// Contains a valid value if \c DeclInfo->IsFilled is true.
+ llvm::StringMap<TParamCommandComment *> TemplateParameterDocs;
+
+ /// AST node for the \\brief command and its aliases.
+ const BlockCommandComment *BriefCommand;
+
+ /// AST node for the \\returns command and its aliases.
+ const BlockCommandComment *ReturnsCommand;
+
+ DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
+ return Diags.Report(Loc, DiagID);
+ }
+
+ /// A stack of HTML tags that are currently open (not matched with closing
+ /// tags).
+ SmallVector<HTMLStartTagComment *, 8> HTMLOpenTags;
+
+public:
+ Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
+ DiagnosticsEngine &Diags, const CommandTraits &Traits);
+
+ void setDecl(const Decl *D);
+
+ /// Returns a copy of array, owned by Sema's allocator.
+ template<typename T>
+ ArrayRef<T> copyArray(ArrayRef<T> Source) {
+ size_t Size = Source.size();
+ if (Size != 0) {
+ T *Mem = Allocator.Allocate<T>(Size);
+ std::uninitialized_copy(Source.begin(), Source.end(), Mem);
+ return llvm::makeArrayRef(Mem, Size);
+ } else
+ return llvm::makeArrayRef(static_cast<T *>(NULL), 0);
+ }
+
+ ParagraphComment *actOnParagraphComment(
+ ArrayRef<InlineContentComment *> Content);
+
+ BlockCommandComment *actOnBlockCommandStart(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name);
+
+ void actOnBlockCommandArgs(BlockCommandComment *Command,
+ ArrayRef<BlockCommandComment::Argument> Args);
+
+ void actOnBlockCommandFinish(BlockCommandComment *Command,
+ ParagraphComment *Paragraph);
+
+ ParamCommandComment *actOnParamCommandStart(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name);
+
+ void actOnParamCommandDirectionArg(ParamCommandComment *Command,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg);
+
+ void actOnParamCommandParamNameArg(ParamCommandComment *Command,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg);
+
+ void actOnParamCommandFinish(ParamCommandComment *Command,
+ ParagraphComment *Paragraph);
+
+ TParamCommandComment *actOnTParamCommandStart(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name);
+
+ void actOnTParamCommandParamNameArg(TParamCommandComment *Command,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg);
+
+ void actOnTParamCommandFinish(TParamCommandComment *Command,
+ ParagraphComment *Paragraph);
+
+ InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin,
+ SourceLocation CommandLocEnd,
+ StringRef CommandName);
+
+ InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin,
+ SourceLocation CommandLocEnd,
+ StringRef CommandName,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg);
+
+ InlineContentComment *actOnUnknownCommand(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name);
+
+ TextComment *actOnText(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Text);
+
+ VerbatimBlockComment *actOnVerbatimBlockStart(SourceLocation Loc,
+ StringRef Name);
+
+ VerbatimBlockLineComment *actOnVerbatimBlockLine(SourceLocation Loc,
+ StringRef Text);
+
+ void actOnVerbatimBlockFinish(VerbatimBlockComment *Block,
+ SourceLocation CloseNameLocBegin,
+ StringRef CloseName,
+ ArrayRef<VerbatimBlockLineComment *> Lines);
+
+ VerbatimLineComment *actOnVerbatimLine(SourceLocation LocBegin,
+ StringRef Name,
+ SourceLocation TextBegin,
+ StringRef Text);
+
+ HTMLStartTagComment *actOnHTMLStartTagStart(SourceLocation LocBegin,
+ StringRef TagName);
+
+ void actOnHTMLStartTagFinish(HTMLStartTagComment *Tag,
+ ArrayRef<HTMLStartTagComment::Attribute> Attrs,
+ SourceLocation GreaterLoc,
+ bool IsSelfClosing);
+
+ HTMLEndTagComment *actOnHTMLEndTag(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef TagName);
+
+ FullComment *actOnFullComment(ArrayRef<BlockContentComment *> Blocks);
+
+ void checkBlockCommandEmptyParagraph(BlockCommandComment *Command);
+
+ void checkReturnsCommand(const BlockCommandComment *Command);
+
+ /// Emit diagnostics about duplicate block commands that should be
+ /// used only once per comment, e.g., \\brief and \\returns.
+ void checkBlockCommandDuplicate(const BlockCommandComment *Command);
+
+ bool isFunctionDecl();
+ bool isTemplateOrSpecialization();
+
+ ArrayRef<const ParmVarDecl *> getParamVars();
+
+ /// Extract all important semantic information from
+ /// \c ThisDeclInfo->ThisDecl into \c ThisDeclInfo members.
+ void inspectThisDecl();
+
+ /// Returns index of a function parameter with a given name.
+ unsigned resolveParmVarReference(StringRef Name,
+ ArrayRef<const ParmVarDecl *> ParamVars);
+
+ /// Returns index of a function parameter with the name closest to a given
+ /// typo.
+ unsigned correctTypoInParmVarReference(StringRef Typo,
+ ArrayRef<const ParmVarDecl *> ParamVars);
+
+ bool resolveTParamReference(StringRef Name,
+ const TemplateParameterList *TemplateParameters,
+ SmallVectorImpl<unsigned> *Position);
+
+ StringRef correctTypoInTParamReference(
+ StringRef Typo,
+ const TemplateParameterList *TemplateParameters);
+
+ InlineCommandComment::RenderKind
+ getInlineCommandRenderKind(StringRef Name) const;
+
+ bool isHTMLEndTagOptional(StringRef Name);
+ bool isHTMLEndTagForbidden(StringRef Name);
+};
+
+} // end namespace comments
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/CommentVisitor.h b/include/clang/AST/CommentVisitor.h
new file mode 100644
index 0000000..47867a6
--- /dev/null
+++ b/include/clang/AST/CommentVisitor.h
@@ -0,0 +1,66 @@
+//===--- CommentVisitor.h - Visitor for Comment subclasses ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Comment.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+namespace comments {
+
+template <typename T> struct make_ptr { typedef T *type; };
+template <typename T> struct make_const_ptr { typedef const T *type; };
+
+template<template <typename> class Ptr, typename ImplClass, typename RetTy=void>
+class CommentVisitorBase {
+public:
+#define PTR(CLASS) typename Ptr<CLASS>::type
+#define DISPATCH(NAME, CLASS) \
+ return static_cast<ImplClass*>(this)->visit ## NAME(static_cast<PTR(CLASS)>(C))
+
+ RetTy visit(PTR(Comment) C) {
+ if (!C)
+ return RetTy();
+
+ switch (C->getCommentKind()) {
+ default: llvm_unreachable("Unknown comment kind!");
+#define ABSTRACT_COMMENT(COMMENT)
+#define COMMENT(CLASS, PARENT) \
+ case Comment::CLASS##Kind: DISPATCH(CLASS, CLASS);
+#include "clang/AST/CommentNodes.inc"
+#undef ABSTRACT_COMMENT
+#undef COMMENT
+ }
+ }
+
+ // If the derived class does not implement a certain Visit* method, fall back
+ // on Visit* method for the superclass.
+#define ABSTRACT_COMMENT(COMMENT) COMMENT
+#define COMMENT(CLASS, PARENT) \
+ RetTy visit ## CLASS(PTR(CLASS) C) { DISPATCH(PARENT, PARENT); }
+#include "clang/AST/CommentNodes.inc"
+#undef ABSTRACT_COMMENT
+#undef COMMENT
+
+ RetTy visitComment(PTR(Comment) C) { return RetTy(); }
+
+#undef PTR
+#undef DISPATCH
+};
+
+template<typename ImplClass, typename RetTy=void>
+class CommentVisitor :
+ public CommentVisitorBase<make_ptr, ImplClass, RetTy> {};
+
+template<typename ImplClass, typename RetTy=void>
+class ConstCommentVisitor :
+ public CommentVisitorBase<make_const_ptr, ImplClass, RetTy> {};
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 0c47f2e..e9f25b3 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -99,7 +99,7 @@ public:
};
/// NamedDecl - This represents a decl with a name. Many decls have names such
-/// as ObjCMethodDecl, but not @class, etc.
+/// as ObjCMethodDecl, but not \@class, etc.
class NamedDecl : public Decl {
virtual void anchor();
/// Name - The name of this declaration, which is typically a normal
@@ -218,6 +218,7 @@ public:
Visibility visibility_;
bool explicit_;
+ void setVisibility(Visibility V, bool E) { visibility_ = V; explicit_ = E; }
public:
LinkageInfo() : linkage_(ExternalLinkage), visibility_(DefaultVisibility),
explicit_(false) {}
@@ -242,8 +243,6 @@ public:
bool visibilityExplicit() const { return explicit_; }
void setLinkage(Linkage L) { linkage_ = L; }
- void setVisibility(Visibility V, bool E) { visibility_ = V; explicit_ = E; }
-
void mergeLinkage(Linkage L) {
setLinkage(minLinkage(linkage(), L));
}
@@ -256,15 +255,15 @@ public:
// down to one of its members. If the member has no explicit visibility,
// the class visibility wins.
void mergeVisibility(Visibility V, bool E = false) {
- // If one has explicit visibility and the other doesn't, keep the
- // explicit one.
- if (visibilityExplicit() && !E)
+ // Never increase the visibility
+ if (visibility() < V)
return;
- if (!visibilityExplicit() && E)
- setVisibility(V, E);
- // If both are explicit or both are implicit, keep the minimum.
- setVisibility(minVisibility(visibility(), V), visibilityExplicit() || E);
+ // If we have an explicit visibility, keep it
+ if (visibilityExplicit())
+ return;
+
+ setVisibility(V, E);
}
// Merge the visibility V, keeping the most restrictive one.
// This is used for cases like merging the visibility of a template
@@ -275,9 +274,16 @@ public:
if (visibility() < V)
return;
+ // FIXME: this
// If this visibility is explicit, keep it.
if (visibilityExplicit() && !E)
return;
+
+ // should be replaced with this
+ // Don't lose the explicit bit for nothing
+ // if (visibility() == V && visibilityExplicit())
+ // return;
+
setVisibility(V, E);
}
void mergeVisibility(LinkageInfo Other) {
@@ -295,11 +301,6 @@ public:
mergeLinkage(Other);
mergeVisibilityWithMin(Other);
}
-
- friend LinkageInfo merge(LinkageInfo L, LinkageInfo R) {
- L.merge(R);
- return L;
- }
};
/// \brief Determine what kind of linkage this entity has.
@@ -1151,7 +1152,7 @@ public:
}
/// \brief Determine whether this variable is the exception variable in a
- /// C++ catch statememt or an Objective-C @catch statement.
+ /// C++ catch statememt or an Objective-C \@catch statement.
bool isExceptionVariable() const {
return VarDeclBits.ExceptionVar;
}
@@ -1182,7 +1183,7 @@ public:
bool isARCPseudoStrong() const { return VarDeclBits.ARCPseudoStrong; }
void setARCPseudoStrong(bool ps) { VarDeclBits.ARCPseudoStrong = ps; }
- /// Whether this variable is (C++0x) constexpr.
+ /// Whether this variable is (C++11) constexpr.
bool isConstexpr() const { return VarDeclBits.IsConstexpr; }
void setConstexpr(bool IC) { VarDeclBits.IsConstexpr = IC; }
@@ -1735,9 +1736,9 @@ public:
bool hasInheritedPrototype() const { return HasInheritedPrototype; }
void setHasInheritedPrototype(bool P = true) { HasInheritedPrototype = P; }
- /// Whether this is a (C++0x) constexpr function or constexpr constructor.
+ /// Whether this is a (C++11) constexpr function or constexpr constructor.
bool isConstexpr() const { return IsConstexpr; }
- void setConstexpr(bool IC) { IsConstexpr = IC; }
+ void setConstexpr(bool IC);
/// \brief Whether this function has been deleted.
///
@@ -2092,25 +2093,26 @@ class FieldDecl : public DeclaratorDecl {
bool Mutable : 1;
mutable unsigned CachedFieldIndex : 31;
- /// \brief A pointer to either the in-class initializer for this field (if
- /// the boolean value is false), or the bit width expression for this bit
- /// field (if the boolean value is true).
+ /// \brief An InClassInitStyle value, and either a bit width expression (if
+ /// the InClassInitStyle value is ICIS_NoInit), or a pointer to the in-class
+ /// initializer for this field (otherwise).
///
/// We can safely combine these two because in-class initializers are not
/// permitted for bit-fields.
///
- /// If the boolean is false and the initializer is null, then this field has
- /// an in-class initializer which has not yet been parsed and attached.
- llvm::PointerIntPair<Expr *, 1, bool> InitializerOrBitWidth;
+ /// If the InClassInitStyle is not ICIS_NoInit and the initializer is null,
+ /// then this field has an in-class initializer which has not yet been parsed
+ /// and attached.
+ llvm::PointerIntPair<Expr *, 2, unsigned> InitializerOrBitWidth;
protected:
FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
- bool HasInit)
+ InClassInitStyle InitStyle)
: DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc),
Mutable(Mutable), CachedFieldIndex(0),
- InitializerOrBitWidth(BW, !HasInit) {
- assert(!(BW && HasInit) && "got initializer for bitfield");
+ InitializerOrBitWidth(BW, InitStyle) {
+ assert((!BW || InitStyle == ICIS_NoInit) && "got initializer for bitfield");
}
public:
@@ -2118,7 +2120,7 @@ public:
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, QualType T,
TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
- bool HasInit);
+ InClassInitStyle InitStyle);
static FieldDecl *CreateDeserialized(ASTContext &C, unsigned ID);
@@ -2129,12 +2131,10 @@ public:
/// isMutable - Determines whether this field is mutable (C++ only).
bool isMutable() const { return Mutable; }
- /// \brief Set whether this field is mutable (C++ only).
- void setMutable(bool M) { Mutable = M; }
-
/// isBitfield - Determines whether this field is a bitfield.
bool isBitField() const {
- return InitializerOrBitWidth.getInt() && InitializerOrBitWidth.getPointer();
+ return getInClassInitStyle() == ICIS_NoInit &&
+ InitializerOrBitWidth.getPointer();
}
/// @brief Determines whether this is an unnamed bitfield.
@@ -2150,39 +2150,44 @@ public:
return isBitField() ? InitializerOrBitWidth.getPointer() : 0;
}
unsigned getBitWidthValue(const ASTContext &Ctx) const;
- void setBitWidth(Expr *BW) {
- assert(!InitializerOrBitWidth.getPointer() &&
- "bit width or initializer already set");
- InitializerOrBitWidth.setPointer(BW);
- InitializerOrBitWidth.setInt(1);
- }
- /// removeBitWidth - Remove the bitfield width from this member.
+
+ /// setBitWidth - Set the bit-field width for this member.
+ // Note: used by some clients (i.e., do not remove it).
+ void setBitWidth(Expr *Width);
+ /// removeBitWidth - Remove the bit-field width from this member.
+ // Note: used by some clients (i.e., do not remove it).
void removeBitWidth() {
- assert(isBitField() && "no bit width to remove");
+ assert(isBitField() && "no bitfield width to remove");
InitializerOrBitWidth.setPointer(0);
}
- /// hasInClassInitializer - Determine whether this member has a C++0x in-class
+ /// getInClassInitStyle - Get the kind of (C++11) in-class initializer which
+ /// this field has.
+ InClassInitStyle getInClassInitStyle() const {
+ return static_cast<InClassInitStyle>(InitializerOrBitWidth.getInt());
+ }
+
+ /// hasInClassInitializer - Determine whether this member has a C++11 in-class
/// initializer.
bool hasInClassInitializer() const {
- return !InitializerOrBitWidth.getInt();
+ return getInClassInitStyle() != ICIS_NoInit;
}
- /// getInClassInitializer - Get the C++0x in-class initializer for this
+ /// getInClassInitializer - Get the C++11 in-class initializer for this
/// member, or null if one has not been set. If a valid declaration has an
/// in-class initializer, but this returns null, then we have not parsed and
/// attached it yet.
Expr *getInClassInitializer() const {
return hasInClassInitializer() ? InitializerOrBitWidth.getPointer() : 0;
}
- /// setInClassInitializer - Set the C++0x in-class initializer for this
+ /// setInClassInitializer - Set the C++11 in-class initializer for this
/// member.
void setInClassInitializer(Expr *Init);
- /// removeInClassInitializer - Remove the C++0x in-class initializer from this
+ /// removeInClassInitializer - Remove the C++11 in-class initializer from this
/// member.
void removeInClassInitializer() {
- assert(!InitializerOrBitWidth.getInt() && "no initializer to remove");
+ assert(hasInClassInitializer() && "no initializer to remove");
InitializerOrBitWidth.setPointer(0);
- InitializerOrBitWidth.setInt(1);
+ InitializerOrBitWidth.setInt(ICIS_NoInit);
}
/// getParent - Returns the parent of this field declaration, which
@@ -2201,6 +2206,9 @@ public:
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classof(const FieldDecl *D) { return true; }
static bool classofKind(Kind K) { return K >= firstField && K <= lastField; }
+
+ friend class ASTDeclReader;
+ friend class ASTDeclWriter;
};
/// EnumConstantDecl - An instance of this object exists for each enum constant
@@ -2308,7 +2316,10 @@ protected:
: NamedDecl(DK, DC, L, Id), TypeForDecl(0), LocStart(StartL) {}
public:
- // Low-level accessor
+ // Low-level accessor. If you just want the type defined by this node,
+ // check out ASTContext::getTypeDeclType or one of
+ // ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
+ // already know the specific kind of node this is.
const Type *getTypeForDecl() const { return TypeForDecl; }
void setTypeForDecl(const Type *TD) { TypeForDecl = TD; }
@@ -2602,6 +2613,7 @@ public:
void setCompleteDefinition(bool V) { IsCompleteDefinition = V; }
+ // FIXME: Return StringRef;
const char *getKindName() const {
return TypeWithKeyword::getTagTypeKindName(getTagKind());
}
@@ -3214,8 +3226,8 @@ public:
/// @__experimental_modules_import std.vector;
/// \endcode
///
-/// Import declarations can also be implicitly generated from #include/#import
-/// directives.
+/// Import declarations can also be implicitly generated from
+/// \#include/\#import directives.
class ImportDecl : public Decl {
/// \brief The imported module, along with a bit that indicates whether
/// we have source-location information for each identifier in the module
diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index 22328912..ac2cc9e 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_AST_DECLBASE_H
#include "clang/AST/Attr.h"
+#include "clang/AST/DeclarationName.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/PointerUnion.h"
@@ -692,17 +693,17 @@ public:
Decl *Starter;
public:
- typedef Decl* value_type;
- typedef Decl* reference;
- typedef Decl* pointer;
+ typedef Decl *value_type;
+ typedef const value_type &reference;
+ typedef const value_type *pointer;
typedef std::forward_iterator_tag iterator_category;
- typedef std::ptrdiff_t difference_type;
+ typedef std::ptrdiff_t difference_type;
redecl_iterator() : Current(0) { }
explicit redecl_iterator(Decl *C) : Current(C), Starter(C) { }
reference operator*() const { return Current; }
- pointer operator->() const { return Current; }
+ value_type operator->() const { return Current; }
redecl_iterator& operator++() {
assert(Current && "Advancing while iterator has reached end");
@@ -856,8 +857,11 @@ public:
static void printGroup(Decl** Begin, unsigned NumDecls,
raw_ostream &Out, const PrintingPolicy &Policy,
unsigned Indentation = 0);
- LLVM_ATTRIBUTE_USED void dump() const;
- LLVM_ATTRIBUTE_USED void dumpXML() const;
+ // Debuggers don't usually respect default arguments.
+ LLVM_ATTRIBUTE_USED void dump() const { dump(llvm::errs()); }
+ void dump(raw_ostream &Out) const;
+ // Debuggers don't usually respect default arguments.
+ LLVM_ATTRIBUTE_USED void dumpXML() const { dumpXML(llvm::errs()); }
void dumpXML(raw_ostream &OS) const;
private:
@@ -1141,7 +1145,7 @@ public:
/// inline, its enclosing namespace, recursively.
bool InEnclosingNamespaceSetOf(const DeclContext *NS) const;
- /// \\brief Collects all of the declaration contexts that are semantically
+ /// \brief Collects all of the declaration contexts that are semantically
/// connected to this declaration context.
///
/// For declaration contexts that have multiple semantically connected but
@@ -1173,9 +1177,9 @@ public:
Decl *Current;
public:
- typedef Decl* value_type;
- typedef Decl* reference;
- typedef Decl* pointer;
+ typedef Decl *value_type;
+ typedef const value_type &reference;
+ typedef const value_type *pointer;
typedef std::forward_iterator_tag iterator_category;
typedef std::ptrdiff_t difference_type;
@@ -1183,7 +1187,8 @@ public:
explicit decl_iterator(Decl *C) : Current(C) { }
reference operator*() const { return Current; }
- pointer operator->() const { return Current; }
+ // This doesn't meet the iterator requirements, but it's convenient
+ value_type operator->() const { return Current; }
decl_iterator& operator++() {
Current = Current->getNextDeclInContext();
@@ -1207,14 +1212,14 @@ public:
/// decls_begin/decls_end - Iterate over the declarations stored in
/// this context.
decl_iterator decls_begin() const;
- decl_iterator decls_end() const;
+ decl_iterator decls_end() const { return decl_iterator(); }
bool decls_empty() const;
/// noload_decls_begin/end - Iterate over the declarations stored in this
/// context that are currently loaded; don't attempt to retrieve anything
/// from an external source.
decl_iterator noload_decls_begin() const;
- decl_iterator noload_decls_end() const;
+ decl_iterator noload_decls_end() const { return decl_iterator(); }
/// specific_decl_iterator - Iterates over a subrange of
/// declarations stored in a DeclContext, providing only those that
@@ -1237,9 +1242,11 @@ public:
}
public:
- typedef SpecificDecl* value_type;
- typedef SpecificDecl* reference;
- typedef SpecificDecl* pointer;
+ typedef SpecificDecl *value_type;
+ // TODO: Add reference and pointer typedefs (with some appropriate proxy
+ // type) if we ever have a need for them.
+ typedef void reference;
+ typedef void pointer;
typedef std::iterator_traits<DeclContext::decl_iterator>::difference_type
difference_type;
typedef std::forward_iterator_tag iterator_category;
@@ -1258,8 +1265,9 @@ public:
SkipToNextDecl();
}
- reference operator*() const { return cast<SpecificDecl>(*Current); }
- pointer operator->() const { return cast<SpecificDecl>(*Current); }
+ value_type operator*() const { return cast<SpecificDecl>(*Current); }
+ // This doesn't meet the iterator requirements, but it's convenient
+ value_type operator->() const { return **this; }
specific_decl_iterator& operator++() {
++Current;
@@ -1311,16 +1319,18 @@ public:
}
public:
- typedef SpecificDecl* value_type;
- typedef SpecificDecl* reference;
- typedef SpecificDecl* pointer;
+ typedef SpecificDecl *value_type;
+ // TODO: Add reference and pointer typedefs (with some appropriate proxy
+ // type) if we ever have a need for them.
+ typedef void reference;
+ typedef void pointer;
typedef std::iterator_traits<DeclContext::decl_iterator>::difference_type
difference_type;
typedef std::forward_iterator_tag iterator_category;
filtered_decl_iterator() : Current() { }
- /// specific_decl_iterator - Construct a new iterator over a
+ /// filtered_decl_iterator - Construct a new iterator over a
/// subset of the declarations the range [C,
/// end-of-declarations). If A is non-NULL, it is a pointer to a
/// member function of SpecificDecl that should return true for
@@ -1332,8 +1342,8 @@ public:
SkipToNextDecl();
}
- reference operator*() const { return cast<SpecificDecl>(*Current); }
- pointer operator->() const { return cast<SpecificDecl>(*Current); }
+ value_type operator*() const { return cast<SpecificDecl>(*Current); }
+ value_type operator->() const { return cast<SpecificDecl>(*Current); }
filtered_decl_iterator& operator++() {
++Current;
@@ -1410,7 +1420,9 @@ public:
/// and enumerator names preceding any tag name. Note that this
/// routine will not look into parent contexts.
lookup_result lookup(DeclarationName Name);
- lookup_const_result lookup(DeclarationName Name) const;
+ lookup_const_result lookup(DeclarationName Name) const {
+ return const_cast<DeclContext*>(this)->lookup(Name);
+ }
/// \brief A simplistic name lookup mechanism that performs name lookup
/// into this declaration context without consulting the external source.
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h
index 7f3ec4c..851e310 100644
--- a/include/clang/AST/DeclCXX.h
+++ b/include/clang/AST/DeclCXX.h
@@ -41,6 +41,7 @@ class CXXFinalOverriderMap;
class CXXIndirectPrimaryBaseSet;
class FriendDecl;
class LambdaExpr;
+class UsingDecl;
/// \brief Represents any kind of function declaration, whether it is a
/// concrete function or a function template.
@@ -98,7 +99,7 @@ namespace llvm {
namespace clang {
-/// AccessSpecDecl - An access specifier followed by colon ':'.
+/// @brief Represents an access specifier followed by colon ':'.
///
/// An objects of this class represents sugar for the syntactic occurrence
/// of an access specifier followed by a colon in the list of member
@@ -110,7 +111,7 @@ namespace clang {
/// "access declarations" (C++98 11.3 [class.access.dcl]).
class AccessSpecDecl : public Decl {
virtual void anchor();
- /// ColonLoc - The location of the ':'.
+ /// \brief The location of the ':'.
SourceLocation ColonLoc;
AccessSpecDecl(AccessSpecifier AS, DeclContext *DC,
@@ -121,14 +122,14 @@ class AccessSpecDecl : public Decl {
AccessSpecDecl(EmptyShell Empty)
: Decl(AccessSpec, Empty) { }
public:
- /// getAccessSpecifierLoc - The location of the access specifier.
+ /// \brief The location of the access specifier.
SourceLocation getAccessSpecifierLoc() const { return getLocation(); }
- /// setAccessSpecifierLoc - Sets the location of the access specifier.
+ /// \brief Sets the location of the access specifier.
void setAccessSpecifierLoc(SourceLocation ASLoc) { setLocation(ASLoc); }
- /// getColonLoc - The location of the colon following the access specifier.
+ /// \brief The location of the colon following the access specifier.
SourceLocation getColonLoc() const { return ColonLoc; }
- /// setColonLoc - Sets the location of the colon.
+ /// \brief Sets the location of the colon.
void setColonLoc(SourceLocation CLoc) { ColonLoc = CLoc; }
SourceRange getSourceRange() const LLVM_READONLY {
@@ -149,7 +150,7 @@ public:
};
-/// CXXBaseSpecifier - A base class of a C++ class.
+/// \brief Represents a base class of a C++ class.
///
/// Each CXXBaseSpecifier represents a single, direct base class (or
/// struct) of a C++ class (or struct). It specifies the type of that
@@ -175,7 +176,7 @@ class CXXBaseSpecifier {
/// expansion.
SourceLocation EllipsisLoc;
- /// Virtual - Whether this is a virtual base class or not.
+ /// \brief Whether this is a virtual base class or not.
bool Virtual : 1;
/// BaseOfClass - Whether this is the base of a class (true) or of a
@@ -357,6 +358,9 @@ class CXXRecordDecl : public RecordDecl {
/// \brief True if there no non-field members declared by the user.
bool HasOnlyCMembers : 1;
+ /// \brief True if any field has an in-class initializer.
+ bool HasInClassInitializer : 1;
+
/// HasTrivialDefaultConstructor - True when, if this class has a default
/// constructor, this default constructor is trivial.
///
@@ -382,26 +386,10 @@ class CXXRecordDecl : public RecordDecl {
/// constructor for this class would be constexpr.
bool DefaultedDefaultConstructorIsConstexpr : 1;
- /// DefaultedCopyConstructorIsConstexpr - True if a defaulted copy
- /// constructor for this class would be constexpr.
- bool DefaultedCopyConstructorIsConstexpr : 1;
-
- /// DefaultedMoveConstructorIsConstexpr - True if a defaulted move
- /// constructor for this class would be constexpr.
- bool DefaultedMoveConstructorIsConstexpr : 1;
-
/// HasConstexprDefaultConstructor - True if this class has a constexpr
/// default constructor (either user-declared or implicitly declared).
bool HasConstexprDefaultConstructor : 1;
- /// HasConstexprCopyConstructor - True if this class has a constexpr copy
- /// constructor (either user-declared or implicitly declared).
- bool HasConstexprCopyConstructor : 1;
-
- /// HasConstexprMoveConstructor - True if this class has a constexpr move
- /// constructor (either user-declared or implicitly declared).
- bool HasConstexprMoveConstructor : 1;
-
/// HasTrivialCopyConstructor - True when this class has a trivial copy
/// constructor.
///
@@ -554,13 +542,21 @@ class CXXRecordDecl : public RecordDecl {
/// \brief Retrieve the set of direct base classes.
CXXBaseSpecifier *getBases() const {
- return Bases.get(Definition->getASTContext().getExternalSource());
+ if (!Bases.isOffset())
+ return Bases.get(0);
+ return getBasesSlowCase();
}
/// \brief Retrieve the set of virtual base classes.
CXXBaseSpecifier *getVBases() const {
- return VBases.get(Definition->getASTContext().getExternalSource());
+ if (!VBases.isOffset())
+ return VBases.get(0);
+ return getVBasesSlowCase();
}
+
+ private:
+ CXXBaseSpecifier *getBasesSlowCase() const;
+ CXXBaseSpecifier *getVBasesSlowCase() const;
} *DefinitionData;
/// \brief Describes a C++ closure type (generated by a lambda expression).
@@ -647,6 +643,9 @@ class CXXRecordDecl : public RecordDecl {
void markedVirtualFunctionPure();
friend void FunctionDecl::setPure(bool);
+ void markedConstructorConstexpr(CXXConstructorDecl *CD);
+ friend void FunctionDecl::setConstexpr(bool);
+
friend class ASTNodeImporter;
protected:
@@ -1040,6 +1039,10 @@ public:
/// no base classes, and no virtual functions (C++ [dcl.init.aggr]p1).
bool isAggregate() const { return data().Aggregate; }
+ /// hasInClassInitializer - Whether this class has any in-class initializers
+ /// for non-static data members.
+ bool hasInClassInitializer() const { return data().HasInClassInitializer; }
+
/// isPOD - Whether this class is a POD-type (C++ [class]p4), which is a class
/// that is an aggregate that has no non-static non-POD data members, no
/// reference data members, no user-defined copy assignment operator and no
@@ -1091,19 +1094,8 @@ public:
/// defaultedDefaultConstructorIsConstexpr - Whether a defaulted default
/// constructor for this class would be constexpr.
bool defaultedDefaultConstructorIsConstexpr() const {
- return data().DefaultedDefaultConstructorIsConstexpr;
- }
-
- /// defaultedCopyConstructorIsConstexpr - Whether a defaulted copy
- /// constructor for this class would be constexpr.
- bool defaultedCopyConstructorIsConstexpr() const {
- return data().DefaultedCopyConstructorIsConstexpr;
- }
-
- /// defaultedMoveConstructorIsConstexpr - Whether a defaulted move
- /// constructor for this class would be constexpr.
- bool defaultedMoveConstructorIsConstexpr() const {
- return data().DefaultedMoveConstructorIsConstexpr;
+ return data().DefaultedDefaultConstructorIsConstexpr &&
+ (!isUnion() || hasInClassInitializer());
}
/// hasConstexprDefaultConstructor - Whether this class has a constexpr
@@ -1111,23 +1103,7 @@ public:
bool hasConstexprDefaultConstructor() const {
return data().HasConstexprDefaultConstructor ||
(!data().UserDeclaredConstructor &&
- data().DefaultedDefaultConstructorIsConstexpr && isLiteral());
- }
-
- /// hasConstexprCopyConstructor - Whether this class has a constexpr copy
- /// constructor.
- bool hasConstexprCopyConstructor() const {
- return data().HasConstexprCopyConstructor ||
- (!data().DeclaredCopyConstructor &&
- data().DefaultedCopyConstructorIsConstexpr && isLiteral());
- }
-
- /// hasConstexprMoveConstructor - Whether this class has a constexpr move
- /// constructor.
- bool hasConstexprMoveConstructor() const {
- return data().HasConstexprMoveConstructor ||
- (needsImplicitMoveConstructor() &&
- data().DefaultedMoveConstructorIsConstexpr && isLiteral());
+ defaultedDefaultConstructorIsConstexpr());
}
// hasTrivialCopyConstructor - Whether this class has a trivial copy
@@ -1212,12 +1188,12 @@ public:
/// This routine will return non-NULL for (non-templated) member
/// classes of class templates. For example, given:
///
- /// \code
+ /// @code
/// template<typename T>
/// struct X {
/// struct A { };
/// };
- /// \endcode
+ /// @endcode
///
/// The declaration for X<int>::A is a (non-templated) CXXRecordDecl
/// whose parent is the class template specialization X<int>. For
@@ -1319,7 +1295,7 @@ public:
///
/// \returns true if this class is virtually derived from Base,
/// false otherwise.
- bool isVirtuallyDerivedFrom(CXXRecordDecl *Base) const;
+ bool isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const;
/// \brief Determine whether this class is provably not derived from
/// the type \p Base.
@@ -1573,6 +1549,9 @@ public:
bool isStatic() const { return getStorageClass() == SC_Static; }
bool isInstance() const { return !isStatic(); }
+ bool isConst() { return getType()->castAs<FunctionType>()->isConst(); }
+ bool isVolatile() { return getType()->castAs<FunctionType>()->isVolatile(); }
+
bool isVirtual() const {
CXXMethodDecl *CD =
cast<CXXMethodDecl>(const_cast<CXXMethodDecl*>(this)->getCanonicalDecl());
@@ -1602,8 +1581,8 @@ public:
return cast<CXXMethodDecl>(FunctionDecl::getCanonicalDecl());
}
- /// isUserProvided - True if it is either an implicit constructor or
- /// if it was defaulted or deleted on first declaration.
+ /// isUserProvided - True if this method is user-declared and was not
+ /// deleted or defaulted on its first declaration.
bool isUserProvided() const {
return !(isDeleted() || getCanonicalDecl()->isDefaulted());
}
@@ -1642,13 +1621,13 @@ public:
///
/// In the following example, \c f() has an lvalue ref-qualifier, \c g()
/// has an rvalue ref-qualifier, and \c h() has no ref-qualifier.
- /// \code
+ /// @code
/// struct X {
/// void f() &;
/// void g() &&;
/// void h();
/// };
- /// \endcode
+ /// @endcode
RefQualifierKind getRefQualifier() const {
return getType()->getAs<FunctionProtoType>()->getRefQualifier();
}
@@ -1663,7 +1642,20 @@ public:
/// supplied by IR generation to either forward to the function call operator
/// or clone the function call operator.
bool isLambdaStaticInvoker() const;
-
+
+ /// \brief Find the method in RD that corresponds to this one.
+ ///
+ /// Find if RD or one of the classes it inherits from override this method.
+ /// If so, return it. RD is assumed to be a base class of the class defining
+ /// this method (or be the class itself).
+ CXXMethodDecl *
+ getCorrespondingMethodInClass(const CXXRecordDecl *RD);
+
+ const CXXMethodDecl *
+ getCorrespondingMethodInClass(const CXXRecordDecl *RD) const {
+ return const_cast<CXXMethodDecl*>(this)->getCorrespondingMethodInClass(RD);
+ }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classof(const CXXMethodDecl *D) { return true; }
@@ -2468,7 +2460,9 @@ public:
friend class ASTDeclReader;
};
-/// NamespaceAliasDecl - Represents a C++ namespace alias. For example:
+/// \brief Represents a C++ namespace alias.
+///
+/// For example:
///
/// @code
/// namespace Foo = Bar;
@@ -2555,17 +2549,19 @@ public:
static bool classofKind(Kind K) { return K == NamespaceAlias; }
};
-/// UsingShadowDecl - Represents a shadow declaration introduced into
-/// a scope by a (resolved) using declaration. For example,
+/// \brief Represents a shadow declaration introduced into a scope by a
+/// (resolved) using declaration.
///
+/// For example,
+/// @code
/// namespace A {
/// void foo();
/// }
/// namespace B {
-/// using A::foo(); // <- a UsingDecl
-/// // Also creates a UsingShadowDecl for A::foo in B
+/// using A::foo; // <- a UsingDecl
+/// // Also creates a UsingShadowDecl for A::foo() in B
/// }
-///
+/// @endcode
class UsingShadowDecl : public NamedDecl {
virtual void anchor();
@@ -2627,8 +2623,12 @@ public:
friend class ASTDeclWriter;
};
-/// UsingDecl - Represents a C++ using-declaration. For example:
+/// \brief Represents a C++ using-declaration.
+///
+/// For example:
+/// @code
/// using someNameSpace::someIdentifier;
+/// @endcode
class UsingDecl : public NamedDecl {
virtual void anchor();
@@ -2643,8 +2643,10 @@ class UsingDecl : public NamedDecl {
DeclarationNameLoc DNLoc;
/// \brief The first shadow declaration of the shadow decl chain associated
- /// with this using declaration. The bool member of the pair store whether
- /// this decl has the 'typename' keyword.
+ /// with this using declaration.
+ ///
+ /// The bool member of the pair store whether this decl has the \c typename
+ /// keyword.
llvm::PointerIntPair<UsingShadowDecl *, 1, bool> FirstUsingShadow;
UsingDecl(DeclContext *DC, SourceLocation UL,
@@ -2753,14 +2755,17 @@ public:
friend class ASTDeclWriter;
};
-/// UnresolvedUsingValueDecl - Represents a dependent using
-/// declaration which was not marked with 'typename'. Unlike
-/// non-dependent using declarations, these *only* bring through
+/// \brief Represents a dependent using declaration which was not marked with
+/// \c typename.
+///
+/// Unlike non-dependent using declarations, these *only* bring through
/// non-types; otherwise they would break two-phase lookup.
///
-/// template <class T> class A : public Base<T> {
+/// @code
+/// template \<class T> class A : public Base<T> {
/// using Base<T>::foo;
/// };
+/// @endcode
class UnresolvedUsingValueDecl : public ValueDecl {
virtual void anchor();
@@ -2824,14 +2829,16 @@ public:
friend class ASTDeclWriter;
};
-/// UnresolvedUsingTypenameDecl - Represents a dependent using
-/// declaration which was marked with 'typename'.
+/// @brief Represents a dependent using declaration which was marked with
+/// \c typename.
///
-/// template <class T> class A : public Base<T> {
+/// @code
+/// template \<class T> class A : public Base<T> {
/// using typename Base<T>::foo;
/// };
+/// @endcode
///
-/// The type associated with a unresolved using typename decl is
+/// The type associated with an unresolved using typename decl is
/// currently always a typename type.
class UnresolvedUsingTypenameDecl : public TypeDecl {
virtual void anchor();
@@ -2885,34 +2892,36 @@ public:
static bool classofKind(Kind K) { return K == UnresolvedUsingTypename; }
};
-/// StaticAssertDecl - Represents a C++0x static_assert declaration.
+/// \brief Represents a C++11 static_assert declaration.
class StaticAssertDecl : public Decl {
virtual void anchor();
- Expr *AssertExpr;
+ llvm::PointerIntPair<Expr *, 1, bool> AssertExprAndFailed;
StringLiteral *Message;
SourceLocation RParenLoc;
StaticAssertDecl(DeclContext *DC, SourceLocation StaticAssertLoc,
- Expr *assertexpr, StringLiteral *message,
- SourceLocation RParenLoc)
- : Decl(StaticAssert, DC, StaticAssertLoc), AssertExpr(assertexpr),
- Message(message), RParenLoc(RParenLoc) { }
+ Expr *AssertExpr, StringLiteral *Message,
+ SourceLocation RParenLoc, bool Failed)
+ : Decl(StaticAssert, DC, StaticAssertLoc),
+ AssertExprAndFailed(AssertExpr, Failed), Message(Message),
+ RParenLoc(RParenLoc) { }
public:
static StaticAssertDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StaticAssertLoc,
Expr *AssertExpr, StringLiteral *Message,
- SourceLocation RParenLoc);
+ SourceLocation RParenLoc, bool Failed);
static StaticAssertDecl *CreateDeserialized(ASTContext &C, unsigned ID);
- Expr *getAssertExpr() { return AssertExpr; }
- const Expr *getAssertExpr() const { return AssertExpr; }
+ Expr *getAssertExpr() { return AssertExprAndFailed.getPointer(); }
+ const Expr *getAssertExpr() const { return AssertExprAndFailed.getPointer(); }
StringLiteral *getMessage() { return Message; }
const StringLiteral *getMessage() const { return Message; }
+ bool isFailed() const { return AssertExprAndFailed.getInt(); }
+
SourceLocation getRParenLoc() const { return RParenLoc; }
- void setRParenLoc(SourceLocation L) { RParenLoc = L; }
SourceRange getSourceRange() const LLVM_READONLY {
return SourceRange(getLocation(), getRParenLoc());
@@ -2925,7 +2934,7 @@ public:
friend class ASTDeclReader;
};
-/// Insertion operator for diagnostics. This allows sending AccessSpecifier's
+/// Insertion operator for diagnostics. This allows sending an AccessSpecifier
/// into a diagnostic with <<.
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
AccessSpecifier AS);
diff --git a/include/clang/AST/DeclContextInternals.h b/include/clang/AST/DeclContextInternals.h
index c5f2aa0..39f04c6 100644
--- a/include/clang/AST/DeclContextInternals.h
+++ b/include/clang/AST/DeclContextInternals.h
@@ -17,8 +17,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/DeclCXX.h"
-#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
@@ -196,7 +196,7 @@ public:
};
class StoredDeclsMap
- : public llvm::DenseMap<DeclarationName, StoredDeclsList> {
+ : public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
public:
static void DestroyAll(StoredDeclsMap *Map, bool Dependent);
diff --git a/include/clang/AST/DeclFriend.h b/include/clang/AST/DeclFriend.h
index ba1eb8d..9a64f08 100644
--- a/include/clang/AST/DeclFriend.h
+++ b/include/clang/AST/DeclFriend.h
@@ -71,10 +71,12 @@ private:
: Decl(Decl::Friend, Empty), NextFriend() { }
FriendDecl *getNextFriend() {
- return cast_or_null<FriendDecl>(
- NextFriend.get(getASTContext().getExternalSource()));
+ if (!NextFriend.isOffset())
+ return cast_or_null<FriendDecl>(NextFriend.get(0));
+ return getNextFriendSlowCase();
}
-
+ FriendDecl *getNextFriendSlowCase();
+
public:
static FriendDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation L, FriendUnion Friend_,
diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h
index 4ae073e..6c39f2c 100644
--- a/include/clang/AST/DeclObjC.h
+++ b/include/clang/AST/DeclObjC.h
@@ -136,7 +136,7 @@ private:
mutable unsigned HasRedeclaration : 1;
// NOTE: VC++ treats enums as signed, avoid using ImplementationControl enum
- /// @required/@optional
+ /// \@required/\@optional
unsigned DeclImplementation : 2;
// NOTE: VC++ treats enums as signed, avoid using the ObjCDeclQualifier enum
@@ -150,6 +150,15 @@ private:
/// "standard" position, a enum SelectorLocationsKind.
unsigned SelLocsKind : 2;
+ /// \brief Whether this method overrides any other in the class hierarchy.
+ ///
+ /// A method is said to override any method in the class's
+ /// base classes, its protocols, or its categories' protocols, that has
+ /// the same selector and is of the same kind (class or instance).
+ /// A method in an implementation is not considered as overriding the same
+ /// method in the interface or its categories.
+ unsigned IsOverriding : 1;
+
// Result type of this method.
QualType MethodDeclType;
@@ -162,7 +171,7 @@ private:
unsigned NumParams;
/// List of attributes for this method declaration.
- SourceLocation EndLoc; // the location of the ';' or '}'.
+ SourceLocation DeclEndLoc; // the location of the ';' or '{'.
// The following are only used for method definitions, null otherwise.
// FIXME: space savings opportunity, consider a sub-class.
@@ -230,10 +239,10 @@ private:
IsDefined(isDefined), IsRedeclaration(0), HasRedeclaration(0),
DeclImplementation(impControl), objcDeclQualifier(OBJC_TQ_None),
RelatedResultType(HasRelatedResultType),
- SelLocsKind(SelLoc_StandardNoSpace),
+ SelLocsKind(SelLoc_StandardNoSpace), IsOverriding(0),
MethodDeclType(T), ResultTInfo(ResultTInfo),
ParamsAndSelLocs(0), NumParams(0),
- EndLoc(endLoc), Body(0), SelfDecl(0), CmdDecl(0) {
+ DeclEndLoc(endLoc), Body(0), SelfDecl(0), CmdDecl(0) {
setImplicit(isImplicitlyDeclared);
}
@@ -281,12 +290,16 @@ public:
bool isRedeclaration() const { return IsRedeclaration; }
void setAsRedeclaration(const ObjCMethodDecl *PrevMethod);
+ /// \brief Returns the location where the declarator ends. It will be
+ /// the location of ';' for a method declaration and the location of '{'
+ /// for a method definition.
+ SourceLocation getDeclaratorEndLoc() const { return DeclEndLoc; }
+
// Location information, modeled after the Stmt API.
SourceLocation getLocStart() const LLVM_READONLY { return getLocation(); }
- SourceLocation getLocEnd() const LLVM_READONLY { return EndLoc; }
- void setEndLoc(SourceLocation Loc) { EndLoc = Loc; }
+ SourceLocation getLocEnd() const LLVM_READONLY;
virtual SourceRange getSourceRange() const LLVM_READONLY {
- return SourceRange(getLocation(), EndLoc);
+ return SourceRange(getLocation(), getLocEnd());
}
SourceLocation getSelectorStartLoc() const {
@@ -301,7 +314,7 @@ public:
getSelLocsKind() == SelLoc_StandardWithSpace,
llvm::makeArrayRef(const_cast<ParmVarDecl**>(getParams()),
NumParams),
- EndLoc);
+ DeclEndLoc);
return getStoredSelLocs()[Index];
}
@@ -396,7 +409,17 @@ public:
bool isDefined() const { return IsDefined; }
void setDefined(bool isDefined) { IsDefined = isDefined; }
- // Related to protocols declared in @protocol
+ /// \brief Whether this method overrides any other in the class hierarchy.
+ ///
+ /// A method is said to override any method in the class's
+ /// base classes, its protocols, or its categories' protocols, that has
+ /// the same selector and is of the same kind (class or instance).
+ /// A method in an implementation is not considered as overriding the same
+ /// method in the interface or its categories.
+ bool isOverriding() const { return IsOverriding; }
+ void setOverriding(bool isOverriding) { IsOverriding = isOverriding; }
+
+ // Related to protocols declared in \@protocol
void setDeclImplementation(ImplementationControl ic) {
DeclImplementation = ic;
}
@@ -528,24 +551,28 @@ public:
}
};
-/// ObjCInterfaceDecl - Represents an ObjC class declaration. For example:
+/// \brief Represents an ObjC class declaration.
+///
+/// For example:
///
+/// \code
/// // MostPrimitive declares no super class (not particularly useful).
-/// @interface MostPrimitive
+/// \@interface MostPrimitive
/// // no instance variables or methods.
-/// @end
+/// \@end
///
/// // NSResponder inherits from NSObject & implements NSCoding (a protocol).
-/// @interface NSResponder : NSObject <NSCoding>
+/// \@interface NSResponder : NSObject \<NSCoding>
/// { // instance variables are represented by ObjCIvarDecl.
/// id nextResponder; // nextResponder instance variable.
/// }
/// - (NSResponder *)nextResponder; // return a pointer to NSResponder.
/// - (void)mouseMoved:(NSEvent *)theEvent; // return void, takes a pointer
-/// @end // to an NSEvent.
+/// \@end // to an NSEvent.
+/// \endcode
///
-/// Unlike C/C++, forward class declarations are accomplished with @class.
-/// Unlike C/C++, @class allows for a list of classes to be forward declared.
+/// Unlike C/C++, forward class declarations are accomplished with \@class.
+/// Unlike C/C++, \@class allows for a list of classes to be forward declared.
/// Unlike C++, ObjC is a single-rooted class model. In Cocoa, classes
/// typically inherit from NSObject (an exception is NSProxy).
///
@@ -566,10 +593,10 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
/// Class's super class.
ObjCInterfaceDecl *SuperClass;
- /// Protocols referenced in the @interface declaration
+ /// Protocols referenced in the \@interface declaration
ObjCProtocolList ReferencedProtocols;
- /// Protocols reference in both the @interface and class extensions.
+ /// Protocols reference in both the \@interface and class extensions.
ObjCList<ObjCProtocolDecl> AllReferencedProtocols;
/// \brief List of categories and class extensions defined for this class.
@@ -799,21 +826,21 @@ public:
bool hasDefinition() const { return Data; }
/// \brief Retrieve the definition of this class, or NULL if this class
- /// has been forward-declared (with @class) but not yet defined (with
- /// @interface).
+ /// has been forward-declared (with \@class) but not yet defined (with
+ /// \@interface).
ObjCInterfaceDecl *getDefinition() {
return hasDefinition()? Data->Definition : 0;
}
/// \brief Retrieve the definition of this class, or NULL if this class
- /// has been forward-declared (with @class) but not yet defined (with
- /// @interface).
+ /// has been forward-declared (with \@class) but not yet defined (with
+ /// \@interface).
const ObjCInterfaceDecl *getDefinition() const {
return hasDefinition()? Data->Definition : 0;
}
/// \brief Starts the definition of this Objective-C class, taking it from
- /// a forward declaration (@class) to a definition (@interface).
+ /// a forward declaration (\@class) to a definition (\@interface).
void startDefinition();
ObjCInterfaceDecl *getSuperClass() const {
@@ -879,8 +906,8 @@ public:
}
/// isObjCRequiresPropertyDefs - Checks that a class or one of its super
- /// classes must not be auto-synthesized. Returns class decl. if it must not be;
- /// 0, otherwise.
+ /// classes must not be auto-synthesized. Returns class decl. if it must not
+ /// be; 0, otherwise.
const ObjCInterfaceDecl *isObjCRequiresPropertyDefs() const {
const ObjCInterfaceDecl *Class = this;
while (Class) {
@@ -912,8 +939,13 @@ public:
}
ObjCInterfaceDecl *lookupInheritedClass(const IdentifierInfo *ICName);
- // Lookup a method in the classes implementation hierarchy.
- ObjCMethodDecl *lookupPrivateMethod(const Selector &Sel, bool Instance=true);
+ /// \brief Lookup a method in the classes implementation hierarchy.
+ ObjCMethodDecl *lookupPrivateMethod(const Selector &Sel,
+ bool Instance=true) const;
+
+ ObjCMethodDecl *lookupPrivateClassMethod(const Selector &Sel) {
+ return lookupPrivateMethod(Sel, false);
+ }
SourceLocation getEndOfDefinitionLoc() const {
if (!hasDefinition())
@@ -928,8 +960,8 @@ public:
SourceLocation getSuperClassLoc() const { return data().SuperClassLoc; }
/// isImplicitInterfaceDecl - check that this is an implicitly declared
- /// ObjCInterfaceDecl node. This is for legacy objective-c @implementation
- /// declaration without an @interface declaration.
+ /// ObjCInterfaceDecl node. This is for legacy objective-c \@implementation
+ /// declaration without an \@interface declaration.
bool isImplicitInterfaceDecl() const {
return hasDefinition() ? Data->Definition->isImplicit() : isImplicit();
}
@@ -972,14 +1004,14 @@ public:
/// instance variables are identical to C. The only exception is Objective-C
/// supports C++ style access control. For example:
///
-/// @interface IvarExample : NSObject
+/// \@interface IvarExample : NSObject
/// {
/// id defaultToProtected;
-/// @public:
+/// \@public:
/// id canBePublic; // same as C++.
-/// @protected:
+/// \@protected:
/// id canBeProtected; // same as C++.
-/// @package:
+/// \@package:
/// id canBePackage; // framework visibility (not available in C++).
/// }
///
@@ -997,7 +1029,7 @@ private:
QualType T, TypeSourceInfo *TInfo, AccessControl ac, Expr *BW,
bool synthesized)
: FieldDecl(ObjCIvar, DC, StartLoc, IdLoc, Id, T, TInfo, BW,
- /*Mutable=*/false, /*HasInit=*/false),
+ /*Mutable=*/false, /*HasInit=*/ICIS_NoInit),
NextIvar(0), DeclAccess(ac), Synthesized(synthesized) {}
public:
@@ -1046,8 +1078,7 @@ private:
};
-/// ObjCAtDefsFieldDecl - Represents a field declaration created by an
-/// @defs(...).
+/// \brief Represents a field declaration created by an \@defs(...).
class ObjCAtDefsFieldDecl : public FieldDecl {
virtual void anchor();
ObjCAtDefsFieldDecl(DeclContext *DC, SourceLocation StartLoc,
@@ -1055,7 +1086,7 @@ class ObjCAtDefsFieldDecl : public FieldDecl {
QualType T, Expr *BW)
: FieldDecl(ObjCAtDefsField, DC, StartLoc, IdLoc, Id, T,
/*TInfo=*/0, // FIXME: Do ObjCAtDefs have declarators ?
- BW, /*Mutable=*/false, /*HasInit=*/false) {}
+ BW, /*Mutable=*/false, /*HasInit=*/ICIS_NoInit) {}
public:
static ObjCAtDefsFieldDecl *Create(ASTContext &C, DeclContext *DC,
@@ -1071,29 +1102,35 @@ public:
static bool classofKind(Kind K) { return K == ObjCAtDefsField; }
};
-/// ObjCProtocolDecl - Represents a protocol declaration. ObjC protocols
-/// declare a pure abstract type (i.e no instance variables are permitted).
-/// Protocols originally drew inspiration from C++ pure virtual functions (a C++
-/// feature with nice semantics and lousy syntax:-). Here is an example:
+/// \brief Represents an Objective-C protocol declaration.
+///
+/// Objective-C protocols declare a pure abstract type (i.e., no instance
+/// variables are permitted). Protocols originally drew inspiration from
+/// C++ pure virtual functions (a C++ feature with nice semantics and lousy
+/// syntax:-). Here is an example:
///
-/// @protocol NSDraggingInfo <refproto1, refproto2>
+/// \code
+/// \@protocol NSDraggingInfo <refproto1, refproto2>
/// - (NSWindow *)draggingDestinationWindow;
/// - (NSImage *)draggedImage;
-/// @end
+/// \@end
+/// \endcode
///
/// This says that NSDraggingInfo requires two methods and requires everything
/// that the two "referenced protocols" 'refproto1' and 'refproto2' require as
/// well.
///
-/// @interface ImplementsNSDraggingInfo : NSObject <NSDraggingInfo>
-/// @end
+/// \code
+/// \@interface ImplementsNSDraggingInfo : NSObject \<NSDraggingInfo>
+/// \@end
+/// \endcode
///
/// ObjC protocols inspired Java interfaces. Unlike Java, ObjC classes and
/// protocols are in distinct namespaces. For example, Cocoa defines both
/// an NSObject protocol and class (which isn't allowed in Java). As a result,
/// protocols are referenced using angle brackets as follows:
///
-/// id <NSDraggingInfo> anyObjectThatImplementsNSDraggingInfo;
+/// id \<NSDraggingInfo> anyObjectThatImplementsNSDraggingInfo;
///
class ObjCProtocolDecl : public ObjCContainerDecl,
public Redeclarable<ObjCProtocolDecl> {
@@ -1255,9 +1292,9 @@ public:
/// you to add instance data. The following example adds "myMethod" to all
/// NSView's within a process:
///
-/// @interface NSView (MyViewMethods)
+/// \@interface NSView (MyViewMethods)
/// - myMethod;
-/// @end
+/// \@end
///
/// Categories also allow you to split the implementation of a class across
/// several files (a feature more naturally supported in C++).
@@ -1279,9 +1316,6 @@ class ObjCCategoryDecl : public ObjCContainerDecl {
/// FIXME: this should not be a singly-linked list. Move storage elsewhere.
ObjCCategoryDecl *NextClassCategory;
- /// true of class extension has at least one bitfield ivar.
- bool HasSynthBitfield : 1;
-
/// \brief The location of the category name in this declaration.
SourceLocation CategoryNameLoc;
@@ -1295,7 +1329,7 @@ class ObjCCategoryDecl : public ObjCContainerDecl {
SourceLocation IvarLBraceLoc=SourceLocation(),
SourceLocation IvarRBraceLoc=SourceLocation())
: ObjCContainerDecl(ObjCCategory, DC, Id, ClassNameLoc, AtLoc),
- ClassInterface(IDecl), NextClassCategory(0), HasSynthBitfield(false),
+ ClassInterface(IDecl), NextClassCategory(0),
CategoryNameLoc(CategoryNameLoc),
IvarLBraceLoc(IvarLBraceLoc), IvarRBraceLoc(IvarRBraceLoc) {
}
@@ -1345,9 +1379,6 @@ public:
bool IsClassExtension() const { return getIdentifier() == 0; }
const ObjCCategoryDecl *getNextClassExtension() const;
- bool hasSynthBitfield() const { return HasSynthBitfield; }
- void setHasSynthBitfield (bool val) { HasSynthBitfield = val; }
-
typedef specific_decl_iterator<ObjCIvarDecl> ivar_iterator;
ivar_iterator ivar_begin() const {
return ivar_iterator(decls_begin());
@@ -1431,16 +1462,16 @@ public:
};
/// ObjCCategoryImplDecl - An object of this class encapsulates a category
-/// @implementation declaration. If a category class has declaration of a
+/// \@implementation declaration. If a category class has declaration of a
/// property, its implementation must be specified in the category's
-/// @implementation declaration. Example:
-/// @interface I @end
-/// @interface I(CATEGORY)
-/// @property int p1, d1;
-/// @end
-/// @implementation I(CATEGORY)
-/// @dynamic p1,d1;
-/// @end
+/// \@implementation declaration. Example:
+/// \@interface I \@end
+/// \@interface I(CATEGORY)
+/// \@property int p1, d1;
+/// \@end
+/// \@implementation I(CATEGORY)
+/// \@dynamic p1,d1;
+/// \@end
///
/// ObjCCategoryImplDecl
class ObjCCategoryImplDecl : public ObjCImplDecl {
@@ -1493,15 +1524,6 @@ public:
return Id ? Id->getNameStart() : "";
}
- /// getNameAsCString - Get the name of identifier for the class
- /// interface associated with this implementation as a C string
- /// (const char*).
- //
- // FIXME: Deprecated, move clients to getName().
- const char *getNameAsCString() const {
- return Id ? Id->getNameStart() : "";
- }
-
/// @brief Get the name of the class associated with this interface.
//
// FIXME: Deprecated, move clients to getName().
@@ -1523,9 +1545,9 @@ raw_ostream &operator<<(raw_ostream &OS, const ObjCCategoryImplDecl &CID);
/// method definitions are specified. For example:
///
/// @code
-/// @implementation MyClass
+/// \@implementation MyClass
/// - (void)myMethod { /* do something */ }
-/// @end
+/// \@end
/// @endcode
///
/// Typically, instance variables are specified in the class interface,
@@ -1537,7 +1559,7 @@ class ObjCImplementationDecl : public ObjCImplDecl {
virtual void anchor();
/// Implementation Class's super class.
ObjCInterfaceDecl *SuperClass;
- /// @implementation may have private ivars.
+ /// \@implementation may have private ivars.
SourceLocation IvarLBraceLoc;
SourceLocation IvarRBraceLoc;
@@ -1549,9 +1571,6 @@ class ObjCImplementationDecl : public ObjCImplDecl {
/// true if class has a .cxx_[construct,destruct] method.
bool HasCXXStructors : 1;
- /// true of class extension has at least one bitfield ivar.
- bool HasSynthBitfield : 1;
-
ObjCImplementationDecl(DeclContext *DC,
ObjCInterfaceDecl *classInterface,
ObjCInterfaceDecl *superDecl,
@@ -1562,7 +1581,7 @@ class ObjCImplementationDecl : public ObjCImplDecl {
SuperClass(superDecl), IvarLBraceLoc(IvarLBraceLoc),
IvarRBraceLoc(IvarRBraceLoc),
IvarInitializers(0), NumIvarInitializers(0),
- HasCXXStructors(false), HasSynthBitfield(false){}
+ HasCXXStructors(false) {}
public:
static ObjCImplementationDecl *Create(ASTContext &C, DeclContext *DC,
ObjCInterfaceDecl *classInterface,
@@ -1609,9 +1628,6 @@ public:
bool hasCXXStructors() const { return HasCXXStructors; }
void setHasCXXStructors(bool val) { HasCXXStructors = val; }
- bool hasSynthBitfield() const { return HasSynthBitfield; }
- void setHasSynthBitfield (bool val) { HasSynthBitfield = val; }
-
/// getIdentifier - Get the identifier that names the class
/// interface associated with this implementation.
IdentifierInfo *getIdentifier() const {
@@ -1628,15 +1644,6 @@ public:
return getIdentifier()->getName();
}
- /// getNameAsCString - Get the name of identifier for the class
- /// interface associated with this implementation as a C string
- /// (const char*).
- //
- // FIXME: Move to StringRef API.
- const char *getNameAsCString() const {
- return getName().data();
- }
-
/// @brief Get the name of the class associated with this interface.
//
// FIXME: Move to StringRef API.
@@ -1679,7 +1686,7 @@ public:
raw_ostream &operator<<(raw_ostream &OS, const ObjCImplementationDecl &ID);
/// ObjCCompatibleAliasDecl - Represents alias of a class. This alias is
-/// declared as @compatibility_alias alias class.
+/// declared as \@compatibility_alias alias class.
class ObjCCompatibleAliasDecl : public NamedDecl {
virtual void anchor();
/// Class that this is an alias of.
@@ -1706,10 +1713,12 @@ public:
};
-/// ObjCPropertyDecl - Represents one property declaration in an interface.
-/// For example:
-/// @property (assign, readwrite) int MyProperty;
+/// \brief Represents one property declaration in an Objective-C interface.
///
+/// For example:
+/// \code{.mm}
+/// \@property (assign, readwrite) int MyProperty;
+/// \endcode
class ObjCPropertyDecl : public NamedDecl {
virtual void anchor();
public:
@@ -1738,12 +1747,12 @@ public:
enum SetterKind { Assign, Retain, Copy, Weak };
enum PropertyControl { None, Required, Optional };
private:
- SourceLocation AtLoc; // location of @property
+ SourceLocation AtLoc; // location of \@property
SourceLocation LParenLoc; // location of '(' starting attribute list or null.
TypeSourceInfo *DeclType;
unsigned PropertyAttributes : NumPropertyAttrsBits;
unsigned PropertyAttributesAsWritten : NumPropertyAttrsBits;
- // @required/@optional
+ // \@required/\@optional
unsigned PropertyImplementation : 2;
Selector GetterName; // getter name of NULL if no getter
@@ -1855,7 +1864,7 @@ public:
ObjCMethodDecl *getSetterMethodDecl() const { return SetterMethodDecl; }
void setSetterMethodDecl(ObjCMethodDecl *gDecl) { SetterMethodDecl = gDecl; }
- // Related to @optional/@required declared in @protocol
+ // Related to \@optional/\@required declared in \@protocol
void setPropertyImplementation(PropertyControl pc) {
PropertyImplementation = pc;
}
@@ -1885,7 +1894,7 @@ public:
/// ObjCPropertyImplDecl - Represents implementation declaration of a property
/// in a class or category implementation block. For example:
-/// @synthesize prop1 = ivar1;
+/// \@synthesize prop1 = ivar1;
///
class ObjCPropertyImplDecl : public Decl {
public:
@@ -1894,26 +1903,27 @@ public:
Dynamic
};
private:
- SourceLocation AtLoc; // location of @synthesize or @dynamic
+ SourceLocation AtLoc; // location of \@synthesize or \@dynamic
- /// \brief For @synthesize, the location of the ivar, if it was written in
+ /// \brief For \@synthesize, the location of the ivar, if it was written in
/// the source code.
///
/// \code
- /// @synthesize int a = b
+ /// \@synthesize int a = b
/// \endcode
SourceLocation IvarLoc;
/// Property declaration being implemented
ObjCPropertyDecl *PropertyDecl;
- /// Null for @dynamic. Required for @synthesize.
+ /// Null for \@dynamic. Required for \@synthesize.
ObjCIvarDecl *PropertyIvarDecl;
- /// Null for @dynamic. Non-null if property must be copy-constructed in getter
+ /// Null for \@dynamic. Non-null if property must be copy-constructed in
+ /// getter.
Expr *GetterCXXConstructor;
- /// Null for @dynamic. Non-null if property has assignment operator to call
+ /// Null for \@dynamic. Non-null if property has assignment operator to call
/// in Setter synthesis.
Expr *SetterCXXAssignment;
@@ -1963,6 +1973,17 @@ public:
this->IvarLoc = IvarLoc;
}
+ /// \brief For \@synthesize, returns true if an ivar name was explicitly
+ /// specified.
+ ///
+ /// \code
+ /// \@synthesize int a = b; // true
+ /// \@synthesize int a; // false
+ /// \endcode
+ bool isIvarNameSpecified() const {
+ return IvarLoc.isValid() && IvarLoc != getLocation();
+ }
+
Expr *getGetterCXXConstructor() const {
return GetterCXXConstructor;
}
diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h
index 36549ea..7affc7e 100644
--- a/include/clang/AST/DeclTemplate.h
+++ b/include/clang/AST/DeclTemplate.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the C++ template declaration subclasses.
-//
+///
+/// \file
+/// \brief Defines the C++ template declaration subclasses.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_DECLTEMPLATE_H
@@ -38,8 +39,8 @@ class TypeAliasTemplateDecl;
typedef llvm::PointerUnion3<TemplateTypeParmDecl*, NonTypeTemplateParmDecl*,
TemplateTemplateParmDecl*> TemplateParameter;
-/// TemplateParameterList - Stores a list of template parameters for a
-/// TemplateDecl and its derived classes.
+/// \brief Stores a list of template parameters for a TemplateDecl and its
+/// derived classes.
class TemplateParameterList {
/// The location of the 'template' keyword.
SourceLocation TemplateLoc;
@@ -64,10 +65,10 @@ public:
unsigned NumParams,
SourceLocation RAngleLoc);
- /// iterator - Iterates through the template parameters in this list.
+ /// \brief Iterates through the template parameters in this list.
typedef NamedDecl** iterator;
- /// const_iterator - Iterates through the template parameters in this list.
+ /// \brief Iterates through the template parameters in this list.
typedef NamedDecl* const* const_iterator;
iterator begin() { return reinterpret_cast<NamedDecl **>(this + 1); }
@@ -90,9 +91,10 @@ public:
}
/// \brief Returns the minimum number of arguments needed to form a
- /// template specialization. This may be fewer than the number of
- /// template parameters, if some of the parameters have default
- /// arguments or if there is a parameter pack.
+ /// template specialization.
+ ///
+ /// This may be fewer than the number of template parameters, if some of
+ /// the parameters have default arguments or if there is a parameter pack.
unsigned getMinRequiredArguments() const;
/// \brief Get the depth of this template parameter list in the set of
@@ -111,8 +113,8 @@ public:
}
};
-/// FixedSizeTemplateParameterList - Stores a list of template parameters for a
-/// TemplateDecl and its derived classes. Suitable for creating on the stack.
+/// \brief Stores a list of template parameters for a TemplateDecl and its
+/// derived classes. Suitable for creating on the stack.
template<size_t N>
class FixedSizeTemplateParameterList : public TemplateParameterList {
NamedDecl *Params[N];
@@ -195,10 +197,11 @@ public:
// Kinds of Templates
//===----------------------------------------------------------------------===//
-/// TemplateDecl - The base class of all kinds of template declarations (e.g.,
-/// class, function, etc.). The TemplateDecl class stores the list of template
-/// parameters and a reference to the templated scoped declaration: the
-/// underlying AST node.
+/// \brief The base class of all kinds of template declarations (e.g.,
+/// class, function, etc.).
+///
+/// The TemplateDecl class stores the list of template parameters and a
+/// reference to the templated scoped declaration: the underlying AST node.
class TemplateDecl : public NamedDecl {
virtual void anchor();
protected:
@@ -404,16 +407,19 @@ public:
};
/// \brief Provides information about a dependent function-template
-/// specialization declaration. Since explicit function template
-/// specialization and instantiation declarations can only appear in
-/// namespace scope, and you can only specialize a member of a
-/// fully-specialized class, the only way to get one of these is in
-/// a friend declaration like the following:
+/// specialization declaration.
+///
+/// Since explicit function template specialization and instantiation
+/// declarations can only appear in namespace scope, and you can only
+/// specialize a member of a fully-specialized class, the only way to
+/// get one of these is in a friend declaration like the following:
///
-/// template <class T> void foo(T);
-/// template <class T> class A {
+/// \code
+/// template \<class T> void foo(T);
+/// template \<class T> class A {
/// friend void foo<>(T);
/// };
+/// \endcode
class DependentFunctionTemplateSpecializationInfo {
union {
// Force sizeof to be a multiple of sizeof(void*) so that the
@@ -512,7 +518,8 @@ protected:
typedef _SETraits SETraits;
typedef _DeclType DeclType;
- typedef typename llvm::FoldingSet<EntryType>::iterator SetIteratorType;
+ typedef typename llvm::FoldingSetVector<EntryType>::iterator
+ SetIteratorType;
SetIteratorType SetIter;
@@ -541,13 +548,13 @@ protected:
};
template <typename EntryType>
- SpecIterator<EntryType> makeSpecIterator(llvm::FoldingSet<EntryType> &Specs,
- bool isEnd) {
+ SpecIterator<EntryType>
+ makeSpecIterator(llvm::FoldingSetVector<EntryType> &Specs, bool isEnd) {
return SpecIterator<EntryType>(isEnd ? Specs.end() : Specs.begin());
}
template <class EntryType> typename SpecEntryTraits<EntryType>::DeclType*
- findSpecializationImpl(llvm::FoldingSet<EntryType> &Specs,
+ findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
const TemplateArgument *Args, unsigned NumArgs,
void *&InsertPos);
@@ -583,7 +590,7 @@ protected:
public:
template <class decl_type> friend class RedeclarableTemplate;
- /// Retrieves the canonical declaration of this template.
+ /// \brief Retrieves the canonical declaration of this template.
RedeclarableTemplateDecl *getCanonicalDecl() { return getFirstDeclaration(); }
const RedeclarableTemplateDecl *getCanonicalDecl() const {
return getFirstDeclaration();
@@ -646,7 +653,7 @@ public:
///
/// which was itself created during the instantiation of \c X<int>. Calling
/// getInstantiatedFromMemberTemplate() on this FunctionTemplateDecl will
- /// retrieve the FunctionTemplateDecl for the original template "f" within
+ /// retrieve the FunctionTemplateDecl for the original template \c f within
/// the class template \c X<T>, i.e.,
///
/// \code
@@ -706,7 +713,7 @@ protected:
/// \brief The function template specializations for this function
/// template, including explicit specializations and instantiations.
- llvm::FoldingSet<FunctionTemplateSpecializationInfo> Specializations;
+ llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> Specializations;
/// \brief The set of "injected" template arguments used within this
/// function template.
@@ -732,13 +739,14 @@ protected:
/// \brief Retrieve the set of function template specializations of this
/// function template.
- llvm::FoldingSet<FunctionTemplateSpecializationInfo> &getSpecializations() {
+ llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> &
+ getSpecializations() {
return getCommonPtr()->Specializations;
}
/// \brief Add a specialization of this function template.
///
- /// \param InsertPos Insert position in the FoldingSet, must have been
+ /// \param InsertPos Insert position in the FoldingSetVector, must have been
/// retrieved by an earlier call to findSpecialization().
void addSpecialization(FunctionTemplateSpecializationInfo* Info,
void *InsertPos);
@@ -830,8 +838,10 @@ public:
// Kinds of Template Parameters
//===----------------------------------------------------------------------===//
-/// The TemplateParmPosition class defines the position of a template parameter
-/// within a template parameter list. Because template parameter can be listed
+/// \brief Defines the position of a template parameter within a template
+/// parameter list.
+///
+/// Because template parameter can be listed
/// sequentially for out-of-line template members, each template parameter is
/// given a Depth - the nesting of template parameter scopes - and a Position -
/// the occurrence within the parameter list.
@@ -866,15 +876,17 @@ public:
unsigned getIndex() const { return Position; }
};
-/// TemplateTypeParmDecl - Declaration of a template type parameter,
-/// e.g., "T" in
-/// @code
+/// \brief Declaration of a template type parameter.
+///
+/// For example, "T" in
+/// \code
/// template<typename T> class vector;
-/// @endcode
+/// \endcode
class TemplateTypeParmDecl : public TypeDecl {
/// \brief Whether this template type parameter was declaration with
- /// the 'typename' keyword. If false, it was declared with the
- /// 'class' keyword.
+ /// the 'typename' keyword.
+ ///
+ /// If false, it was declared with the 'class' keyword.
bool Typename : 1;
/// \brief Whether this template type parameter inherited its
@@ -904,8 +916,9 @@ public:
unsigned ID);
/// \brief Whether this template type parameter was declared with
- /// the 'typename' keyword. If not, it was declared with the 'class'
- /// keyword.
+ /// the 'typename' keyword.
+ ///
+ /// If not, it was declared with the 'class' keyword.
bool wasDeclaredWithTypename() const { return Typename; }
/// \brief Determine whether this template parameter has a default
@@ -1688,11 +1701,11 @@ protected:
/// \brief The class template specializations for this class
/// template, including explicit specializations and instantiations.
- llvm::FoldingSet<ClassTemplateSpecializationDecl> Specializations;
+ llvm::FoldingSetVector<ClassTemplateSpecializationDecl> Specializations;
/// \brief The class template partial specializations for this class
/// template.
- llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>
+ llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>
PartialSpecializations;
/// \brief The injected-class-name type for this class template.
@@ -1710,11 +1723,11 @@ protected:
void LoadLazySpecializations();
/// \brief Retrieve the set of specializations of this class template.
- llvm::FoldingSet<ClassTemplateSpecializationDecl> &getSpecializations();
+ llvm::FoldingSetVector<ClassTemplateSpecializationDecl> &getSpecializations();
/// \brief Retrieve the set of partial specializations of this class
/// template.
- llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> &
+ llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
getPartialSpecializations();
ClassTemplateDecl(DeclContext *DC, SourceLocation L, DeclarationName Name,
@@ -1732,18 +1745,18 @@ protected:
}
public:
- /// Get the underlying class declarations of the template.
+ /// \brief Get the underlying class declarations of the template.
CXXRecordDecl *getTemplatedDecl() const {
return static_cast<CXXRecordDecl *>(TemplatedDecl);
}
- /// Returns whether this template declaration defines the primary
+ /// \brief Returns whether this template declaration defines the primary
/// class pattern.
bool isThisDeclarationADefinition() const {
return getTemplatedDecl()->isThisDeclarationADefinition();
}
- /// Create a class template node.
+ /// \brief Create a class template node.
static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation L,
DeclarationName Name,
@@ -1751,7 +1764,7 @@ public:
NamedDecl *Decl,
ClassTemplateDecl *PrevDecl);
- /// Create an empty class template node.
+ /// \brief Create an empty class template node.
static ClassTemplateDecl *CreateDeserialized(ASTContext &C, unsigned ID);
/// \brief Return the specialization with the provided arguments if it exists,
@@ -1880,14 +1893,18 @@ public:
friend class ASTDeclWriter;
};
-/// Declaration of a friend template. For example:
+/// \brief Declaration of a friend template.
///
-/// template <typename T> class A {
+/// For example:
+/// \code
+/// template \<typename T> class A {
/// friend class MyVector<T>; // not a friend template
-/// template <typename U> friend class B; // not a friend template
-/// template <typename U> friend class Foo<T>::Nested; // friend template
+/// template \<typename U> friend class B; // not a friend template
+/// template \<typename U> friend class Foo<T>::Nested; // friend template
/// };
-/// NOTE: This class is not currently in use. All of the above
+/// \endcode
+///
+/// \note This class is not currently in use. All of the above
/// will yield a FriendDecl, not a FriendTemplateDecl.
class FriendTemplateDecl : public Decl {
virtual void anchor();
@@ -1950,7 +1967,7 @@ public:
return Friend.dyn_cast<NamedDecl*>();
}
- /// Retrieves the location of the 'friend' keyword.
+ /// \brief Retrieves the location of the 'friend' keyword.
SourceLocation getFriendLoc() const {
return FriendLoc;
}
@@ -1972,9 +1989,12 @@ public:
friend class ASTDeclReader;
};
-/// Declaration of an alias template. For example:
+/// \brief Declaration of an alias template.
///
-/// template <typename T> using V = std::map<T*, int, MyCompare<T>>;
+/// For example:
+/// \code
+/// template \<typename T> using V = std::map<T*, int, MyCompare<T>>;
+/// \endcode
class TypeAliasTemplateDecl : public RedeclarableTemplateDecl {
static void DeallocateCommon(void *Ptr);
@@ -2046,14 +2066,18 @@ public:
friend class ASTDeclWriter;
};
-/// Declaration of a function specialization at template class scope.
+/// \brief Declaration of a function specialization at template class scope.
+///
/// This is a non standard extension needed to support MSVC.
+///
/// For example:
+/// \code
/// template <class T>
/// class A {
/// template <class U> void foo(U a) { }
/// template<> void foo(int a) { }
/// }
+/// \endcode
///
/// "template<> foo(int a)" will be saved in Specialization as a normal
/// CXXMethodDecl. Then during an instantiation of class A, it will be
@@ -2062,23 +2086,33 @@ class ClassScopeFunctionSpecializationDecl : public Decl {
virtual void anchor();
ClassScopeFunctionSpecializationDecl(DeclContext *DC, SourceLocation Loc,
- CXXMethodDecl *FD)
+ CXXMethodDecl *FD, bool Args,
+ TemplateArgumentListInfo TemplArgs)
: Decl(Decl::ClassScopeFunctionSpecialization, DC, Loc),
- Specialization(FD) {}
+ Specialization(FD), HasExplicitTemplateArgs(Args),
+ TemplateArgs(TemplArgs) {}
ClassScopeFunctionSpecializationDecl(EmptyShell Empty)
: Decl(Decl::ClassScopeFunctionSpecialization, Empty) {}
CXXMethodDecl *Specialization;
+ bool HasExplicitTemplateArgs;
+ TemplateArgumentListInfo TemplateArgs;
public:
CXXMethodDecl *getSpecialization() const { return Specialization; }
+ bool hasExplicitTemplateArgs() const { return HasExplicitTemplateArgs; }
+ const TemplateArgumentListInfo& templateArgs() const { return TemplateArgs; }
static ClassScopeFunctionSpecializationDecl *Create(ASTContext &C,
DeclContext *DC,
SourceLocation Loc,
- CXXMethodDecl *FD) {
- return new (C) ClassScopeFunctionSpecializationDecl(DC , Loc, FD);
+ CXXMethodDecl *FD,
+ bool HasExplicitTemplateArgs,
+ TemplateArgumentListInfo TemplateArgs) {
+ return new (C) ClassScopeFunctionSpecializationDecl(DC , Loc, FD,
+ HasExplicitTemplateArgs,
+ TemplateArgs);
}
static ClassScopeFunctionSpecializationDecl *
diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h
index 6349d9c..6146525 100644
--- a/include/clang/AST/DeclarationName.h
+++ b/include/clang/AST/DeclarationName.h
@@ -58,11 +58,14 @@ public:
private:
/// StoredNameKind - The kind of name that is actually stored in the
/// upper bits of the Ptr field. This is only used internally.
+ ///
+ /// Note: The entries here are synchronized with the entries in Selector,
+ /// for efficient translation between the two.
enum StoredNameKind {
StoredIdentifier = 0,
- StoredObjCZeroArgSelector,
- StoredObjCOneArgSelector,
- StoredDeclarationNameExtra,
+ StoredObjCZeroArgSelector = 0x01,
+ StoredObjCOneArgSelector = 0x02,
+ StoredDeclarationNameExtra = 0x03,
PtrMask = 0x03
};
@@ -106,8 +109,8 @@ private:
/// CXXSpecialName, returns a pointer to it. Otherwise, returns
/// a NULL pointer.
CXXSpecialName *getAsCXXSpecialName() const {
- if (getNameKind() >= CXXConstructorName &&
- getNameKind() <= CXXConversionFunctionName)
+ NameKind Kind = getNameKind();
+ if (Kind >= CXXConstructorName && Kind <= CXXConversionFunctionName)
return reinterpret_cast<CXXSpecialName *>(Ptr & ~PtrMask);
return 0;
}
@@ -153,9 +156,9 @@ private:
friend class DeclarationNameTable;
friend class NamedDecl;
- /// getFETokenInfoAsVoid - Retrieves the front end-specified pointer
- /// for this name as a void pointer.
- void *getFETokenInfoAsVoid() const;
+ /// getFETokenInfoAsVoidSlow - Retrieves the front end-specified pointer
+ /// for this name as a void pointer if it's not an identifier.
+ void *getFETokenInfoAsVoidSlow() const;
public:
/// DeclarationName - Used to create an empty selector.
@@ -168,7 +171,7 @@ public:
}
// Construct a declaration name from an Objective-C selector.
- DeclarationName(Selector Sel);
+ DeclarationName(Selector Sel) : Ptr(Sel.InfoPtr) { }
/// getUsingDirectiveName - Return name for all using-directives.
static DeclarationName getUsingDirectiveName();
@@ -251,14 +254,24 @@ public:
/// getObjCSelector - Get the Objective-C selector stored in this
/// declaration name.
- Selector getObjCSelector() const;
+ Selector getObjCSelector() const {
+ assert((getNameKind() == ObjCZeroArgSelector ||
+ getNameKind() == ObjCOneArgSelector ||
+ getNameKind() == ObjCMultiArgSelector ||
+ Ptr == 0) && "Not a selector!");
+ return Selector(Ptr);
+ }
/// getFETokenInfo/setFETokenInfo - The language front-end is
/// allowed to associate arbitrary metadata with some kinds of
/// declaration names, including normal identifiers and C++
/// constructors, destructors, and conversion functions.
template<typename T>
- T *getFETokenInfo() const { return static_cast<T*>(getFETokenInfoAsVoid()); }
+ T *getFETokenInfo() const {
+ if (const IdentifierInfo *Info = getAsIdentifierInfo())
+ return Info->getFETokenInfo<T>();
+ return static_cast<T*>(getFETokenInfoAsVoidSlow());
+ }
void setFETokenInfo(void *T);
@@ -564,7 +577,9 @@ struct DenseMapInfo<clang::DeclarationName> {
return clang::DeclarationName::getTombstoneMarker();
}
- static unsigned getHashValue(clang::DeclarationName);
+ static unsigned getHashValue(clang::DeclarationName Name) {
+ return DenseMapInfo<void*>::getHashValue(Name.getAsOpaquePtr());
+ }
static inline bool
isEqual(clang::DeclarationName LHS, clang::DeclarationName RHS) {
diff --git a/include/clang/AST/EvaluatedExprVisitor.h b/include/clang/AST/EvaluatedExprVisitor.h
index bab1606..d5e9c8c 100644
--- a/include/clang/AST/EvaluatedExprVisitor.h
+++ b/include/clang/AST/EvaluatedExprVisitor.h
@@ -24,7 +24,7 @@ namespace clang {
class ASTContext;
-/// \begin Given a potentially-evaluated expression, this visitor visits all
+/// \brief Given a potentially-evaluated expression, this visitor visits all
/// of its potentially-evaluated subexpressions, recursively.
template<typename ImplClass>
class EvaluatedExprVisitor : public StmtVisitor<ImplClass> {
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index b0b9b0f..89c003c 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_AST_EXPR_H
#include "clang/AST/APValue.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/AST/DeclAccessPair.h"
@@ -179,11 +180,12 @@ public:
SourceLocation getExprLoc() const LLVM_READONLY;
/// isUnusedResultAWarning - Return true if this immediate expression should
- /// be warned about if the result is unused. If so, fill in Loc and Ranges
- /// with location to warn on and the source range[s] to report with the
- /// warning.
- bool isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
- SourceRange &R2, ASTContext &Ctx) const;
+ /// be warned about if the result is unused. If so, fill in expr, location,
+ /// and ranges with expr to warn on and source locations/ranges appropriate
+ /// for a warning.
+ bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc,
+ SourceRange &R1, SourceRange &R2,
+ ASTContext &Ctx) const;
/// isLValue - True if this expression is an "l-value" according to
/// the rules of the current language. C and C++ give somewhat
@@ -212,7 +214,8 @@ public:
LV_InvalidMessageExpression,
LV_MemberFunction,
LV_SubObjCPropertySetting,
- LV_ClassTemporary
+ LV_ClassTemporary,
+ LV_ArrayTemporary
};
/// Reasons why an expression might not be an l-value.
LValueClassification ClassifyLValue(ASTContext &Ctx) const;
@@ -223,7 +226,7 @@ public:
/// recursively, any member or element of all contained aggregates or unions)
/// with a const-qualified type.
///
- /// \param Loc [in] [out] - A source location which *may* be filled
+ /// \param Loc [in,out] - A source location which *may* be filled
/// in with the location of the expression making this a
/// non-modifiable lvalue, if specified.
enum isModifiableLvalueResult {
@@ -241,7 +244,8 @@ public:
MLV_MemberFunction,
MLV_SubObjCPropertySetting,
MLV_InvalidMessageExpression,
- MLV_ClassTemporary
+ MLV_ClassTemporary,
+ MLV_ArrayTemporary
};
isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx,
SourceLocation *Loc = 0) const;
@@ -260,7 +264,8 @@ public:
CL_DuplicateVectorComponents, // A vector shuffle with dupes.
CL_MemberFunction, // An expression referring to a member function
CL_SubObjCPropertySetting,
- CL_ClassTemporary, // A prvalue of class type
+ CL_ClassTemporary, // A temporary of class type, or subobject thereof.
+ CL_ArrayTemporary, // A temporary of array type.
CL_ObjCMessageRValue, // ObjC message is an rvalue
CL_PRValue // A prvalue for any other reason, of any other type
};
@@ -505,9 +510,8 @@ public:
bool isEvaluatable(const ASTContext &Ctx) const;
/// HasSideEffects - This routine returns true for all those expressions
- /// which must be evaluated each time and must not be optimized away
- /// or evaluated at compile time. Example is a function call, volatile
- /// variable read.
+ /// which have any effect other than producing a value. Example is a function
+ /// call, volatile variable read, or throwing an exception.
bool HasSideEffects(const ASTContext &Ctx) const;
/// \brief Determine whether this expression involves a call to any function
@@ -537,8 +541,15 @@ public:
/// \brief Expression is not a Null pointer constant.
NPCK_NotNull = 0,
- /// \brief Expression is a Null pointer constant built from a zero integer.
- NPCK_ZeroInteger,
+ /// \brief Expression is a Null pointer constant built from a zero integer
+ /// expression that is not a simple, possibly parenthesized, zero literal.
+ /// C++ Core Issue 903 will classify these expressions as "not pointers"
+ /// once it is adopted.
+ /// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#903
+ NPCK_ZeroExpression,
+
+ /// \brief Expression is a Null pointer constant built from a literal zero.
+ NPCK_ZeroLiteral,
/// \brief Expression is a C++0X nullptr.
NPCK_CXX0X_nullptr,
@@ -591,6 +602,10 @@ public:
return cast<Expr>(Stmt::IgnoreImplicit());
}
+ const Expr *IgnoreImplicit() const LLVM_READONLY {
+ return const_cast<Expr*>(this)->IgnoreImplicit();
+ }
+
/// IgnoreParens - Ignore parentheses. If this Expr is a ParenExpr, return
/// its subexpression. If that subexpression is also a ParenExpr,
/// then this method recursively returns its subexpression, and so forth.
@@ -630,6 +645,13 @@ public:
/// ParenExpr or CastExprs, returning their operand.
Expr *IgnoreParenNoopCasts(ASTContext &Ctx) LLVM_READONLY;
+ /// Ignore parentheses and derived-to-base casts.
+ Expr *ignoreParenBaseCasts() LLVM_READONLY;
+
+ const Expr *ignoreParenBaseCasts() const LLVM_READONLY {
+ return const_cast<Expr*>(this)->ignoreParenBaseCasts();
+ }
+
/// \brief Determine whether this expression is a default function argument.
///
/// Default arguments are implicitly generated in the abstract syntax tree
@@ -661,6 +683,15 @@ public:
static bool hasAnyTypeDependentArguments(llvm::ArrayRef<Expr *> Exprs);
+ /// \brief For an expression of class type or pointer to class type,
+ /// return the most derived class decl the expression is known to refer to.
+ ///
+ /// If this expression is a cast, this method looks through it to find the
+ /// most derived decl that can be inferred from the expression.
+ /// This is valid because derived-to-base conversions have undefined
+ /// behavior if the object isn't dynamically of the derived type.
+ const CXXRecordDecl *getBestDynamicClassType() const;
+
static bool classof(const Stmt *T) {
return T->getStmtClass() >= firstExprConstant &&
T->getStmtClass() <= lastExprConstant;
@@ -1043,6 +1074,7 @@ public:
enum IdentType {
Func,
Function,
+ LFunction, // Same as Function, but as wide string.
PrettyFunction,
/// PrettyFunctionNoVirtual - The same as PrettyFunction, except that the
/// 'virtual' keyword is omitted for virtual member functions.
@@ -1142,16 +1174,8 @@ class IntegerLiteral : public Expr, public APIntStorage {
public:
// type should be IntTy, LongTy, LongLongTy, UnsignedIntTy, UnsignedLongTy,
// or UnsignedLongLongTy
- IntegerLiteral(ASTContext &C, const llvm::APInt &V,
- QualType type, SourceLocation l)
- : Expr(IntegerLiteralClass, type, VK_RValue, OK_Ordinary, false, false,
- false, false),
- Loc(l) {
- assert(type->isIntegerType() && "Illegal type in IntegerLiteral");
- assert(V.getBitWidth() == C.getIntWidth(type) &&
- "Integer type is not the correct size for constant.");
- setValue(C, V);
- }
+ IntegerLiteral(ASTContext &C, const llvm::APInt &V, QualType type,
+ SourceLocation l);
/// \brief Returns a new integer literal with value 'V' and type 'type'.
/// \param type - either IntTy, LongTy, LongLongTy, UnsignedIntTy,
@@ -1229,22 +1253,10 @@ class FloatingLiteral : public Expr, private APFloatStorage {
SourceLocation Loc;
FloatingLiteral(ASTContext &C, const llvm::APFloat &V, bool isexact,
- QualType Type, SourceLocation L)
- : Expr(FloatingLiteralClass, Type, VK_RValue, OK_Ordinary, false, false,
- false, false), Loc(L) {
- FloatingLiteralBits.IsIEEE =
- &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad;
- FloatingLiteralBits.IsExact = isexact;
- setValue(C, V);
- }
+ QualType Type, SourceLocation L);
/// \brief Construct an empty floating-point literal.
- explicit FloatingLiteral(ASTContext &C, EmptyShell Empty)
- : Expr(FloatingLiteralClass, Empty) {
- FloatingLiteralBits.IsIEEE =
- &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad;
- FloatingLiteralBits.IsExact = false;
- }
+ explicit FloatingLiteral(ASTContext &C, EmptyShell Empty);
public:
static FloatingLiteral *Create(ASTContext &C, const llvm::APFloat &V,
@@ -1381,8 +1393,8 @@ public:
return StringRef(StrData.asChar, getByteLength());
}
- /// Allow clients that need the byte representation, such as ASTWriterStmt
- /// ::VisitStringLiteral(), access.
+ /// Allow access to clients that need the byte representation, such as
+ /// ASTWriterStmt::VisitStringLiteral().
StringRef getBytes() const {
// FIXME: StringRef may not be the right type to use as a result for this.
if (CharByteWidth == 1)
@@ -1395,6 +1407,8 @@ public:
getByteLength());
}
+ void outputString(raw_ostream &OS);
+
uint32_t getCodeUnit(size_t i) const {
assert(i < Length && "out of bounds access");
if (CharByteWidth == 1)
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index b69693d..ecfa9e2 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H
+#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/AST/TemplateBase.h"
@@ -50,14 +51,18 @@ class TemplateArgumentListInfo;
class CXXOperatorCallExpr : public CallExpr {
/// \brief The overloaded operator.
OverloadedOperatorKind Operator;
+ SourceRange Range;
+ SourceRange getSourceRangeImpl() const LLVM_READONLY;
public:
CXXOperatorCallExpr(ASTContext& C, OverloadedOperatorKind Op, Expr *fn,
Expr **args, unsigned numargs, QualType t,
ExprValueKind VK, SourceLocation operatorloc)
: CallExpr(C, CXXOperatorCallExprClass, fn, 0, args, numargs, t, VK,
operatorloc),
- Operator(Op) {}
+ Operator(Op) {
+ Range = getSourceRangeImpl();
+ }
explicit CXXOperatorCallExpr(ASTContext& C, EmptyShell Empty) :
CallExpr(C, CXXOperatorCallExprClass, Empty) { }
@@ -65,7 +70,6 @@ public:
/// getOperator - Returns the kind of overloaded operator that this
/// expression refers to.
OverloadedOperatorKind getOperator() const { return Operator; }
- void setOperator(OverloadedOperatorKind Kind) { Operator = Kind; }
/// getOperatorLoc - Returns the location of the operator symbol in
/// the expression. When @c getOperator()==OO_Call, this is the
@@ -74,12 +78,15 @@ public:
/// bracket.
SourceLocation getOperatorLoc() const { return getRParenLoc(); }
- SourceRange getSourceRange() const LLVM_READONLY;
+ SourceRange getSourceRange() const { return Range; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXOperatorCallExprClass;
}
static bool classof(const CXXOperatorCallExpr *) { return true; }
+
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
};
/// CXXMemberCallExpr - Represents a call to a member function that
@@ -112,7 +119,7 @@ public:
/// declaration as that of the class context of the CXXMethodDecl which this
/// function is calling.
/// FIXME: Returns 0 for member pointer call exprs.
- CXXRecordDecl *getRecordDecl();
+ CXXRecordDecl *getRecordDecl() const;
static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXMemberCallExprClass;
@@ -369,10 +376,21 @@ public:
return const_cast<UserDefinedLiteral*>(this)->getCookedLiteral();
}
+ SourceLocation getLocStart() const {
+ if (getLiteralOperatorKind() == LOK_Template)
+ return getRParenLoc();
+ return getArg(0)->getLocStart();
+ }
+ SourceLocation getLocEnd() const { return getRParenLoc(); }
+ SourceRange getSourceRange() const {
+ return SourceRange(getLocStart(), getLocEnd());
+ }
+
+
/// getUDSuffixLoc - Returns the location of a ud-suffix in the expression.
/// For a string literal, there may be multiple identical suffixes. This
/// returns the first.
- SourceLocation getUDSuffixLoc() const { return getRParenLoc(); }
+ SourceLocation getUDSuffixLoc() const { return UDSuffixLoc; }
/// getUDSuffix - Returns the ud-suffix specified for this literal.
const IdentifierInfo *getUDSuffix() const;
@@ -481,6 +499,10 @@ public:
Operand = (TypeSourceInfo*)0;
}
+ /// Determine whether this typeid has a type operand which is potentially
+ /// evaluated, per C++11 [expr.typeid]p3.
+ bool isPotentiallyEvaluated() const;
+
bool isTypeOperand() const { return Operand.is<TypeSourceInfo *>(); }
/// \brief Retrieves the type operand of this typeid() expression after
@@ -859,7 +881,7 @@ public:
child_range children() { return child_range(&SubExpr, &SubExpr + 1); }
};
-/// CXXConstructExpr - Represents a call to a C++ constructor.
+/// \brief Represents a call to a C++ constructor.
class CXXConstructExpr : public Expr {
public:
enum ConstructionKind {
@@ -983,6 +1005,7 @@ public:
SourceRange getSourceRange() const LLVM_READONLY;
SourceRange getParenRange() const { return ParenRange; }
+ void setParenRange(SourceRange Range) { ParenRange = Range; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXConstructExprClass ||
@@ -998,9 +1021,13 @@ public:
friend class ASTStmtReader;
};
-/// CXXFunctionalCastExpr - Represents an explicit C++ type conversion
-/// that uses "functional" notion (C++ [expr.type.conv]). Example: @c
-/// x = int(0.5);
+/// \brief Represents an explicit C++ type conversion that uses "functional"
+/// notation (C++ [expr.type.conv]).
+///
+/// Example:
+/// @code
+/// x = int(0.5);
+/// @endcode
class CXXFunctionalCastExpr : public ExplicitCastExpr {
SourceLocation TyBeginLoc;
SourceLocation RParenLoc;
@@ -1231,7 +1258,8 @@ private:
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
- SourceLocation ClosingBrace);
+ SourceLocation ClosingBrace,
+ bool ContainsUnexpandedParameterPack);
/// \brief Construct an empty lambda expression.
LambdaExpr(EmptyShell Empty, unsigned NumCaptures, bool HasArrayIndexVars)
@@ -1269,7 +1297,8 @@ public:
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
- SourceLocation ClosingBrace);
+ SourceLocation ClosingBrace,
+ bool ContainsUnexpandedParameterPack);
/// \brief Construct a new lambda expression that will be deserialized from
/// an external source.
@@ -1419,15 +1448,16 @@ public:
child_range children() { return child_range(); }
};
-/// CXXNewExpr - A new expression for memory allocation and constructor calls,
-/// e.g: "new CXXNewExpr(foo)".
+/// @brief Represents a new-expression for memory allocation and constructor
+// calls, e.g: "new CXXNewExpr(foo)".
class CXXNewExpr : public Expr {
// Contains an optional array size expression, an optional initialization
// expression, and any number of optional placement arguments, in that order.
Stmt **SubExprs;
- // Points to the allocation function used.
+ /// \brief Points to the allocation function used.
FunctionDecl *OperatorNew;
- // Points to the deallocation function used in case of error. May be null.
+ /// \brief Points to the deallocation function used in case of error. May be
+ /// null.
FunctionDecl *OperatorDelete;
/// \brief The allocated type-source information, as written in the source.
@@ -1607,8 +1637,8 @@ public:
}
};
-/// CXXDeleteExpr - A delete expression for memory deallocation and destructor
-/// calls, e.g. "delete[] pArray".
+/// \brief Represents a \c delete expression for memory deallocation and
+/// destructor calls, e.g. "delete[] pArray".
class CXXDeleteExpr : public Expr {
// Points to the operator delete overload that is used. Could be a member.
FunctionDecl *OperatorDelete;
@@ -1678,8 +1708,7 @@ public:
friend class ASTStmtReader;
};
-/// \brief Structure used to store the type being destroyed by a
-/// pseudo-destructor expression.
+/// \brief Stores the type being destroyed by a pseudo-destructor expression.
class PseudoDestructorTypeStorage {
/// \brief Either the type source information or the name of the type, if
/// it couldn't be resolved due to type-dependence.
@@ -1866,11 +1895,14 @@ public:
child_range children() { return child_range(&Base, &Base + 1); }
};
-/// UnaryTypeTraitExpr - A GCC or MS unary type trait, as used in the
-/// implementation of TR1/C++0x type trait templates.
+/// \brief Represents a GCC or MS unary type trait, as used in the
+/// implementation of TR1/C++11 type trait templates.
+///
/// Example:
-/// __is_pod(int) == true
-/// __is_enum(std::string) == false
+/// @code
+/// __is_pod(int) == true
+/// __is_enum(std::string) == false
+/// @endcode
class UnaryTypeTraitExpr : public Expr {
/// UTT - The trait. A UnaryTypeTrait enum in MSVC compat unsigned.
unsigned UTT : 31;
@@ -1921,10 +1953,13 @@ public:
friend class ASTStmtReader;
};
-/// BinaryTypeTraitExpr - A GCC or MS binary type trait, as used in the
-/// implementation of TR1/C++0x type trait templates.
+/// \brief Represents a GCC or MS binary type trait, as used in the
+/// implementation of TR1/C++11 type trait templates.
+///
/// Example:
-/// __is_base_of(Base, Derived) == true
+/// @code
+/// __is_base_of(Base, Derived) == true
+/// @endcode
class BinaryTypeTraitExpr : public Expr {
/// BTT - The trait. A BinaryTypeTrait enum in MSVC compat unsigned.
unsigned BTT : 8;
@@ -2086,30 +2121,33 @@ public:
};
-/// ArrayTypeTraitExpr - An Embarcadero array type trait, as used in the
-/// implementation of __array_rank and __array_extent.
+/// \brief An Embarcadero array type trait, as used in the implementation of
+/// __array_rank and __array_extent.
+///
/// Example:
-/// __array_rank(int[10][20]) == 2
-/// __array_extent(int, 1) == 20
+/// @code
+/// __array_rank(int[10][20]) == 2
+/// __array_extent(int, 1) == 20
+/// @endcode
class ArrayTypeTraitExpr : public Expr {
virtual void anchor();
- /// ATT - The trait. An ArrayTypeTrait enum in MSVC compat unsigned.
+ /// \brief The trait. An ArrayTypeTrait enum in MSVC compat unsigned.
unsigned ATT : 2;
- /// The value of the type trait. Unspecified if dependent.
+ /// \brief The value of the type trait. Unspecified if dependent.
uint64_t Value;
- /// The array dimension being queried, or -1 if not used
+ /// \brief The array dimension being queried, or -1 if not used.
Expr *Dimension;
- /// Loc - The location of the type trait keyword.
+ /// \brief The location of the type trait keyword.
SourceLocation Loc;
- /// RParen - The location of the closing paren.
+ /// \brief The location of the closing paren.
SourceLocation RParen;
- /// The type being queried.
+ /// \brief The type being queried.
TypeSourceInfo *QueriedType;
public:
@@ -2156,22 +2194,26 @@ public:
friend class ASTStmtReader;
};
-/// ExpressionTraitExpr - An expression trait intrinsic
+/// \brief An expression trait intrinsic.
+///
/// Example:
-/// __is_lvalue_expr(std::cout) == true
-/// __is_lvalue_expr(1) == false
+/// @code
+/// __is_lvalue_expr(std::cout) == true
+/// __is_lvalue_expr(1) == false
+/// @endcode
class ExpressionTraitExpr : public Expr {
- /// ET - The trait. A ExpressionTrait enum in MSVC compat unsigned.
+ /// \brief The trait. A ExpressionTrait enum in MSVC compat unsigned.
unsigned ET : 31;
- /// The value of the type trait. Unspecified if dependent.
+ /// \brief The value of the type trait. Unspecified if dependent.
bool Value : 1;
- /// Loc - The location of the type trait keyword.
+ /// \brief The location of the type trait keyword.
SourceLocation Loc;
- /// RParen - The location of the closing paren.
+ /// \brief The location of the closing paren.
SourceLocation RParen;
+ /// \brief The expression being queried.
Expr* QueriedExpression;
public:
ExpressionTraitExpr(SourceLocation loc, ExpressionTrait et,
@@ -2190,7 +2232,9 @@ public:
: Expr(ExpressionTraitExprClass, Empty), ET(0), Value(false),
QueriedExpression() { }
- SourceRange getSourceRange() const LLVM_READONLY { return SourceRange(Loc, RParen);}
+ SourceRange getSourceRange() const LLVM_READONLY {
+ return SourceRange(Loc, RParen);
+ }
ExpressionTrait getTrait() const { return static_cast<ExpressionTrait>(ET); }
@@ -2211,9 +2255,9 @@ public:
/// \brief A reference to an overloaded function set, either an
-/// \t UnresolvedLookupExpr or an \t UnresolvedMemberExpr.
+/// \c UnresolvedLookupExpr or an \c UnresolvedMemberExpr.
class OverloadExpr : public Expr {
- /// The common name of these declarations.
+ /// \brief The common name of these declarations.
DeclarationNameInfo NameInfo;
/// \brief The nested-name-specifier that qualifies the name, if any.
@@ -2292,7 +2336,7 @@ public:
return Result;
}
- /// Gets the naming class of this lookup, if any.
+ /// \brief Gets the naming class of this lookup, if any.
CXXRecordDecl *getNamingClass() const;
typedef UnresolvedSetImpl::iterator decls_iterator;
@@ -2301,25 +2345,25 @@ public:
return UnresolvedSetIterator(Results + NumResults);
}
- /// Gets the number of declarations in the unresolved set.
+ /// \brief Gets the number of declarations in the unresolved set.
unsigned getNumDecls() const { return NumResults; }
- /// Gets the full name info.
+ /// \brief Gets the full name info.
const DeclarationNameInfo &getNameInfo() const { return NameInfo; }
- /// Gets the name looked up.
+ /// \brief Gets the name looked up.
DeclarationName getName() const { return NameInfo.getName(); }
- /// Gets the location of the name.
+ /// \brief Gets the location of the name.
SourceLocation getNameLoc() const { return NameInfo.getLoc(); }
- /// Fetches the nested-name qualifier, if one was given.
+ /// \brief Fetches the nested-name qualifier, if one was given.
NestedNameSpecifier *getQualifier() const {
return QualifierLoc.getNestedNameSpecifier();
}
- /// Fetches the nested-name qualifier with source-location information, if
- /// one was given.
+ /// \brief Fetches the nested-name qualifier with source-location
+ /// information, if one was given.
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
/// \brief Retrieve the location of the template keyword preceding
@@ -2343,10 +2387,10 @@ public:
return getTemplateKWAndArgsInfo()->RAngleLoc;
}
- /// Determines whether the name was preceded by the template keyword.
+ /// \brief Determines whether the name was preceded by the template keyword.
bool hasTemplateKeyword() const { return getTemplateKeywordLoc().isValid(); }
- /// Determines whether this expression had explicit template arguments.
+ /// \brief Determines whether this expression had explicit template arguments.
bool hasExplicitTemplateArgs() const { return getLAngleLoc().isValid(); }
// Note that, inconsistently with the explicit-template-argument AST
@@ -2370,12 +2414,13 @@ public:
return getExplicitTemplateArgs().NumTemplateArgs;
}
- /// Copies the template arguments into the given structure.
+ /// \brief Copies the template arguments into the given structure.
void copyTemplateArgumentsInto(TemplateArgumentListInfo &List) const {
getExplicitTemplateArgs().copyInto(List);
}
/// \brief Retrieves the optional explicit template arguments.
+ ///
/// This points to the same data as getExplicitTemplateArgs(), but
/// returns null if there are no explicit template arguments.
const ASTTemplateArgumentListInfo *getOptionalExplicitTemplateArgs() {
@@ -2394,15 +2439,15 @@ public:
};
/// \brief A reference to a name which we were able to look up during
-/// parsing but could not resolve to a specific declaration. This
-/// arises in several ways:
+/// parsing but could not resolve to a specific declaration.
+///
+/// This arises in several ways:
/// * we might be waiting for argument-dependent lookup
/// * the name might resolve to an overloaded function
/// and eventually:
/// * the lookup might have included a function template
-/// These never include UnresolvedUsingValueDecls, which are always
-/// class members and therefore appear only in
-/// UnresolvedMemberLookupExprs.
+/// These never include UnresolvedUsingValueDecls, which are always class
+/// members and therefore appear only in UnresolvedMemberLookupExprs.
class UnresolvedLookupExpr : public OverloadExpr {
/// True if these lookup results should be extended by
/// argument-dependent lookup if this is the operand of a function
@@ -2483,8 +2528,8 @@ public:
/// argument-dependent lookup.
bool requiresADL() const { return RequiresADL; }
- /// True if namespace ::std should be artificially added to the set of
- /// associated namespaecs for argument-dependent lookup purposes.
+ /// True if namespace \::std should be artificially added to the set of
+ /// associated namespaces for argument-dependent lookup purposes.
bool isStdAssociatedNamespace() const { return StdIsAssociatedNamespace; }
/// True if this lookup is overloaded.
@@ -2744,9 +2789,9 @@ public:
/// type-dependent.
///
/// The explicit type conversions expressed by
-/// CXXUnresolvedConstructExpr have the form \c T(a1, a2, ..., aN),
-/// where \c T is some type and \c a1, a2, ..., aN are values, and
-/// either \C T is a dependent type or one or more of the \c a's is
+/// CXXUnresolvedConstructExpr have the form <tt>T(a1, a2, ..., aN)</tt>,
+/// where \c T is some type and \c a1, \c a2, ..., \c aN are values, and
+/// either \c T is a dependent type or one or more of the <tt>a</tt>'s is
/// type-dependent. For example, this would occur in a template such
/// as:
///
diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h
index 4bfd12c..93a5ada 100644
--- a/include/clang/AST/ExprObjC.h
+++ b/include/clang/AST/ExprObjC.h
@@ -87,43 +87,45 @@ public:
child_range children() { return child_range(); }
};
-/// ObjCNumericLiteral - used for objective-c numeric literals;
-/// as in: @42 or @true (c++/objc++) or @__yes (c/objc)
-class ObjCNumericLiteral : public Expr {
- /// Number - expression AST node for the numeric literal
- Stmt *Number;
- ObjCMethodDecl *ObjCNumericLiteralMethod;
- SourceLocation AtLoc;
+/// ObjCBoxedExpr - used for generalized expression boxing.
+/// as in: @(strdup("hello world")) or @(random())
+/// Also used for boxing non-parenthesized numeric literals;
+/// as in: @42 or \@true (c++/objc++) or \@__yes (c/objc).
+class ObjCBoxedExpr : public Expr {
+ Stmt *SubExpr;
+ ObjCMethodDecl *BoxingMethod;
+ SourceRange Range;
public:
- ObjCNumericLiteral(Stmt *NL, QualType T, ObjCMethodDecl *method,
- SourceLocation L)
- : Expr(ObjCNumericLiteralClass, T, VK_RValue, OK_Ordinary,
- false, false, false, false), Number(NL),
- ObjCNumericLiteralMethod(method), AtLoc(L) {}
- explicit ObjCNumericLiteral(EmptyShell Empty)
- : Expr(ObjCNumericLiteralClass, Empty) {}
+ ObjCBoxedExpr(Expr *E, QualType T, ObjCMethodDecl *method,
+ SourceRange R)
+ : Expr(ObjCBoxedExprClass, T, VK_RValue, OK_Ordinary,
+ E->isTypeDependent(), E->isValueDependent(),
+ E->isInstantiationDependent(), E->containsUnexpandedParameterPack()),
+ SubExpr(E), BoxingMethod(method), Range(R) {}
+ explicit ObjCBoxedExpr(EmptyShell Empty)
+ : Expr(ObjCBoxedExprClass, Empty) {}
- Expr *getNumber() { return cast<Expr>(Number); }
- const Expr *getNumber() const { return cast<Expr>(Number); }
+ Expr *getSubExpr() { return cast<Expr>(SubExpr); }
+ const Expr *getSubExpr() const { return cast<Expr>(SubExpr); }
- ObjCMethodDecl *getObjCNumericLiteralMethod() const {
- return ObjCNumericLiteralMethod;
+ ObjCMethodDecl *getBoxingMethod() const {
+ return BoxingMethod;
}
-
- SourceLocation getAtLoc() const { return AtLoc; }
+
+ SourceLocation getAtLoc() const { return Range.getBegin(); }
SourceRange getSourceRange() const LLVM_READONLY {
- return SourceRange(AtLoc, Number->getSourceRange().getEnd());
+ return Range;
}
-
+
static bool classof(const Stmt *T) {
- return T->getStmtClass() == ObjCNumericLiteralClass;
+ return T->getStmtClass() == ObjCBoxedExprClass;
}
- static bool classof(const ObjCNumericLiteral *) { return true; }
+ static bool classof(const ObjCBoxedExpr *) { return true; }
// Iterators
- child_range children() { return child_range(&Number, &Number+1); }
-
+ child_range children() { return child_range(&SubExpr, &SubExpr+1); }
+
friend class ASTStmtReader;
};
@@ -332,9 +334,9 @@ public:
};
-/// ObjCEncodeExpr, used for @encode in Objective-C. @encode has the same type
-/// and behavior as StringLiteral except that the string initializer is obtained
-/// from ASTContext with the encoding type as an argument.
+/// ObjCEncodeExpr, used for \@encode in Objective-C. \@encode has the same
+/// type and behavior as StringLiteral except that the string initializer is
+/// obtained from ASTContext with the encoding type as an argument.
class ObjCEncodeExpr : public Expr {
TypeSourceInfo *EncodedType;
SourceLocation AtLoc, RParenLoc;
@@ -376,7 +378,7 @@ public:
child_range children() { return child_range(); }
};
-/// ObjCSelectorExpr used for @selector in Objective-C.
+/// ObjCSelectorExpr used for \@selector in Objective-C.
class ObjCSelectorExpr : public Expr {
Selector SelName;
SourceLocation AtLoc, RParenLoc;
@@ -419,19 +421,20 @@ public:
/// The return type is "Protocol*".
class ObjCProtocolExpr : public Expr {
ObjCProtocolDecl *TheProtocol;
- SourceLocation AtLoc, RParenLoc;
+ SourceLocation AtLoc, ProtoLoc, RParenLoc;
public:
ObjCProtocolExpr(QualType T, ObjCProtocolDecl *protocol,
- SourceLocation at, SourceLocation rp)
+ SourceLocation at, SourceLocation protoLoc, SourceLocation rp)
: Expr(ObjCProtocolExprClass, T, VK_RValue, OK_Ordinary, false, false,
false, false),
- TheProtocol(protocol), AtLoc(at), RParenLoc(rp) {}
+ TheProtocol(protocol), AtLoc(at), ProtoLoc(protoLoc), RParenLoc(rp) {}
explicit ObjCProtocolExpr(EmptyShell Empty)
: Expr(ObjCProtocolExprClass, Empty) {}
ObjCProtocolDecl *getProtocol() const { return TheProtocol; }
void setProtocol(ObjCProtocolDecl *P) { TheProtocol = P; }
+ SourceLocation getProtocolIdLoc() const { return ProtoLoc; }
SourceLocation getAtLoc() const { return AtLoc; }
SourceLocation getRParenLoc() const { return RParenLoc; }
void setAtLoc(SourceLocation L) { AtLoc = L; }
@@ -448,6 +451,9 @@ public:
// Iterators
child_range children() { return child_range(); }
+
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
};
/// ObjCIvarRefExpr - A reference to an ObjC instance variable.
@@ -1019,7 +1025,7 @@ public:
/// a l-value or r-value reference will be an l-value or x-value,
/// respectively.
///
- /// \param LBrac The location of the open square bracket '['.
+ /// \param LBracLoc The location of the open square bracket '['.
///
/// \param SuperLoc The location of the "super" keyword.
///
@@ -1033,8 +1039,6 @@ public:
///
/// \param Args The message send arguments.
///
- /// \param NumArgs The number of arguments.
- ///
/// \param RBracLoc The location of the closing square bracket ']'.
static ObjCMessageExpr *Create(ASTContext &Context, QualType T,
ExprValueKind VK,
@@ -1059,7 +1063,7 @@ public:
/// a l-value or r-value reference will be an l-value or x-value,
/// respectively.
///
- /// \param LBrac The location of the open square bracket '['.
+ /// \param LBracLoc The location of the open square bracket '['.
///
/// \param Receiver The type of the receiver, including
/// source-location information.
@@ -1071,8 +1075,6 @@ public:
///
/// \param Args The message send arguments.
///
- /// \param NumArgs The number of arguments.
- ///
/// \param RBracLoc The location of the closing square bracket ']'.
static ObjCMessageExpr *Create(ASTContext &Context, QualType T,
ExprValueKind VK,
@@ -1095,7 +1097,7 @@ public:
/// a l-value or r-value reference will be an l-value or x-value,
/// respectively.
///
- /// \param LBrac The location of the open square bracket '['.
+ /// \param LBracLoc The location of the open square bracket '['.
///
/// \param Receiver The expression used to produce the object that
/// will receive this message.
@@ -1107,8 +1109,6 @@ public:
///
/// \param Args The message send arguments.
///
- /// \param NumArgs The number of arguments.
- ///
/// \param RBracLoc The location of the closing square bracket ']'.
static ObjCMessageExpr *Create(ASTContext &Context, QualType T,
ExprValueKind VK,
diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h
index e2a60d5..7aedfe2 100644
--- a/include/clang/AST/ExternalASTSource.h
+++ b/include/clang/AST/ExternalASTSource.h
@@ -179,6 +179,9 @@ public:
/// \c ObjCInterfaceDecl::setExternallyCompleted().
virtual void CompleteType(ObjCInterfaceDecl *Class) { }
+ /// \brief Loads comment ranges.
+ virtual void ReadComments() { }
+
/// \brief Notify ExternalASTSource that we started deserialization of
/// a decl or type so until FinishedDeserializing is called there may be
/// decls that are initializing. Must be paired with FinishedDeserializing.
diff --git a/include/clang/AST/Makefile b/include/clang/AST/Makefile
index 2854b7f..f6dd4ce 100644
--- a/include/clang/AST/Makefile
+++ b/include/clang/AST/Makefile
@@ -1,6 +1,6 @@
CLANG_LEVEL := ../../..
TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic
-BUILT_SOURCES = Attrs.inc AttrImpl.inc StmtNodes.inc DeclNodes.inc
+BUILT_SOURCES = Attrs.inc AttrImpl.inc StmtNodes.inc DeclNodes.inc CommentNodes.inc
TABLEGEN_INC_FILES_COMMON = 1
@@ -27,3 +27,9 @@ $(ObjDir)/DeclNodes.inc.tmp : $(TD_SRC_DIR)/DeclNodes.td $(CLANG_TBLGEN) \
$(ObjDir)/.dir
$(Echo) "Building Clang declaration node tables with tblgen"
$(Verb) $(ClangTableGen) -gen-clang-decl-nodes -o $(call SYSPATH, $@) $<
+
+$(ObjDir)/CommentNodes.inc.tmp : $(TD_SRC_DIR)/CommentNodes.td $(CLANG_TBLGEN) \
+ $(ObjDir)/.dir
+ $(Echo) "Building Clang comment node tables with tblgen"
+ $(Verb) $(ClangTableGen) -gen-clang-comment-nodes -o $(call SYSPATH, $@) $<
+
diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h
index ca22ed6..a0dffb9 100644
--- a/include/clang/AST/Mangle.h
+++ b/include/clang/AST/Mangle.h
@@ -121,6 +121,7 @@ public:
raw_ostream &) = 0;
void mangleGlobalBlock(const BlockDecl *BD,
+ const NamedDecl *ID,
raw_ostream &Out);
void mangleCtorBlock(const CXXConstructorDecl *CD, CXXCtorType CT,
const BlockDecl *BD, raw_ostream &Out);
@@ -129,7 +130,8 @@ public:
void mangleBlock(const DeclContext *DC, const BlockDecl *BD,
raw_ostream &Out);
// Do the right thing.
- void mangleBlock(const BlockDecl *BD, raw_ostream &Out);
+ void mangleBlock(const BlockDecl *BD, raw_ostream &Out,
+ const NamedDecl *ID=0);
void mangleObjCMethodName(const ObjCMethodDecl *MD,
raw_ostream &);
diff --git a/include/clang/AST/NSAPI.h b/include/clang/AST/NSAPI.h
index 40e9759..51ae1da 100644
--- a/include/clang/AST/NSAPI.h
+++ b/include/clang/AST/NSAPI.h
@@ -11,11 +11,13 @@
#define LLVM_CLANG_AST_NSAPI_H
#include "clang/Basic/IdentifierTable.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
namespace clang {
class ASTContext;
class QualType;
+ class Expr;
// \brief Provides info and caches identifiers/selectors for NSFoundation API.
class NSAPI {
@@ -37,15 +39,33 @@ public:
enum NSStringMethodKind {
NSStr_stringWithString,
+ NSStr_stringWithUTF8String,
+ NSStr_stringWithCStringEncoding,
+ NSStr_stringWithCString,
NSStr_initWithString
};
- static const unsigned NumNSStringMethods = 2;
+ static const unsigned NumNSStringMethods = 5;
IdentifierInfo *getNSClassId(NSClassIdKindKind K) const;
/// \brief The Objective-C NSString selectors.
Selector getNSStringSelector(NSStringMethodKind MK) const;
+ /// \brief Return NSStringMethodKind if \param Sel is such a selector.
+ llvm::Optional<NSStringMethodKind> getNSStringMethodKind(Selector Sel) const;
+
+ /// \brief Returns true if the expression \param E is a reference of
+ /// "NSUTF8StringEncoding" enum constant.
+ bool isNSUTF8StringEncodingConstant(const Expr *E) const {
+ return isObjCEnumerator(E, "NSUTF8StringEncoding", NSUTF8StringEncodingId);
+ }
+
+ /// \brief Returns true if the expression \param E is a reference of
+ /// "NSASCIIStringEncoding" enum constant.
+ bool isNSASCIIStringEncodingConstant(const Expr *E) const {
+ return isObjCEnumerator(E, "NSASCIIStringEncoding",NSASCIIStringEncodingId);
+ }
+
/// \brief Enumerates the NSArray methods used to generate literals.
enum NSArrayMethodKind {
NSArr_array,
@@ -88,6 +108,35 @@ public:
llvm::Optional<NSDictionaryMethodKind>
getNSDictionaryMethodKind(Selector Sel);
+ /// \brief Returns selector for "objectForKeyedSubscript:".
+ Selector getObjectForKeyedSubscriptSelector() const {
+ return getOrInitSelector(StringRef("objectForKeyedSubscript"),
+ objectForKeyedSubscriptSel);
+ }
+
+ /// \brief Returns selector for "objectAtIndexedSubscript:".
+ Selector getObjectAtIndexedSubscriptSelector() const {
+ return getOrInitSelector(StringRef("objectAtIndexedSubscript"),
+ objectAtIndexedSubscriptSel);
+ }
+
+ /// \brief Returns selector for "setObject:forKeyedSubscript".
+ Selector getSetObjectForKeyedSubscriptSelector() const {
+ StringRef Ids[] = { "setObject", "forKeyedSubscript" };
+ return getOrInitSelector(Ids, setObjectForKeyedSubscriptSel);
+ }
+
+ /// \brief Returns selector for "setObject:atIndexedSubscript".
+ Selector getSetObjectAtIndexedSubscriptSelector() const {
+ StringRef Ids[] = { "setObject", "atIndexedSubscript" };
+ return getOrInitSelector(Ids, setObjectAtIndexedSubscriptSel);
+ }
+
+ /// \brief Returns selector for "isEqual:".
+ Selector getIsEqualSelector() const {
+ return getOrInitSelector(StringRef("isEqual"), isEqualSel);
+ }
+
/// \brief Enumerates the NSNumber methods used to generate literals.
enum NSNumberLiteralMethodKind {
NSNumberWithChar,
@@ -126,10 +175,22 @@ public:
/// \brief Determine the appropriate NSNumber factory method kind for a
/// literal of the given type.
- static llvm::Optional<NSNumberLiteralMethodKind>
- getNSNumberFactoryMethodKind(QualType T);
+ llvm::Optional<NSNumberLiteralMethodKind>
+ getNSNumberFactoryMethodKind(QualType T) const;
+
+ /// \brief Returns true if \param T is a typedef of "BOOL" in objective-c.
+ bool isObjCBOOLType(QualType T) const;
+ /// \brief Returns true if \param T is a typedef of "NSInteger" in objective-c.
+ bool isObjCNSIntegerType(QualType T) const;
+ /// \brief Returns true if \param T is a typedef of "NSUInteger" in objective-c.
+ bool isObjCNSUIntegerType(QualType T) const;
private:
+ bool isObjCTypedef(QualType T, StringRef name, IdentifierInfo *&II) const;
+ bool isObjCEnumerator(const Expr *E,
+ StringRef name, IdentifierInfo *&II) const;
+ Selector getOrInitSelector(ArrayRef<StringRef> Ids, Selector &Sel) const;
+
ASTContext &Ctx;
mutable IdentifierInfo *ClassIds[NumClassIds];
@@ -145,6 +206,13 @@ private:
/// \brief The Objective-C NSNumber selectors used to create NSNumber literals.
mutable Selector NSNumberClassSelectors[NumNSNumberLiteralMethods];
mutable Selector NSNumberInstanceSelectors[NumNSNumberLiteralMethods];
+
+ mutable Selector objectForKeyedSubscriptSel, objectAtIndexedSubscriptSel,
+ setObjectForKeyedSubscriptSel,setObjectAtIndexedSubscriptSel,
+ isEqualSel;
+
+ mutable IdentifierInfo *BOOLId, *NSIntegerId, *NSUIntegerId;
+ mutable IdentifierInfo *NSASCIIStringEncodingId, *NSUTF8StringEncodingId;
};
} // end namespace clang
diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h
index b5bd824..a5aec1f 100644
--- a/include/clang/AST/NestedNameSpecifier.h
+++ b/include/clang/AST/NestedNameSpecifier.h
@@ -31,7 +31,7 @@ class TypeLoc;
class LangOptions;
/// \brief Represents a C++ nested name specifier, such as
-/// "::std::vector<int>::".
+/// "\::std::vector<int>::".
///
/// C++ nested name specifiers are the prefixes to qualified
/// namespaces. For example, "foo::" in "foo::x" is a nested name
@@ -190,7 +190,7 @@ public:
bool isInstantiationDependent() const;
/// \brief Whether this nested-name-specifier contains an unexpanded
- /// parameter pack (for C++0x variadic templates).
+ /// parameter pack (for C++11 variadic templates).
bool containsUnexpandedParameterPack() const;
/// \brief Print this nested name specifier to the given output
@@ -247,7 +247,7 @@ public:
/// nested-name-specifier.
///
/// For example, if this instance refers to a nested-name-specifier
- /// \c ::std::vector<int>::, the returned source range would cover
+ /// \c \::std::vector<int>::, the returned source range would cover
/// from the initial '::' to the last '::'.
SourceRange getSourceRange() const LLVM_READONLY;
@@ -255,7 +255,7 @@ public:
/// this nested-name-specifier, not including the prefix.
///
/// For example, if this instance refers to a nested-name-specifier
- /// \c ::std::vector<int>::, the returned source range would cover
+ /// \c \::std::vector<int>::, the returned source range would cover
/// from "vector" to the last '::'.
SourceRange getLocalSourceRange() const;
@@ -286,7 +286,7 @@ public:
/// \brief Return the prefix of this nested-name-specifier.
///
/// For example, if this instance refers to a nested-name-specifier
- /// \c ::std::vector<int>::, the prefix is \c ::std::. Note that the
+ /// \c \::std::vector<int>::, the prefix is \c \::std::. Note that the
/// returned prefix may be empty, if this is the first component of
/// the nested-name-specifier.
NestedNameSpecifierLoc getPrefix() const {
@@ -443,8 +443,9 @@ public:
NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const;
/// \brief Retrieve a nested-name-specifier with location
- /// information based on the information in this builder. This loc
- /// will contain references to the builder's internal data and may
+ /// information based on the information in this builder.
+ ///
+ /// This loc will contain references to the builder's internal data and may
/// be invalidated by any change to the builder.
NestedNameSpecifierLoc getTemporary() const {
return NestedNameSpecifierLoc(Representation, Buffer);
diff --git a/include/clang/AST/OperationKinds.h b/include/clang/AST/OperationKinds.h
index 258637d..6359414 100644
--- a/include/clang/AST/OperationKinds.h
+++ b/include/clang/AST/OperationKinds.h
@@ -291,7 +291,7 @@ enum CastKind {
CK_CopyAndAutoreleaseBlockObject
};
-#define CK_Invalid ((CastKind) -1)
+static const CastKind CK_Invalid = static_cast<CastKind>(-1);
enum BinaryOperatorKind {
// Operators listed in order of precedence.
diff --git a/include/clang/AST/RawCommentList.h b/include/clang/AST/RawCommentList.h
new file mode 100644
index 0000000..4901d07
--- /dev/null
+++ b/include/clang/AST/RawCommentList.h
@@ -0,0 +1,220 @@
+//===--- RawCommentList.h - Classes for processing raw comments -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_RAW_COMMENT_LIST_H
+#define LLVM_CLANG_AST_RAW_COMMENT_LIST_H
+
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+
+class ASTContext;
+class ASTReader;
+class Decl;
+
+namespace comments {
+ class FullComment;
+} // end namespace comments
+
+class RawComment {
+public:
+ enum CommentKind {
+ RCK_Invalid, ///< Invalid comment
+ RCK_OrdinaryBCPL, ///< Any normal BCPL comments
+ RCK_OrdinaryC, ///< Any normal C comment
+ RCK_BCPLSlash, ///< \code /// stuff \endcode
+ RCK_BCPLExcl, ///< \code //! stuff \endcode
+ RCK_JavaDoc, ///< \code /** stuff */ \endcode
+ RCK_Qt, ///< \code /*! stuff */ \endcode, also used by HeaderDoc
+ RCK_Merged ///< Two or more documentation comments merged together
+ };
+
+ RawComment() : Kind(RCK_Invalid), IsAlmostTrailingComment(false) { }
+
+ RawComment(const SourceManager &SourceMgr, SourceRange SR,
+ bool Merged = false);
+
+ CommentKind getKind() const LLVM_READONLY {
+ return (CommentKind) Kind;
+ }
+
+ bool isInvalid() const LLVM_READONLY {
+ return Kind == RCK_Invalid;
+ }
+
+ bool isMerged() const LLVM_READONLY {
+ return Kind == RCK_Merged;
+ }
+
+ /// Is this comment attached to any declaration?
+ bool isAttached() const LLVM_READONLY {
+ return !DeclOrParsedComment.isNull();
+ }
+
+ /// Return the declaration that this comment is attached to.
+ const Decl *getDecl() const;
+
+ /// Set the declaration that this comment is attached to.
+ void setDecl(const Decl *D) {
+ assert(DeclOrParsedComment.isNull());
+ DeclOrParsedComment = D;
+ }
+
+ /// Returns true if it is a comment that should be put after a member:
+ /// \code ///< stuff \endcode
+ /// \code //!< stuff \endcode
+ /// \code /**< stuff */ \endcode
+ /// \code /*!< stuff */ \endcode
+ bool isTrailingComment() const LLVM_READONLY {
+ assert(isDocumentation());
+ return IsTrailingComment;
+ }
+
+ /// Returns true if it is a probable typo:
+ /// \code //< stuff \endcode
+ /// \code /*< stuff */ \endcode
+ bool isAlmostTrailingComment() const LLVM_READONLY {
+ return IsAlmostTrailingComment;
+ }
+
+ /// Returns true if this comment is not a documentation comment.
+ bool isOrdinary() const LLVM_READONLY {
+ return (Kind == RCK_OrdinaryBCPL) || (Kind == RCK_OrdinaryC);
+ }
+
+ /// Returns true if this comment any kind of a documentation comment.
+ bool isDocumentation() const LLVM_READONLY {
+ return !isInvalid() && !isOrdinary();
+ }
+
+ /// Returns raw comment text with comment markers.
+ StringRef getRawText(const SourceManager &SourceMgr) const {
+ if (RawTextValid)
+ return RawText;
+
+ RawText = getRawTextSlow(SourceMgr);
+ RawTextValid = true;
+ return RawText;
+ }
+
+ SourceRange getSourceRange() const LLVM_READONLY {
+ return Range;
+ }
+
+ unsigned getBeginLine(const SourceManager &SM) const;
+ unsigned getEndLine(const SourceManager &SM) const;
+
+ const char *getBriefText(const ASTContext &Context) const {
+ if (BriefTextValid)
+ return BriefText;
+
+ return extractBriefText(Context);
+ }
+
+ /// Returns a \c FullComment AST node, parsing the comment if needed.
+ comments::FullComment *getParsed(const ASTContext &Context) const {
+ if (comments::FullComment *FC =
+ DeclOrParsedComment.dyn_cast<comments::FullComment *>())
+ return FC;
+
+ return parse(Context);
+ }
+
+private:
+ SourceRange Range;
+
+ mutable StringRef RawText;
+ mutable const char *BriefText;
+ mutable llvm::PointerUnion<const Decl *, comments::FullComment *>
+ DeclOrParsedComment;
+
+ mutable bool RawTextValid : 1; ///< True if RawText is valid
+ mutable bool BriefTextValid : 1; ///< True if BriefText is valid
+
+ unsigned Kind : 3;
+
+ bool IsTrailingComment : 1;
+ bool IsAlmostTrailingComment : 1;
+
+ mutable bool BeginLineValid : 1; ///< True if BeginLine is valid
+ mutable bool EndLineValid : 1; ///< True if EndLine is valid
+ mutable unsigned BeginLine; ///< Cached line number
+ mutable unsigned EndLine; ///< Cached line number
+
+ /// \brief Constructor for AST deserialization.
+ RawComment(SourceRange SR, CommentKind K, bool IsTrailingComment,
+ bool IsAlmostTrailingComment) :
+ Range(SR), RawTextValid(false), BriefTextValid(false), Kind(K),
+ IsTrailingComment(IsTrailingComment),
+ IsAlmostTrailingComment(IsAlmostTrailingComment),
+ BeginLineValid(false), EndLineValid(false)
+ { }
+
+ StringRef getRawTextSlow(const SourceManager &SourceMgr) const;
+
+ const char *extractBriefText(const ASTContext &Context) const;
+
+ comments::FullComment *parse(const ASTContext &Context) const;
+
+ friend class ASTReader;
+};
+
+/// \brief Compare comments' source locations.
+template<>
+class BeforeThanCompare<RawComment> {
+ const SourceManager &SM;
+
+public:
+ explicit BeforeThanCompare(const SourceManager &SM) : SM(SM) { }
+
+ bool operator()(const RawComment &LHS, const RawComment &RHS) {
+ return SM.isBeforeInTranslationUnit(LHS.getSourceRange().getBegin(),
+ RHS.getSourceRange().getBegin());
+ }
+
+ bool operator()(const RawComment *LHS, const RawComment *RHS) {
+ return operator()(*LHS, *RHS);
+ }
+};
+
+/// \brief This class represents all comments included in the translation unit,
+/// sorted in order of appearance in the translation unit.
+class RawCommentList {
+public:
+ RawCommentList(SourceManager &SourceMgr) :
+ SourceMgr(SourceMgr), OnlyWhitespaceSeen(true) { }
+
+ void addComment(const RawComment &RC, llvm::BumpPtrAllocator &Allocator);
+
+ ArrayRef<RawComment *> getComments() const {
+ return Comments;
+ }
+
+private:
+ SourceManager &SourceMgr;
+ std::vector<RawComment *> Comments;
+ RawComment LastComment;
+ bool OnlyWhitespaceSeen;
+
+ void addCommentsToFront(const std::vector<RawComment *> &C) {
+ size_t OldSize = Comments.size();
+ Comments.resize(C.size() + OldSize);
+ std::copy_backward(Comments.begin(), Comments.begin() + OldSize,
+ Comments.end());
+ std::copy(C.begin(), C.end(), Comments.begin());
+ }
+
+ friend class ASTReader;
+};
+
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/AST/RecordLayout.h b/include/clang/AST/RecordLayout.h
index ec07267..3a870d0 100644
--- a/include/clang/AST/RecordLayout.h
+++ b/include/clang/AST/RecordLayout.h
@@ -14,10 +14,9 @@
#ifndef LLVM_CLANG_AST_LAYOUTINFO_H
#define LLVM_CLANG_AST_LAYOUTINFO_H
-#include "llvm/Support/DataTypes.h"
-#include "llvm/ADT/DenseMap.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
+#include "llvm/ADT/DenseMap.h"
namespace clang {
class ASTContext;
@@ -33,18 +32,43 @@ namespace clang {
/// ObjCInterfaceDecl. FIXME - Find appropriate name.
/// These objects are managed by ASTContext.
class ASTRecordLayout {
+public:
+ struct VBaseInfo {
+ /// The offset to this virtual base in the complete-object layout
+ /// of this class.
+ CharUnits VBaseOffset;
+
+ private:
+ /// Whether this virtual base requires a vtordisp field in the
+ /// Microsoft ABI. These fields are required for certain operations
+ /// in constructors and destructors.
+ bool HasVtorDisp;
+
+ public:
+ bool hasVtorDisp() const { return HasVtorDisp; }
+
+ VBaseInfo() : HasVtorDisp(false) {}
+
+ VBaseInfo(CharUnits VBaseOffset, bool hasVtorDisp) :
+ VBaseOffset(VBaseOffset), HasVtorDisp(hasVtorDisp) {}
+ };
+
+ typedef llvm::DenseMap<const CXXRecordDecl *, VBaseInfo>
+ VBaseOffsetsMapTy;
+
+private:
/// Size - Size of record in characters.
CharUnits Size;
/// DataSize - Size of record in characters without tail padding.
CharUnits DataSize;
- /// FieldOffsets - Array of field offsets in bits.
- uint64_t *FieldOffsets;
-
// Alignment - Alignment of record in characters.
CharUnits Alignment;
+ /// FieldOffsets - Array of field offsets in bits.
+ uint64_t *FieldOffsets;
+
// FieldCount - Number of fields.
unsigned FieldCount;
@@ -63,11 +87,13 @@ class ASTRecordLayout {
/// any empty subobjects.
CharUnits SizeOfLargestEmptySubobject;
- /// VFPtrOffset - Virtual function table offset (Microsoft-only).
- CharUnits VFPtrOffset;
-
/// VBPtrOffset - Virtual base table offset (Microsoft-only).
CharUnits VBPtrOffset;
+
+ /// HasOwnVFPtr - Does this class provide a virtual function table
+ /// (vtable in Itanium, vftbl in Microsoft) that is independent from
+ /// its base classes?
+ bool HasOwnVFPtr; // TODO: stash this somewhere more efficient
/// PrimaryBase - The primary base info for this record.
llvm::PointerIntPair<const CXXRecordDecl *, 1, bool> PrimaryBase;
@@ -79,7 +105,7 @@ class ASTRecordLayout {
BaseOffsetsMapTy BaseOffsets;
/// VBaseOffsets - Contains a map from vbase classes to their offset.
- BaseOffsetsMapTy VBaseOffsets;
+ VBaseOffsetsMapTy VBaseOffsets;
};
/// CXXInfo - If the record layout is for a C++ record, this will have
@@ -96,7 +122,7 @@ class ASTRecordLayout {
typedef CXXRecordLayoutInfo::BaseOffsetsMapTy BaseOffsetsMapTy;
ASTRecordLayout(const ASTContext &Ctx,
CharUnits size, CharUnits alignment,
- CharUnits vfptroffset, CharUnits vbptroffset,
+ bool hasOwnVFPtr, CharUnits vbptroffset,
CharUnits datasize,
const uint64_t *fieldoffsets, unsigned fieldcount,
CharUnits nonvirtualsize, CharUnits nonvirtualalign,
@@ -104,7 +130,7 @@ class ASTRecordLayout {
const CXXRecordDecl *PrimaryBase,
bool IsPrimaryBaseVirtual,
const BaseOffsetsMapTy& BaseOffsets,
- const BaseOffsetsMapTy& VBaseOffsets);
+ const VBaseOffsetsMapTy& VBaseOffsets);
~ASTRecordLayout() {}
@@ -180,27 +206,7 @@ public:
assert(CXXInfo && "Record layout does not have C++ specific info!");
assert(CXXInfo->VBaseOffsets.count(VBase) && "Did not find base!");
- return CXXInfo->VBaseOffsets[VBase];
- }
-
- /// getBaseClassOffsetInBits - Get the offset, in bits, for the given
- /// base class.
- uint64_t getBaseClassOffsetInBits(const CXXRecordDecl *Base) const {
- assert(CXXInfo && "Record layout does not have C++ specific info!");
- assert(CXXInfo->BaseOffsets.count(Base) && "Did not find base!");
-
- return getBaseClassOffset(Base).getQuantity() *
- Base->getASTContext().getCharWidth();
- }
-
- /// getVBaseClassOffsetInBits - Get the offset, in bits, for the given
- /// base class.
- uint64_t getVBaseClassOffsetInBits(const CXXRecordDecl *VBase) const {
- assert(CXXInfo && "Record layout does not have C++ specific info!");
- assert(CXXInfo->VBaseOffsets.count(VBase) && "Did not find base!");
-
- return getVBaseClassOffset(VBase).getQuantity() *
- VBase->getASTContext().getCharWidth();
+ return CXXInfo->VBaseOffsets[VBase].VBaseOffset;
}
CharUnits getSizeOfLargestEmptySubobject() const {
@@ -208,11 +214,16 @@ public:
return CXXInfo->SizeOfLargestEmptySubobject;
}
- /// getVFPtrOffset - Get the offset for virtual function table pointer.
- /// This is only meaningful with the Microsoft ABI.
- CharUnits getVFPtrOffset() const {
+ /// hasOwnVFPtr - Does this class provide its own virtual-function
+ /// table pointer, rather than inheriting one from a primary base
+ /// class? If so, it is at offset zero.
+ ///
+ /// This implies that the ABI has no primary base class, meaning
+ /// that it has no base classes that are suitable under the conditions
+ /// of the ABI.
+ bool hasOwnVFPtr() const {
assert(CXXInfo && "Record layout does not have C++ specific info!");
- return CXXInfo->VFPtrOffset;
+ return CXXInfo->HasOwnVFPtr;
}
/// getVBPtrOffset - Get the offset for virtual base table pointer.
@@ -221,6 +232,11 @@ public:
assert(CXXInfo && "Record layout does not have C++ specific info!");
return CXXInfo->VBPtrOffset;
}
+
+ const VBaseOffsetsMapTy &getVBaseOffsetsMap() const {
+ assert(CXXInfo && "Record layout does not have C++ specific info!");
+ return CXXInfo->VBaseOffsets;
+ }
};
} // end namespace clang
diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
index f1b5171..2e56a48 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -148,10 +148,15 @@ public:
/// TypeLocs.
bool shouldWalkTypesOfTypeLocs() const { return true; }
+ /// \brief Return whether this visitor should recurse into implicit
+ /// code, e.g., implicit constructors and destructors.
+ bool shouldVisitImplicitCode() const { return false; }
+
/// \brief Return whether \param S should be traversed using data recursion
/// to avoid a stack overflow with extreme cases.
bool shouldUseDataRecursionFor(Stmt *S) const {
- return isa<BinaryOperator>(S) || isa<UnaryOperator>(S) || isa<CaseStmt>(S);
+ return isa<BinaryOperator>(S) || isa<UnaryOperator>(S) ||
+ isa<CaseStmt>(S) || isa<CXXOperatorCallExpr>(S);
}
/// \brief Recursively visit a statement or expression, by
@@ -392,8 +397,8 @@ public:
private:
// These are helper methods used by more than one Traverse* method.
bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
- bool TraverseClassInstantiations(ClassTemplateDecl* D, Decl *Pattern);
- bool TraverseFunctionInstantiations(FunctionTemplateDecl* D) ;
+ bool TraverseClassInstantiations(ClassTemplateDecl *D);
+ bool TraverseFunctionInstantiations(FunctionTemplateDecl *D) ;
bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
unsigned Count);
bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
@@ -404,18 +409,14 @@ private:
bool TraverseFunctionHelper(FunctionDecl *D);
bool TraverseVarHelper(VarDecl *D);
- bool Walk(Stmt *S);
-
struct EnqueueJob {
Stmt *S;
Stmt::child_iterator StmtIt;
- EnqueueJob(Stmt *S) : S(S), StmtIt() {
- if (Expr *E = dyn_cast_or_null<Expr>(S))
- S = E->IgnoreParens();
- }
+ EnqueueJob(Stmt *S) : S(S), StmtIt() {}
};
bool dataTraverse(Stmt *S);
+ bool dataTraverseNode(Stmt *S, bool &EnqueueChildren);
};
template<typename Derived>
@@ -434,7 +435,12 @@ bool RecursiveASTVisitor<Derived>::dataTraverse(Stmt *S) {
if (getDerived().shouldUseDataRecursionFor(CurrS)) {
if (job.StmtIt == Stmt::child_iterator()) {
- if (!Walk(CurrS)) return false;
+ bool EnqueueChildren = true;
+ if (!dataTraverseNode(CurrS, EnqueueChildren)) return false;
+ if (!EnqueueChildren) {
+ Queue.pop_back();
+ continue;
+ }
job.StmtIt = CurrS->child_begin();
} else {
++job.StmtIt;
@@ -455,10 +461,25 @@ bool RecursiveASTVisitor<Derived>::dataTraverse(Stmt *S) {
}
template<typename Derived>
-bool RecursiveASTVisitor<Derived>::Walk(Stmt *S) {
-
+bool RecursiveASTVisitor<Derived>::dataTraverseNode(Stmt *S,
+ bool &EnqueueChildren) {
+
+// The cast for DISPATCH_WALK is needed for older versions of g++, but causes
+// problems for MSVC. So we'll skip the cast entirely for MSVC.
+#if defined(_MSC_VER)
+ #define GCC_CAST(CLASS)
+#else
+ #define GCC_CAST(CLASS) (bool (RecursiveASTVisitor::*)(CLASS*))
+#endif
+
+ // Dispatch to the corresponding WalkUpFrom* function only if the derived
+ // class didn't override Traverse* (and thus the traversal is trivial).
#define DISPATCH_WALK(NAME, CLASS, VAR) \
- return getDerived().WalkUpFrom##NAME(static_cast<CLASS*>(VAR));
+ if (&RecursiveASTVisitor::Traverse##NAME == \
+ GCC_CAST(CLASS)&Derived::Traverse##NAME) \
+ return getDerived().WalkUpFrom##NAME(static_cast<CLASS*>(VAR)); \
+ EnqueueChildren = false; \
+ return getDerived().Traverse##NAME(static_cast<CLASS*>(VAR));
if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
switch (BinOp->getOpcode()) {
@@ -495,6 +516,7 @@ bool RecursiveASTVisitor<Derived>::Walk(Stmt *S) {
}
#undef DISPATCH_WALK
+#undef GCC_CAST
return true;
}
@@ -591,10 +613,9 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
if (!D)
return true;
- // As a syntax visitor, we want to ignore declarations for
- // implicitly-defined declarations (ones not typed explicitly by the
- // user).
- if (D->isImplicit())
+ // As a syntax visitor, by default we want to ignore declarations for
+ // implicit declarations (ones not typed explicitly by the user).
+ if (!getDerived().shouldVisitImplicitCode() && D->isImplicit())
return true;
switch (D->getKind()) {
@@ -1231,7 +1252,8 @@ bool RecursiveASTVisitor<Derived>::Traverse##DECL (DECL *D) { \
DEF_TRAVERSE_DECL(AccessSpecDecl, { })
DEF_TRAVERSE_DECL(BlockDecl, {
- TRY_TO(TraverseTypeLoc(D->getSignatureAsWritten()->getTypeLoc()));
+ if (TypeSourceInfo *TInfo = D->getSignatureAsWritten())
+ TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
TRY_TO(TraverseStmt(D->getBody()));
// This return statement makes sure the traversal of nodes in
// decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
@@ -1269,7 +1291,13 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, {
})
DEF_TRAVERSE_DECL(ClassScopeFunctionSpecializationDecl, {
- TRY_TO(TraverseDecl(D->getSpecialization()));
+ TRY_TO(TraverseDecl(D->getSpecialization()));
+
+ if (D->hasExplicitTemplateArgs()) {
+ const TemplateArgumentListInfo& args = D->templateArgs();
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ args.getArgumentArray(), args.size()));
+ }
})
DEF_TRAVERSE_DECL(LinkageSpecDecl, { })
@@ -1377,35 +1405,20 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateParameterListHelper(
}
// A helper method for traversing the implicit instantiations of a
-// class.
+// class template.
template<typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseClassInstantiations(
- ClassTemplateDecl* D, Decl *Pattern) {
- assert(isa<ClassTemplateDecl>(Pattern) ||
- isa<ClassTemplatePartialSpecializationDecl>(Pattern));
-
+ ClassTemplateDecl *D) {
ClassTemplateDecl::spec_iterator end = D->spec_end();
for (ClassTemplateDecl::spec_iterator it = D->spec_begin(); it != end; ++it) {
ClassTemplateSpecializationDecl* SD = *it;
switch (SD->getSpecializationKind()) {
// Visit the implicit instantiations with the requested pattern.
- case TSK_ImplicitInstantiation: {
- llvm::PointerUnion<ClassTemplateDecl *,
- ClassTemplatePartialSpecializationDecl *> U
- = SD->getInstantiatedFrom();
-
- bool ShouldVisit;
- if (U.is<ClassTemplateDecl*>())
- ShouldVisit = (U.get<ClassTemplateDecl*>() == Pattern);
- else
- ShouldVisit
- = (U.get<ClassTemplatePartialSpecializationDecl*>() == Pattern);
-
- if (ShouldVisit)
- TRY_TO(TraverseDecl(SD));
+ case TSK_Undeclared:
+ case TSK_ImplicitInstantiation:
+ TRY_TO(TraverseDecl(SD));
break;
- }
// We don't need to do anything on an explicit instantiation
// or explicit specialization because there will be an explicit
@@ -1414,11 +1427,6 @@ bool RecursiveASTVisitor<Derived>::TraverseClassInstantiations(
case TSK_ExplicitInstantiationDefinition:
case TSK_ExplicitSpecialization:
break;
-
- // We don't need to do anything for an uninstantiated
- // specialization.
- case TSK_Undeclared:
- break;
}
}
@@ -1433,12 +1441,12 @@ DEF_TRAVERSE_DECL(ClassTemplateDecl, {
// By default, we do not traverse the instantiations of
// class templates since they do not appear in the user code. The
// following code optionally traverses them.
- if (getDerived().shouldVisitTemplateInstantiations()) {
- // If this is the definition of the primary template, visit
- // instantiations which were formed from this pattern.
- if (D->isThisDeclarationADefinition())
- TRY_TO(TraverseClassInstantiations(D, D));
- }
+ //
+ // We only traverse the class instantiations when we see the canonical
+ // declaration of the template, to ensure we only visit them once.
+ if (getDerived().shouldVisitTemplateInstantiations() &&
+ D == D->getCanonicalDecl())
+ TRY_TO(TraverseClassInstantiations(D));
// Note that getInstantiatedFromMemberTemplate() is just a link
// from a template instantiation back to the template from which
@@ -1449,24 +1457,25 @@ DEF_TRAVERSE_DECL(ClassTemplateDecl, {
// function while skipping its specializations.
template<typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseFunctionInstantiations(
- FunctionTemplateDecl* D) {
+ FunctionTemplateDecl *D) {
FunctionTemplateDecl::spec_iterator end = D->spec_end();
for (FunctionTemplateDecl::spec_iterator it = D->spec_begin(); it != end;
++it) {
FunctionDecl* FD = *it;
switch (FD->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
case TSK_ImplicitInstantiation:
// We don't know what kind of FunctionDecl this is.
TRY_TO(TraverseDecl(FD));
break;
- // No need to visit explicit instantiations, we'll find the node
- // eventually.
+ // FIXME: For now traverse explicit instantiations here. Change that
+ // once they are represented as dedicated nodes in the AST.
case TSK_ExplicitInstantiationDeclaration:
case TSK_ExplicitInstantiationDefinition:
+ TRY_TO(TraverseDecl(FD));
break;
- case TSK_Undeclared: // Declaration of the template definition.
case TSK_ExplicitSpecialization:
break;
}
@@ -1480,26 +1489,21 @@ DEF_TRAVERSE_DECL(FunctionTemplateDecl, {
TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
// By default, we do not traverse the instantiations of
- // function templates since they do not apprear in the user code. The
+ // function templates since they do not appear in the user code. The
// following code optionally traverses them.
- if (getDerived().shouldVisitTemplateInstantiations()) {
- // Explicit function specializations will be traversed from the
- // context of their declaration. There is therefore no need to
- // traverse them for here.
- //
- // In addition, we only traverse the function instantiations when
- // the function template is a function template definition.
- if (D->isThisDeclarationADefinition()) {
- TRY_TO(TraverseFunctionInstantiations(D));
- }
- }
+ //
+ // We only traverse the function instantiations when we see the canonical
+ // declaration of the template, to ensure we only visit them once.
+ if (getDerived().shouldVisitTemplateInstantiations() &&
+ D == D->getCanonicalDecl())
+ TRY_TO(TraverseFunctionInstantiations(D));
})
DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, {
// D is the "T" in something like
// template <template <typename> class T> class container { };
TRY_TO(TraverseDecl(D->getTemplatedDecl()));
- if (D->hasDefaultArgument()) {
+ if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument()));
}
TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
@@ -1509,7 +1513,7 @@ DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
// D is the "T" in something like "template<typename T> class vector;"
if (D->getTypeForDecl())
TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
- if (D->hasDefaultArgument())
+ if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc()));
})
@@ -1567,7 +1571,7 @@ bool RecursiveASTVisitor<Derived>::TraverseCXXRecordHelper(
CXXRecordDecl *D) {
if (!TraverseRecordHelper(D))
return false;
- if (D->hasDefinition()) {
+ if (D->isCompleteDefinition()) {
for (CXXRecordDecl::base_class_iterator I = D->bases_begin(),
E = D->bases_end();
I != E; ++I) {
@@ -1634,11 +1638,7 @@ DEF_TRAVERSE_DECL(ClassTemplatePartialSpecializationDecl, {
// template args here.
TRY_TO(TraverseCXXRecordHelper(D));
- // If we're visiting instantiations, visit the instantiations of
- // this template now.
- if (getDerived().shouldVisitTemplateInstantiations() &&
- D->isThisDeclarationADefinition())
- TRY_TO(TraverseClassInstantiations(D->getSpecializedTemplate(), D));
+ // Instantiations will have been visited with the primary template.
})
DEF_TRAVERSE_DECL(EnumConstantDecl, {
@@ -1714,7 +1714,9 @@ bool RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
// FunctionNoProtoType or FunctionProtoType, or a typedef. This
// also covers the return type and the function parameters,
// including exception specifications.
- TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+ if (clang::TypeSourceInfo *TSI = D->getTypeSourceInfo()) {
+ TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+ }
if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
// Constructor initializers.
@@ -1767,7 +1769,8 @@ template<typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseVarHelper(VarDecl *D) {
TRY_TO(TraverseDeclaratorHelper(D));
// Default params are taken care of when we traverse the ParmVarDecl.
- if (!isa<ParmVarDecl>(D))
+ if (!isa<ParmVarDecl>(D) &&
+ (!D->isCXXForRangeDecl() || getDerived().shouldVisitImplicitCode()))
TRY_TO(TraverseStmt(D->getInit()));
return true;
}
@@ -1783,7 +1786,8 @@ DEF_TRAVERSE_DECL(ImplicitParamDecl, {
DEF_TRAVERSE_DECL(NonTypeTemplateParmDecl, {
// A non-type template parameter, e.g. "S" in template<int S> class Foo ...
TRY_TO(TraverseDeclaratorHelper(D));
- TRY_TO(TraverseStmt(D->getDefaultArgument()));
+ if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
+ TRY_TO(TraverseStmt(D->getDefaultArgument()));
})
DEF_TRAVERSE_DECL(ParmVarDecl, {
@@ -1837,6 +1841,11 @@ DEF_TRAVERSE_STMT(AsmStmt, {
// children() iterates over inputExpr and outputExpr.
})
+DEF_TRAVERSE_STMT(MSAsmStmt, {
+ // FIXME: MS Asm doesn't currently parse Constraints, Clobbers, etc. Once
+ // added this needs to be implemented.
+ })
+
DEF_TRAVERSE_STMT(CXXCatchStmt, {
TRY_TO(TraverseDecl(S->getExceptionDecl()));
// children() iterates over the handler block.
@@ -1879,7 +1888,15 @@ DEF_TRAVERSE_STMT(ObjCAtThrowStmt, { })
DEF_TRAVERSE_STMT(ObjCAtTryStmt, { })
DEF_TRAVERSE_STMT(ObjCForCollectionStmt, { })
DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, { })
-DEF_TRAVERSE_STMT(CXXForRangeStmt, { })
+DEF_TRAVERSE_STMT(CXXForRangeStmt, {
+ if (!getDerived().shouldVisitImplicitCode()) {
+ TRY_TO(TraverseStmt(S->getLoopVarStmt()));
+ TRY_TO(TraverseStmt(S->getRangeInit()));
+ TRY_TO(TraverseStmt(S->getBody()));
+ // Visit everything else only if shouldVisitImplicitCode().
+ return true;
+ }
+})
DEF_TRAVERSE_STMT(MSDependentExistsStmt, {
TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
@@ -2146,7 +2163,10 @@ DEF_TRAVERSE_STMT(ExtVectorElementExpr, { })
DEF_TRAVERSE_STMT(GNUNullExpr, { })
DEF_TRAVERSE_STMT(ImplicitValueInitExpr, { })
DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, { })
-DEF_TRAVERSE_STMT(ObjCEncodeExpr, { })
+DEF_TRAVERSE_STMT(ObjCEncodeExpr, {
+ if (TypeSourceInfo *TInfo = S->getEncodedTypeSourceInfo())
+ TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+})
DEF_TRAVERSE_STMT(ObjCIsaExpr, { })
DEF_TRAVERSE_STMT(ObjCIvarRefExpr, { })
DEF_TRAVERSE_STMT(ObjCMessageExpr, { })
@@ -2209,7 +2229,7 @@ DEF_TRAVERSE_STMT(FloatingLiteral, { })
DEF_TRAVERSE_STMT(ImaginaryLiteral, { })
DEF_TRAVERSE_STMT(StringLiteral, { })
DEF_TRAVERSE_STMT(ObjCStringLiteral, { })
-DEF_TRAVERSE_STMT(ObjCNumericLiteral, { })
+DEF_TRAVERSE_STMT(ObjCBoxedExpr, { })
DEF_TRAVERSE_STMT(ObjCArrayLiteral, { })
DEF_TRAVERSE_STMT(ObjCDictionaryLiteral, { })
diff --git a/include/clang/AST/Redeclarable.h b/include/clang/AST/Redeclarable.h
index 88abadb..e3b340a 100644
--- a/include/clang/AST/Redeclarable.h
+++ b/include/clang/AST/Redeclarable.h
@@ -25,26 +25,25 @@ template<typename decl_type>
class Redeclarable {
protected:
- // FIXME: PointerIntPair is a value class that should not be inherited from.
- // This should change to using containment.
- struct DeclLink : public llvm::PointerIntPair<decl_type *, 1, bool> {
+ class DeclLink {
+ llvm::PointerIntPair<decl_type *, 1, bool> NextAndIsPrevious;
+ public:
DeclLink(decl_type *D, bool isLatest)
- : llvm::PointerIntPair<decl_type *, 1, bool>(D, isLatest) { }
-
- typedef llvm::PointerIntPair<decl_type *, 1, bool> base_type;
+ : NextAndIsPrevious(D, isLatest) { }
- bool NextIsPrevious() const { return base_type::getInt() == false; }
- bool NextIsLatest() const { return base_type::getInt() == true; }
- decl_type *getNext() const { return base_type::getPointer(); }
+ bool NextIsPrevious() const { return !NextAndIsPrevious.getInt(); }
+ bool NextIsLatest() const { return NextAndIsPrevious.getInt(); }
+ decl_type *getNext() const { return NextAndIsPrevious.getPointer(); }
+ void setNext(decl_type *D) { NextAndIsPrevious.setPointer(D); }
};
- struct PreviousDeclLink : public DeclLink {
- PreviousDeclLink(decl_type *D) : DeclLink(D, false) { }
- };
+ static DeclLink PreviousDeclLink(decl_type *D) {
+ return DeclLink(D, false);
+ }
- struct LatestDeclLink : public DeclLink {
- LatestDeclLink(decl_type *D) : DeclLink(D, true) { }
- };
+ static DeclLink LatestDeclLink(decl_type *D) {
+ return DeclLink(D, true);
+ }
/// \brief Points to the next redeclaration in the chain.
///
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index 1b0f576..79e1920 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -19,8 +19,8 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/StmtIterator.h"
#include "clang/AST/DeclGroup.h"
-#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
+#include "clang/Lex/Token.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
@@ -37,9 +37,11 @@ namespace clang {
class ParmVarDecl;
class QualType;
class IdentifierInfo;
+ class LabelDecl;
class SourceManager;
class StringLiteral;
class SwitchStmt;
+ class VarDecl;
//===--------------------------------------------------------------------===//
// ExprIterator - Iterators for iterating over Stmt* arrays that contain
@@ -499,6 +501,14 @@ public:
decl_iterator decl_end() { return DG.end(); }
const_decl_iterator decl_begin() const { return DG.begin(); }
const_decl_iterator decl_end() const { return DG.end(); }
+
+ typedef std::reverse_iterator<decl_iterator> reverse_decl_iterator;
+ reverse_decl_iterator decl_rbegin() {
+ return reverse_decl_iterator(decl_end());
+ }
+ reverse_decl_iterator decl_rend() {
+ return reverse_decl_iterator(decl_begin());
+ }
};
/// NullStmt - This is the null statement ";": C99 6.8.3p3.
@@ -545,20 +555,13 @@ class CompoundStmt : public Stmt {
Stmt** Body;
SourceLocation LBracLoc, RBracLoc;
public:
- CompoundStmt(ASTContext& C, Stmt **StmtStart, unsigned NumStmts,
- SourceLocation LB, SourceLocation RB)
- : Stmt(CompoundStmtClass), LBracLoc(LB), RBracLoc(RB) {
- CompoundStmtBits.NumStmts = NumStmts;
- assert(CompoundStmtBits.NumStmts == NumStmts &&
- "NumStmts doesn't fit in bits of CompoundStmtBits.NumStmts!");
-
- if (NumStmts == 0) {
- Body = 0;
- return;
- }
+ CompoundStmt(ASTContext &C, Stmt **StmtStart, unsigned NumStmts,
+ SourceLocation LB, SourceLocation RB);
- Body = new (C) Stmt*[NumStmts];
- memcpy(Body, StmtStart, NumStmts * sizeof(*Body));
+ // \brief Build an empty compound statment with a location.
+ explicit CompoundStmt(SourceLocation Loc)
+ : Stmt(CompoundStmtClass), Body(0), LBracLoc(Loc), RBracLoc(Loc) {
+ CompoundStmtBits.NumStmts = 0;
}
// \brief Build an empty compound statement.
@@ -803,24 +806,32 @@ public:
class AttributedStmt : public Stmt {
Stmt *SubStmt;
SourceLocation AttrLoc;
- AttrVec Attrs;
- // TODO: It can be done as Attr *Attrs[1]; and variable size array as in
- // StringLiteral
+ unsigned NumAttrs;
+ const Attr *Attrs[1];
friend class ASTStmtReader;
-public:
- AttributedStmt(SourceLocation loc, const AttrVec &attrs, Stmt *substmt)
- : Stmt(AttributedStmtClass), SubStmt(substmt), AttrLoc(loc), Attrs(attrs) {
+ AttributedStmt(SourceLocation Loc, ArrayRef<const Attr*> Attrs, Stmt *SubStmt)
+ : Stmt(AttributedStmtClass), SubStmt(SubStmt), AttrLoc(Loc),
+ NumAttrs(Attrs.size()) {
+ memcpy(this->Attrs, Attrs.data(), Attrs.size() * sizeof(Attr*));
}
- // \brief Build an empty attributed statement.
- explicit AttributedStmt(EmptyShell Empty)
- : Stmt(AttributedStmtClass, Empty) {
+ explicit AttributedStmt(EmptyShell Empty, unsigned NumAttrs)
+ : Stmt(AttributedStmtClass, Empty), NumAttrs(NumAttrs) {
+ memset(Attrs, 0, NumAttrs * sizeof(Attr*));
}
+public:
+ static AttributedStmt *Create(ASTContext &C, SourceLocation Loc,
+ ArrayRef<const Attr*> Attrs, Stmt *SubStmt);
+ // \brief Build an empty attributed statement.
+ static AttributedStmt *CreateEmpty(ASTContext &C, unsigned NumAttrs);
+
SourceLocation getAttrLoc() const { return AttrLoc; }
- const AttrVec &getAttrs() const { return Attrs; }
+ ArrayRef<const Attr*> getAttrs() const {
+ return ArrayRef<const Attr*>(Attrs, NumAttrs);
+ }
Stmt *getSubStmt() { return SubStmt; }
const Stmt *getSubStmt() const { return SubStmt; }
@@ -1606,6 +1617,69 @@ public:
}
};
+/// MSAsmStmt - This represents a MS inline-assembly statement extension.
+///
+class MSAsmStmt : public Stmt {
+ SourceLocation AsmLoc, EndLoc;
+ std::string AsmStr;
+
+ bool IsSimple;
+ bool IsVolatile;
+
+ unsigned NumAsmToks;
+ unsigned NumLineEnds;
+ unsigned NumClobbers;
+
+ Token *AsmToks;
+ unsigned *LineEnds;
+ Stmt **Exprs;
+ StringRef *Clobbers;
+
+public:
+ MSAsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple,
+ bool isvolatile, ArrayRef<Token> asmtoks,
+ ArrayRef<unsigned> lineends, StringRef asmstr,
+ ArrayRef<StringRef> clobbers, SourceLocation endloc);
+
+ SourceLocation getAsmLoc() const { return AsmLoc; }
+ void setAsmLoc(SourceLocation L) { AsmLoc = L; }
+ SourceLocation getEndLoc() const { return EndLoc; }
+ void setEndLoc(SourceLocation L) { EndLoc = L; }
+
+ unsigned getNumAsmToks() { return NumAsmToks; }
+ Token *getAsmToks() { return AsmToks; }
+ unsigned getNumLineEnds() { return NumLineEnds; }
+ unsigned *getLineEnds() { return LineEnds; }
+
+ bool isVolatile() const { return IsVolatile; }
+ void setVolatile(bool V) { IsVolatile = V; }
+ bool isSimple() const { return IsSimple; }
+ void setSimple(bool V) { IsSimple = V; }
+
+ //===--- Asm String Analysis ---===//
+
+ const std::string *getAsmString() const { return &AsmStr; }
+ std::string *getAsmString() { return &AsmStr; }
+ void setAsmString(StringRef &E) { AsmStr = E.str(); }
+
+ //===--- Other ---===//
+
+ unsigned getNumClobbers() const { return NumClobbers; }
+ StringRef getClobber(unsigned i) { return Clobbers[i]; }
+
+ SourceRange getSourceRange() const LLVM_READONLY {
+ return SourceRange(AsmLoc, EndLoc);
+ }
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == MSAsmStmtClass;
+ }
+ static bool classof(const MSAsmStmt *) { return true; }
+
+ child_range children() {
+ return child_range(&Exprs[0], &Exprs[0]);
+ }
+};
+
class SEHExceptStmt : public Stmt {
SourceLocation Loc;
Stmt *Children[2];
diff --git a/include/clang/AST/StmtObjC.h b/include/clang/AST/StmtObjC.h
index a321041..e7e1232 100644
--- a/include/clang/AST/StmtObjC.h
+++ b/include/clang/AST/StmtObjC.h
@@ -6,10 +6,9 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the Objective-C statement AST node classes.
-//
-//===----------------------------------------------------------------------===//
+
+/// \file
+/// \brief Defines the Objective-C statement AST node classes.
#ifndef LLVM_CLANG_AST_STMTOBJC_H
#define LLVM_CLANG_AST_STMTOBJC_H
@@ -19,9 +18,9 @@
namespace clang {
-/// ObjCForCollectionStmt - This represents Objective-c's collection statement;
-/// represented as 'for (element 'in' collection-expression)' stmt.
+/// \brief Represents Objective-C's collection statement.
///
+/// This is represented as 'for (element 'in' collection-expression)' stmt.
class ObjCForCollectionStmt : public Stmt {
enum { ELEM, COLLECTION, BODY, END_EXPR };
Stmt* SubExprs[END_EXPR]; // SubExprs[ELEM] is an expression or declstmt.
@@ -70,7 +69,7 @@ public:
}
};
-/// ObjCAtCatchStmt - This represents objective-c's @catch statement.
+/// \brief Represents Objective-C's \@catch statement.
class ObjCAtCatchStmt : public Stmt {
private:
VarDecl *ExceptionDecl;
@@ -118,7 +117,7 @@ public:
child_range children() { return child_range(&Body, &Body + 1); }
};
-/// ObjCAtFinallyStmt - This represent objective-c's @finally Statement
+/// \brief Represents Objective-C's \@finally statement
class ObjCAtFinallyStmt : public Stmt {
Stmt *AtFinallyStmt;
SourceLocation AtFinallyLoc;
@@ -151,24 +150,23 @@ public:
}
};
-/// ObjCAtTryStmt - This represent objective-c's over-all
-/// @try ... @catch ... @finally statement.
+/// \brief Represents Objective-C's \@try ... \@catch ... \@finally statement.
class ObjCAtTryStmt : public Stmt {
private:
- // The location of the
+ // The location of the @ in the \@try.
SourceLocation AtTryLoc;
// The number of catch blocks in this statement.
unsigned NumCatchStmts : 16;
- // Whether this statement has a @finally statement.
+ // Whether this statement has a \@finally statement.
bool HasFinally : 1;
- /// \brief Retrieve the statements that are stored after this @try statement.
+ /// \brief Retrieve the statements that are stored after this \@try statement.
///
/// The order of the statements in memory follows the order in the source,
- /// with the @try body first, followed by the @catch statements (if any) and,
- /// finally, the @finally (if it exists).
+ /// with the \@try body first, followed by the \@catch statements (if any)
+ /// and, finally, the \@finally (if it exists).
Stmt **getStmts() { return reinterpret_cast<Stmt **> (this + 1); }
const Stmt* const *getStmts() const {
return reinterpret_cast<const Stmt * const*> (this + 1);
@@ -192,26 +190,26 @@ public:
unsigned NumCatchStmts,
bool HasFinally);
- /// \brief Retrieve the location of the @ in the @try.
+ /// \brief Retrieve the location of the @ in the \@try.
SourceLocation getAtTryLoc() const { return AtTryLoc; }
void setAtTryLoc(SourceLocation Loc) { AtTryLoc = Loc; }
- /// \brief Retrieve the @try body.
+ /// \brief Retrieve the \@try body.
const Stmt *getTryBody() const { return getStmts()[0]; }
Stmt *getTryBody() { return getStmts()[0]; }
void setTryBody(Stmt *S) { getStmts()[0] = S; }
- /// \brief Retrieve the number of @catch statements in this try-catch-finally
+ /// \brief Retrieve the number of \@catch statements in this try-catch-finally
/// block.
unsigned getNumCatchStmts() const { return NumCatchStmts; }
- /// \brief Retrieve a @catch statement.
+ /// \brief Retrieve a \@catch statement.
const ObjCAtCatchStmt *getCatchStmt(unsigned I) const {
assert(I < NumCatchStmts && "Out-of-bounds @catch index");
return cast_or_null<ObjCAtCatchStmt>(getStmts()[I + 1]);
}
- /// \brief Retrieve a @catch statement.
+ /// \brief Retrieve a \@catch statement.
ObjCAtCatchStmt *getCatchStmt(unsigned I) {
assert(I < NumCatchStmts && "Out-of-bounds @catch index");
return cast_or_null<ObjCAtCatchStmt>(getStmts()[I + 1]);
@@ -223,7 +221,7 @@ public:
getStmts()[I + 1] = S;
}
- /// Retrieve the @finally statement, if any.
+ /// \brief Retrieve the \@finally statement, if any.
const ObjCAtFinallyStmt *getFinallyStmt() const {
if (!HasFinally)
return 0;
@@ -254,11 +252,14 @@ public:
}
};
-/// ObjCAtSynchronizedStmt - This is for objective-c's @synchronized statement.
-/// Example: @synchronized (sem) {
-/// do-something;
-/// }
+/// \brief Represents Objective-C's \@synchronized statement.
///
+/// Example:
+/// \code
+/// @synchronized (sem) {
+/// do-something;
+/// }
+/// \endcode
class ObjCAtSynchronizedStmt : public Stmt {
private:
enum { SYNC_EXPR, SYNC_BODY, END_EXPR };
@@ -309,7 +310,7 @@ public:
}
};
-/// ObjCAtThrowStmt - This represents objective-c's @throw statement.
+/// \brief Represents Objective-C's \@throw statement.
class ObjCAtThrowStmt : public Stmt {
Stmt *Throw;
SourceLocation AtThrowLoc;
@@ -343,8 +344,7 @@ public:
child_range children() { return child_range(&Throw, &Throw+1); }
};
-/// ObjCAutoreleasePoolStmt - This represent objective-c's
-/// @autoreleasepool Statement
+/// \brief Represents Objective-C's \@autoreleasepool Statement
class ObjCAutoreleasePoolStmt : public Stmt {
Stmt *SubStmt;
SourceLocation AtLoc;
diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h
index 65f5460..54c9f2c 100644
--- a/include/clang/AST/TemplateBase.h
+++ b/include/clang/AST/TemplateBase.h
@@ -73,7 +73,15 @@ private:
union {
uintptr_t TypeOrValue;
struct {
- char Value[sizeof(llvm::APSInt)];
+ // We store a decomposed APSInt with the data allocated by ASTContext if
+ // BitWidth > 64. The memory may be shared between multiple
+ // TemplateArgument instances.
+ union {
+ uint64_t VAL; ///< Used to store the <= 64 bits integer value.
+ const uint64_t *pVal; ///< Used to store the >64 bits integer value.
+ };
+ unsigned BitWidth : 31;
+ unsigned IsUnsigned : 1;
void *Type;
} Integer;
struct {
@@ -104,11 +112,15 @@ public:
TypeOrValue = reinterpret_cast<uintptr_t>(D);
}
- /// \brief Construct an integral constant template argument.
- TemplateArgument(const llvm::APSInt &Value, QualType Type) : Kind(Integral) {
- // FIXME: Large integral values will get leaked. Do something
- // similar to what we did with IntegerLiteral.
- new (Integer.Value) llvm::APSInt(Value);
+ /// \brief Construct an integral constant template argument. The memory to
+ /// store the value is allocated with Ctx.
+ TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type);
+
+ /// \brief Construct an integral constant template argument with the same
+ /// value as Other but a different type.
+ TemplateArgument(const TemplateArgument &Other, QualType Type)
+ : Kind(Integral) {
+ Integer = Other.Integer;
Integer.Type = Type.getAsOpaquePtr();
}
@@ -165,62 +177,6 @@ public:
this->Args.NumArgs = NumArgs;
}
- /// \brief Copy constructor for a template argument.
- TemplateArgument(const TemplateArgument &Other) : Kind(Other.Kind) {
- // FIXME: Large integral values will get leaked. Do something
- // similar to what we did with IntegerLiteral.
- if (Kind == Integral) {
- new (Integer.Value) llvm::APSInt(*Other.getAsIntegral());
- Integer.Type = Other.Integer.Type;
- } else if (Kind == Pack) {
- Args.NumArgs = Other.Args.NumArgs;
- Args.Args = Other.Args.Args;
- } else if (Kind == Template || Kind == TemplateExpansion) {
- TemplateArg.Name = Other.TemplateArg.Name;
- TemplateArg.NumExpansions = Other.TemplateArg.NumExpansions;
- } else
- TypeOrValue = Other.TypeOrValue;
- }
-
- TemplateArgument& operator=(const TemplateArgument& Other) {
- using llvm::APSInt;
-
- if (Kind == Other.Kind && Kind == Integral) {
- // Copy integral values.
- *this->getAsIntegral() = *Other.getAsIntegral();
- Integer.Type = Other.Integer.Type;
- return *this;
- }
-
- // Destroy the current integral value, if that's what we're holding.
- if (Kind == Integral)
- getAsIntegral()->~APSInt();
-
- Kind = Other.Kind;
-
- if (Other.Kind == Integral) {
- new (Integer.Value) llvm::APSInt(*Other.getAsIntegral());
- Integer.Type = Other.Integer.Type;
- } else if (Other.Kind == Pack) {
- Args.NumArgs = Other.Args.NumArgs;
- Args.Args = Other.Args.Args;
- } else if (Kind == Template || Kind == TemplateExpansion) {
- TemplateArg.Name = Other.TemplateArg.Name;
- TemplateArg.NumExpansions = Other.TemplateArg.NumExpansions;
- } else {
- TypeOrValue = Other.TypeOrValue;
- }
-
- return *this;
- }
-
- ~TemplateArgument() {
- using llvm::APSInt;
-
- if (Kind == Integral)
- getAsIntegral()->~APSInt();
- }
-
/// \brief Create a new template argument pack by copying the given set of
/// template arguments.
static TemplateArgument CreatePackCopy(ASTContext &Context,
@@ -286,14 +242,15 @@ public:
llvm::Optional<unsigned> getNumTemplateExpansions() const;
/// \brief Retrieve the template argument as an integral value.
- llvm::APSInt *getAsIntegral() {
- if (Kind != Integral)
- return 0;
- return reinterpret_cast<llvm::APSInt*>(&Integer.Value[0]);
- }
+ // FIXME: Provide a way to read the integral data without copying the value.
+ llvm::APSInt getAsIntegral() const {
+ using namespace llvm;
+ if (Integer.BitWidth <= 64)
+ return APSInt(APInt(Integer.BitWidth, Integer.VAL), Integer.IsUnsigned);
- const llvm::APSInt *getAsIntegral() const {
- return const_cast<TemplateArgument*>(this)->getAsIntegral();
+ unsigned NumWords = APInt::getNumWords(Integer.BitWidth);
+ return APSInt(APInt(Integer.BitWidth, makeArrayRef(Integer.pVal, NumWords)),
+ Integer.IsUnsigned);
}
/// \brief Retrieve the type of the integral value.
@@ -342,14 +299,12 @@ public:
return Args.NumArgs;
}
- /// Determines whether two template arguments are superficially the
+ /// \brief Determines whether two template arguments are superficially the
/// same.
bool structurallyEquals(const TemplateArgument &Other) const;
- /// \brief When the template argument is a pack expansion, returns
+ /// \brief When the template argument is a pack expansion, returns
/// the pattern of the pack expansion.
- ///
- /// \param Ellipsis Will be set to the location of the ellipsis.
TemplateArgument getPackExpansionPattern() const;
/// \brief Print this template argument to the given output stream.
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index 7b615c1..6564b66 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -29,6 +29,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/Twine.h"
#include "clang/Basic/LLVM.h"
namespace clang {
@@ -377,8 +378,6 @@ public:
return hasConst();
}
- bool isSupersetOf(Qualifiers Other) const;
-
/// \brief Determine whether this set of qualifiers is a strict superset of
/// another set of qualifiers, not considering qualifier compatibility.
bool isStrictSupersetOf(Qualifiers Other) const;
@@ -412,12 +411,11 @@ public:
}
std::string getAsString() const;
- std::string getAsString(const PrintingPolicy &Policy) const {
- std::string Buffer;
- getAsStringInternal(Buffer, Policy);
- return Buffer;
- }
- void getAsStringInternal(std::string &S, const PrintingPolicy &Policy) const;
+ std::string getAsString(const PrintingPolicy &Policy) const;
+
+ bool isEmptyWhenPrinted(const PrintingPolicy &Policy) const;
+ void print(raw_ostream &OS, const PrintingPolicy &Policy,
+ bool appendSpaceIfNonEmpty = false) const;
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(Mask);
@@ -522,8 +520,6 @@ public:
void setLocalFastQualifiers(unsigned Quals) { Value.setInt(Quals); }
/// Retrieves a pointer to the underlying (unqualified) type.
- /// This should really return a const Type, but it's not worth
- /// changing all the users right now.
///
/// This function requires that the type not be NULL. If the type might be
/// NULL, use the (slightly less efficient) \c getTypePtrOrNull().
@@ -634,6 +630,11 @@ public:
/// \brief Determine whether this is a Plain Old Data (POD) type (C++ 3.9p10).
bool isPODType(ASTContext &Context) const;
+ /// isCXX98PODType() - Return true if this is a POD type according to the
+ /// rules of the C++98 standard, regardless of the current compilation's
+ /// language.
+ bool isCXX98PODType(ASTContext &Context) const;
+
/// isCXX11PODType() - Return true if this is a POD type according to the
/// more relaxed rules of the C++11 standard, regardless of the current
/// compilation's language.
@@ -824,11 +825,20 @@ public:
}
static std::string getAsString(const Type *ty, Qualifiers qs);
- std::string getAsString(const PrintingPolicy &Policy) const {
- std::string S;
- getAsStringInternal(S, Policy);
- return S;
+ std::string getAsString(const PrintingPolicy &Policy) const;
+
+ void print(raw_ostream &OS, const PrintingPolicy &Policy,
+ const Twine &PlaceHolder = Twine()) const {
+ print(split(), OS, Policy, PlaceHolder);
+ }
+ static void print(SplitQualType split, raw_ostream &OS,
+ const PrintingPolicy &policy, const Twine &PlaceHolder) {
+ return print(split.Ty, split.Quals, OS, policy, PlaceHolder);
}
+ static void print(const Type *ty, Qualifiers qs,
+ raw_ostream &OS, const PrintingPolicy &policy,
+ const Twine &PlaceHolder);
+
void getAsStringInternal(std::string &Str,
const PrintingPolicy &Policy) const {
return getAsStringInternal(split(), Str, Policy);
@@ -841,6 +851,27 @@ public:
std::string &out,
const PrintingPolicy &policy);
+ class StreamedQualTypeHelper {
+ const QualType &T;
+ const PrintingPolicy &Policy;
+ const Twine &PlaceHolder;
+ public:
+ StreamedQualTypeHelper(const QualType &T, const PrintingPolicy &Policy,
+ const Twine &PlaceHolder)
+ : T(T), Policy(Policy), PlaceHolder(PlaceHolder) { }
+
+ friend raw_ostream &operator<<(raw_ostream &OS,
+ const StreamedQualTypeHelper &SQT) {
+ SQT.T.print(OS, SQT.Policy, SQT.PlaceHolder);
+ return OS;
+ }
+ };
+
+ StreamedQualTypeHelper stream(const PrintingPolicy &Policy,
+ const Twine &PlaceHolder = Twine()) const {
+ return StreamedQualTypeHelper(*this, Policy, PlaceHolder);
+ }
+
void dump(const char *s) const;
void dump() const;
@@ -1107,8 +1138,6 @@ private:
unsigned TC : 8;
/// Dependent - Whether this type is a dependent type (C++ [temp.dep.type]).
- /// Note that this should stay at the end of the ivars for Type so that
- /// subclasses can pack their bitfields into the same word.
unsigned Dependent : 1;
/// \brief Whether this type somehow involves a template parameter, even
@@ -1614,7 +1643,7 @@ public:
AutoType *getContainedAutoType() const;
/// Member-template getAs<specific type>'. Look through sugar for
- /// an instance of <specific type>. This scheme will eventually
+ /// an instance of \<specific type>. This scheme will eventually
/// replace the specific getAsXXXX methods above.
///
/// There are some specializations of this member template listed
@@ -1626,7 +1655,7 @@ public:
const ArrayType *getAsArrayTypeUnsafe() const;
/// Member-template castAs<specific type>. Look through sugar for
- /// the underlying instance of <specific type>.
+ /// the underlying instance of \<specific type>.
///
/// This method has the same relationship to getAs<T> as cast<T> has
/// to dyn_cast<T>; which is to say, the underlying type *must*
@@ -1715,9 +1744,9 @@ public:
friend class ASTWriter;
};
-template <> inline const TypedefType *Type::getAs() const {
- return dyn_cast<TypedefType>(this);
-}
+/// \brief This will check for a TypedefType by removing any existing sugar
+/// until it reaches a TypedefType or a non-sugared type.
+template <> const TypedefType *Type::getAs() const;
// We can do canonical leaf types faster, because we don't have to
// worry about preserving child type decoration.
@@ -1752,7 +1781,13 @@ public:
}
Kind getKind() const { return static_cast<Kind>(BuiltinTypeBits.Kind); }
- const char *getName(const PrintingPolicy &Policy) const;
+ StringRef getName(const PrintingPolicy &Policy) const;
+ const char *getNameAsCString(const PrintingPolicy &Policy) const {
+ // The StringRef is null-terminated.
+ StringRef str = getName(Policy);
+ assert(!str.empty() && str.data()[str.size()] == '\0');
+ return str.data();
+ }
bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }
@@ -2641,6 +2676,9 @@ public:
bool getNoReturnAttr() const { return getExtInfo().getNoReturn(); }
CallingConv getCallConv() const { return getExtInfo().getCC(); }
ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); }
+ bool isConst() const { return getTypeQuals() & Qualifiers::Const; }
+ bool isVolatile() const { return getTypeQuals() & Qualifiers::Volatile; }
+ bool isRestrict() const { return getTypeQuals() & Qualifiers::Restrict; }
/// \brief Determine the type of an expression that calls a function of
/// this type.
@@ -2808,6 +2846,8 @@ public:
} else if (EPI.ExceptionSpecType == EST_Uninstantiated) {
EPI.ExceptionSpecDecl = getExceptionSpecDecl();
EPI.ExceptionSpecTemplate = getExceptionSpecTemplate();
+ } else if (EPI.ExceptionSpecType == EST_Unevaluated) {
+ EPI.ExceptionSpecDecl = getExceptionSpecDecl();
}
if (hasAnyConsumedArgs())
EPI.ConsumedArguments = getConsumedArgsBuffer();
@@ -2851,11 +2891,13 @@ public:
// NoexceptExpr sits where the arguments end.
return *reinterpret_cast<Expr *const *>(arg_type_end());
}
- /// \brief If this function type has an uninstantiated exception
- /// specification, this is the function whose exception specification
- /// is represented by this type.
+ /// \brief If this function type has an exception specification which hasn't
+ /// been determined yet (either because it has not been evaluated or because
+ /// it has not been instantiated), this is the function whose exception
+ /// specification is represented by this type.
FunctionDecl *getExceptionSpecDecl() const {
- if (getExceptionSpecType() != EST_Uninstantiated)
+ if (getExceptionSpecType() != EST_Uninstantiated &&
+ getExceptionSpecType() != EST_Unevaluated)
return 0;
return reinterpret_cast<FunctionDecl * const *>(arg_type_end())[0];
}
@@ -2870,7 +2912,7 @@ public:
}
bool isNothrow(ASTContext &Ctx) const {
ExceptionSpecificationType EST = getExceptionSpecType();
- assert(EST != EST_Delayed && EST != EST_Uninstantiated);
+ assert(EST != EST_Unevaluated && EST != EST_Uninstantiated);
if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
return true;
if (EST != EST_ComputedNoexcept)
@@ -2928,8 +2970,11 @@ public:
bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }
+ // FIXME: Remove the string version.
void printExceptionSpecification(std::string &S,
PrintingPolicy Policy) const;
+ void printExceptionSpecification(raw_ostream &OS,
+ PrintingPolicy Policy) const;
static bool classof(const Type *T) {
return T->getTypeClass() == FunctionProto;
@@ -3582,6 +3627,7 @@ public:
/// \brief Print a template argument list, including the '<' and '>'
/// enclosing the template arguments.
+ // FIXME: remove the string ones.
static std::string PrintTemplateArgumentList(const TemplateArgument *Args,
unsigned NumArgs,
const PrintingPolicy &Policy,
@@ -3594,6 +3640,23 @@ public:
static std::string PrintTemplateArgumentList(const TemplateArgumentListInfo &,
const PrintingPolicy &Policy);
+ /// \brief Print a template argument list, including the '<' and '>'
+ /// enclosing the template arguments.
+ static void PrintTemplateArgumentList(raw_ostream &OS,
+ const TemplateArgument *Args,
+ unsigned NumArgs,
+ const PrintingPolicy &Policy,
+ bool SkipBrackets = false);
+
+ static void PrintTemplateArgumentList(raw_ostream &OS,
+ const TemplateArgumentLoc *Args,
+ unsigned NumArgs,
+ const PrintingPolicy &Policy);
+
+ static void PrintTemplateArgumentList(raw_ostream &OS,
+ const TemplateArgumentListInfo &,
+ const PrintingPolicy &Policy);
+
/// True if this template specialization type matches a current
/// instantiation in the context in which it is found.
bool isCurrentInstantiation() const {
@@ -3641,7 +3704,7 @@ public:
unsigned getNumArgs() const { return NumArgs; }
/// \brief Retrieve a specific template argument as a type.
- /// \precondition @c isArgType(Arg)
+ /// \pre @c isArgType(Arg)
const TemplateArgument &getArg(unsigned Idx) const; // in TemplateBase.h
bool isSugared() const {
@@ -4045,7 +4108,7 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode {
PackExpansionType(QualType Pattern, QualType Canon,
llvm::Optional<unsigned> NumExpansions)
- : Type(PackExpansion, Canon, /*Dependent=*/true,
+ : Type(PackExpansion, Canon, /*Dependent=*/Pattern->isDependentType(),
/*InstantiationDependent=*/true,
/*VariableModified=*/Pattern->isVariablyModifiedType(),
/*ContainsUnexpandedParameterPack=*/false),
@@ -4097,8 +4160,10 @@ public:
/// list of protocols.
///
/// Given the following declarations:
-/// @class C;
-/// @protocol P;
+/// \code
+/// \@class C;
+/// \@protocol P;
+/// \endcode
///
/// 'C' is an ObjCInterfaceType C. It is sugar for an ObjCObjectType
/// with base C and no protocols.
@@ -4312,11 +4377,13 @@ public:
/// This method is equivalent to getPointeeType() except that
/// it discards any typedefs (or other sugar) between this
/// type and the "outermost" object type. So for:
- /// @class A; @protocol P; @protocol Q;
+ /// \code
+ /// \@class A; \@protocol P; \@protocol Q;
/// typedef A<P> AP;
/// typedef A A1;
/// typedef A1<P> A1P;
/// typedef A1P<Q> A1PQ;
+ /// \endcode
/// For 'A*', getObjectType() will return 'A'.
/// For 'A<P>*', getObjectType() will return 'A<P>'.
/// For 'AP*', getObjectType() will return 'A<P>'.
@@ -4333,7 +4400,7 @@ public:
}
/// getInterfaceType - If this pointer points to an Objective C
- /// @interface type, gets the type for that interface. Any protocol
+ /// \@interface type, gets the type for that interface. Any protocol
/// qualifiers on the interface are ignored.
///
/// \return null if the base type for this pointer is 'id' or 'Class'
@@ -4341,7 +4408,7 @@ public:
return getObjectType()->getBaseType()->getAs<ObjCInterfaceType>();
}
- /// getInterfaceDecl - If this pointer points to an Objective @interface
+ /// getInterfaceDecl - If this pointer points to an Objective \@interface
/// type, gets the declaration for that interface.
///
/// \return null if the base type for this pointer is 'id' or 'Class'
@@ -4970,7 +5037,7 @@ struct ArrayType_cannot_be_used_with_getAs { };
template<typename T>
struct ArrayType_cannot_be_used_with_getAs<T, true>;
-/// Member-template getAs<specific type>'.
+// Member-template getAs<specific type>'.
template <typename T> const T *Type::getAs() const {
ArrayType_cannot_be_used_with_getAs<T> at;
(void)at;
diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h
index aab87be..1d1c1d1 100644
--- a/include/clang/AST/TypeLoc.h
+++ b/include/clang/AST/TypeLoc.h
@@ -831,6 +831,7 @@ public:
struct ObjCInterfaceLocInfo {
SourceLocation NameLoc;
+ SourceLocation NameEndLoc;
};
/// \brief Wrapper for source info for ObjC interfaces.
@@ -850,9 +851,17 @@ public:
void setNameLoc(SourceLocation Loc) {
getLocalData()->NameLoc = Loc;
}
-
+
SourceRange getLocalSourceRange() const {
- return SourceRange(getNameLoc());
+ return SourceRange(getNameLoc(), getNameEndLoc());
+ }
+
+ SourceLocation getNameEndLoc() const {
+ return getLocalData()->NameEndLoc;
+ }
+
+ void setNameEndLoc(SourceLocation Loc) {
+ getLocalData()->NameEndLoc = Loc;
}
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h
new file mode 100644
index 0000000..dd237ee
--- /dev/null
+++ b/include/clang/ASTMatchers/ASTMatchFinder.h
@@ -0,0 +1,141 @@
+//===--- ASTMatchFinder.h - Structural query framework ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides a way to construct an ASTConsumer that runs given matchers
+// over the AST and invokes a given callback on every match.
+//
+// The general idea is to construct a matcher expression that describes a
+// subtree match on the AST. Next, a callback that is executed every time the
+// expression matches is registered, and the matcher is run over the AST of
+// some code. Matched subexpressions can be bound to string IDs and easily
+// be accessed from the registered callback. The callback can than use the
+// AST nodes that the subexpressions matched on to output information about
+// the match or construct changes that can be applied to the code.
+//
+// Example:
+// class HandleMatch : public MatchFinder::MatchCallback {
+// public:
+// virtual void Run(const MatchFinder::MatchResult &Result) {
+// const CXXRecordDecl *Class =
+// Result.Nodes.GetDeclAs<CXXRecordDecl>("id");
+// ...
+// }
+// };
+//
+// int main(int argc, char **argv) {
+// ClangTool Tool(argc, argv);
+// MatchFinder finder;
+// finder.AddMatcher(Id("id", record(hasName("::a_namespace::AClass"))),
+// new HandleMatch);
+// return Tool.Run(newFrontendActionFactory(&finder));
+// }
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCH_FINDER_H
+#define LLVM_CLANG_AST_MATCHERS_AST_MATCH_FINDER_H
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+namespace clang {
+
+namespace ast_matchers {
+
+/// \brief A class to allow finding matches over the Clang AST.
+///
+/// After creation, you can add multiple matchers to the MatchFinder via
+/// calls to addMatcher(...).
+///
+/// Once all matchers are added, newASTConsumer() returns an ASTConsumer
+/// that will trigger the callbacks specified via addMatcher(...) when a match
+/// is found.
+///
+/// See ASTMatchers.h for more information about how to create matchers.
+///
+/// Not intended to be subclassed.
+class MatchFinder {
+public:
+ /// \brief Contains all information for a given match.
+ ///
+ /// Every time a match is found, the MatchFinder will invoke the registered
+ /// MatchCallback with a MatchResult containing information about the match.
+ struct MatchResult {
+ MatchResult(const BoundNodes &Nodes, clang::ASTContext *Context);
+
+ /// \brief Contains the nodes bound on the current match.
+ ///
+ /// This allows user code to easily extract matched AST nodes.
+ const BoundNodes Nodes;
+
+ /// \brief Utilities for interpreting the matched AST structures.
+ /// @{
+ clang::ASTContext * const Context;
+ clang::SourceManager * const SourceManager;
+ /// @}
+ };
+
+ /// \brief Called when the Match registered for it was successfully found
+ /// in the AST.
+ class MatchCallback {
+ public:
+ virtual ~MatchCallback();
+ virtual void run(const MatchResult &Result) = 0;
+ };
+
+ /// \brief Called when parsing is finished. Intended for testing only.
+ class ParsingDoneTestCallback {
+ public:
+ virtual ~ParsingDoneTestCallback();
+ virtual void run() = 0;
+ };
+
+ MatchFinder();
+ ~MatchFinder();
+
+ /// \brief Adds a matcher to execute when running over the AST.
+ ///
+ /// Calls 'Action' with the BoundNodes on every match.
+ /// Adding more than one 'NodeMatch' allows finding different matches in a
+ /// single pass over the AST.
+ ///
+ /// Does not take ownership of 'Action'.
+ /// @{
+ void addMatcher(const DeclarationMatcher &NodeMatch,
+ MatchCallback *Action);
+ void addMatcher(const TypeMatcher &NodeMatch,
+ MatchCallback *Action);
+ void addMatcher(const StatementMatcher &NodeMatch,
+ MatchCallback *Action);
+ /// @}
+
+ /// \brief Creates a clang ASTConsumer that finds all matches.
+ clang::ASTConsumer *newASTConsumer();
+
+ /// \brief Registers a callback to notify the end of parsing.
+ ///
+ /// The provided closure is called after parsing is done, before the AST is
+ /// traversed. Useful for benchmarking.
+ /// Each call to FindAll(...) will call the closure once.
+ void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone);
+
+private:
+ /// \brief The MatchCallback*'s will be called every time the
+ /// UntypedBaseMatcher matches on the AST.
+ std::vector< std::pair<
+ const internal::UntypedBaseMatcher*,
+ MatchCallback*> > Triggers;
+
+ /// \brief Called when parsing is done.
+ ParsingDoneTestCallback *ParsingDone;
+};
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCH_FINDER_H
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
new file mode 100644
index 0000000..37e82e8
--- /dev/null
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -0,0 +1,1799 @@
+//===--- ASTMatchers.h - Structural query framework -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements matchers to be used together with the MatchFinder to
+// match AST nodes.
+//
+// Matchers are created by generator functions, which can be combined in
+// a functional in-language DSL to express queries over the C++ AST.
+//
+// For example, to match a class with a certain name, one would call:
+// record(hasName("MyClass"))
+// which returns a matcher that can be used to find all AST nodes that declare
+// a class named 'MyClass'.
+//
+// For more complicated match expressions we're often interested in accessing
+// multiple parts of the matched AST nodes once a match is found. In that case,
+// use the id(...) matcher around the match expressions that match the nodes
+// you want to access.
+//
+// For example, when we're interested in child classes of a certain class, we
+// would write:
+// record(hasName("MyClass"), hasChild(id("child", record())))
+// When the match is found via the MatchFinder, a user provided callback will
+// be called with a BoundNodes instance that contains a mapping from the
+// strings that we provided for the id(...) calls to the nodes that were
+// matched.
+// In the given example, each time our matcher finds a match we get a callback
+// where "child" is bound to the CXXRecordDecl node of the matching child
+// class declaration.
+//
+// See ASTMatchersInternal.h for a more in-depth explanation of the
+// implementation details of the matcher framework.
+//
+// See ASTMatchFinder.h for how to use the generated matchers to run over
+// an AST.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_H
+#define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_H
+
+#include "clang/AST/DeclTemplate.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace ast_matchers {
+
+/// \brief Maps string IDs to AST nodes matched by parts of a matcher.
+///
+/// The bound nodes are generated by adding id(...) matchers into the
+/// match expression around the matchers for the nodes we want to access later.
+///
+/// The instances of BoundNodes are created by MatchFinder when the user's
+/// callbacks are executed every time a match is found.
+class BoundNodes {
+public:
+ /// \brief Returns the AST node bound to 'ID'.
+ /// Returns NULL if there was no node bound to 'ID' or if there is a node but
+ /// it cannot be converted to the specified type.
+ /// FIXME: We'll need one of those for every base type.
+ /// @{
+ template <typename T>
+ const T *getDeclAs(StringRef ID) const {
+ return getNodeAs<T>(DeclBindings, ID);
+ }
+ template <typename T>
+ const T *getStmtAs(StringRef ID) const {
+ return getNodeAs<T>(StmtBindings, ID);
+ }
+ /// @}
+
+private:
+ /// \brief Create BoundNodes from a pre-filled map of bindings.
+ BoundNodes(const std::map<std::string, const Decl*> &DeclBindings,
+ const std::map<std::string, const Stmt*> &StmtBindings)
+ : DeclBindings(DeclBindings), StmtBindings(StmtBindings) {}
+
+ template <typename T, typename MapT>
+ const T *getNodeAs(const MapT &Bindings, StringRef ID) const {
+ typename MapT::const_iterator It = Bindings.find(ID);
+ if (It == Bindings.end()) {
+ return NULL;
+ }
+ return llvm::dyn_cast<T>(It->second);
+ }
+
+ std::map<std::string, const Decl*> DeclBindings;
+ std::map<std::string, const Stmt*> StmtBindings;
+
+ friend class internal::BoundNodesTree;
+};
+
+/// \brief If the provided matcher matches a node, binds the node to 'ID'.
+///
+/// FIXME: Add example for accessing it.
+template <typename T>
+internal::Matcher<T> id(const std::string &ID,
+ const internal::BindableMatcher<T> &InnerMatcher) {
+ return InnerMatcher.bind(ID);
+}
+
+/// \brief Types of matchers for the top-level classes in the AST class
+/// hierarchy.
+/// @{
+typedef internal::Matcher<Decl> DeclarationMatcher;
+typedef internal::Matcher<QualType> TypeMatcher;
+typedef internal::Matcher<Stmt> StatementMatcher;
+/// @}
+
+/// \brief Matches any node.
+///
+/// Useful when another matcher requires a child matcher, but there's no
+/// additional constraint. This will often be used with an explicit conversion
+/// to a internal::Matcher<> type such as TypeMatcher.
+///
+/// Example: DeclarationMatcher(anything()) matches all declarations, e.g.,
+/// "int* p" and "void f()" in
+/// int* p;
+/// void f();
+inline internal::PolymorphicMatcherWithParam0<internal::TrueMatcher> anything() {
+ return internal::PolymorphicMatcherWithParam0<internal::TrueMatcher>();
+}
+
+/// \brief Matches declarations.
+///
+/// Examples matches \c X, \c C, and the friend declaration inside \c C;
+/// \code
+/// void X();
+/// class C {
+/// friend X;
+/// };
+/// \endcode
+const internal::VariadicDynCastAllOfMatcher<Decl, Decl> decl;
+
+/// \brief Matches a declaration of anything that could have a name.
+///
+/// Example matches X, S, the anonymous union type, i, and U;
+/// typedef int X;
+/// struct S {
+/// union {
+/// int i;
+/// } U;
+/// };
+const internal::VariadicDynCastAllOfMatcher<
+ Decl,
+ NamedDecl> nameableDeclaration;
+
+/// \brief Matches C++ class declarations.
+///
+/// Example matches X, Z
+/// class X;
+/// template<class T> class Z {};
+const internal::VariadicDynCastAllOfMatcher<
+ Decl,
+ CXXRecordDecl> record;
+
+/// \brief Matches C++ class template specializations.
+///
+/// Given
+/// template<typename T> class A {};
+/// template<> class A<double> {};
+/// A<int> a;
+/// classTemplateSpecialization()
+/// matches the specializations \c A<int> and \c A<double>
+const internal::VariadicDynCastAllOfMatcher<
+ Decl,
+ ClassTemplateSpecializationDecl> classTemplateSpecialization;
+
+/// \brief Matches classTemplateSpecializations that have at least one
+/// TemplateArgument matching the given Matcher.
+///
+/// Given
+/// template<typename T> class A {};
+/// template<> class A<double> {};
+/// A<int> a;
+/// classTemplateSpecialization(hasAnyTemplateArgument(
+/// refersToType(asString("int"))))
+/// matches the specialization \c A<int>
+AST_MATCHER_P(ClassTemplateSpecializationDecl, hasAnyTemplateArgument,
+ internal::Matcher<TemplateArgument>, Matcher) {
+ const TemplateArgumentList &List = Node.getTemplateArgs();
+ for (unsigned i = 0; i < List.size(); ++i) {
+ if (Matcher.matches(List.get(i), Finder, Builder))
+ return true;
+ }
+ return false;
+}
+
+/// \brief Matches classTemplateSpecializations where the n'th TemplateArgument
+/// matches the given Matcher.
+///
+/// Given
+/// template<typename T, typename U> class A {};
+/// A<bool, int> b;
+/// A<int, bool> c;
+/// classTemplateSpecialization(hasTemplateArgument(
+/// 1, refersToType(asString("int"))))
+/// matches the specialization \c A<bool, int>
+AST_MATCHER_P2(ClassTemplateSpecializationDecl, hasTemplateArgument,
+ unsigned, N, internal::Matcher<TemplateArgument>, Matcher) {
+ const TemplateArgumentList &List = Node.getTemplateArgs();
+ if (List.size() <= N)
+ return false;
+ return Matcher.matches(List.get(N), Finder, Builder);
+}
+
+/// \brief Matches a TemplateArgument that refers to a certain type.
+///
+/// Given
+/// struct X {};
+/// template<typename T> struct A {};
+/// A<X> a;
+/// classTemplateSpecialization(hasAnyTemplateArgument(
+/// refersToType(class(hasName("X")))))
+/// matches the specialization \c A<X>
+AST_MATCHER_P(TemplateArgument, refersToType,
+ internal::Matcher<QualType>, Matcher) {
+ if (Node.getKind() != TemplateArgument::Type)
+ return false;
+ return Matcher.matches(Node.getAsType(), Finder, Builder);
+}
+
+/// \brief Matches a TemplateArgument that refers to a certain declaration.
+///
+/// Given
+/// template<typename T> struct A {};
+/// struct B { B* next; };
+/// A<&B::next> a;
+/// classTemplateSpecialization(hasAnyTemplateArgument(
+/// refersToDeclaration(field(hasName("next"))))
+/// matches the specialization \c A<&B::next> with \c field(...) matching
+/// \c B::next
+AST_MATCHER_P(TemplateArgument, refersToDeclaration,
+ internal::Matcher<Decl>, Matcher) {
+ if (const Decl *Declaration = Node.getAsDecl())
+ return Matcher.matches(*Declaration, Finder, Builder);
+ return false;
+}
+
+/// \brief Matches C++ constructor declarations.
+///
+/// Example matches Foo::Foo() and Foo::Foo(int)
+/// class Foo {
+/// public:
+/// Foo();
+/// Foo(int);
+/// int DoSomething();
+/// };
+const internal::VariadicDynCastAllOfMatcher<
+ Decl,
+ CXXConstructorDecl> constructor;
+
+/// \brief Matches explicit C++ destructor declarations.
+///
+/// Example matches Foo::~Foo()
+/// class Foo {
+/// public:
+/// virtual ~Foo();
+/// };
+const internal::VariadicDynCastAllOfMatcher<Decl, CXXDestructorDecl> destructor;
+
+/// \brief Matches enum declarations.
+///
+/// Example matches X
+/// enum X {
+/// A, B, C
+/// };
+const internal::VariadicDynCastAllOfMatcher<Decl, EnumDecl> enumDecl;
+
+/// \brief Matches enum constants.
+///
+/// Example matches A, B, C
+/// enum X {
+/// A, B, C
+/// };
+const internal::VariadicDynCastAllOfMatcher<
+ Decl,
+ EnumConstantDecl> enumConstant;
+
+/// \brief Matches method declarations.
+///
+/// Example matches y
+/// class X { void y() };
+const internal::VariadicDynCastAllOfMatcher<Decl, CXXMethodDecl> method;
+
+/// \brief Matches variable declarations.
+///
+/// Note: this does not match declarations of member variables, which are
+/// "field" declarations in Clang parlance.
+///
+/// Example matches a
+/// int a;
+const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> variable;
+
+/// \brief Matches field declarations.
+///
+/// Given
+/// class X { int m; };
+/// field()
+/// matches 'm'.
+const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> field;
+
+/// \brief Matches function declarations.
+///
+/// Example matches f
+/// void f();
+const internal::VariadicDynCastAllOfMatcher<Decl, FunctionDecl> function;
+
+
+/// \brief Matches statements.
+///
+/// Given
+/// { ++a; }
+/// statement()
+/// matches both the compound statement '{ ++a; }' and '++a'.
+const internal::VariadicDynCastAllOfMatcher<Stmt, Stmt> statement;
+
+/// \brief Matches declaration statements.
+///
+/// Given
+/// int a;
+/// declarationStatement()
+/// matches 'int a'.
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ DeclStmt> declarationStatement;
+
+/// \brief Matches member expressions.
+///
+/// Given
+/// class Y {
+/// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+/// int a; static int b;
+/// };
+/// memberExpression()
+/// matches this->x, x, y.x, a, this->b
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ MemberExpr> memberExpression;
+
+/// \brief Matches call expressions.
+///
+/// Example matches x.y() and y()
+/// X x;
+/// x.y();
+/// y();
+const internal::VariadicDynCastAllOfMatcher<Stmt, CallExpr> call;
+
+/// \brief Matches member call expressions.
+///
+/// Example matches x.y()
+/// X x;
+/// x.y();
+const internal::VariadicDynCastAllOfMatcher<Stmt, CXXMemberCallExpr> memberCall;
+
+/// \brief Matches init list expressions.
+///
+/// Given
+/// int a[] = { 1, 2 };
+/// struct B { int x, y; };
+/// B b = { 5, 6 };
+/// initList()
+/// matches "{ 1, 2 }" and "{ 5, 6 }"
+const internal::VariadicDynCastAllOfMatcher<Stmt, InitListExpr> initListExpr;
+
+/// \brief Matches using declarations.
+///
+/// Given
+/// namespace X { int x; }
+/// using X::x;
+/// usingDecl()
+/// matches \code using X::x \endcode
+const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl;
+
+/// \brief Matches constructor call expressions (including implicit ones).
+///
+/// Example matches string(ptr, n) and ptr within arguments of f
+/// (matcher = constructorCall())
+/// void f(const string &a, const string &b);
+/// char *ptr;
+/// int n;
+/// f(string(ptr, n), ptr);
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CXXConstructExpr> constructorCall;
+
+/// \brief Matches nodes where temporaries are created.
+///
+/// Example matches FunctionTakesString(GetStringByValue())
+/// (matcher = bindTemporaryExpression())
+/// FunctionTakesString(GetStringByValue());
+/// FunctionTakesStringByPointer(GetStringPointer());
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CXXBindTemporaryExpr> bindTemporaryExpression;
+
+/// \brief Matches new expressions.
+///
+/// Given
+/// new X;
+/// newExpression()
+/// matches 'new X'.
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CXXNewExpr> newExpression;
+
+/// \brief Matches delete expressions.
+///
+/// Given
+/// delete X;
+/// deleteExpression()
+/// matches 'delete X'.
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CXXDeleteExpr> deleteExpression;
+
+/// \brief Matches array subscript expressions.
+///
+/// Given
+/// int i = a[1];
+/// arraySubscriptExpr()
+/// matches "a[1]"
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ ArraySubscriptExpr> arraySubscriptExpr;
+
+/// \brief Matches the value of a default argument at the call site.
+///
+/// Example matches the CXXDefaultArgExpr placeholder inserted for the
+/// default value of the second parameter in the call expression f(42)
+/// (matcher = defaultArgument())
+/// void f(int x, int y = 0);
+/// f(42);
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CXXDefaultArgExpr> defaultArgument;
+
+/// \brief Matches overloaded operator calls.
+///
+/// Note that if an operator isn't overloaded, it won't match. Instead, use
+/// binaryOperator matcher.
+/// Currently it does not match operators such as new delete.
+/// FIXME: figure out why these do not match?
+///
+/// Example matches both operator<<((o << b), c) and operator<<(o, b)
+/// (matcher = overloadedOperatorCall())
+/// ostream &operator<< (ostream &out, int i) { };
+/// ostream &o; int b = 1, c = 1;
+/// o << b << c;
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CXXOperatorCallExpr> overloadedOperatorCall;
+
+/// \brief Matches expressions.
+///
+/// Example matches x()
+/// void f() { x(); }
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ Expr> expression;
+
+/// \brief Matches expressions that refer to declarations.
+///
+/// Example matches x in if (x)
+/// bool x;
+/// if (x) {}
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ DeclRefExpr> declarationReference;
+
+/// \brief Matches if statements.
+///
+/// Example matches 'if (x) {}'
+/// if (x) {}
+const internal::VariadicDynCastAllOfMatcher<Stmt, IfStmt> ifStmt;
+
+/// \brief Matches for statements.
+///
+/// Example matches 'for (;;) {}'
+/// for (;;) {}
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt, ForStmt> forStmt;
+
+/// \brief Matches the increment statement of a for loop.
+///
+/// Example:
+/// forStmt(hasIncrement(unaryOperator(hasOperatorName("++"))))
+/// matches '++x' in
+/// for (x; x < N; ++x) { }
+AST_MATCHER_P(ForStmt, hasIncrement, internal::Matcher<Stmt>,
+ InnerMatcher) {
+ const Stmt *const Increment = Node.getInc();
+ return (Increment != NULL &&
+ InnerMatcher.matches(*Increment, Finder, Builder));
+}
+
+/// \brief Matches the initialization statement of a for loop.
+///
+/// Example:
+/// forStmt(hasLoopInit(declarationStatement()))
+/// matches 'int x = 0' in
+/// for (int x = 0; x < N; ++x) { }
+AST_MATCHER_P(ForStmt, hasLoopInit, internal::Matcher<Stmt>,
+ InnerMatcher) {
+ const Stmt *const Init = Node.getInit();
+ return (Init != NULL && InnerMatcher.matches(*Init, Finder, Builder));
+}
+
+/// \brief Matches while statements.
+///
+/// Given
+/// while (true) {}
+/// whileStmt()
+/// matches 'while (true) {}'.
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ WhileStmt> whileStmt;
+
+/// \brief Matches do statements.
+///
+/// Given
+/// do {} while (true);
+/// doStmt()
+/// matches 'do {} while(true)'
+const internal::VariadicDynCastAllOfMatcher<Stmt, DoStmt> doStmt;
+
+/// \brief Matches case and default statements inside switch statements.
+///
+/// Given
+/// switch(a) { case 42: break; default: break; }
+/// switchCase()
+/// matches 'case 42: break;' and 'default: break;'.
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ SwitchCase> switchCase;
+
+/// \brief Matches compound statements.
+///
+/// Example matches '{}' and '{{}}'in 'for (;;) {{}}'
+/// for (;;) {{}}
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ CompoundStmt> compoundStatement;
+
+/// \brief Matches bool literals.
+///
+/// Example matches true
+/// true
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CXXBoolLiteralExpr> boolLiteral;
+
+/// \brief Matches string literals (also matches wide string literals).
+///
+/// Example matches "abcd", L"abcd"
+/// char *s = "abcd"; wchar_t *ws = L"abcd"
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ StringLiteral> stringLiteral;
+
+/// \brief Matches character literals (also matches wchar_t).
+///
+/// Not matching Hex-encoded chars (e.g. 0x1234, which is a IntegerLiteral),
+/// though.
+///
+/// Example matches 'a', L'a'
+/// char ch = 'a'; wchar_t chw = L'a';
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CharacterLiteral> characterLiteral;
+
+/// \brief Matches integer literals of all sizes / encodings.
+///
+/// Not matching character-encoded integers such as L'a'.
+///
+/// Example matches 1, 1L, 0x1, 1U
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ IntegerLiteral> integerLiteral;
+
+/// \brief Matches binary operator expressions.
+///
+/// Example matches a || b
+/// !(a || b)
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ BinaryOperator> binaryOperator;
+
+/// \brief Matches unary operator expressions.
+///
+/// Example matches !a
+/// !a || b
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ UnaryOperator> unaryOperator;
+
+/// \brief Matches conditional operator expressions.
+///
+/// Example matches a ? b : c
+/// (a ? b : c) + 42
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ ConditionalOperator> conditionalOperator;
+
+/// \brief Matches a reinterpret_cast expression.
+///
+/// Either the source expression or the destination type can be matched
+/// using has(), but hasDestinationType() is more specific and can be
+/// more readable.
+///
+/// Example matches reinterpret_cast<char*>(&p) in
+/// void* p = reinterpret_cast<char*>(&p);
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CXXReinterpretCastExpr> reinterpretCast;
+
+/// \brief Matches a C++ static_cast expression.
+///
+/// \see hasDestinationType
+/// \see reinterpretCast
+///
+/// Example:
+/// staticCast()
+/// matches
+/// static_cast<long>(8)
+/// in
+/// long eight(static_cast<long>(8));
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CXXStaticCastExpr> staticCast;
+
+/// \brief Matches a dynamic_cast expression.
+///
+/// Example:
+/// dynamicCast()
+/// matches
+/// dynamic_cast<D*>(&b);
+/// in
+/// struct B { virtual ~B() {} }; struct D : B {};
+/// B b;
+/// D* p = dynamic_cast<D*>(&b);
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CXXDynamicCastExpr> dynamicCast;
+
+/// \brief Matches a const_cast expression.
+///
+/// Example: Matches const_cast<int*>(&r) in
+/// int n = 42;
+/// const int& r(n);
+/// int* p = const_cast<int*>(&r);
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CXXConstCastExpr> constCast;
+
+/// \brief Matches explicit cast expressions.
+///
+/// Matches any cast expression written in user code, whether it be a
+/// C-style cast, a functional-style cast, or a keyword cast.
+///
+/// Does not match implicit conversions.
+///
+/// Note: the name "explicitCast" is chosen to match Clang's terminology, as
+/// Clang uses the term "cast" to apply to implicit conversions as well as to
+/// actual cast expressions.
+///
+/// \see hasDestinationType.
+///
+/// Example: matches all five of the casts in
+/// int((int)(reinterpret_cast<int>(static_cast<int>(const_cast<int>(42)))))
+/// but does not match the implicit conversion in
+/// long ell = 42;
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ ExplicitCastExpr> explicitCast;
+
+/// \brief Matches the implicit cast nodes of Clang's AST.
+///
+/// This matches many different places, including function call return value
+/// eliding, as well as any type conversions.
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ ImplicitCastExpr> implicitCast;
+
+/// \brief Matches functional cast expressions
+///
+/// Example: Matches Foo(bar);
+/// Foo f = bar;
+/// Foo g = (Foo) bar;
+/// Foo h = Foo(bar);
+const internal::VariadicDynCastAllOfMatcher<
+ Expr,
+ CXXFunctionalCastExpr> functionalCast;
+
+/// \brief Various overloads for the anyOf matcher.
+/// @{
+template<typename C1, typename C2>
+internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1, C2>
+anyOf(const C1 &P1, const C2 &P2) {
+ return internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher,
+ C1, C2 >(P1, P2);
+}
+template<typename C1, typename C2, typename C3>
+internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1,
+ internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C2, C3> >
+anyOf(const C1 &P1, const C2 &P2, const C3 &P3) {
+ return anyOf(P1, anyOf(P2, P3));
+}
+template<typename C1, typename C2, typename C3, typename C4>
+internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1,
+ internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C2,
+ internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher,
+ C3, C4> > >
+anyOf(const C1 &P1, const C2 &P2, const C3 &P3, const C4 &P4) {
+ return anyOf(P1, anyOf(P2, anyOf(P3, P4)));
+}
+template<typename C1, typename C2, typename C3, typename C4, typename C5>
+internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1,
+ internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C2,
+ internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C3,
+ internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher,
+ C4, C5> > > >
+anyOf(const C1& P1, const C2& P2, const C3& P3, const C4& P4, const C5& P5) {
+ return anyOf(P1, anyOf(P2, anyOf(P3, anyOf(P4, P5))));
+}
+/// @}
+
+/// \brief Various overloads for the allOf matcher.
+/// @{
+template<typename C1, typename C2>
+internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, C1, C2>
+allOf(const C1 &P1, const C2 &P2) {
+ return internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher,
+ C1, C2>(P1, P2);
+}
+template<typename C1, typename C2, typename C3>
+internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, C1,
+ internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, C2, C3> >
+allOf(const C1& P1, const C2& P2, const C3& P3) {
+ return allOf(P1, allOf(P2, P3));
+}
+/// @}
+
+/// \brief Matches sizeof (C99), alignof (C++11) and vec_step (OpenCL)
+///
+/// Given
+/// Foo x = bar;
+/// int y = sizeof(x) + alignof(x);
+/// unaryExprOrTypeTraitExpr()
+/// matches \c sizeof(x) and \c alignof(x)
+const internal::VariadicDynCastAllOfMatcher<
+ Stmt,
+ UnaryExprOrTypeTraitExpr> unaryExprOrTypeTraitExpr;
+
+/// \brief Matches unary expressions that have a specific type of argument.
+///
+/// Given
+/// int a, c; float b; int s = sizeof(a) + sizeof(b) + alignof(c);
+/// unaryExprOrTypeTraitExpr(hasArgumentOfType(asString("int"))
+/// matches \c sizeof(a) and \c alignof(c)
+AST_MATCHER_P(UnaryExprOrTypeTraitExpr, hasArgumentOfType,
+ internal::Matcher<QualType>, Matcher) {
+ const QualType ArgumentType = Node.getTypeOfArgument();
+ return Matcher.matches(ArgumentType, Finder, Builder);
+}
+
+/// \brief Matches unary expressions of a certain kind.
+///
+/// Given
+/// int x;
+/// int s = sizeof(x) + alignof(x)
+/// unaryExprOrTypeTraitExpr(ofKind(UETT_SizeOf))
+/// matches \c sizeof(x)
+AST_MATCHER_P(UnaryExprOrTypeTraitExpr, ofKind, UnaryExprOrTypeTrait, Kind) {
+ return Node.getKind() == Kind;
+}
+
+/// \brief Same as unaryExprOrTypeTraitExpr, but only matching
+/// alignof.
+inline internal::Matcher<Stmt> alignOfExpr(
+ const internal::Matcher<UnaryExprOrTypeTraitExpr> &Matcher) {
+ return internal::Matcher<Stmt>(unaryExprOrTypeTraitExpr(allOf(
+ ofKind(UETT_AlignOf), Matcher)));
+}
+
+/// \brief Same as unaryExprOrTypeTraitExpr, but only matching
+/// sizeof.
+inline internal::Matcher<Stmt> sizeOfExpr(
+ const internal::Matcher<UnaryExprOrTypeTraitExpr> &Matcher) {
+ return internal::Matcher<Stmt>(unaryExprOrTypeTraitExpr(allOf(
+ ofKind(UETT_SizeOf), Matcher)));
+}
+
+/// \brief Matches NamedDecl nodes that have the specified name.
+///
+/// Supports specifying enclosing namespaces or classes by prefixing the name
+/// with '<enclosing>::'.
+/// Does not match typedefs of an underlying type with the given name.
+///
+/// Example matches X (Name == "X")
+/// class X;
+///
+/// Example matches X (Name is one of "::a::b::X", "a::b::X", "b::X", "X")
+/// namespace a { namespace b { class X; } }
+AST_MATCHER_P(NamedDecl, hasName, std::string, Name) {
+ assert(!Name.empty());
+ const std::string FullNameString = "::" + Node.getQualifiedNameAsString();
+ const llvm::StringRef FullName = FullNameString;
+ const llvm::StringRef Pattern = Name;
+ if (Pattern.startswith("::")) {
+ return FullName == Pattern;
+ } else {
+ return FullName.endswith(("::" + Pattern).str());
+ }
+}
+
+/// \brief Matches NamedDecl nodes whose full names partially match the
+/// given RegExp.
+///
+/// Supports specifying enclosing namespaces or classes by
+/// prefixing the name with '<enclosing>::'. Does not match typedefs
+/// of an underlying type with the given name.
+///
+/// Example matches X (regexp == "::X")
+/// class X;
+///
+/// Example matches X (regexp is one of "::X", "^foo::.*X", among others)
+/// namespace foo { namespace bar { class X; } }
+AST_MATCHER_P(NamedDecl, matchesName, std::string, RegExp) {
+ assert(!RegExp.empty());
+ std::string FullNameString = "::" + Node.getQualifiedNameAsString();
+ llvm::Regex RE(RegExp);
+ return RE.match(FullNameString);
+}
+
+/// \brief Matches overloaded operator names.
+///
+/// Matches overloaded operator names specified in strings without the
+/// "operator" prefix, such as "<<", for OverloadedOperatorCall's.
+///
+/// Example matches a << b
+/// (matcher == overloadedOperatorCall(hasOverloadedOperatorName("<<")))
+/// a << b;
+/// c && d; // assuming both operator<<
+/// // and operator&& are overloaded somewhere.
+AST_MATCHER_P(CXXOperatorCallExpr,
+ hasOverloadedOperatorName, std::string, Name) {
+ return getOperatorSpelling(Node.getOperator()) == Name;
+}
+
+/// \brief Matches C++ classes that are directly or indirectly derived from
+/// a class matching \c Base.
+///
+/// Note that a class is considered to be also derived from itself.
+///
+/// Example matches X, Y, Z, C (Base == hasName("X"))
+/// class X; // A class is considered to be derived from itself
+/// class Y : public X {}; // directly derived
+/// class Z : public Y {}; // indirectly derived
+/// typedef X A;
+/// typedef A B;
+/// class C : public B {}; // derived from a typedef of X
+///
+/// In the following example, Bar matches isDerivedFrom(hasName("X")):
+/// class Foo;
+/// typedef Foo X;
+/// class Bar : public Foo {}; // derived from a type that X is a typedef of
+AST_MATCHER_P(CXXRecordDecl, isDerivedFrom,
+ internal::Matcher<NamedDecl>, Base) {
+ return Finder->classIsDerivedFrom(&Node, Base, Builder);
+}
+
+/// \brief Overloaded method as shortcut for \c isDerivedFrom(hasName(...)).
+inline internal::Matcher<CXXRecordDecl> isDerivedFrom(StringRef BaseName) {
+ assert(!BaseName.empty());
+ return isDerivedFrom(hasName(BaseName));
+}
+
+/// \brief Matches AST nodes that have child AST nodes that match the
+/// provided matcher.
+///
+/// Example matches X, Y (matcher = record(has(record(hasName("X")))
+/// class X {}; // Matches X, because X::X is a class of name X inside X.
+/// class Y { class X {}; };
+/// class Z { class Y { class X {}; }; }; // Does not match Z.
+///
+/// ChildT must be an AST base type.
+template <typename ChildT>
+internal::ArgumentAdaptingMatcher<internal::HasMatcher, ChildT> has(
+ const internal::Matcher<ChildT> &ChildMatcher) {
+ return internal::ArgumentAdaptingMatcher<internal::HasMatcher,
+ ChildT>(ChildMatcher);
+}
+
+/// \brief Matches AST nodes that have descendant AST nodes that match the
+/// provided matcher.
+///
+/// Example matches X, Y, Z
+/// (matcher = record(hasDescendant(record(hasName("X")))))
+/// class X {}; // Matches X, because X::X is a class of name X inside X.
+/// class Y { class X {}; };
+/// class Z { class Y { class X {}; }; };
+///
+/// DescendantT must be an AST base type.
+template <typename DescendantT>
+internal::ArgumentAdaptingMatcher<internal::HasDescendantMatcher, DescendantT>
+hasDescendant(const internal::Matcher<DescendantT> &DescendantMatcher) {
+ return internal::ArgumentAdaptingMatcher<
+ internal::HasDescendantMatcher,
+ DescendantT>(DescendantMatcher);
+}
+
+
+/// \brief Matches AST nodes that have child AST nodes that match the
+/// provided matcher.
+///
+/// Example matches X, Y (matcher = record(forEach(record(hasName("X")))
+/// class X {}; // Matches X, because X::X is a class of name X inside X.
+/// class Y { class X {}; };
+/// class Z { class Y { class X {}; }; }; // Does not match Z.
+///
+/// ChildT must be an AST base type.
+///
+/// As opposed to 'has', 'forEach' will cause a match for each result that
+/// matches instead of only on the first one.
+template <typename ChildT>
+internal::ArgumentAdaptingMatcher<internal::ForEachMatcher, ChildT> forEach(
+ const internal::Matcher<ChildT>& ChildMatcher) {
+ return internal::ArgumentAdaptingMatcher<
+ internal::ForEachMatcher,
+ ChildT>(ChildMatcher);
+}
+
+/// \brief Matches AST nodes that have descendant AST nodes that match the
+/// provided matcher.
+///
+/// Example matches X, A, B, C
+/// (matcher = record(forEachDescendant(record(hasName("X")))))
+/// class X {}; // Matches X, because X::X is a class of name X inside X.
+/// class A { class X {}; };
+/// class B { class C { class X {}; }; };
+///
+/// DescendantT must be an AST base type.
+///
+/// As opposed to 'hasDescendant', 'forEachDescendant' will cause a match for
+/// each result that matches instead of only on the first one.
+///
+/// Note: Recursively combined ForEachDescendant can cause many matches:
+/// record(forEachDescendant(record(forEachDescendant(record()))))
+/// will match 10 times (plus injected class name matches) on:
+/// class A { class B { class C { class D { class E {}; }; }; }; };
+template <typename DescendantT>
+internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher, DescendantT>
+forEachDescendant(
+ const internal::Matcher<DescendantT>& DescendantMatcher) {
+ return internal::ArgumentAdaptingMatcher<
+ internal::ForEachDescendantMatcher,
+ DescendantT>(DescendantMatcher);
+}
+
+/// \brief Matches if the provided matcher does not match.
+///
+/// Example matches Y (matcher = record(unless(hasName("X"))))
+/// class X {};
+/// class Y {};
+template <typename M>
+internal::PolymorphicMatcherWithParam1<internal::NotMatcher, M> unless(const M &InnerMatcher) {
+ return internal::PolymorphicMatcherWithParam1<
+ internal::NotMatcher, M>(InnerMatcher);
+}
+
+/// \brief Matches a type if the declaration of the type matches the given
+/// matcher.
+///
+/// Usable as: Matcher<QualType>, Matcher<CallExpr>, Matcher<CXXConstructExpr>
+inline internal::PolymorphicMatcherWithParam1< internal::HasDeclarationMatcher,
+ internal::Matcher<Decl> >
+ hasDeclaration(const internal::Matcher<Decl> &InnerMatcher) {
+ return internal::PolymorphicMatcherWithParam1<
+ internal::HasDeclarationMatcher,
+ internal::Matcher<Decl> >(InnerMatcher);
+}
+
+/// \brief Matches on the implicit object argument of a member call expression.
+///
+/// Example matches y.x() (matcher = call(on(hasType(record(hasName("Y"))))))
+/// class Y { public: void x(); };
+/// void z() { Y y; y.x(); }",
+///
+/// FIXME: Overload to allow directly matching types?
+AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher<Expr>,
+ InnerMatcher) {
+ const Expr *ExprNode = const_cast<CXXMemberCallExpr&>(Node)
+ .getImplicitObjectArgument()
+ ->IgnoreParenImpCasts();
+ return (ExprNode != NULL &&
+ InnerMatcher.matches(*ExprNode, Finder, Builder));
+}
+
+/// \brief Matches if the call expression's callee expression matches.
+///
+/// Given
+/// class Y { void x() { this->x(); x(); Y y; y.x(); } };
+/// void f() { f(); }
+/// call(callee(expression()))
+/// matches this->x(), x(), y.x(), f()
+/// with callee(...)
+/// matching this->x, x, y.x, f respectively
+///
+/// Note: Callee cannot take the more general internal::Matcher<Expr>
+/// because this introduces ambiguous overloads with calls to Callee taking a
+/// internal::Matcher<Decl>, as the matcher hierarchy is purely
+/// implemented in terms of implicit casts.
+AST_MATCHER_P(CallExpr, callee, internal::Matcher<Stmt>,
+ InnerMatcher) {
+ const Expr *ExprNode = Node.getCallee();
+ return (ExprNode != NULL &&
+ InnerMatcher.matches(*ExprNode, Finder, Builder));
+}
+
+/// \brief Matches if the call expression's callee's declaration matches the
+/// given matcher.
+///
+/// Example matches y.x() (matcher = call(callee(method(hasName("x")))))
+/// class Y { public: void x(); };
+/// void z() { Y y; y.x();
+inline internal::Matcher<CallExpr> callee(
+ const internal::Matcher<Decl> &InnerMatcher) {
+ return internal::Matcher<CallExpr>(hasDeclaration(InnerMatcher));
+}
+
+/// \brief Matches if the expression's or declaration's type matches a type
+/// matcher.
+///
+/// Example matches x (matcher = expression(hasType(
+/// hasDeclaration(record(hasName("X"))))))
+/// and z (matcher = variable(hasType(
+/// hasDeclaration(record(hasName("X"))))))
+/// class X {};
+/// void y(X &x) { x; X z; }
+AST_POLYMORPHIC_MATCHER_P(hasType, internal::Matcher<QualType>,
+ InnerMatcher) {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<Expr, NodeType>::value ||
+ llvm::is_base_of<ValueDecl, NodeType>::value),
+ instantiated_with_wrong_types);
+ return InnerMatcher.matches(Node.getType(), Finder, Builder);
+}
+
+/// \brief Overloaded to match the declaration of the expression's or value
+/// declaration's type.
+///
+/// In case of a value declaration (for example a variable declaration),
+/// this resolves one layer of indirection. For example, in the value
+/// declaration "X x;", record(hasName("X")) matches the declaration of X,
+/// while variable(hasType(record(hasName("X")))) matches the declaration
+/// of x."
+///
+/// Example matches x (matcher = expression(hasType(record(hasName("X")))))
+/// and z (matcher = variable(hasType(record(hasName("X")))))
+/// class X {};
+/// void y(X &x) { x; X z; }
+///
+/// Usable as: Matcher<Expr>, Matcher<ValueDecl>
+inline internal::PolymorphicMatcherWithParam1<
+ internal::matcher_hasTypeMatcher,
+ internal::Matcher<QualType> >
+hasType(const internal::Matcher<Decl> &InnerMatcher) {
+ return hasType(internal::Matcher<QualType>(
+ hasDeclaration(InnerMatcher)));
+}
+
+/// \brief Matches if the matched type is represented by the given string.
+///
+/// Given
+/// class Y { public: void x(); };
+/// void z() { Y* y; y->x(); }
+/// call(on(hasType(asString("class Y *"))))
+/// matches y->x()
+AST_MATCHER_P(QualType, asString, std::string, Name) {
+ return Name == Node.getAsString();
+}
+
+/// \brief Matches if the matched type is a pointer type and the pointee type
+/// matches the specified matcher.
+///
+/// Example matches y->x()
+/// (matcher = call(on(hasType(pointsTo(record(hasName("Y")))))))
+/// class Y { public: void x(); };
+/// void z() { Y *y; y->x(); }
+AST_MATCHER_P(
+ QualType, pointsTo, internal::Matcher<QualType>,
+ InnerMatcher) {
+ return (!Node.isNull() && Node->isPointerType() &&
+ InnerMatcher.matches(Node->getPointeeType(), Finder, Builder));
+}
+
+/// \brief Overloaded to match the pointee type's declaration.
+inline internal::Matcher<QualType> pointsTo(
+ const internal::Matcher<Decl> &InnerMatcher) {
+ return pointsTo(internal::Matcher<QualType>(
+ hasDeclaration(InnerMatcher)));
+}
+
+/// \brief Matches if the matched type is a reference type and the referenced
+/// type matches the specified matcher.
+///
+/// Example matches X &x and const X &y
+/// (matcher = variable(hasType(references(record(hasName("X"))))))
+/// class X {
+/// void a(X b) {
+/// X &x = b;
+/// const X &y = b;
+/// };
+AST_MATCHER_P(QualType, references, internal::Matcher<QualType>,
+ InnerMatcher) {
+ return (!Node.isNull() && Node->isReferenceType() &&
+ InnerMatcher.matches(Node->getPointeeType(), Finder, Builder));
+}
+
+/// \brief Overloaded to match the referenced type's declaration.
+inline internal::Matcher<QualType> references(
+ const internal::Matcher<Decl> &InnerMatcher) {
+ return references(internal::Matcher<QualType>(
+ hasDeclaration(InnerMatcher)));
+}
+
+AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument,
+ internal::Matcher<Expr>, InnerMatcher) {
+ const Expr *ExprNode =
+ const_cast<CXXMemberCallExpr&>(Node).getImplicitObjectArgument();
+ return (ExprNode != NULL &&
+ InnerMatcher.matches(*ExprNode, Finder, Builder));
+}
+
+/// \brief Matches if the expression's type either matches the specified
+/// matcher, or is a pointer to a type that matches the InnerMatcher.
+inline internal::Matcher<CXXMemberCallExpr> thisPointerType(
+ const internal::Matcher<QualType> &InnerMatcher) {
+ return onImplicitObjectArgument(
+ anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher))));
+}
+
+/// \brief Overloaded to match the type's declaration.
+inline internal::Matcher<CXXMemberCallExpr> thisPointerType(
+ const internal::Matcher<Decl> &InnerMatcher) {
+ return onImplicitObjectArgument(
+ anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher))));
+}
+
+/// \brief Matches a DeclRefExpr that refers to a declaration that matches the
+/// specified matcher.
+///
+/// Example matches x in if(x)
+/// (matcher = declarationReference(to(variable(hasName("x")))))
+/// bool x;
+/// if (x) {}
+AST_MATCHER_P(DeclRefExpr, to, internal::Matcher<Decl>,
+ InnerMatcher) {
+ const Decl *DeclNode = Node.getDecl();
+ return (DeclNode != NULL &&
+ InnerMatcher.matches(*DeclNode, Finder, Builder));
+}
+
+/// \brief Matches a \c DeclRefExpr that refers to a declaration through a
+/// specific using shadow declaration.
+///
+/// FIXME: This currently only works for functions. Fix.
+///
+/// Given
+/// namespace a { void f() {} }
+/// using a::f;
+/// void g() {
+/// f(); // Matches this ..
+/// a::f(); // .. but not this.
+/// }
+/// declarationReference(throughUsingDeclaration(anything()))
+/// matches \c f()
+AST_MATCHER_P(DeclRefExpr, throughUsingDecl,
+ internal::Matcher<UsingShadowDecl>, Matcher) {
+ const NamedDecl *FoundDecl = Node.getFoundDecl();
+ if (const UsingShadowDecl *UsingDecl =
+ llvm::dyn_cast<UsingShadowDecl>(FoundDecl))
+ return Matcher.matches(*UsingDecl, Finder, Builder);
+ return false;
+}
+
+/// \brief Matches a variable declaration that has an initializer expression
+/// that matches the given matcher.
+///
+/// Example matches x (matcher = variable(hasInitializer(call())))
+/// bool y() { return true; }
+/// bool x = y();
+AST_MATCHER_P(
+ VarDecl, hasInitializer, internal::Matcher<Expr>,
+ InnerMatcher) {
+ const Expr *Initializer = Node.getAnyInitializer();
+ return (Initializer != NULL &&
+ InnerMatcher.matches(*Initializer, Finder, Builder));
+}
+
+/// \brief Checks that a call expression or a constructor call expression has
+/// a specific number of arguments (including absent default arguments).
+///
+/// Example matches f(0, 0) (matcher = call(argumentCountIs(2)))
+/// void f(int x, int y);
+/// f(0, 0);
+AST_POLYMORPHIC_MATCHER_P(argumentCountIs, unsigned, N) {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<CallExpr, NodeType>::value ||
+ llvm::is_base_of<CXXConstructExpr,
+ NodeType>::value),
+ instantiated_with_wrong_types);
+ return Node.getNumArgs() == N;
+}
+
+/// \brief Matches the n'th argument of a call expression or a constructor
+/// call expression.
+///
+/// Example matches y in x(y)
+/// (matcher = call(hasArgument(0, declarationReference())))
+/// void x(int) { int y; x(y); }
+AST_POLYMORPHIC_MATCHER_P2(
+ hasArgument, unsigned, N, internal::Matcher<Expr>, InnerMatcher) {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<CallExpr, NodeType>::value ||
+ llvm::is_base_of<CXXConstructExpr,
+ NodeType>::value),
+ instantiated_with_wrong_types);
+ return (N < Node.getNumArgs() &&
+ InnerMatcher.matches(
+ *Node.getArg(N)->IgnoreParenImpCasts(), Finder, Builder));
+}
+
+/// \brief Matches a constructor initializer.
+///
+/// Given
+/// struct Foo {
+/// Foo() : foo_(1) { }
+/// int foo_;
+/// };
+/// record(has(constructor(hasAnyConstructorInitializer(anything()))))
+/// record matches Foo, hasAnyConstructorInitializer matches foo_(1)
+AST_MATCHER_P(CXXConstructorDecl, hasAnyConstructorInitializer,
+ internal::Matcher<CXXCtorInitializer>, InnerMatcher) {
+ for (CXXConstructorDecl::init_const_iterator I = Node.init_begin();
+ I != Node.init_end(); ++I) {
+ if (InnerMatcher.matches(**I, Finder, Builder)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// \brief Matches the field declaration of a constructor initializer.
+///
+/// Given
+/// struct Foo {
+/// Foo() : foo_(1) { }
+/// int foo_;
+/// };
+/// record(has(constructor(hasAnyConstructorInitializer(
+/// forField(hasName("foo_"))))))
+/// matches Foo
+/// with forField matching foo_
+AST_MATCHER_P(CXXCtorInitializer, forField,
+ internal::Matcher<FieldDecl>, InnerMatcher) {
+ const FieldDecl *NodeAsDecl = Node.getMember();
+ return (NodeAsDecl != NULL &&
+ InnerMatcher.matches(*NodeAsDecl, Finder, Builder));
+}
+
+/// \brief Matches the initializer expression of a constructor initializer.
+///
+/// Given
+/// struct Foo {
+/// Foo() : foo_(1) { }
+/// int foo_;
+/// };
+/// record(has(constructor(hasAnyConstructorInitializer(
+/// withInitializer(integerLiteral(equals(1)))))))
+/// matches Foo
+/// with withInitializer matching (1)
+AST_MATCHER_P(CXXCtorInitializer, withInitializer,
+ internal::Matcher<Expr>, InnerMatcher) {
+ const Expr* NodeAsExpr = Node.getInit();
+ return (NodeAsExpr != NULL &&
+ InnerMatcher.matches(*NodeAsExpr, Finder, Builder));
+}
+
+/// \brief Matches a contructor initializer if it is explicitly written in
+/// code (as opposed to implicitly added by the compiler).
+///
+/// Given
+/// struct Foo {
+/// Foo() { }
+/// Foo(int) : foo_("A") { }
+/// string foo_;
+/// };
+/// constructor(hasAnyConstructorInitializer(isWritten()))
+/// will match Foo(int), but not Foo()
+AST_MATCHER(CXXCtorInitializer, isWritten) {
+ return Node.isWritten();
+}
+
+/// \brief Matches a constructor declaration that has been implicitly added
+/// by the compiler (eg. implicit default/copy constructors).
+AST_MATCHER(CXXConstructorDecl, isImplicit) {
+ return Node.isImplicit();
+}
+
+/// \brief Matches any argument of a call expression or a constructor call
+/// expression.
+///
+/// Given
+/// void x(int, int, int) { int y; x(1, y, 42); }
+/// call(hasAnyArgument(declarationReference()))
+/// matches x(1, y, 42)
+/// with hasAnyArgument(...)
+/// matching y
+AST_POLYMORPHIC_MATCHER_P(hasAnyArgument, internal::Matcher<Expr>,
+ InnerMatcher) {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<CallExpr, NodeType>::value ||
+ llvm::is_base_of<CXXConstructExpr,
+ NodeType>::value),
+ instantiated_with_wrong_types);
+ for (unsigned I = 0; I < Node.getNumArgs(); ++I) {
+ if (InnerMatcher.matches(*Node.getArg(I)->IgnoreParenImpCasts(),
+ Finder, Builder)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// \brief Matches the n'th parameter of a function declaration.
+///
+/// Given
+/// class X { void f(int x) {} };
+/// method(hasParameter(0, hasType(variable())))
+/// matches f(int x) {}
+/// with hasParameter(...)
+/// matching int x
+AST_MATCHER_P2(FunctionDecl, hasParameter,
+ unsigned, N, internal::Matcher<ParmVarDecl>,
+ InnerMatcher) {
+ return (N < Node.getNumParams() &&
+ InnerMatcher.matches(
+ *Node.getParamDecl(N), Finder, Builder));
+}
+
+/// \brief Matches any parameter of a function declaration.
+///
+/// Does not match the 'this' parameter of a method.
+///
+/// Given
+/// class X { void f(int x, int y, int z) {} };
+/// method(hasAnyParameter(hasName("y")))
+/// matches f(int x, int y, int z) {}
+/// with hasAnyParameter(...)
+/// matching int y
+AST_MATCHER_P(FunctionDecl, hasAnyParameter,
+ internal::Matcher<ParmVarDecl>, InnerMatcher) {
+ for (unsigned I = 0; I < Node.getNumParams(); ++I) {
+ if (InnerMatcher.matches(*Node.getParamDecl(I), Finder, Builder)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// \brief Matches the return type of a function declaration.
+///
+/// Given:
+/// class X { int f() { return 1; } };
+/// method(returns(asString("int")))
+/// matches int f() { return 1; }
+AST_MATCHER_P(FunctionDecl, returns, internal::Matcher<QualType>, Matcher) {
+ return Matcher.matches(Node.getResultType(), Finder, Builder);
+}
+
+/// \brief Matches the condition expression of an if statement, for loop,
+/// or conditional operator.
+///
+/// Example matches true (matcher = hasCondition(boolLiteral(equals(true))))
+/// if (true) {}
+AST_POLYMORPHIC_MATCHER_P(hasCondition, internal::Matcher<Expr>,
+ InnerMatcher) {
+ TOOLING_COMPILE_ASSERT(
+ (llvm::is_base_of<IfStmt, NodeType>::value) ||
+ (llvm::is_base_of<ForStmt, NodeType>::value) ||
+ (llvm::is_base_of<WhileStmt, NodeType>::value) ||
+ (llvm::is_base_of<DoStmt, NodeType>::value) ||
+ (llvm::is_base_of<ConditionalOperator, NodeType>::value),
+ has_condition_requires_if_statement_conditional_operator_or_loop);
+ const Expr *const Condition = Node.getCond();
+ return (Condition != NULL &&
+ InnerMatcher.matches(*Condition, Finder, Builder));
+}
+
+/// \brief Matches the condition variable statement in an if statement.
+///
+/// Given
+/// if (A* a = GetAPointer()) {}
+/// hasConditionVariableStatment(...)
+/// matches 'A* a = GetAPointer()'.
+AST_MATCHER_P(IfStmt, hasConditionVariableStatement,
+ internal::Matcher<DeclStmt>, InnerMatcher) {
+ const DeclStmt* const DeclarationStatement =
+ Node.getConditionVariableDeclStmt();
+ return DeclarationStatement != NULL &&
+ InnerMatcher.matches(*DeclarationStatement, Finder, Builder);
+}
+
+/// \brief Matches the index expression of an array subscript expression.
+///
+/// Given
+/// int i[5];
+/// void f() { i[1] = 42; }
+/// arraySubscriptExpression(hasIndex(integerLiteral()))
+/// matches \c i[1] with the \c integerLiteral() matching \c 1
+AST_MATCHER_P(ArraySubscriptExpr, hasIndex,
+ internal::Matcher<Expr>, matcher) {
+ if (const Expr* Expression = Node.getIdx())
+ return matcher.matches(*Expression, Finder, Builder);
+ return false;
+}
+
+/// \brief Matches the base expression of an array subscript expression.
+///
+/// Given
+/// int i[5];
+/// void f() { i[1] = 42; }
+/// arraySubscriptExpression(hasBase(implicitCast(
+/// hasSourceExpression(declarationReference()))))
+/// matches \c i[1] with the \c declarationReference() matching \c i
+AST_MATCHER_P(ArraySubscriptExpr, hasBase,
+ internal::Matcher<Expr>, matcher) {
+ if (const Expr* Expression = Node.getBase())
+ return matcher.matches(*Expression, Finder, Builder);
+ return false;
+}
+
+/// \brief Matches a 'for', 'while', or 'do while' statement that has
+/// a given body.
+///
+/// Given
+/// for (;;) {}
+/// hasBody(compoundStatement())
+/// matches 'for (;;) {}'
+/// with compoundStatement()
+/// matching '{}'
+AST_POLYMORPHIC_MATCHER_P(hasBody, internal::Matcher<Stmt>,
+ InnerMatcher) {
+ TOOLING_COMPILE_ASSERT(
+ (llvm::is_base_of<DoStmt, NodeType>::value) ||
+ (llvm::is_base_of<ForStmt, NodeType>::value) ||
+ (llvm::is_base_of<WhileStmt, NodeType>::value),
+ has_body_requires_for_while_or_do_statement);
+ const Stmt *const Statement = Node.getBody();
+ return (Statement != NULL &&
+ InnerMatcher.matches(*Statement, Finder, Builder));
+}
+
+/// \brief Matches compound statements where at least one substatement matches
+/// a given matcher.
+///
+/// Given
+/// { {}; 1+2; }
+/// hasAnySubstatement(compoundStatement())
+/// matches '{ {}; 1+2; }'
+/// with compoundStatement()
+/// matching '{}'
+AST_MATCHER_P(CompoundStmt, hasAnySubstatement,
+ internal::Matcher<Stmt>, InnerMatcher) {
+ for (CompoundStmt::const_body_iterator It = Node.body_begin();
+ It != Node.body_end();
+ ++It) {
+ if (InnerMatcher.matches(**It, Finder, Builder)) return true;
+ }
+ return false;
+}
+
+/// \brief Checks that a compound statement contains a specific number of
+/// child statements.
+///
+/// Example: Given
+/// { for (;;) {} }
+/// compoundStatement(statementCountIs(0)))
+/// matches '{}'
+/// but does not match the outer compound statement.
+AST_MATCHER_P(CompoundStmt, statementCountIs, unsigned, N) {
+ return Node.size() == N;
+}
+
+/// \brief Matches literals that are equal to the given value.
+///
+/// Example matches true (matcher = boolLiteral(equals(true)))
+/// true
+///
+/// Usable as: Matcher<CharacterLiteral>, Matcher<CXXBoolLiteral>,
+/// Matcher<FloatingLiteral>, Matcher<IntegerLiteral>
+template <typename ValueT>
+internal::PolymorphicMatcherWithParam1<internal::ValueEqualsMatcher, ValueT>
+equals(const ValueT &Value) {
+ return internal::PolymorphicMatcherWithParam1<
+ internal::ValueEqualsMatcher,
+ ValueT>(Value);
+}
+
+/// \brief Matches the operator Name of operator expressions (binary or
+/// unary).
+///
+/// Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
+/// !(a || b)
+AST_POLYMORPHIC_MATCHER_P(hasOperatorName, std::string, Name) {
+ TOOLING_COMPILE_ASSERT(
+ (llvm::is_base_of<BinaryOperator, NodeType>::value) ||
+ (llvm::is_base_of<UnaryOperator, NodeType>::value),
+ has_condition_requires_if_statement_or_conditional_operator);
+ return Name == Node.getOpcodeStr(Node.getOpcode());
+}
+
+/// \brief Matches the left hand side of binary operator expressions.
+///
+/// Example matches a (matcher = binaryOperator(hasLHS()))
+/// a || b
+AST_MATCHER_P(BinaryOperator, hasLHS,
+ internal::Matcher<Expr>, InnerMatcher) {
+ Expr *LeftHandSide = Node.getLHS();
+ return (LeftHandSide != NULL &&
+ InnerMatcher.matches(*LeftHandSide, Finder, Builder));
+}
+
+/// \brief Matches the right hand side of binary operator expressions.
+///
+/// Example matches b (matcher = binaryOperator(hasRHS()))
+/// a || b
+AST_MATCHER_P(BinaryOperator, hasRHS,
+ internal::Matcher<Expr>, InnerMatcher) {
+ Expr *RightHandSide = Node.getRHS();
+ return (RightHandSide != NULL &&
+ InnerMatcher.matches(*RightHandSide, Finder, Builder));
+}
+
+/// \brief Matches if either the left hand side or the right hand side of a
+/// binary operator matches.
+inline internal::Matcher<BinaryOperator> hasEitherOperand(
+ const internal::Matcher<Expr> &InnerMatcher) {
+ return anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher));
+}
+
+/// \brief Matches if the operand of a unary operator matches.
+///
+/// Example matches true (matcher = hasOperand(boolLiteral(equals(true))))
+/// !true
+AST_MATCHER_P(UnaryOperator, hasUnaryOperand,
+ internal::Matcher<Expr>, InnerMatcher) {
+ const Expr * const Operand = Node.getSubExpr();
+ return (Operand != NULL &&
+ InnerMatcher.matches(*Operand, Finder, Builder));
+}
+
+/// \brief Matches if the cast's source expression matches the given matcher.
+///
+/// Example: matches "a string" (matcher =
+/// hasSourceExpression(constructorCall()))
+///
+/// class URL { URL(string); };
+/// URL url = "a string";
+AST_MATCHER_P(CastExpr, hasSourceExpression,
+ internal::Matcher<Expr>, InnerMatcher) {
+ const Expr* const SubExpression = Node.getSubExpr();
+ return (SubExpression != NULL &&
+ InnerMatcher.matches(*SubExpression, Finder, Builder));
+}
+
+/// \brief Matches casts whose destination type matches a given matcher.
+///
+/// (Note: Clang's AST refers to other conversions as "casts" too, and calls
+/// actual casts "explicit" casts.)
+AST_MATCHER_P(ExplicitCastExpr, hasDestinationType,
+ internal::Matcher<QualType>, InnerMatcher) {
+ const QualType NodeType = Node.getTypeAsWritten();
+ return InnerMatcher.matches(NodeType, Finder, Builder);
+}
+
+/// \brief Matches implicit casts whose destination type matches a given
+/// matcher.
+///
+/// FIXME: Unit test this matcher
+AST_MATCHER_P(ImplicitCastExpr, hasImplicitDestinationType,
+ internal::Matcher<QualType>, InnerMatcher) {
+ return InnerMatcher.matches(Node.getType(), Finder, Builder);
+}
+
+/// \brief Matches the true branch expression of a conditional operator.
+///
+/// Example matches a
+/// condition ? a : b
+AST_MATCHER_P(ConditionalOperator, hasTrueExpression,
+ internal::Matcher<Expr>, InnerMatcher) {
+ Expr *Expression = Node.getTrueExpr();
+ return (Expression != NULL &&
+ InnerMatcher.matches(*Expression, Finder, Builder));
+}
+
+/// \brief Matches the false branch expression of a conditional operator.
+///
+/// Example matches b
+/// condition ? a : b
+AST_MATCHER_P(ConditionalOperator, hasFalseExpression,
+ internal::Matcher<Expr>, InnerMatcher) {
+ Expr *Expression = Node.getFalseExpr();
+ return (Expression != NULL &&
+ InnerMatcher.matches(*Expression, Finder, Builder));
+}
+
+/// \brief Matches if a declaration has a body attached.
+///
+/// Example matches A, va, fa
+/// class A {};
+/// class B; // Doesn't match, as it has no body.
+/// int va;
+/// extern int vb; // Doesn't match, as it doesn't define the variable.
+/// void fa() {}
+/// void fb(); // Doesn't match, as it has no body.
+///
+/// Usable as: Matcher<TagDecl>, Matcher<VarDecl>, Matcher<FunctionDecl>
+inline internal::PolymorphicMatcherWithParam0<internal::IsDefinitionMatcher>
+isDefinition() {
+ return internal::PolymorphicMatcherWithParam0<
+ internal::IsDefinitionMatcher>();
+}
+
+/// \brief Matches the class declaration that the given method declaration
+/// belongs to.
+///
+/// FIXME: Generalize this for other kinds of declarations.
+/// FIXME: What other kind of declarations would we need to generalize
+/// this to?
+///
+/// Example matches A() in the last line
+/// (matcher = constructorCall(hasDeclaration(method(
+/// ofClass(hasName("A"))))))
+/// class A {
+/// public:
+/// A();
+/// };
+/// A a = A();
+AST_MATCHER_P(CXXMethodDecl, ofClass,
+ internal::Matcher<CXXRecordDecl>, InnerMatcher) {
+ const CXXRecordDecl *Parent = Node.getParent();
+ return (Parent != NULL &&
+ InnerMatcher.matches(*Parent, Finder, Builder));
+}
+
+/// \brief Matches member expressions that are called with '->' as opposed
+/// to '.'.
+///
+/// Member calls on the implicit this pointer match as called with '->'.
+///
+/// Given
+/// class Y {
+/// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+/// int a;
+/// static int b;
+/// };
+/// memberExpression(isArrow())
+/// matches this->x, x, y.x, a, this->b
+inline internal::Matcher<MemberExpr> isArrow() {
+ return makeMatcher(new internal::IsArrowMatcher());
+}
+
+/// \brief Matches QualType nodes that are of integer type.
+///
+/// Given
+/// void a(int);
+/// void b(long);
+/// void c(double);
+/// function(hasAnyParameter(hasType(isInteger())))
+/// matches "a(int)", "b(long)", but not "c(double)".
+AST_MATCHER(QualType, isInteger) {
+ return Node->isIntegerType();
+}
+
+/// \brief Matches QualType nodes that are const-qualified, i.e., that
+/// include "top-level" const.
+///
+/// Given
+/// void a(int);
+/// void b(int const);
+/// void c(const int);
+/// void d(const int*);
+/// void e(int const) {};
+/// function(hasAnyParameter(hasType(isConstQualified())))
+/// matches "void b(int const)", "void c(const int)" and
+/// "void e(int const) {}". It does not match d as there
+/// is no top-level const on the parameter type "const int *".
+inline internal::Matcher<QualType> isConstQualified() {
+ return makeMatcher(new internal::IsConstQualifiedMatcher());
+}
+
+/// \brief Matches a member expression where the member is matched by a
+/// given matcher.
+///
+/// Given
+/// struct { int first, second; } first, second;
+/// int i(second.first);
+/// int j(first.second);
+/// memberExpression(member(hasName("first")))
+/// matches second.first
+/// but not first.second (because the member name there is "second").
+AST_MATCHER_P(MemberExpr, member,
+ internal::Matcher<ValueDecl>, InnerMatcher) {
+ return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder);
+}
+
+/// \brief Matches a member expression where the object expression is
+/// matched by a given matcher.
+///
+/// Given
+/// struct X { int m; };
+/// void f(X x) { x.m; m; }
+/// memberExpression(hasObjectExpression(hasType(record(hasName("X")))))))
+/// matches "x.m" and "m"
+/// with hasObjectExpression(...)
+/// matching "x" and the implicit object expression of "m" which has type X*.
+AST_MATCHER_P(MemberExpr, hasObjectExpression,
+ internal::Matcher<Expr>, InnerMatcher) {
+ return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
+}
+
+/// \brief Matches any using shadow declaration.
+///
+/// Given
+/// namespace X { void b(); }
+/// using X::b;
+/// usingDecl(hasAnyUsingShadowDecl(hasName("b"))))
+/// matches \code using X::b \endcode
+AST_MATCHER_P(UsingDecl, hasAnyUsingShadowDecl,
+ internal::Matcher<UsingShadowDecl>, Matcher) {
+ for (UsingDecl::shadow_iterator II = Node.shadow_begin();
+ II != Node.shadow_end(); ++II) {
+ if (Matcher.matches(**II, Finder, Builder))
+ return true;
+ }
+ return false;
+}
+
+/// \brief Matches a using shadow declaration where the target declaration is
+/// matched by the given matcher.
+///
+/// Given
+/// namespace X { int a; void b(); }
+/// using X::a;
+/// using X::b;
+/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(function())))
+/// matches \code using X::b \endcode
+/// but not \code using X::a \endcode
+AST_MATCHER_P(UsingShadowDecl, hasTargetDecl,
+ internal::Matcher<NamedDecl>, Matcher) {
+ return Matcher.matches(*Node.getTargetDecl(), Finder, Builder);
+}
+
+/// \brief Matches template instantiations of function, class, or static
+/// member variable template instantiations.
+///
+/// Given
+/// template <typename T> class X {}; class A {}; X<A> x;
+/// or
+/// template <typename T> class X {}; class A {}; template class X<A>;
+/// record(hasName("::X"), isTemplateInstantiation())
+/// matches the template instantiation of X<A>.
+///
+/// But given
+/// template <typename T> class X {}; class A {};
+/// template <> class X<A> {}; X<A> x;
+/// record(hasName("::X"), isTemplateInstantiation())
+/// does not match, as X<A> is an explicit template specialization.
+///
+/// Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl>
+inline internal::PolymorphicMatcherWithParam0<
+ internal::IsTemplateInstantiationMatcher>
+isTemplateInstantiation() {
+ return internal::PolymorphicMatcherWithParam0<
+ internal::IsTemplateInstantiationMatcher>();
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_H
diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h
new file mode 100644
index 0000000..3f55685
--- /dev/null
+++ b/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -0,0 +1,901 @@
+//===--- ASTMatchersInternal.h - Structural query framework -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the base layer of the matcher framework.
+//
+// Matchers are methods that return a Matcher<T> which provides a method
+// Matches(...) which is a predicate on an AST node. The Matches method's
+// parameters define the context of the match, which allows matchers to recurse
+// or store the current node as bound to a specific string, so that it can be
+// retrieved later.
+//
+// In general, matchers have two parts:
+// 1. A function Matcher<T> MatcherName(<arguments>) which returns a Matcher<T>
+// based on the arguments and optionally on template type deduction based
+// on the arguments. Matcher<T>s form an implicit reverse hierarchy
+// to clang's AST class hierarchy, meaning that you can use a Matcher<Base>
+// everywhere a Matcher<Derived> is required.
+// 2. An implementation of a class derived from MatcherInterface<T>.
+//
+// The matcher functions are defined in ASTMatchers.h. To make it possible
+// to implement both the matcher function and the implementation of the matcher
+// interface in one place, ASTMatcherMacros.h defines macros that allow
+// implementing a matcher in a single place.
+//
+// This file contains the base classes needed to construct the actual matchers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H
+#define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Stmt.h"
+#include "llvm/ADT/VariadicFunction.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace ast_matchers {
+
+/// FIXME: Move into the llvm support library.
+template <bool> struct CompileAssert {};
+#define TOOLING_COMPILE_ASSERT(Expr, Msg) \
+ typedef CompileAssert<(bool(Expr))> Msg[bool(Expr) ? 1 : -1]
+
+class BoundNodes;
+
+namespace internal {
+
+class BoundNodesTreeBuilder;
+
+/// \brief A tree of bound nodes in match results.
+///
+/// If a match can contain multiple matches on the same node with different
+/// matching subexpressions, BoundNodesTree contains a branch for each of
+/// those matching subexpressions.
+///
+/// BoundNodesTree's are created during the matching process; when a match
+/// is found, we iterate over the tree and create a BoundNodes object containing
+/// the union of all bound nodes on the path from the root to a each leaf.
+class BoundNodesTree {
+public:
+ /// \brief A visitor interface to visit all BoundNodes results for a
+ /// BoundNodesTree.
+ class Visitor {
+ public:
+ virtual ~Visitor() {}
+
+ /// \brief Called multiple times during a single call to VisitMatches(...).
+ ///
+ /// 'BoundNodesView' contains the bound nodes for a single match.
+ virtual void visitMatch(const BoundNodes& BoundNodesView) = 0;
+ };
+
+ BoundNodesTree();
+
+ /// \brief Create a BoundNodesTree from pre-filled maps of bindings.
+ BoundNodesTree(const std::map<std::string, const Decl*>& DeclBindings,
+ const std::map<std::string, const Stmt*>& StmtBindings,
+ const std::vector<BoundNodesTree> RecursiveBindings);
+
+ /// \brief Adds all bound nodes to bound_nodes_builder.
+ void copyTo(BoundNodesTreeBuilder* Builder) const;
+
+ /// \brief Visits all matches that this BoundNodesTree represents.
+ ///
+ /// The ownership of 'ResultVisitor' remains at the caller.
+ void visitMatches(Visitor* ResultVisitor);
+
+private:
+ void visitMatchesRecursively(
+ Visitor* ResultVistior,
+ std::map<std::string, const Decl*> DeclBindings,
+ std::map<std::string, const Stmt*> StmtBindings);
+
+ template <typename T>
+ void copyBindingsTo(const T& bindings, BoundNodesTreeBuilder* Builder) const;
+
+ // FIXME: Find out whether we want to use different data structures here -
+ // first benchmarks indicate that it doesn't matter though.
+
+ std::map<std::string, const Decl*> DeclBindings;
+ std::map<std::string, const Stmt*> StmtBindings;
+
+ std::vector<BoundNodesTree> RecursiveBindings;
+};
+
+/// \brief Creates BoundNodesTree objects.
+///
+/// The tree builder is used during the matching process to insert the bound
+/// nodes from the Id matcher.
+class BoundNodesTreeBuilder {
+public:
+ BoundNodesTreeBuilder();
+
+ /// \brief Add a binding from an id to a node.
+ ///
+ /// FIXME: Add overloads for all AST base types.
+ /// @{
+ void setBinding(const std::string &Id, const Decl *Node);
+ void setBinding(const std::string &Id, const Stmt *Node);
+ /// @}
+
+ /// \brief Adds a branch in the tree.
+ void addMatch(const BoundNodesTree& Bindings);
+
+ /// \brief Returns a BoundNodes object containing all current bindings.
+ BoundNodesTree build() const;
+
+private:
+ BoundNodesTreeBuilder(const BoundNodesTreeBuilder&); // DO NOT IMPLEMENT
+ void operator=(const BoundNodesTreeBuilder&); // DO NOT IMPLEMENT
+
+ std::map<std::string, const Decl*> DeclBindings;
+ std::map<std::string, const Stmt*> StmtBindings;
+
+ std::vector<BoundNodesTree> RecursiveBindings;
+};
+
+class ASTMatchFinder;
+
+/// \brief Generic interface for matchers on an AST node of type T.
+///
+/// Implement this if your matcher may need to inspect the children or
+/// descendants of the node or bind matched nodes to names. If you are
+/// writing a simple matcher that only inspects properties of the
+/// current node and doesn't care about its children or descendants,
+/// implement SingleNodeMatcherInterface instead.
+template <typename T>
+class MatcherInterface : public llvm::RefCountedBaseVPTR {
+public:
+ virtual ~MatcherInterface() {}
+
+ /// \brief Returns true if 'Node' can be matched.
+ ///
+ /// May bind 'Node' to an ID via 'Builder', or recurse into
+ /// the AST via 'Finder'.
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const = 0;
+};
+
+/// \brief Interface for matchers that only evaluate properties on a single node.
+template <typename T>
+class SingleNodeMatcherInterface : public MatcherInterface<T> {
+public:
+ /// \brief Returns true if the matcher matches the provided node.
+ ///
+ /// A subclass must implement this instead of Matches().
+ virtual bool matchesNode(const T &Node) const = 0;
+
+private:
+ /// Implements MatcherInterface::Matches.
+ virtual bool matches(const T &Node,
+ ASTMatchFinder * /* Finder */,
+ BoundNodesTreeBuilder * /* Builder */) const {
+ return matchesNode(Node);
+ }
+};
+
+/// \brief Wrapper of a MatcherInterface<T> *that allows copying.
+///
+/// A Matcher<Base> can be used anywhere a Matcher<Derived> is
+/// required. This establishes an is-a relationship which is reverse
+/// to the AST hierarchy. In other words, Matcher<T> is contravariant
+/// with respect to T. The relationship is built via a type conversion
+/// operator rather than a type hierarchy to be able to templatize the
+/// type hierarchy instead of spelling it out.
+template <typename T>
+class Matcher {
+public:
+ /// \brief Takes ownership of the provided implementation pointer.
+ explicit Matcher(MatcherInterface<T> *Implementation)
+ : Implementation(Implementation) {}
+
+ /// \brief Forwards the call to the underlying MatcherInterface<T> pointer.
+ bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return Implementation->matches(Node, Finder, Builder);
+ }
+
+ /// \brief Implicitly converts this object to a Matcher<Derived>.
+ ///
+ /// Requires Derived to be derived from T.
+ template <typename Derived>
+ operator Matcher<Derived>() const {
+ return Matcher<Derived>(new ImplicitCastMatcher<Derived>(*this));
+ }
+
+ /// \brief Returns an ID that uniquely identifies the matcher.
+ uint64_t getID() const {
+ /// FIXME: Document the requirements this imposes on matcher
+ /// implementations (no new() implementation_ during a Matches()).
+ return reinterpret_cast<uint64_t>(Implementation.getPtr());
+ }
+
+private:
+ /// \brief Allows conversion from Matcher<T> to Matcher<Derived> if Derived
+ /// is derived from T.
+ template <typename Derived>
+ class ImplicitCastMatcher : public MatcherInterface<Derived> {
+ public:
+ explicit ImplicitCastMatcher(const Matcher<T> &From)
+ : From(From) {}
+
+ virtual bool matches(const Derived &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return From.matches(Node, Finder, Builder);
+ }
+
+ private:
+ const Matcher<T> From;
+ };
+
+ llvm::IntrusiveRefCntPtr< MatcherInterface<T> > Implementation;
+}; // class Matcher
+
+/// \brief A convenient helper for creating a Matcher<T> without specifying
+/// the template type argument.
+template <typename T>
+inline Matcher<T> makeMatcher(MatcherInterface<T> *Implementation) {
+ return Matcher<T>(Implementation);
+}
+
+/// \brief Matches declarations for QualType and CallExpr.
+///
+/// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but
+/// not actually used.
+template <typename T, typename DeclMatcherT>
+class HasDeclarationMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT((llvm::is_same< DeclMatcherT,
+ Matcher<Decl> >::value),
+ instantiated_with_wrong_types);
+public:
+ explicit HasDeclarationMatcher(const Matcher<Decl> &InnerMatcher)
+ : InnerMatcher(InnerMatcher) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return matchesSpecialized(Node, Finder, Builder);
+ }
+
+private:
+ /// \brief Extracts the CXXRecordDecl of a QualType and returns whether the
+ /// inner matcher matches on it.
+ bool matchesSpecialized(const QualType &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ /// FIXME: Add other ways to convert...
+ if (Node.isNull())
+ return false;
+ CXXRecordDecl *NodeAsRecordDecl = Node->getAsCXXRecordDecl();
+ return NodeAsRecordDecl != NULL &&
+ InnerMatcher.matches(*NodeAsRecordDecl, Finder, Builder);
+ }
+
+ /// \brief Extracts the Decl of the callee of a CallExpr and returns whether
+ /// the inner matcher matches on it.
+ bool matchesSpecialized(const CallExpr &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ const Decl *NodeAsDecl = Node.getCalleeDecl();
+ return NodeAsDecl != NULL &&
+ InnerMatcher.matches(*NodeAsDecl, Finder, Builder);
+ }
+
+ /// \brief Extracts the Decl of the constructor call and returns whether the
+ /// inner matcher matches on it.
+ bool matchesSpecialized(const CXXConstructExpr &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ const Decl *NodeAsDecl = Node.getConstructor();
+ return NodeAsDecl != NULL &&
+ InnerMatcher.matches(*NodeAsDecl, Finder, Builder);
+ }
+
+ const Matcher<Decl> InnerMatcher;
+};
+
+/// \brief IsBaseType<T>::value is true if T is a "base" type in the AST
+/// node class hierarchies (i.e. if T is Decl, Stmt, or QualType).
+template <typename T>
+struct IsBaseType {
+ static const bool value =
+ (llvm::is_same<T, Decl>::value ||
+ llvm::is_same<T, Stmt>::value ||
+ llvm::is_same<T, QualType>::value ||
+ llvm::is_same<T, CXXCtorInitializer>::value);
+};
+template <typename T>
+const bool IsBaseType<T>::value;
+
+/// \brief Interface that can match any AST base node type and contains default
+/// implementations returning false.
+class UntypedBaseMatcher : public llvm::RefCountedBaseVPTR {
+public:
+ virtual ~UntypedBaseMatcher() {}
+
+ virtual bool matches(const Decl &DeclNode, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return false;
+ }
+ virtual bool matches(const QualType &TypeNode, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return false;
+ }
+ virtual bool matches(const Stmt &StmtNode, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return false;
+ }
+ virtual bool matches(const CXXCtorInitializer &CtorInitNode,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return false;
+ }
+
+ /// \brief Returns a unique ID for the matcher.
+ virtual uint64_t getID() const = 0;
+};
+
+/// \brief An UntypedBaseMatcher that overwrites the Matches(...) method for
+/// node type T. T must be an AST base type.
+template <typename T>
+class TypedBaseMatcher : public UntypedBaseMatcher {
+ TOOLING_COMPILE_ASSERT(IsBaseType<T>::value,
+ typed_base_matcher_can_only_be_used_with_base_type);
+public:
+ explicit TypedBaseMatcher(const Matcher<T> &InnerMatcher)
+ : InnerMatcher(InnerMatcher) {}
+
+ using UntypedBaseMatcher::matches;
+ /// \brief Implements UntypedBaseMatcher::Matches.
+ ///
+ /// Since T is guaranteed to be a "base" AST node type, this method is
+ /// guaranteed to override one of the matches() methods from
+ /// UntypedBaseMatcher.
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return InnerMatcher.matches(Node, Finder, Builder);
+ }
+
+ /// \brief Implements UntypedBaseMatcher::getID.
+ virtual uint64_t getID() const {
+ return InnerMatcher.getID();
+ }
+
+private:
+ Matcher<T> InnerMatcher;
+};
+
+/// \brief Interface that allows matchers to traverse the AST.
+/// FIXME: Find a better name.
+///
+/// This provides two entry methods for each base node type in the AST:
+/// - matchesChildOf:
+/// Matches a matcher on every child node of the given node. Returns true
+/// if at least one child node could be matched.
+/// - matchesDescendantOf:
+/// Matches a matcher on all descendant nodes of the given node. Returns true
+/// if at least one descendant matched.
+class ASTMatchFinder {
+public:
+ /// \brief Defines how we descend a level in the AST when we pass
+ /// through expressions.
+ enum TraversalKind {
+ /// Will traverse any child nodes.
+ TK_AsIs,
+ /// Will not traverse implicit casts and parentheses.
+ TK_IgnoreImplicitCastsAndParentheses
+ };
+
+ /// \brief Defines how bindings are processed on recursive matches.
+ enum BindKind {
+ /// Stop at the first match and only bind the first match.
+ BK_First,
+ /// Create results for all combinations of bindings that match.
+ BK_All
+ };
+
+ virtual ~ASTMatchFinder() {}
+
+ /// \brief Returns true if the given class is directly or indirectly derived
+ /// from a base type matching \c base.
+ ///
+ /// A class is considered to be also derived from itself.
+ virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration,
+ const Matcher<NamedDecl> &Base,
+ BoundNodesTreeBuilder *Builder) = 0;
+
+ // FIXME: Implement for other base nodes.
+ virtual bool matchesChildOf(const Decl &DeclNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ TraversalKind Traverse,
+ BindKind Bind) = 0;
+ virtual bool matchesChildOf(const Stmt &StmtNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ TraversalKind Traverse,
+ BindKind Bind) = 0;
+
+ virtual bool matchesDescendantOf(const Decl &DeclNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ BindKind Bind) = 0;
+ virtual bool matchesDescendantOf(const Stmt &StmtNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ BindKind Bind) = 0;
+};
+
+/// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by
+/// "adapting" a \c To into a \c T.
+///
+/// The \c ArgumentAdapterT argument specifies how the adaptation is done.
+///
+/// For example:
+/// \c ArgumentAdaptingMatcher<HasMatcher, T>(InnerMatcher);
+/// Given that \c InnerMatcher is of type \c Matcher<T>, this returns a matcher
+/// that is convertible into any matcher of type \c To by constructing
+/// \c HasMatcher<To, T>(InnerMatcher).
+///
+/// If a matcher does not need knowledge about the inner type, prefer to use
+/// PolymorphicMatcherWithParam1.
+template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
+ typename T>
+class ArgumentAdaptingMatcher {
+public:
+ explicit ArgumentAdaptingMatcher(const Matcher<T> &InnerMatcher)
+ : InnerMatcher(InnerMatcher) {}
+
+ template <typename To>
+ operator Matcher<To>() const {
+ return Matcher<To>(new ArgumentAdapterT<To, T>(InnerMatcher));
+ }
+
+private:
+ const Matcher<T> InnerMatcher;
+};
+
+/// \brief A PolymorphicMatcherWithParamN<MatcherT, P1, ..., PN> object can be
+/// created from N parameters p1, ..., pN (of type P1, ..., PN) and
+/// used as a Matcher<T> where a MatcherT<T, P1, ..., PN>(p1, ..., pN)
+/// can be constructed.
+///
+/// For example:
+/// - PolymorphicMatcherWithParam0<IsDefinitionMatcher>()
+/// creates an object that can be used as a Matcher<T> for any type T
+/// where an IsDefinitionMatcher<T>() can be constructed.
+/// - PolymorphicMatcherWithParam1<ValueEqualsMatcher, int>(42)
+/// creates an object that can be used as a Matcher<T> for any type T
+/// where a ValueEqualsMatcher<T, int>(42) can be constructed.
+template <template <typename T> class MatcherT>
+class PolymorphicMatcherWithParam0 {
+public:
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new MatcherT<T>());
+ }
+};
+
+template <template <typename T, typename P1> class MatcherT,
+ typename P1>
+class PolymorphicMatcherWithParam1 {
+public:
+ explicit PolymorphicMatcherWithParam1(const P1 &Param1)
+ : Param1(Param1) {}
+
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new MatcherT<T, P1>(Param1));
+ }
+
+private:
+ const P1 Param1;
+};
+
+template <template <typename T, typename P1, typename P2> class MatcherT,
+ typename P1, typename P2>
+class PolymorphicMatcherWithParam2 {
+public:
+ PolymorphicMatcherWithParam2(const P1 &Param1, const P2 &Param2)
+ : Param1(Param1), Param2(Param2) {}
+
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new MatcherT<T, P1, P2>(Param1, Param2));
+ }
+
+private:
+ const P1 Param1;
+ const P2 Param2;
+};
+
+/// \brief Matches any instance of the given NodeType.
+///
+/// This is useful when a matcher syntactically requires a child matcher,
+/// but the context doesn't care. See for example: anything().
+///
+/// FIXME: Alternatively we could also create a IsAMatcher or something
+/// that checks that a dyn_cast is possible. This is purely needed for the
+/// difference between calling for example:
+/// record()
+/// and
+/// record(SomeMatcher)
+/// In the second case we need the correct type we were dyn_cast'ed to in order
+/// to get the right type for the inner matcher. In the first case we don't need
+/// that, but we use the type conversion anyway and insert a TrueMatcher.
+template <typename T>
+class TrueMatcher : public SingleNodeMatcherInterface<T> {
+public:
+ virtual bool matchesNode(const T &Node) const {
+ return true;
+ }
+};
+
+/// \brief Provides a MatcherInterface<T> for a Matcher<To> that matches if T is
+/// dyn_cast'able into To and the given Matcher<To> matches on the dyn_cast'ed
+/// node.
+template <typename T, typename To>
+class DynCastMatcher : public MatcherInterface<T> {
+public:
+ explicit DynCastMatcher(const Matcher<To> &InnerMatcher)
+ : InnerMatcher(InnerMatcher) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ const To *InnerMatchValue = llvm::dyn_cast<To>(&Node);
+ return InnerMatchValue != NULL &&
+ InnerMatcher.matches(*InnerMatchValue, Finder, Builder);
+ }
+
+private:
+ const Matcher<To> InnerMatcher;
+};
+
+/// \brief Matcher<T> that wraps an inner Matcher<T> and binds the matched node
+/// to an ID if the inner matcher matches on the node.
+template <typename T>
+class IdMatcher : public MatcherInterface<T> {
+public:
+ /// \brief Creates an IdMatcher that binds to 'ID' if 'InnerMatcher' matches
+ /// the node.
+ IdMatcher(StringRef ID, const Matcher<T> &InnerMatcher)
+ : ID(ID), InnerMatcher(InnerMatcher) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ bool Result = InnerMatcher.matches(Node, Finder, Builder);
+ if (Result) {
+ Builder->setBinding(ID, &Node);
+ }
+ return Result;
+ }
+
+private:
+ const std::string ID;
+ const Matcher<T> InnerMatcher;
+};
+
+/// \brief A Matcher that allows binding the node it matches to an id.
+///
+/// BindableMatcher provides a \a bind() method that allows binding the
+/// matched node to an id if the match was successful.
+template <typename T>
+class BindableMatcher : public Matcher<T> {
+public:
+ BindableMatcher(MatcherInterface<T> *Implementation)
+ : Matcher<T>(Implementation) {}
+
+ /// \brief Returns a matcher that will bind the matched node on a match.
+ ///
+ /// The returned matcher is equivalent to this matcher, but will
+ /// bind the matched node on a match.
+ Matcher<T> bind(StringRef ID) const {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<Stmt, T>::value ||
+ llvm::is_base_of<Decl, T>::value),
+ trying_to_bind_unsupported_node_type__only_decl_and_stmt_supported);
+ return Matcher<T>(new IdMatcher<T>(ID, *this));
+ }
+};
+
+/// \brief Matches nodes of type T that have child nodes of type ChildT for
+/// which a specified child matcher matches.
+///
+/// ChildT must be an AST base type.
+template <typename T, typename ChildT>
+class HasMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT(IsBaseType<ChildT>::value,
+ has_only_accepts_base_type_matcher);
+public:
+ explicit HasMatcher(const Matcher<ChildT> &ChildMatcher)
+ : ChildMatcher(ChildMatcher) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return Finder->matchesChildOf(
+ Node, ChildMatcher, Builder,
+ ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
+ ASTMatchFinder::BK_First);
+ }
+
+ private:
+ const TypedBaseMatcher<ChildT> ChildMatcher;
+};
+
+/// \brief Matches nodes of type T that have child nodes of type ChildT for
+/// which a specified child matcher matches. ChildT must be an AST base
+/// type.
+/// As opposed to the HasMatcher, the ForEachMatcher will produce a match
+/// for each child that matches.
+template <typename T, typename ChildT>
+class ForEachMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT(IsBaseType<ChildT>::value,
+ for_each_only_accepts_base_type_matcher);
+ public:
+ explicit ForEachMatcher(const Matcher<ChildT> &ChildMatcher)
+ : ChildMatcher(ChildMatcher) {}
+
+ virtual bool matches(const T& Node,
+ ASTMatchFinder* Finder,
+ BoundNodesTreeBuilder* Builder) const {
+ return Finder->matchesChildOf(
+ Node, ChildMatcher, Builder,
+ ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
+ ASTMatchFinder::BK_All);
+ }
+
+private:
+ const TypedBaseMatcher<ChildT> ChildMatcher;
+};
+
+/// \brief Matches nodes of type T if the given Matcher<T> does not match.
+///
+/// Type argument MatcherT is required by PolymorphicMatcherWithParam1
+/// but not actually used. It will always be instantiated with a type
+/// convertible to Matcher<T>.
+template <typename T, typename MatcherT>
+class NotMatcher : public MatcherInterface<T> {
+public:
+ explicit NotMatcher(const Matcher<T> &InnerMatcher)
+ : InnerMatcher(InnerMatcher) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return !InnerMatcher.matches(Node, Finder, Builder);
+ }
+
+private:
+ const Matcher<T> InnerMatcher;
+};
+
+/// \brief Matches nodes of type T for which both provided matchers match.
+///
+/// Type arguments MatcherT1 and MatcherT2 are required by
+/// PolymorphicMatcherWithParam2 but not actually used. They will
+/// always be instantiated with types convertible to Matcher<T>.
+template <typename T, typename MatcherT1, typename MatcherT2>
+class AllOfMatcher : public MatcherInterface<T> {
+public:
+ AllOfMatcher(const Matcher<T> &InnerMatcher1, const Matcher<T> &InnerMatcher2)
+ : InnerMatcher1(InnerMatcher1), InnerMatcher2(InnerMatcher2) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return InnerMatcher1.matches(Node, Finder, Builder) &&
+ InnerMatcher2.matches(Node, Finder, Builder);
+ }
+
+private:
+ const Matcher<T> InnerMatcher1;
+ const Matcher<T> InnerMatcher2;
+};
+
+/// \brief Matches nodes of type T for which at least one of the two provided
+/// matchers matches.
+///
+/// Type arguments MatcherT1 and MatcherT2 are
+/// required by PolymorphicMatcherWithParam2 but not actually
+/// used. They will always be instantiated with types convertible to
+/// Matcher<T>.
+template <typename T, typename MatcherT1, typename MatcherT2>
+class AnyOfMatcher : public MatcherInterface<T> {
+public:
+ AnyOfMatcher(const Matcher<T> &InnerMatcher1, const Matcher<T> &InnerMatcher2)
+ : InnerMatcher1(InnerMatcher1), InnertMatcher2(InnerMatcher2) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return InnerMatcher1.matches(Node, Finder, Builder) ||
+ InnertMatcher2.matches(Node, Finder, Builder);
+ }
+
+private:
+ const Matcher<T> InnerMatcher1;
+ const Matcher<T> InnertMatcher2;
+};
+
+/// \brief Creates a Matcher<T> that matches if
+/// T is dyn_cast'able into InnerT and all inner matchers match.
+///
+/// Returns BindableMatcher, as matchers that use dyn_cast have
+/// the same object both to match on and to run submatchers on,
+/// so there is no ambiguity with what gets bound.
+template<typename T, typename InnerT>
+BindableMatcher<T> makeDynCastAllOfComposite(
+ ArrayRef<const Matcher<InnerT> *> InnerMatchers) {
+ if (InnerMatchers.empty()) {
+ Matcher<InnerT> InnerMatcher = makeMatcher(new TrueMatcher<InnerT>);
+ return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher));
+ }
+ Matcher<InnerT> InnerMatcher = *InnerMatchers.back();
+ for (int i = InnerMatchers.size() - 2; i >= 0; --i) {
+ InnerMatcher = makeMatcher(
+ new AllOfMatcher<InnerT, Matcher<InnerT>, Matcher<InnerT> >(
+ *InnerMatchers[i], InnerMatcher));
+ }
+ return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher));
+}
+
+/// \brief Matches nodes of type T that have at least one descendant node of
+/// type DescendantT for which the given inner matcher matches.
+///
+/// DescendantT must be an AST base type.
+template <typename T, typename DescendantT>
+class HasDescendantMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT(IsBaseType<DescendantT>::value,
+ has_descendant_only_accepts_base_type_matcher);
+public:
+ explicit HasDescendantMatcher(const Matcher<DescendantT> &DescendantMatcher)
+ : DescendantMatcher(DescendantMatcher) {}
+
+ virtual bool matches(const T &Node,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return Finder->matchesDescendantOf(
+ Node, DescendantMatcher, Builder, ASTMatchFinder::BK_First);
+ }
+
+ private:
+ const TypedBaseMatcher<DescendantT> DescendantMatcher;
+};
+
+/// \brief Matches nodes of type T that have at least one descendant node of
+/// type DescendantT for which the given inner matcher matches.
+///
+/// DescendantT must be an AST base type.
+/// As opposed to HasDescendantMatcher, ForEachDescendantMatcher will match
+/// for each descendant node that matches instead of only for the first.
+template <typename T, typename DescendantT>
+class ForEachDescendantMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT(IsBaseType<DescendantT>::value,
+ for_each_descendant_only_accepts_base_type_matcher);
+ public:
+ explicit ForEachDescendantMatcher(
+ const Matcher<DescendantT>& DescendantMatcher)
+ : DescendantMatcher(DescendantMatcher) {}
+
+ virtual bool matches(const T& Node,
+ ASTMatchFinder* Finder,
+ BoundNodesTreeBuilder* Builder) const {
+ return Finder->matchesDescendantOf(Node, DescendantMatcher, Builder,
+ ASTMatchFinder::BK_All);
+ }
+
+private:
+ const TypedBaseMatcher<DescendantT> DescendantMatcher;
+};
+
+/// \brief Matches on nodes that have a getValue() method if getValue() equals
+/// the value the ValueEqualsMatcher was constructed with.
+template <typename T, typename ValueT>
+class ValueEqualsMatcher : public SingleNodeMatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<CharacterLiteral, T>::value ||
+ llvm::is_base_of<CXXBoolLiteralExpr,
+ T>::value ||
+ llvm::is_base_of<FloatingLiteral, T>::value ||
+ llvm::is_base_of<IntegerLiteral, T>::value),
+ the_node_must_have_a_getValue_method);
+public:
+ explicit ValueEqualsMatcher(const ValueT &ExpectedValue)
+ : ExpectedValue(ExpectedValue) {}
+
+ virtual bool matchesNode(const T &Node) const {
+ return Node.getValue() == ExpectedValue;
+ }
+
+private:
+ const ValueT ExpectedValue;
+};
+
+template <typename T>
+class IsDefinitionMatcher : public SingleNodeMatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT(
+ (llvm::is_base_of<TagDecl, T>::value) ||
+ (llvm::is_base_of<VarDecl, T>::value) ||
+ (llvm::is_base_of<FunctionDecl, T>::value),
+ is_definition_requires_isThisDeclarationADefinition_method);
+public:
+ virtual bool matchesNode(const T &Node) const {
+ return Node.isThisDeclarationADefinition();
+ }
+};
+
+/// \brief Matches on template instantiations for FunctionDecl, VarDecl or
+/// CXXRecordDecl nodes.
+template <typename T>
+class IsTemplateInstantiationMatcher : public MatcherInterface<T> {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<FunctionDecl, T>::value) ||
+ (llvm::is_base_of<VarDecl, T>::value) ||
+ (llvm::is_base_of<CXXRecordDecl, T>::value),
+ requires_getTemplateSpecializationKind_method);
+ public:
+ virtual bool matches(const T& Node,
+ ASTMatchFinder* Finder,
+ BoundNodesTreeBuilder* Builder) const {
+ return (Node.getTemplateSpecializationKind() ==
+ TSK_ImplicitInstantiation ||
+ Node.getTemplateSpecializationKind() ==
+ TSK_ExplicitInstantiationDefinition);
+ }
+};
+
+class IsArrowMatcher : public SingleNodeMatcherInterface<MemberExpr> {
+public:
+ virtual bool matchesNode(const MemberExpr &Node) const {
+ return Node.isArrow();
+ }
+};
+
+class IsConstQualifiedMatcher
+ : public SingleNodeMatcherInterface<QualType> {
+ public:
+ virtual bool matchesNode(const QualType& Node) const {
+ return Node.isConstQualified();
+ }
+};
+
+/// \brief A VariadicDynCastAllOfMatcher<SourceT, TargetT> object is a
+/// variadic functor that takes a number of Matcher<TargetT> and returns a
+/// Matcher<SourceT> that matches TargetT nodes that are matched by all of the
+/// given matchers, if SourceT can be dynamically casted into TargetT.
+///
+/// For example:
+/// const VariadicDynCastAllOfMatcher<
+/// Decl, CXXRecordDecl> record;
+/// Creates a functor record(...) that creates a Matcher<Decl> given
+/// a variable number of arguments of type Matcher<CXXRecordDecl>.
+/// The returned matcher matches if the given Decl can by dynamically
+/// casted to CXXRecordDecl and all given matchers match.
+template <typename SourceT, typename TargetT>
+class VariadicDynCastAllOfMatcher
+ : public llvm::VariadicFunction<
+ BindableMatcher<SourceT>, Matcher<TargetT>,
+ makeDynCastAllOfComposite<SourceT, TargetT> > {
+public:
+ VariadicDynCastAllOfMatcher() {}
+};
+
+} // end namespace internal
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H
diff --git a/include/clang/ASTMatchers/ASTMatchersMacros.h b/include/clang/ASTMatchers/ASTMatchersMacros.h
new file mode 100644
index 0000000..c68534a
--- /dev/null
+++ b/include/clang/ASTMatchers/ASTMatchersMacros.h
@@ -0,0 +1,224 @@
+//===--- ASTMatchersMacros.h - Structural query framework -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines macros that enable us to define new matchers in a single place.
+// Since a matcher is a function which returns a Matcher<T> object, where
+// T is the type of the actual implementation of the matcher, the macros allow
+// us to write matchers like functions and take care of the definition of the
+// class boilerplate.
+//
+// Note that when you define a matcher with an AST_MATCHER* macro, only the
+// function which creates the matcher goes into the current namespace - the
+// class that implements the actual matcher, which gets returned by the
+// generator function, is put into the 'internal' namespace. This allows us
+// to only have the functions (which is all the user cares about) in the
+// 'ast_matchers' namespace and hide the boilerplate.
+//
+// To define a matcher in user code, always put it into the clang::ast_matchers
+// namespace and refer to the internal types via the 'internal::':
+//
+// namespace clang {
+// namespace ast_matchers {
+// AST_MATCHER_P(MemberExpr, Member,
+// internal::Matcher<ValueDecl>, InnerMatcher) {
+// return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder);
+// }
+// } // end namespace ast_matchers
+// } // end namespace clang
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H
+#define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H
+
+/// \brief AST_MATCHER(Type, DefineMatcher) { ... }
+/// defines a zero parameter function named DefineMatcher() that returns a
+/// Matcher<Type> object.
+///
+/// The code between the curly braces has access to the following variables:
+///
+/// Node: the AST node being matched; its type is Type.
+/// Finder: an ASTMatchFinder*.
+/// Builder: a BoundNodesTreeBuilder*.
+///
+/// The code should return true if 'Node' matches.
+#define AST_MATCHER(Type, DefineMatcher) \
+ namespace internal { \
+ class matcher_##DefineMatcher##Matcher \
+ : public MatcherInterface<Type> { \
+ public: \
+ explicit matcher_##DefineMatcher##Matcher() {} \
+ virtual bool matches( \
+ const Type &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const; \
+ }; \
+ } \
+ inline internal::Matcher<Type> DefineMatcher() { \
+ return internal::makeMatcher( \
+ new internal::matcher_##DefineMatcher##Matcher()); \
+ } \
+ inline bool internal::matcher_##DefineMatcher##Matcher::matches( \
+ const Type &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const
+
+/// \brief AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... }
+/// defines a single-parameter function named DefineMatcher() that returns a
+/// Matcher<Type> object.
+///
+/// The code between the curly braces has access to the following variables:
+///
+/// Node: the AST node being matched; its type is Type.
+/// Param: the parameter passed to the function; its type
+/// is ParamType.
+/// Finder: an ASTMatchFinder*.
+/// Builder: a BoundNodesTreeBuilder*.
+///
+/// The code should return true if 'Node' matches.
+#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) \
+ namespace internal { \
+ class matcher_##DefineMatcher##Matcher \
+ : public MatcherInterface<Type> { \
+ public: \
+ explicit matcher_##DefineMatcher##Matcher( \
+ const ParamType &A##Param) : Param(A##Param) {} \
+ virtual bool matches( \
+ const Type &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const; \
+ private: \
+ const ParamType Param; \
+ }; \
+ } \
+ inline internal::Matcher<Type> DefineMatcher(const ParamType &Param) { \
+ return internal::makeMatcher( \
+ new internal::matcher_##DefineMatcher##Matcher(Param)); \
+ } \
+ inline bool internal::matcher_##DefineMatcher##Matcher::matches( \
+ const Type &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const
+
+/// \brief AST_MATCHER_P2(
+/// Type, DefineMatcher, ParamType1, Param1, ParamType2, Param2) { ... }
+/// defines a two-parameter function named DefineMatcher() that returns a
+/// Matcher<Type> object.
+///
+/// The code between the curly braces has access to the following variables:
+///
+/// Node: the AST node being matched; its type is Type.
+/// Param1, Param2: the parameters passed to the function; their types
+/// are ParamType1 and ParamType2.
+/// Finder: an ASTMatchFinder*.
+/// Builder: a BoundNodesTreeBuilder*.
+///
+/// The code should return true if 'Node' matches.
+#define AST_MATCHER_P2( \
+ Type, DefineMatcher, ParamType1, Param1, ParamType2, Param2) \
+ namespace internal { \
+ class matcher_##DefineMatcher##Matcher \
+ : public MatcherInterface<Type> { \
+ public: \
+ matcher_##DefineMatcher##Matcher( \
+ const ParamType1 &A##Param1, const ParamType2 &A##Param2) \
+ : Param1(A##Param1), Param2(A##Param2) {} \
+ virtual bool matches( \
+ const Type &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const; \
+ private: \
+ const ParamType1 Param1; \
+ const ParamType2 Param2; \
+ }; \
+ } \
+ inline internal::Matcher<Type> DefineMatcher( \
+ const ParamType1 &Param1, const ParamType2 &Param2) { \
+ return internal::makeMatcher( \
+ new internal::matcher_##DefineMatcher##Matcher( \
+ Param1, Param2)); \
+ } \
+ inline bool internal::matcher_##DefineMatcher##Matcher::matches( \
+ const Type &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const
+
+/// \brief AST_POLYMORPHIC_MATCHER_P(DefineMatcher, ParamType, Param) { ... }
+/// defines a single-parameter function named DefineMatcher() that is
+/// polymorphic in the return type.
+///
+/// The variables are the same as for
+/// AST_MATCHER_P, with the addition of NodeType, which specifies the node type
+/// of the matcher Matcher<NodeType> returned by the function matcher().
+///
+/// FIXME: Pull out common code with above macro?
+#define AST_POLYMORPHIC_MATCHER_P(DefineMatcher, ParamType, Param) \
+ namespace internal { \
+ template <typename NodeType, typename ParamT> \
+ class matcher_##DefineMatcher##Matcher \
+ : public MatcherInterface<NodeType> { \
+ public: \
+ explicit matcher_##DefineMatcher##Matcher( \
+ const ParamType &A##Param) : Param(A##Param) {} \
+ virtual bool matches( \
+ const NodeType &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const; \
+ private: \
+ const ParamType Param; \
+ }; \
+ } \
+ inline internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##Matcher, \
+ ParamType > \
+ DefineMatcher(const ParamType &Param) { \
+ return internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##Matcher, \
+ ParamType >(Param); \
+ } \
+ template <typename NodeType, typename ParamT> \
+ bool internal::matcher_##DefineMatcher##Matcher<NodeType, ParamT>::matches( \
+ const NodeType &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const
+
+/// \brief AST_POLYMORPHIC_MATCHER_P2(
+/// DefineMatcher, ParamType1, Param1, ParamType2, Param2) { ... }
+/// defines a two-parameter function named matcher() that is polymorphic in
+/// the return type.
+///
+/// The variables are the same as for AST_MATCHER_P2, with the
+/// addition of NodeType, which specifies the node type of the matcher
+/// Matcher<NodeType> returned by the function DefineMatcher().
+#define AST_POLYMORPHIC_MATCHER_P2( \
+ DefineMatcher, ParamType1, Param1, ParamType2, Param2) \
+ namespace internal { \
+ template <typename NodeType, typename ParamT1, typename ParamT2> \
+ class matcher_##DefineMatcher##Matcher \
+ : public MatcherInterface<NodeType> { \
+ public: \
+ matcher_##DefineMatcher##Matcher( \
+ const ParamType1 &A##Param1, const ParamType2 &A##Param2) \
+ : Param1(A##Param1), Param2(A##Param2) {} \
+ virtual bool matches( \
+ const NodeType &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const; \
+ private: \
+ const ParamType1 Param1; \
+ const ParamType2 Param2; \
+ }; \
+ } \
+ inline internal::PolymorphicMatcherWithParam2< \
+ internal::matcher_##DefineMatcher##Matcher, \
+ ParamType1, ParamType2 > \
+ DefineMatcher(const ParamType1 &Param1, const ParamType2 &Param2) { \
+ return internal::PolymorphicMatcherWithParam2< \
+ internal::matcher_##DefineMatcher##Matcher, \
+ ParamType1, ParamType2 >( \
+ Param1, Param2); \
+ } \
+ template <typename NodeType, typename ParamT1, typename ParamT2> \
+ bool internal::matcher_##DefineMatcher##Matcher< \
+ NodeType, ParamT1, ParamT2>::matches( \
+ const NodeType &Node, ASTMatchFinder *Finder, \
+ BoundNodesTreeBuilder *Builder) const
+
+#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H
diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h
index 9ec27ce..7f50ee3 100644
--- a/include/clang/Analysis/Analyses/FormatString.h
+++ b/include/clang/Analysis/Analyses/FormatString.h
@@ -175,6 +175,7 @@ public:
switch (kind) {
case PrintErrno:
assert(IsPrintf);
+ return false;
case PercentArg:
return false;
default:
@@ -200,7 +201,7 @@ protected:
Kind kind;
};
-class ArgTypeResult {
+class ArgType {
public:
enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy,
AnyCharTy, CStrTy, WCStrTy, WIntTy };
@@ -208,26 +209,26 @@ private:
const Kind K;
QualType T;
const char *Name;
- ArgTypeResult(bool) : K(InvalidTy), Name(0) {}
+ bool Ptr;
public:
- ArgTypeResult(Kind k = UnknownTy) : K(k), Name(0) {}
- ArgTypeResult(Kind k, const char *n) : K(k), Name(n) {}
- ArgTypeResult(QualType t) : K(SpecificTy), T(t), Name(0) {}
- ArgTypeResult(QualType t, const char *n) : K(SpecificTy), T(t), Name(n) {}
- ArgTypeResult(CanQualType t) : K(SpecificTy), T(t), Name(0) {}
-
- static ArgTypeResult Invalid() { return ArgTypeResult(true); }
+ ArgType(Kind k = UnknownTy, const char *n = 0) : K(k), Name(n), Ptr(false) {}
+ ArgType(QualType t, const char *n = 0)
+ : K(SpecificTy), T(t), Name(n), Ptr(false) {}
+ ArgType(CanQualType t) : K(SpecificTy), T(t), Name(0), Ptr(false) {}
+ static ArgType Invalid() { return ArgType(InvalidTy); }
bool isValid() const { return K != InvalidTy; }
- const QualType *getSpecificType() const {
- return K == SpecificTy ? &T : 0;
+ /// Create an ArgType which corresponds to the type pointer to A.
+ static ArgType PtrTo(const ArgType& A) {
+ assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown");
+ ArgType Res = A;
+ Res.Ptr = true;
+ return Res;
}
bool matchesType(ASTContext &C, QualType argTy) const;
- bool matchesAnyObjCObjectRef() const { return K == ObjCPointerTy; }
-
QualType getRepresentativeType(ASTContext &C) const;
std::string getRepresentativeTypeName(ASTContext &C) const;
@@ -278,7 +279,7 @@ public:
return length + UsesDotPrefix;
}
- ArgTypeResult getArgType(ASTContext &Ctx) const;
+ ArgType getArgType(ASTContext &Ctx) const;
void toString(raw_ostream &os) const;
@@ -354,6 +355,10 @@ public:
bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const;
bool hasStandardLengthConversionCombination() const;
+
+ /// For a TypedefType QT, if it is a named integer type such as size_t,
+ /// assign the appropriate value to LM and return true.
+ static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM);
};
} // end analyze_format_string namespace
@@ -387,7 +392,7 @@ public:
}
};
-using analyze_format_string::ArgTypeResult;
+using analyze_format_string::ArgType;
using analyze_format_string::LengthModifier;
using analyze_format_string::OptionalAmount;
using analyze_format_string::OptionalFlag;
@@ -462,7 +467,7 @@ public:
/// will return null if the format specifier does not have
/// a matching data argument or the matching argument matches
/// more than one type.
- ArgTypeResult getArgType(ASTContext &Ctx, bool IsObjCLiteral) const;
+ ArgType getArgType(ASTContext &Ctx, bool IsObjCLiteral) const;
const OptionalFlag &hasThousandsGrouping() const {
return HasThousandsGrouping;
@@ -516,35 +521,11 @@ public:
}
};
-using analyze_format_string::ArgTypeResult;
+using analyze_format_string::ArgType;
using analyze_format_string::LengthModifier;
using analyze_format_string::OptionalAmount;
using analyze_format_string::OptionalFlag;
-class ScanfArgTypeResult : public ArgTypeResult {
-public:
- enum Kind { UnknownTy, InvalidTy, CStrTy, WCStrTy, PtrToArgTypeResultTy };
-private:
- Kind K;
- ArgTypeResult A;
- const char *Name;
- QualType getRepresentativeType(ASTContext &C) const;
-public:
- ScanfArgTypeResult(Kind k = UnknownTy, const char* n = 0) : K(k), Name(n) {}
- ScanfArgTypeResult(ArgTypeResult a, const char *n = 0)
- : K(PtrToArgTypeResultTy), A(a), Name(n) {
- assert(A.isValid());
- }
-
- static ScanfArgTypeResult Invalid() { return ScanfArgTypeResult(InvalidTy); }
-
- bool isValid() const { return K != InvalidTy; }
-
- bool matchesType(ASTContext& C, QualType argTy) const;
-
- std::string getRepresentativeTypeName(ASTContext& C) const;
-};
-
class ScanfSpecifier : public analyze_format_string::FormatSpecifier {
OptionalFlag SuppressAssignment; // '*'
public:
@@ -573,7 +554,7 @@ public:
return CS.consumesDataArgument() && !SuppressAssignment;
}
- ScanfArgTypeResult getArgType(ASTContext &Ctx) const;
+ ArgType getArgType(ASTContext &Ctx) const;
bool fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx);
diff --git a/include/clang/Analysis/Analyses/ThreadSafety.h b/include/clang/Analysis/Analyses/ThreadSafety.h
index 26e258d..742cc04 100644
--- a/include/clang/Analysis/Analyses/ThreadSafety.h
+++ b/include/clang/Analysis/Analyses/ThreadSafety.h
@@ -60,7 +60,8 @@ enum AccessKind {
enum LockErrorKind {
LEK_LockedSomeLoopIterations,
LEK_LockedSomePredecessors,
- LEK_LockedAtEndOfFunction
+ LEK_LockedAtEndOfFunction,
+ LEK_NotLockedAtEndOfFunction
};
/// Handler class for thread safety warnings.
@@ -123,11 +124,11 @@ public:
/// Warn when a protected operation occurs while the specific mutex protecting
/// the operation is not locked.
- /// \param LockName -- A StringRef name for the lock expression, to be printed
- /// in the error message.
/// \param D -- The decl for the protected variable or function
/// \param POK -- The kind of protected operation (e.g. variable access)
- /// \param AK -- The kind of access (i.e. read or write) that occurred
+ /// \param LockName -- A StringRef name for the lock expression, to be printed
+ /// in the error message.
+ /// \param LK -- The kind of access (i.e. read or write) that occurred
/// \param Loc -- The location of the protected operation.
virtual void handleMutexNotHeld(const NamedDecl *D,
ProtectedOperationKind POK, Name LockName,
diff --git a/include/clang/Analysis/Analyses/UninitializedValues.h b/include/clang/Analysis/Analyses/UninitializedValues.h
index 4ee6698..45ce4de 100644
--- a/include/clang/Analysis/Analyses/UninitializedValues.h
+++ b/include/clang/Analysis/Analyses/UninitializedValues.h
@@ -15,6 +15,8 @@
#ifndef LLVM_CLANG_UNINIT_VALS_H
#define LLVM_CLANG_UNINIT_VALS_H
+#include "llvm/ADT/SmallVector.h"
+
namespace clang {
class AnalysisDeclContext;
@@ -23,15 +25,67 @@ class DeclContext;
class Expr;
class VarDecl;
+/// A use of a variable, which might be uninitialized.
+class UninitUse {
+public:
+ struct Branch {
+ const Stmt *Terminator;
+ unsigned Output;
+ };
+
+private:
+ /// The expression which uses this variable.
+ const Expr *User;
+
+ /// Does this use always see an uninitialized value?
+ bool AlwaysUninit;
+
+ /// This use is always uninitialized if it occurs after any of these branches
+ /// is taken.
+ llvm::SmallVector<Branch, 2> UninitBranches;
+
+public:
+ UninitUse(const Expr *User, bool AlwaysUninit) :
+ User(User), AlwaysUninit(AlwaysUninit) {}
+
+ void addUninitBranch(Branch B) {
+ UninitBranches.push_back(B);
+ }
+
+ /// Get the expression containing the uninitialized use.
+ const Expr *getUser() const { return User; }
+
+ /// The kind of uninitialized use.
+ enum Kind {
+ /// The use might be uninitialized.
+ Maybe,
+ /// The use is uninitialized whenever a certain branch is taken.
+ Sometimes,
+ /// The use is always uninitialized.
+ Always
+ };
+
+ /// Get the kind of uninitialized use.
+ Kind getKind() const {
+ return AlwaysUninit ? Always :
+ !branch_empty() ? Sometimes : Maybe;
+ }
+
+ typedef llvm::SmallVectorImpl<Branch>::const_iterator branch_iterator;
+ /// Branches which inevitably result in the variable being used uninitialized.
+ branch_iterator branch_begin() const { return UninitBranches.begin(); }
+ branch_iterator branch_end() const { return UninitBranches.end(); }
+ bool branch_empty() const { return UninitBranches.empty(); }
+};
+
class UninitVariablesHandler {
public:
UninitVariablesHandler() {}
virtual ~UninitVariablesHandler();
/// Called when the uninitialized variable is used at the given expression.
- virtual void handleUseOfUninitVariable(const Expr *ex,
- const VarDecl *vd,
- bool isAlwaysUninit) {}
+ virtual void handleUseOfUninitVariable(const VarDecl *vd,
+ const UninitUse &use) {}
/// Called when the uninitialized variable analysis detects the
/// idiom 'int x = x'. All other uses of 'x' within the initializer
diff --git a/include/clang/Analysis/AnalysisContext.h b/include/clang/Analysis/AnalysisContext.h
index 6b6f8ef..46b4e93 100644
--- a/include/clang/Analysis/AnalysisContext.h
+++ b/include/clang/Analysis/AnalysisContext.h
@@ -38,6 +38,7 @@ class PseudoConstantAnalysis;
class ImplicitParamDecl;
class LocationContextManager;
class StackFrameContext;
+class BlockInvocationContext;
class AnalysisDeclContextManager;
class LocationContext;
@@ -73,9 +74,6 @@ class AnalysisDeclContext {
const Decl *D;
- // TranslationUnit is NULL if we don't have multiple translation units.
- idx::TranslationUnit *TU;
-
OwningPtr<CFG> cfg, completeCFG;
OwningPtr<CFGStmtMap> cfgStmtMap;
@@ -98,12 +96,10 @@ class AnalysisDeclContext {
public:
AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
- const Decl *D,
- idx::TranslationUnit *TU);
+ const Decl *D);
AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
const Decl *D,
- idx::TranslationUnit *TU,
const CFG::BuildOptions &BuildOptions);
~AnalysisDeclContext();
@@ -111,8 +107,6 @@ public:
ASTContext &getASTContext() { return D->getASTContext(); }
const Decl *getDecl() const { return D; }
- idx::TranslationUnit *getTranslationUnit() const { return TU; }
-
/// Return the build options used to construct the CFG.
CFG::BuildOptions &getCFGBuildOptions() {
return cfgBuildOptions;
@@ -169,6 +163,11 @@ public:
const Stmt *S,
const CFGBlock *Blk,
unsigned Idx);
+
+ const BlockInvocationContext *
+ getBlockInvocationContext(const LocationContext *parent,
+ const BlockDecl *BD,
+ const void *ContextData);
/// Return the specified analysis object, lazily running the analysis if
/// necessary. Return NULL if the analysis could not run.
@@ -212,10 +211,6 @@ public:
AnalysisDeclContext *getAnalysisDeclContext() const { return Ctx; }
- idx::TranslationUnit *getTranslationUnit() const {
- return Ctx->getTranslationUnit();
- }
-
const LocationContext *getParent() const { return Parent; }
bool isParentOf(const LocationContext *LC) const;
@@ -238,8 +233,6 @@ public:
}
const StackFrameContext *getCurrentStackFrame() const;
- const StackFrameContext *
- getStackFrameForDeclContext(const DeclContext *DC) const;
virtual void Profile(llvm::FoldingSetNodeID &ID) = 0;
@@ -318,27 +311,32 @@ public:
};
class BlockInvocationContext : public LocationContext {
- // FIXME: Add back context-sensivity (we don't want libAnalysis to know
- // about MemRegion).
const BlockDecl *BD;
+
+ // FIXME: Come up with a more type-safe way to model context-sensitivity.
+ const void *ContextData;
friend class LocationContextManager;
BlockInvocationContext(AnalysisDeclContext *ctx,
const LocationContext *parent,
- const BlockDecl *bd)
- : LocationContext(Block, ctx, parent), BD(bd) {}
+ const BlockDecl *bd, const void *contextData)
+ : LocationContext(Block, ctx, parent), BD(bd), ContextData(contextData) {}
public:
~BlockInvocationContext() {}
const BlockDecl *getBlockDecl() const { return BD; }
+
+ const void *getContextData() const { return ContextData; }
void Profile(llvm::FoldingSetNodeID &ID);
static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx,
- const LocationContext *parent, const BlockDecl *bd) {
+ const LocationContext *parent, const BlockDecl *bd,
+ const void *contextData) {
ProfileCommon(ID, Block, ctx, parent, bd);
+ ID.AddPointer(contextData);
}
static bool classof(const LocationContext *Ctx) {
@@ -359,6 +357,12 @@ public:
const ScopeContext *getScope(AnalysisDeclContext *ctx,
const LocationContext *parent,
const Stmt *s);
+
+ const BlockInvocationContext *
+ getBlockInvocationContext(AnalysisDeclContext *ctx,
+ const LocationContext *parent,
+ const BlockDecl *BD,
+ const void *ContextData);
/// Discard all previously created LocationContext objects.
void clear();
@@ -383,7 +387,7 @@ public:
~AnalysisDeclContextManager();
- AnalysisDeclContext *getContext(const Decl *D, idx::TranslationUnit *TU = 0);
+ AnalysisDeclContext *getContext(const Decl *D);
bool getUseUnoptimizedCFG() const {
return !cfgBuildOptions.PruneTriviallyFalseEdges;
@@ -402,9 +406,8 @@ public:
}
// Get the top level stack frame.
- const StackFrameContext *getStackFrame(Decl const *D,
- idx::TranslationUnit *TU) {
- return LocContexts.getStackFrame(getContext(D, TU), 0, 0, 0, 0);
+ const StackFrameContext *getStackFrame(const Decl *D) {
+ return LocContexts.getStackFrame(getContext(D), 0, 0, 0, 0);
}
// Get a stack frame with parent.
@@ -416,7 +419,6 @@ public:
return LocContexts.getStackFrame(getContext(D), Parent, S, Blk, Idx);
}
-
/// Discard all previously created AnalysisDeclContexts.
void clear();
diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h
index 27b22b8..4d087e7 100644
--- a/include/clang/Analysis/CFG.h
+++ b/include/clang/Analysis/CFG.h
@@ -21,10 +21,10 @@
#include "llvm/Support/Casting.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/BitVector.h"
#include "clang/AST/Stmt.h"
#include "clang/Analysis/Support/BumpVector.h"
#include "clang/Basic/SourceLocation.h"
+#include <bitset>
#include <cassert>
#include <iterator>
@@ -277,6 +277,7 @@ class CFGBlock {
typedef std::reverse_iterator<ImplTy::const_iterator> const_iterator;
typedef ImplTy::iterator reverse_iterator;
typedef ImplTy::const_iterator const_reverse_iterator;
+ typedef ImplTy::const_reference const_reference;
void push_back(CFGElement e, BumpVectorContext &C) { Impl.push_back(e, C); }
reverse_iterator insert(reverse_iterator I, size_t Cnt, CFGElement E,
@@ -284,8 +285,8 @@ class CFGBlock {
return Impl.insert(I, Cnt, E, C);
}
- CFGElement front() const { return Impl.back(); }
- CFGElement back() const { return Impl.front(); }
+ const_reference front() const { return Impl.back(); }
+ const_reference back() const { return Impl.front(); }
iterator begin() { return Impl.rbegin(); }
iterator end() { return Impl.rend(); }
@@ -558,7 +559,7 @@ public:
//===--------------------------------------------------------------------===//
class BuildOptions {
- llvm::BitVector alwaysAddMask;
+ std::bitset<Stmt::lastStmtConstant> alwaysAddMask;
public:
typedef llvm::DenseMap<const Stmt *, const CFGBlock*> ForcedBlkExprs;
ForcedBlkExprs **forcedBlkExprs;
@@ -583,8 +584,7 @@ public:
}
BuildOptions()
- : alwaysAddMask(Stmt::lastStmtConstant, false)
- ,forcedBlkExprs(0), PruneTriviallyFalseEdges(true)
+ : forcedBlkExprs(0), PruneTriviallyFalseEdges(true)
,AddEHEdges(false)
,AddInitializers(false)
,AddImplicitDtors(false) {}
diff --git a/include/clang/Analysis/CallGraph.h b/include/clang/Analysis/CallGraph.h
index 9b68073..509de7b 100644
--- a/include/clang/Analysis/CallGraph.h
+++ b/include/clang/Analysis/CallGraph.h
@@ -26,7 +26,7 @@
namespace clang {
class CallGraphNode;
-/// \class The AST-based call graph.
+/// \brief The AST-based call graph.
///
/// The call graph extends itself with the given declarations by implementing
/// the recursive AST visitor, which constructs the graph by visiting the given
@@ -102,7 +102,8 @@ public:
void dump() const;
void viewGraph() const;
- /// Part of recursive declaration visitation.
+ /// Part of recursive declaration visitation. We recursively visit all the
+ /// Declarations to collect the root functions.
bool VisitFunctionDecl(FunctionDecl *FD) {
// We skip function template definitions, as their semantics is
// only determined when they are instantiated.
@@ -121,6 +122,11 @@ public:
return true;
}
+ // We are only collecting the declarations, so do not step into the bodies.
+ bool TraverseStmt(Stmt *S) { return true; }
+
+ bool shouldWalkTypesOfTypeLocs() const { return false; }
+
private:
/// \brief Add the given declaration to the call graph.
void addNodeForDecl(Decl *D, bool IsGlobal);
diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h
index aa7a33c..5de06cd 100644
--- a/include/clang/Analysis/ProgramPoint.h
+++ b/include/clang/Analysis/ProgramPoint.h
@@ -40,30 +40,36 @@ public:
BlockEntranceKind,
BlockExitKind,
PreStmtKind,
+ PreStmtPurgeDeadSymbolsKind,
+ PostStmtPurgeDeadSymbolsKind,
PostStmtKind,
PreLoadKind,
PostLoadKind,
PreStoreKind,
PostStoreKind,
- PostPurgeDeadSymbolsKind,
PostConditionKind,
PostLValueKind,
+ MinPostStmtKind = PostStmtKind,
+ MaxPostStmtKind = PostLValueKind,
PostInitializerKind,
CallEnterKind,
- CallExitKind,
- MinPostStmtKind = PostStmtKind,
- MaxPostStmtKind = CallExitKind,
+ CallExitBeginKind,
+ CallExitEndKind,
+ PreImplicitCallKind,
+ PostImplicitCallKind,
+ MinImplicitCallKind = PreImplicitCallKind,
+ MaxImplicitCallKind = PostImplicitCallKind,
EpsilonKind};
private:
- llvm::PointerIntPair<const void *, 2, unsigned> Data1;
+ const void *Data1;
llvm::PointerIntPair<const void *, 2, unsigned> Data2;
// The LocationContext could be NULL to allow ProgramPoint to be used in
// context insensitive analysis.
llvm::PointerIntPair<const LocationContext *, 2, unsigned> L;
- const ProgramPointTag *Tag;
+ llvm::PointerIntPair<const ProgramPointTag *, 2, unsigned> Tag;
ProgramPoint();
@@ -72,10 +78,10 @@ protected:
Kind k,
const LocationContext *l,
const ProgramPointTag *tag = 0)
- : Data1(P, ((unsigned) k) & 0x3),
- Data2(0, (((unsigned) k) >> 2) & 0x3),
- L(l, (((unsigned) k) >> 4) & 0x3),
- Tag(tag) {
+ : Data1(P),
+ Data2(0, (((unsigned) k) >> 0) & 0x3),
+ L(l, (((unsigned) k) >> 2) & 0x3),
+ Tag(tag, (((unsigned) k) >> 4) & 0x3) {
assert(getKind() == k);
assert(getLocationContext() == l);
assert(getData1() == P);
@@ -86,13 +92,13 @@ protected:
Kind k,
const LocationContext *l,
const ProgramPointTag *tag = 0)
- : Data1(P1, ((unsigned) k) & 0x3),
- Data2(P2, (((unsigned) k) >> 2) & 0x3),
- L(l, (((unsigned) k) >> 4) & 0x3),
- Tag(tag) {}
+ : Data1(P1),
+ Data2(P2, (((unsigned) k) >> 0) & 0x3),
+ L(l, (((unsigned) k) >> 2) & 0x3),
+ Tag(tag, (((unsigned) k) >> 4) & 0x3) {}
protected:
- const void *getData1() const { return Data1.getPointer(); }
+ const void *getData1() const { return Data1; }
const void *getData2() const { return Data2.getPointer(); }
void setData2(const void *d) { Data2.setPointer(d); }
@@ -105,15 +111,23 @@ public:
}
Kind getKind() const {
- unsigned x = L.getInt();
+ unsigned x = Tag.getInt();
x <<= 2;
- x |= Data2.getInt();
+ x |= L.getInt();
x <<= 2;
- x |= Data1.getInt();
+ x |= Data2.getInt();
return (Kind) x;
}
- const ProgramPointTag *getTag() const { return Tag; }
+ /// \brief Is this a program point corresponding to purge/removal of dead
+ /// symbols and bindings.
+ bool isPurgeKind() {
+ Kind K = getKind();
+ return (K == PostStmtPurgeDeadSymbolsKind ||
+ K == PreStmtPurgeDeadSymbolsKind);
+ }
+
+ const ProgramPointTag *getTag() const { return Tag.getPointer(); }
const LocationContext *getLocationContext() const {
return L.getPointer();
@@ -147,7 +161,7 @@ public:
ID.AddPointer(getData1());
ID.AddPointer(getData2());
ID.AddPointer(getLocationContext());
- ID.AddPointer(Tag);
+ ID.AddPointer(getTag());
}
static ProgramPoint getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
@@ -304,7 +318,7 @@ public:
}
};
-/// \class Represents a program point after a store evaluation.
+/// \brief Represents a program point after a store evaluation.
class PostStore : public PostStmt {
public:
/// Construct the post store point.
@@ -340,14 +354,29 @@ public:
}
};
-class PostPurgeDeadSymbols : public PostStmt {
+/// Represents a point after we ran remove dead bindings BEFORE
+/// processing the given statement.
+class PreStmtPurgeDeadSymbols : public StmtPoint {
public:
- PostPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
+ PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = 0)
- : PostStmt(S, PostPurgeDeadSymbolsKind, L, tag) {}
+ : StmtPoint(S, 0, PreStmtPurgeDeadSymbolsKind, L, tag) { }
static bool classof(const ProgramPoint* Location) {
- return Location->getKind() == PostPurgeDeadSymbolsKind;
+ return Location->getKind() == PreStmtPurgeDeadSymbolsKind;
+ }
+};
+
+/// Represents a point after we ran remove dead bindings AFTER
+/// processing the given statement.
+class PostStmtPurgeDeadSymbols : public StmtPoint {
+public:
+ PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
+ const ProgramPointTag *tag = 0)
+ : StmtPoint(S, 0, PostStmtPurgeDeadSymbolsKind, L, tag) { }
+
+ static bool classof(const ProgramPoint* Location) {
+ return Location->getKind() == PostStmtPurgeDeadSymbolsKind;
}
};
@@ -383,11 +412,60 @@ public:
}
};
-class CallEnter : public StmtPoint {
+/// Represents an implicit call event.
+///
+/// The nearest statement is provided for diagnostic purposes.
+class ImplicitCallPoint : public ProgramPoint {
+public:
+ ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K,
+ const LocationContext *L, const ProgramPointTag *Tag)
+ : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag) {}
+
+ const Decl *getDecl() const { return static_cast<const Decl *>(getData2()); }
+ SourceLocation getLocation() const {
+ return SourceLocation::getFromPtrEncoding(getData1());
+ }
+
+ static bool classof(const ProgramPoint *Location) {
+ return Location->getKind() >= MinImplicitCallKind &&
+ Location->getKind() <= MaxImplicitCallKind;
+ }
+};
+
+/// Represents a program point just before an implicit call event.
+///
+/// Explicit calls will appear as PreStmt program points.
+class PreImplicitCall : public ImplicitCallPoint {
+public:
+ PreImplicitCall(const Decl *D, SourceLocation Loc,
+ const LocationContext *L, const ProgramPointTag *Tag = 0)
+ : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag) {}
+
+ static bool classof(const ProgramPoint *Location) {
+ return Location->getKind() == PreImplicitCallKind;
+ }
+};
+
+/// Represents a program point just after an implicit call event.
+///
+/// Explicit calls will appear as PostStmt program points.
+class PostImplicitCall : public ImplicitCallPoint {
+public:
+ PostImplicitCall(const Decl *D, SourceLocation Loc,
+ const LocationContext *L, const ProgramPointTag *Tag = 0)
+ : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag) {}
+
+ static bool classof(const ProgramPoint *Location) {
+ return Location->getKind() == PostImplicitCallKind;
+ }
+};
+
+/// Represents a point when we begin processing an inlined call.
+class CallEnter : public ProgramPoint {
public:
CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx,
const LocationContext *callerCtx)
- : StmtPoint(stmt, calleeCtx, CallEnterKind, callerCtx, 0) {}
+ : ProgramPoint(stmt, calleeCtx, CallEnterKind, callerCtx, 0) {}
const Stmt *getCallExpr() const {
return static_cast<const Stmt *>(getData1());
@@ -402,14 +480,41 @@ public:
}
};
-class CallExit : public StmtPoint {
+/// Represents a point when we start the call exit sequence (for inlined call).
+///
+/// The call exit is simulated with a sequence of nodes, which occur between
+/// CallExitBegin and CallExitEnd. The following operations occur between the
+/// two program points:
+/// - CallExitBegin
+/// - Bind the return value
+/// - Run Remove dead bindings (to clean up the dead symbols from the callee).
+/// - CallExitEnd
+class CallExitBegin : public ProgramPoint {
+public:
+ // CallExitBegin uses the callee's location context.
+ CallExitBegin(const StackFrameContext *L)
+ : ProgramPoint(0, CallExitBeginKind, L, 0) {}
+
+ static bool classof(const ProgramPoint *Location) {
+ return Location->getKind() == CallExitBeginKind;
+ }
+};
+
+/// Represents a point when we finish the call exit sequence (for inlined call).
+/// \sa CallExitBegin
+class CallExitEnd : public ProgramPoint {
public:
- // CallExit uses the callee's location context.
- CallExit(const Stmt *S, const LocationContext *L)
- : StmtPoint(S, 0, CallExitKind, L, 0) {}
+ // CallExitEnd uses the caller's location context.
+ CallExitEnd(const StackFrameContext *CalleeCtx,
+ const LocationContext *CallerCtx)
+ : ProgramPoint(CalleeCtx, CallExitEndKind, CallerCtx, 0) {}
+
+ const StackFrameContext *getCalleeContext() const {
+ return static_cast<const StackFrameContext *>(getData1());
+ }
static bool classof(const ProgramPoint *Location) {
- return Location->getKind() == CallExitKind;
+ return Location->getKind() == CallExitEndKind;
}
};
diff --git a/include/clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h b/include/clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h
index 97eb287..c510e20 100644
--- a/include/clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h
+++ b/include/clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h
@@ -69,6 +69,7 @@ public:
DISPATCH_CASE(Field)
DISPATCH_CASE(UsingDirective)
DISPATCH_CASE(Using)
+ DISPATCH_CASE(NamespaceAlias)
default:
llvm_unreachable("Subtype of ScopedDecl not handled.");
}
@@ -90,6 +91,7 @@ public:
DEFAULT_DISPATCH(ObjCCategory)
DEFAULT_DISPATCH(UsingDirective)
DEFAULT_DISPATCH(Using)
+ DEFAULT_DISPATCH(NamespaceAlias)
void VisitCXXRecordDecl(CXXRecordDecl *D) {
static_cast<ImplClass*>(this)->VisitRecordDecl(D);
diff --git a/include/clang/Basic/ABI.h b/include/clang/Basic/ABI.h
index 018f500..fecf613 100644
--- a/include/clang/Basic/ABI.h
+++ b/include/clang/Basic/ABI.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// These enums/classes describe ABI related information about constructors,
-// destructors and thunks.
-//
+///
+/// \file
+/// \brief Enums/classes describing ABI related information about constructors,
+/// destructors and thunks.
+///
//===----------------------------------------------------------------------===//
#ifndef CLANG_BASIC_ABI_H
@@ -19,27 +20,27 @@
namespace clang {
-/// CXXCtorType - C++ constructor types
+/// \brief C++ constructor types.
enum CXXCtorType {
- Ctor_Complete, // Complete object ctor
- Ctor_Base, // Base object ctor
- Ctor_CompleteAllocating // Complete object allocating ctor
+ Ctor_Complete, ///< Complete object ctor
+ Ctor_Base, ///< Base object ctor
+ Ctor_CompleteAllocating ///< Complete object allocating ctor
};
-/// CXXDtorType - C++ destructor types
+/// \brief C++ destructor types.
enum CXXDtorType {
- Dtor_Deleting, // Deleting dtor
- Dtor_Complete, // Complete object dtor
- Dtor_Base // Base object dtor
+ Dtor_Deleting, ///< Deleting dtor
+ Dtor_Complete, ///< Complete object dtor
+ Dtor_Base ///< Base object dtor
};
-/// ReturnAdjustment - A return adjustment.
+/// \brief A return adjustment.
struct ReturnAdjustment {
- /// NonVirtual - The non-virtual adjustment from the derived object to its
+ /// \brief The non-virtual adjustment from the derived object to its
/// nearest virtual base.
int64_t NonVirtual;
- /// VBaseOffsetOffset - The offset (in bytes), relative to the address point
+ /// \brief The offset (in bytes), relative to the address point
/// of the virtual base class offset.
int64_t VBaseOffsetOffset;
@@ -63,13 +64,13 @@ struct ReturnAdjustment {
}
};
-/// ThisAdjustment - A 'this' pointer adjustment.
+/// \brief A \c this pointer adjustment.
struct ThisAdjustment {
- /// NonVirtual - The non-virtual adjustment from the derived object to its
+ /// \brief The non-virtual adjustment from the derived object to its
/// nearest virtual base.
int64_t NonVirtual;
- /// VCallOffsetOffset - The offset (in bytes), relative to the address point,
+ /// \brief The offset (in bytes), relative to the address point,
/// of the virtual call offset.
int64_t VCallOffsetOffset;
@@ -93,13 +94,13 @@ struct ThisAdjustment {
}
};
-/// ThunkInfo - The 'this' pointer adjustment as well as an optional return
+/// \brief The \c this pointer adjustment as well as an optional return
/// adjustment for a thunk.
struct ThunkInfo {
- /// This - The 'this' pointer adjustment.
+ /// \brief The \c this pointer adjustment.
ThisAdjustment This;
- /// Return - The return adjustment.
+ /// \brief The return adjustment.
ReturnAdjustment Return;
ThunkInfo() { }
diff --git a/include/clang/Basic/AddressSpaces.h b/include/clang/Basic/AddressSpaces.h
index d44a9c3b..4b1cea5 100644
--- a/include/clang/Basic/AddressSpaces.h
+++ b/include/clang/Basic/AddressSpaces.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file provides definitions for the various language-specific address
-// spaces.
-//
+///
+/// \file
+/// \brief Provides definitions for the various language-specific address
+/// spaces.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_ADDRESSSPACES_H
@@ -19,8 +20,9 @@ namespace clang {
namespace LangAS {
-/// This enum defines the set of possible language-specific address spaces.
-/// It uses a high starting offset so as not to conflict with any address
+/// \brief Defines the set of possible language-specific address spaces.
+///
+/// This uses a high starting offset so as not to conflict with any address
/// space used by a target.
enum ID {
Offset = 0xFFFF00,
@@ -29,6 +31,10 @@ enum ID {
opencl_local,
opencl_constant,
+ cuda_device,
+ cuda_constant,
+ cuda_shared,
+
Last,
Count = Last-Offset
};
diff --git a/include/clang/Basic/AllDiagnostics.h b/include/clang/Basic/AllDiagnostics.h
index 7e77435..7304c8f 100644
--- a/include/clang/Basic/AllDiagnostics.h
+++ b/include/clang/Basic/AllDiagnostics.h
@@ -6,16 +6,17 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file includes all the separate Diagnostic headers & some related
-// helpers.
-//
+///
+/// \file
+/// \brief Includes all the separate Diagnostic headers & some related helpers.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ALL_DIAGNOSTICS_H
#define LLVM_CLANG_ALL_DIAGNOSTICS_H
#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/CommentDiagnostic.h"
#include "clang/Analysis/AnalysisDiagnostic.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Frontend/FrontendDiagnostic.h"
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index e8e0f35..99180e4 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -80,23 +80,40 @@ class EnumArgument<string name, string type, list<string> values,
list<string> Enums = enums;
}
+// This handles one spelling of an attribute.
+class Spelling<string name, string variety> {
+ string Name = name;
+ string Variety = variety;
+}
+
+class GNU<string name> : Spelling<name, "GNU">;
+class Declspec<string name> : Spelling<name, "Declspec">;
+class CXX11<string namespace, string name> : Spelling<name, "CXX11"> {
+ string Namespace = namespace;
+}
+
class Attr {
// The various ways in which an attribute can be spelled in source
- list<string> Spellings;
+ list<Spelling> Spellings;
// The things to which an attribute can appertain
list<AttrSubject> Subjects;
// The arguments allowed on an attribute
list<Argument> Args = [];
- // The namespaces in which the attribute appears in C++0x attributes.
- // The attribute will not be permitted in C++0x attribute-specifiers if
- // this is empty; the empty string can be used as a namespace.
- list<string> Namespaces = [];
// Set to true for attributes with arguments which require delayed parsing.
bit LateParsed = 0;
+ // Set to false to prevent an attribute from being propagated from a template
+ // to the instantiation.
+ bit Clone = 1;
// Set to true for attributes which must be instantiated within templates
bit TemplateDependent = 0;
+ // Set to true for attributes that have a corresponding AST node.
+ bit ASTNode = 1;
// Set to true for attributes which have handler in Sema.
bit SemaHandler = 1;
+ // Set to true for attributes that are completely ignored.
+ bit Ignored = 0;
+ // Set to true if each of the spellings is a distinct attribute.
+ bit DistinctSpellings = 0;
// Any additional text that should be included verbatim in the class.
code AdditionalMembers = [{}];
}
@@ -112,16 +129,21 @@ class InheritableParamAttr : InheritableAttr;
// Attributes begin here
//
+def AddressSpace : Attr {
+ let Spellings = [GNU<"address_space">];
+ let Args = [IntArgument<"AddressSpace">];
+ let ASTNode = 0;
+}
+
def Alias : InheritableAttr {
- let Spellings = ["alias"];
+ let Spellings = [GNU<"alias">];
let Args = [StringArgument<"Aliasee">];
}
def Aligned : InheritableAttr {
- let Spellings = ["aligned"];
+ let Spellings = [GNU<"aligned">, GNU<"align">];
let Subjects = [NonBitField, NormalVar, Tag];
- let Args = [AlignedArgument<"Alignment">];
- let Namespaces = ["", "std"];
+ let Args = [AlignedArgument<"Alignment">, BoolArgument<"IsMSDeclSpec">];
}
def AlignMac68k : InheritableAttr {
@@ -129,16 +151,27 @@ def AlignMac68k : InheritableAttr {
let SemaHandler = 0;
}
+def AllocSize : Attr {
+ let Spellings = [GNU<"alloc_size">];
+ let Args = [VariadicUnsignedArgument<"Args">];
+}
+
def AlwaysInline : InheritableAttr {
- let Spellings = ["always_inline"];
+ let Spellings = [GNU<"always_inline">];
+}
+
+def TLSModel : InheritableAttr {
+ let Spellings = [GNU<"tls_model">];
+ let Subjects = [Var];
+ let Args = [StringArgument<"Model">];
}
def AnalyzerNoReturn : InheritableAttr {
- let Spellings = ["analyzer_noreturn"];
+ let Spellings = [GNU<"analyzer_noreturn">];
}
def Annotate : InheritableParamAttr {
- let Spellings = ["annotate"];
+ let Spellings = [GNU<"annotate">];
let Args = [StringArgument<"Annotation">];
}
@@ -149,7 +182,7 @@ def AsmLabel : InheritableAttr {
}
def Availability : InheritableAttr {
- let Spellings = ["availability"];
+ let Spellings = [GNU<"availability">];
let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">,
VersionArgument<"deprecated">, VersionArgument<"obsoleted">,
BoolArgument<"unavailable">, StringArgument<"message">];
@@ -163,18 +196,25 @@ def Availability : InheritableAttr {
}
def Blocks : InheritableAttr {
- let Spellings = ["blocks"];
+ let Spellings = [GNU<"blocks">];
let Args = [EnumArgument<"Type", "BlockType", ["byref"], ["ByRef"]>];
}
+def Bounded : Attr {
+ let Spellings = [GNU<"bounded">];
+ let ASTNode = 0;
+ let SemaHandler = 0;
+ let Ignored = 1;
+}
+
def CarriesDependency : InheritableParamAttr {
- let Spellings = ["carries_dependency"];
+ let Spellings = [GNU<"carries_dependency">, CXX11<"","carries_dependency">,
+ CXX11<"std","carries_dependency">];
let Subjects = [ParmVar, Function];
- let Namespaces = ["", "std"];
}
def CDecl : InheritableAttr {
- let Spellings = ["cdecl", "__cdecl"];
+ let Spellings = [GNU<"cdecl">, GNU<"__cdecl">];
}
// cf_audited_transfer indicates that the given function has been
@@ -182,7 +222,7 @@ def CDecl : InheritableAttr {
// cf_returns_retained attributes. It is generally applied by
// '#pragma clang arc_cf_code_audited' rather than explicitly.
def CFAuditedTransfer : InheritableAttr {
- let Spellings = ["cf_audited_transfer"];
+ let Spellings = [GNU<"cf_audited_transfer">];
let Subjects = [Function];
}
@@ -190,133 +230,151 @@ def CFAuditedTransfer : InheritableAttr {
// It indicates that the function has unknown or unautomatable
// transfer semantics.
def CFUnknownTransfer : InheritableAttr {
- let Spellings = ["cf_unknown_transfer"];
+ let Spellings = [GNU<"cf_unknown_transfer">];
let Subjects = [Function];
}
+def CFReturnsAutoreleased : Attr {
+ let Spellings = [GNU<"cf_returns_autoreleased">];
+ let ASTNode = 0;
+}
+
def CFReturnsRetained : InheritableAttr {
- let Spellings = ["cf_returns_retained"];
+ let Spellings = [GNU<"cf_returns_retained">];
let Subjects = [ObjCMethod, Function];
}
def CFReturnsNotRetained : InheritableAttr {
- let Spellings = ["cf_returns_not_retained"];
+ let Spellings = [GNU<"cf_returns_not_retained">];
let Subjects = [ObjCMethod, Function];
}
def CFConsumed : InheritableParamAttr {
- let Spellings = ["cf_consumed"];
+ let Spellings = [GNU<"cf_consumed">];
let Subjects = [ParmVar];
}
def Cleanup : InheritableAttr {
- let Spellings = ["cleanup"];
+ let Spellings = [GNU<"cleanup">];
let Args = [FunctionArgument<"FunctionDecl">];
}
+def Cold : InheritableAttr {
+ let Spellings = [GNU<"cold">];
+}
+
def Common : InheritableAttr {
- let Spellings = ["common"];
+ let Spellings = [GNU<"common">];
}
def Const : InheritableAttr {
- let Spellings = ["const"];
+ let Spellings = [GNU<"const">, GNU<"__const">];
}
def Constructor : InheritableAttr {
- let Spellings = ["constructor"];
+ let Spellings = [GNU<"constructor">];
let Args = [IntArgument<"Priority">];
}
def CUDAConstant : InheritableAttr {
- let Spellings = ["constant"];
+ let Spellings = [GNU<"constant">];
}
-def CUDADevice : Attr {
- let Spellings = ["device"];
+def CUDADevice : InheritableAttr {
+ let Spellings = [GNU<"device">];
}
def CUDAGlobal : InheritableAttr {
- let Spellings = ["global"];
+ let Spellings = [GNU<"global">];
}
-def CUDAHost : Attr {
- let Spellings = ["host"];
+def CUDAHost : InheritableAttr {
+ let Spellings = [GNU<"host">];
}
def CUDALaunchBounds : InheritableAttr {
- let Spellings = ["launch_bounds"];
+ let Spellings = [GNU<"launch_bounds">];
let Args = [IntArgument<"MaxThreads">, DefaultIntArgument<"MinBlocks", 0>];
}
def CUDAShared : InheritableAttr {
- let Spellings = ["shared"];
+ let Spellings = [GNU<"shared">];
}
def OpenCLKernel : Attr {
- let Spellings = ["opencl_kernel_function"];
+ let Spellings = [GNU<"opencl_kernel_function">];
+}
+
+def OpenCLImageAccess : Attr {
+ let Spellings = [GNU<"opencl_image_access">];
+ let Args = [IntArgument<"Access">];
+ let ASTNode = 0;
}
def Deprecated : InheritableAttr {
- let Spellings = ["deprecated"];
+ let Spellings = [GNU<"deprecated">];
let Args = [StringArgument<"Message">];
}
def Destructor : InheritableAttr {
- let Spellings = ["destructor"];
+ let Spellings = [GNU<"destructor">];
let Args = [IntArgument<"Priority">];
}
-def DLLExport : InheritableAttr {
- let Spellings = ["dllexport"];
+def ExtVectorType : Attr {
+ let Spellings = [GNU<"ext_vector_type">];
+ let Args = [ExprArgument<"NumElements">];
+ let ASTNode = 0;
}
-def DLLImport : InheritableAttr {
- let Spellings = ["dllimport"];
+def FallThrough : Attr {
+ let Spellings = [CXX11<"clang","fallthrough">];
+ let Subjects = [NullStmt];
}
def FastCall : InheritableAttr {
- let Spellings = ["fastcall", "__fastcall"];
+ let Spellings = [GNU<"fastcall">, GNU<"__fastcall">];
}
-def Final : InheritableAttr {
+def Final : InheritableAttr {
let Spellings = [];
let SemaHandler = 0;
}
-def MsStruct : InheritableAttr {
- let Spellings = ["__ms_struct__"];
-}
-
def Format : InheritableAttr {
- let Spellings = ["format"];
+ let Spellings = [GNU<"format">];
let Args = [StringArgument<"Type">, IntArgument<"FormatIdx">,
IntArgument<"FirstArg">];
}
def FormatArg : InheritableAttr {
- let Spellings = ["format_arg"];
+ let Spellings = [GNU<"format_arg">];
let Args = [IntArgument<"FormatIdx">];
}
def GNUInline : InheritableAttr {
- let Spellings = ["gnu_inline"];
+ let Spellings = [GNU<"gnu_inline">];
+}
+
+def Hot : InheritableAttr {
+ let Spellings = [GNU<"hot">];
}
def IBAction : InheritableAttr {
- let Spellings = ["ibaction"];
+ let Spellings = [GNU<"ibaction">];
}
def IBOutlet : InheritableAttr {
- let Spellings = ["iboutlet"];
+ let Spellings = [GNU<"iboutlet">];
}
def IBOutletCollection : InheritableAttr {
- let Spellings = ["iboutletcollection"];
+ let Spellings = [GNU<"iboutletcollection">];
let Args = [TypeArgument<"Interface">, SourceLocArgument<"InterfaceLoc">];
}
def Malloc : InheritableAttr {
- let Spellings = ["malloc"];
+ let Spellings = [GNU<"malloc">];
}
def MaxFieldAlignment : InheritableAttr {
@@ -326,7 +384,7 @@ def MaxFieldAlignment : InheritableAttr {
}
def MayAlias : InheritableAttr {
- let Spellings = ["may_alias"];
+ let Spellings = [GNU<"may_alias">];
}
def MSP430Interrupt : InheritableAttr {
@@ -345,28 +403,46 @@ def MBlazeSaveVolatiles : InheritableAttr {
let SemaHandler = 0;
}
+def Mode : Attr {
+ let Spellings = [GNU<"mode">];
+ let Args = [IdentifierArgument<"Mode">];
+ let ASTNode = 0;
+}
+
def Naked : InheritableAttr {
- let Spellings = ["naked"];
+ let Spellings = [GNU<"naked">];
+}
+
+def NeonPolyVectorType : Attr {
+ let Spellings = [GNU<"neon_polyvector_type">];
+ let Args = [IntArgument<"NumElements">];
+ let ASTNode = 0;
+}
+
+def NeonVectorType : Attr {
+ let Spellings = [GNU<"neon_vector_type">];
+ let Args = [IntArgument<"NumElements">];
+ let ASTNode = 0;
}
def ReturnsTwice : InheritableAttr {
- let Spellings = ["returns_twice"];
+ let Spellings = [GNU<"returns_twice">];
}
def NoCommon : InheritableAttr {
- let Spellings = ["nocommon"];
+ let Spellings = [GNU<"nocommon">];
}
def NoDebug : InheritableAttr {
- let Spellings = ["nodebug"];
+ let Spellings = [GNU<"nodebug">];
}
def NoInline : InheritableAttr {
- let Spellings = ["noinline"];
+ let Spellings = [GNU<"noinline">];
}
def NonNull : InheritableAttr {
- let Spellings = ["nonnull"];
+ let Spellings = [GNU<"nonnull">];
let Args = [VariadicUnsignedArgument<"Args">];
let AdditionalMembers =
[{bool isNonNull(unsigned idx) const {
@@ -379,58 +455,58 @@ def NonNull : InheritableAttr {
}
def NoReturn : InheritableAttr {
- let Spellings = ["noreturn"];
+ let Spellings = [GNU<"noreturn">, CXX11<"","noreturn">,
+ CXX11<"std","noreturn">];
// FIXME: Does GCC allow this on the function instead?
let Subjects = [Function];
- let Namespaces = ["", "std"];
}
def NoInstrumentFunction : InheritableAttr {
- let Spellings = ["no_instrument_function"];
+ let Spellings = [GNU<"no_instrument_function">];
let Subjects = [Function];
}
def NoThrow : InheritableAttr {
- let Spellings = ["nothrow"];
+ let Spellings = [GNU<"nothrow">];
}
def NSBridged : InheritableAttr {
- let Spellings = ["ns_bridged"];
+ let Spellings = [GNU<"ns_bridged">];
let Subjects = [Record];
let Args = [IdentifierArgument<"BridgedType">];
}
def NSReturnsRetained : InheritableAttr {
- let Spellings = ["ns_returns_retained"];
+ let Spellings = [GNU<"ns_returns_retained">];
let Subjects = [ObjCMethod, Function];
}
def NSReturnsNotRetained : InheritableAttr {
- let Spellings = ["ns_returns_not_retained"];
+ let Spellings = [GNU<"ns_returns_not_retained">];
let Subjects = [ObjCMethod, Function];
}
def NSReturnsAutoreleased : InheritableAttr {
- let Spellings = ["ns_returns_autoreleased"];
+ let Spellings = [GNU<"ns_returns_autoreleased">];
let Subjects = [ObjCMethod, Function];
}
def NSConsumesSelf : InheritableAttr {
- let Spellings = ["ns_consumes_self"];
+ let Spellings = [GNU<"ns_consumes_self">];
let Subjects = [ObjCMethod];
}
def NSConsumed : InheritableParamAttr {
- let Spellings = ["ns_consumed"];
+ let Spellings = [GNU<"ns_consumed">];
let Subjects = [ParmVar];
}
def ObjCException : InheritableAttr {
- let Spellings = ["objc_exception"];
+ let Spellings = [GNU<"objc_exception">];
}
def ObjCMethodFamily : InheritableAttr {
- let Spellings = ["objc_method_family"];
+ let Spellings = [GNU<"objc_method_family">];
let Subjects = [ObjCMethod];
let Args = [EnumArgument<"Family", "FamilyKind",
["none", "alloc", "copy", "init", "mutableCopy", "new"],
@@ -439,26 +515,26 @@ def ObjCMethodFamily : InheritableAttr {
}
def ObjCNSObject : InheritableAttr {
- let Spellings = ["NSObject"];
+ let Spellings = [GNU<"NSObject">];
}
def ObjCPreciseLifetime : Attr {
- let Spellings = ["objc_precise_lifetime"];
+ let Spellings = [GNU<"objc_precise_lifetime">];
let Subjects = [Var];
}
def ObjCReturnsInnerPointer : Attr {
- let Spellings = ["objc_returns_inner_pointer"];
+ let Spellings = [GNU<"objc_returns_inner_pointer">];
let Subjects = [ObjCMethod];
}
def ObjCRootClass : Attr {
- let Spellings = ["objc_root_class"];
+ let Spellings = [GNU<"objc_root_class">];
let Subjects = [ObjCInterface];
}
def Overloadable : Attr {
- let Spellings = ["overloadable"];
+ let Spellings = [GNU<"overloadable">];
}
def Override : InheritableAttr {
@@ -467,7 +543,9 @@ def Override : InheritableAttr {
}
def Ownership : InheritableAttr {
- let Spellings = ["ownership_holds", "ownership_returns", "ownership_takes"];
+ let Spellings = [GNU<"ownership_holds">, GNU<"ownership_returns">,
+ GNU<"ownership_takes">];
+ let DistinctSpellings = 1;
let Args = [EnumArgument<"OwnKind", "OwnershipKind",
["ownership_holds", "ownership_returns", "ownership_takes"],
["Holds", "Returns", "Takes"]>,
@@ -475,118 +553,151 @@ def Ownership : InheritableAttr {
}
def Packed : InheritableAttr {
- let Spellings = ["packed"];
+ let Spellings = [GNU<"packed">];
}
def Pcs : InheritableAttr {
- let Spellings = ["pcs"];
+ let Spellings = [GNU<"pcs">];
let Args = [EnumArgument<"PCS", "PCSType",
["aapcs", "aapcs-vfp"],
["AAPCS", "AAPCS_VFP"]>];
}
def Pure : InheritableAttr {
- let Spellings = ["pure"];
+ let Spellings = [GNU<"pure">];
}
def Regparm : InheritableAttr {
- let Spellings = ["regparm"];
+ let Spellings = [GNU<"regparm">];
let Args = [UnsignedArgument<"NumParams">];
}
def ReqdWorkGroupSize : InheritableAttr {
- let Spellings = ["reqd_work_group_size"];
+ let Spellings = [GNU<"reqd_work_group_size">];
let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">,
UnsignedArgument<"ZDim">];
}
+def WorkGroupSizeHint : InheritableAttr {
+ let Spellings = [GNU<"work_group_size_hint">];
+ let Args = [UnsignedArgument<"XDim">,
+ UnsignedArgument<"YDim">,
+ UnsignedArgument<"ZDim">];
+}
+
def InitPriority : InheritableAttr {
- let Spellings = ["init_priority"];
+ let Spellings = [GNU<"init_priority">];
let Args = [UnsignedArgument<"Priority">];
}
def Section : InheritableAttr {
- let Spellings = ["section"];
+ let Spellings = [GNU<"section">];
let Args = [StringArgument<"Name">];
}
def Sentinel : InheritableAttr {
- let Spellings = ["sentinel"];
+ let Spellings = [GNU<"sentinel">];
let Args = [DefaultIntArgument<"Sentinel", 0>,
DefaultIntArgument<"NullPos", 0>];
}
def StdCall : InheritableAttr {
- let Spellings = ["stdcall", "__stdcall"];
+ let Spellings = [GNU<"stdcall">, GNU<"__stdcall">];
}
def ThisCall : InheritableAttr {
- let Spellings = ["thiscall", "__thiscall"];
+ let Spellings = [GNU<"thiscall">, GNU<"__thiscall">];
}
def Pascal : InheritableAttr {
- let Spellings = ["pascal", "__pascal"];
+ let Spellings = [GNU<"pascal">];
}
def TransparentUnion : InheritableAttr {
- let Spellings = ["transparent_union"];
+ let Spellings = [GNU<"transparent_union">];
}
def Unavailable : InheritableAttr {
- let Spellings = ["unavailable"];
+ let Spellings = [GNU<"unavailable">];
let Args = [StringArgument<"Message">];
}
def ArcWeakrefUnavailable : InheritableAttr {
- let Spellings = ["objc_arc_weak_reference_unavailable"];
+ let Spellings = [GNU<"objc_arc_weak_reference_unavailable">];
let Subjects = [ObjCInterface];
}
+def ObjCGC : Attr {
+ let Spellings = [GNU<"objc_gc">];
+ let Args = [IdentifierArgument<"Kind">];
+ let ASTNode = 0;
+}
+
+def ObjCOwnership : Attr {
+ let Spellings = [GNU<"objc_ownership">];
+ let Args = [IdentifierArgument<"Kind">];
+ let ASTNode = 0;
+}
+
def ObjCRequiresPropertyDefs : InheritableAttr {
- let Spellings = ["objc_requires_property_definitions"];
+ let Spellings = [GNU<"objc_requires_property_definitions">];
let Subjects = [ObjCInterface];
}
def Unused : InheritableAttr {
- let Spellings = ["unused"];
+ let Spellings = [GNU<"unused">];
}
def Used : InheritableAttr {
- let Spellings = ["used"];
+ let Spellings = [GNU<"used">];
}
def Uuid : InheritableAttr {
- let Spellings = ["uuid"];
+ let Spellings = [GNU<"uuid">];
let Args = [StringArgument<"Guid">];
let Subjects = [CXXRecord];
}
+def VectorSize : Attr {
+ let Spellings = [GNU<"vector_size">];
+ let Args = [ExprArgument<"NumBytes">];
+ let ASTNode = 0;
+}
+
+def VecTypeHint : Attr {
+ let Spellings = [GNU<"vec_type_hint">];
+ let ASTNode = 0;
+ let SemaHandler = 0;
+ let Ignored = 1;
+}
+
def Visibility : InheritableAttr {
- let Spellings = ["visibility"];
+ let Clone = 0;
+ let Spellings = [GNU<"visibility">];
let Args = [EnumArgument<"Visibility", "VisibilityType",
["default", "hidden", "internal", "protected"],
["Default", "Hidden", "Hidden", "Protected"]>];
}
def VecReturn : InheritableAttr {
- let Spellings = ["vecreturn"];
+ let Spellings = [GNU<"vecreturn">];
let Subjects = [CXXRecord];
}
def WarnUnusedResult : InheritableAttr {
- let Spellings = ["warn_unused_result"];
+ let Spellings = [GNU<"warn_unused_result">];
}
def Weak : InheritableAttr {
- let Spellings = ["weak"];
+ let Spellings = [GNU<"weak">];
}
def WeakImport : InheritableAttr {
- let Spellings = ["weak_import"];
+ let Spellings = [GNU<"weak_import">];
}
def WeakRef : InheritableAttr {
- let Spellings = ["weakref"];
+ let Spellings = [GNU<"weakref">];
}
def X86ForceAlignArgPointer : InheritableAttr {
@@ -595,68 +706,68 @@ def X86ForceAlignArgPointer : InheritableAttr {
// AddressSafety attribute (e.g. for AddressSanitizer)
def NoAddressSafetyAnalysis : InheritableAttr {
- let Spellings = ["no_address_safety_analysis"];
+ let Spellings = [GNU<"no_address_safety_analysis">];
}
// C/C++ Thread safety attributes (e.g. for deadlock, data race checking)
def GuardedVar : InheritableAttr {
- let Spellings = ["guarded_var"];
+ let Spellings = [GNU<"guarded_var">];
}
def PtGuardedVar : InheritableAttr {
- let Spellings = ["pt_guarded_var"];
+ let Spellings = [GNU<"pt_guarded_var">];
}
def Lockable : InheritableAttr {
- let Spellings = ["lockable"];
+ let Spellings = [GNU<"lockable">];
}
def ScopedLockable : InheritableAttr {
- let Spellings = ["scoped_lockable"];
+ let Spellings = [GNU<"scoped_lockable">];
}
def NoThreadSafetyAnalysis : InheritableAttr {
- let Spellings = ["no_thread_safety_analysis"];
+ let Spellings = [GNU<"no_thread_safety_analysis">];
}
def GuardedBy : InheritableAttr {
- let Spellings = ["guarded_by"];
+ let Spellings = [GNU<"guarded_by">];
let Args = [ExprArgument<"Arg">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def PtGuardedBy : InheritableAttr {
- let Spellings = ["pt_guarded_by"];
+ let Spellings = [GNU<"pt_guarded_by">];
let Args = [ExprArgument<"Arg">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def AcquiredAfter : InheritableAttr {
- let Spellings = ["acquired_after"];
+ let Spellings = [GNU<"acquired_after">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def AcquiredBefore : InheritableAttr {
- let Spellings = ["acquired_before"];
+ let Spellings = [GNU<"acquired_before">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def ExclusiveLockFunction : InheritableAttr {
- let Spellings = ["exclusive_lock_function"];
+ let Spellings = [GNU<"exclusive_lock_function">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def SharedLockFunction : InheritableAttr {
- let Spellings = ["shared_lock_function"];
+ let Spellings = [GNU<"shared_lock_function">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
@@ -665,7 +776,7 @@ def SharedLockFunction : InheritableAttr {
// The first argument is an integer or boolean value specifying the return value
// of a successful lock acquisition.
def ExclusiveTrylockFunction : InheritableAttr {
- let Spellings = ["exclusive_trylock_function"];
+ let Spellings = [GNU<"exclusive_trylock_function">];
let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
@@ -674,43 +785,85 @@ def ExclusiveTrylockFunction : InheritableAttr {
// The first argument is an integer or boolean value specifying the return value
// of a successful lock acquisition.
def SharedTrylockFunction : InheritableAttr {
- let Spellings = ["shared_trylock_function"];
+ let Spellings = [GNU<"shared_trylock_function">];
let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def UnlockFunction : InheritableAttr {
- let Spellings = ["unlock_function"];
+ let Spellings = [GNU<"unlock_function">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def LockReturned : InheritableAttr {
- let Spellings = ["lock_returned"];
+ let Spellings = [GNU<"lock_returned">];
let Args = [ExprArgument<"Arg">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def LocksExcluded : InheritableAttr {
- let Spellings = ["locks_excluded"];
+ let Spellings = [GNU<"locks_excluded">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def ExclusiveLocksRequired : InheritableAttr {
- let Spellings = ["exclusive_locks_required"];
+ let Spellings = [GNU<"exclusive_locks_required">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
def SharedLocksRequired : InheritableAttr {
- let Spellings = ["shared_locks_required"];
+ let Spellings = [GNU<"shared_locks_required">];
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
let TemplateDependent = 1;
}
+
+// Microsoft-related attributes
+
+def MsStruct : InheritableAttr {
+ let Spellings = [Declspec<"ms_struct">];
+}
+
+def DLLExport : InheritableAttr {
+ let Spellings = [Declspec<"dllexport">];
+}
+
+def DLLImport : InheritableAttr {
+ let Spellings = [Declspec<"dllimport">];
+}
+
+def ForceInline : InheritableAttr {
+ let Spellings = [Declspec<"__forceinline">];
+}
+
+def Win64 : InheritableAttr {
+ let Spellings = [Declspec<"w64">];
+}
+
+def Ptr32 : InheritableAttr {
+ let Spellings = [Declspec<"__ptr32">];
+}
+
+def Ptr64 : InheritableAttr {
+ let Spellings = [Declspec<"__ptr64">];
+}
+
+def SingleInheritance : InheritableAttr {
+ let Spellings = [Declspec<"__single_inheritance">];
+}
+
+def MultipleInheritance : InheritableAttr {
+ let Spellings = [Declspec<"__multiple_inheritance">];
+}
+
+def VirtualInheritance : InheritableAttr {
+ let Spellings = [Declspec<"__virtual_inheritance">];
+}
diff --git a/include/clang/Basic/AttrKinds.h b/include/clang/Basic/AttrKinds.h
index 9d5ae58..150a30e 100644
--- a/include/clang/Basic/AttrKinds.h
+++ b/include/clang/Basic/AttrKinds.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the attr::Kind enum
-//
+///
+/// \file
+/// \brief Defines the clang::attr::Kind enum.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ATTRKINDS_H
@@ -18,7 +19,7 @@ namespace clang {
namespace attr {
-// Kind - This is a list of all the recognized kinds of attributes.
+// \brief A list of all the recognized kinds of attributes.
enum Kind {
#define ATTR(X) X,
#define LAST_INHERITABLE_ATTR(X) X, LAST_INHERITABLE = X,
diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def
index d1af218..1b060a5 100644
--- a/include/clang/Basic/Builtins.def
+++ b/include/clang/Basic/Builtins.def
@@ -33,7 +33,8 @@
// H -> SEL
// a -> __builtin_va_list
// A -> "reference" to __builtin_va_list
-// V -> Vector, following num elements and a base type.
+// V -> Vector, followed by the number of elements and the base type.
+// E -> ext_vector, followed by the number of elements and the base type.
// X -> _Complex, followed by the base type.
// Y -> ptrdiff_t
// P -> FILE
@@ -475,6 +476,7 @@ BUILTIN(__builtin___vprintf_chk, "iicC*a", "FP:1:")
BUILTIN(__builtin_expect, "LiLiLi" , "nc")
BUILTIN(__builtin_prefetch, "vvC*.", "nc")
+BUILTIN(__builtin_readcyclecounter, "ULLi", "n")
BUILTIN(__builtin_trap, "v", "nr")
BUILTIN(__builtin_unreachable, "v", "nr")
BUILTIN(__builtin_shufflevector, "v." , "nc")
@@ -725,6 +727,10 @@ LIBBUILTIN(strndup, "c*cC*z", "f", "string.h", ALL_LANGUAGES)
LIBBUILTIN(index, "c*cC*i", "f", "strings.h", ALL_LANGUAGES)
LIBBUILTIN(rindex, "c*cC*i", "f", "strings.h", ALL_LANGUAGES)
LIBBUILTIN(bzero, "vv*z", "f", "strings.h", ALL_LANGUAGES)
+// In some systems str[n]casejmp is a macro that expands to _str[n]icmp.
+// We undefine then here to avoid wrong name.
+#undef strcasecmp
+#undef strncasecmp
LIBBUILTIN(strcasecmp, "icC*cC*", "f", "strings.h", ALL_LANGUAGES)
LIBBUILTIN(strncasecmp, "icC*cC*z", "f", "strings.h", ALL_LANGUAGES)
// POSIX unistd.h
@@ -804,33 +810,85 @@ LIBBUILTIN(NSLog, "vG.", "fp:0:", "Foundation/NSObjCRuntime.h", OBJC_LANG)
LIBBUILTIN(NSLogv, "vGa", "fP:0:", "Foundation/NSObjCRuntime.h", OBJC_LANG)
// Builtin math library functions
-LIBBUILTIN(pow, "ddd", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(powl, "LdLdLd", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(powf, "fff", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(acos, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(acosl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(acosf, "ff", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(sqrt, "dd", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(sqrtl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(sqrtf, "ff", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(asin, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(asinl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(asinf, "ff", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(sin, "dd", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(sinl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
-LIBBUILTIN(sinf, "ff", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(atan, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(atanl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(atanf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(atan2, "ddd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(atan2l, "LdLdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(atan2f, "fff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(ceil, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(ceill, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(ceilf, "ff", "fe", "math.h", ALL_LANGUAGES)
LIBBUILTIN(cos, "dd", "fe", "math.h", ALL_LANGUAGES)
LIBBUILTIN(cosl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
LIBBUILTIN(cosf, "ff", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(exp, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(expl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(expf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(fabs, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fabsl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fabsf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(floor, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(floorl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(floorf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
LIBBUILTIN(fma, "dddd", "fc", "math.h", ALL_LANGUAGES)
LIBBUILTIN(fmal, "LdLdLdLd", "fc", "math.h", ALL_LANGUAGES)
LIBBUILTIN(fmaf, "ffff", "fc", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fmax, "ddd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fmaxl, "LdLdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fmaxf, "fff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(fmin, "ddd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fminl, "LdLdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(fminf, "fff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(log, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(logl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(logf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(pow, "ddd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(powl, "LdLdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(powf, "fff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(round, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(roundl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(roundf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(sin, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(sinl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(sinf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(sqrt, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(sqrtl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(sqrtf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
+LIBBUILTIN(tan, "dd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(tanl, "LdLd", "fe", "math.h", ALL_LANGUAGES)
+LIBBUILTIN(tanf, "ff", "fe", "math.h", ALL_LANGUAGES)
+
// Blocks runtime Builtin math library functions
LIBBUILTIN(_Block_object_assign, "vv*vC*iC", "f", "Blocks.h", ALL_LANGUAGES)
LIBBUILTIN(_Block_object_dispose, "vvC*iC", "f", "Blocks.h", ALL_LANGUAGES)
// FIXME: Also declare NSConcreteGlobalBlock and NSConcreteStackBlock.
// Annotation function
-BUILTIN(__builtin_annotation, "UiUicC*", "nc")
+BUILTIN(__builtin_annotation, "v.", "tn")
#undef BUILTIN
#undef LIBBUILTIN
diff --git a/include/clang/Basic/Builtins.h b/include/clang/Basic/Builtins.h
index 5afa020..257daf1 100644
--- a/include/clang/Basic/Builtins.h
+++ b/include/clang/Basic/Builtins.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines enum values for all the target-independent builtin
-// functions.
-//
+///
+/// \file
+/// \brief Defines enum values for all the target-independent builtin
+/// functions.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_BUILTINS_H
@@ -56,7 +57,7 @@ struct Info {
bool operator!=(const Info &RHS) const { return !(*this == RHS); }
};
-/// Builtin::Context - This holds information about target-independent and
+/// \brief Holds information about both target-independent and
/// target-specific builtins, allowing easy queries by clients.
class Context {
const Info *TSRecords;
@@ -67,7 +68,7 @@ public:
/// \brief Perform target-specific initialization
void InitializeTarget(const TargetInfo &Target);
- /// InitializeBuiltins - Mark the identifiers for all the builtins with their
+ /// \brief Mark the identifiers for all the builtins with their
/// appropriate builtin ID # and mark any non-portable builtin identifiers as
/// such.
void InitializeBuiltins(IdentifierTable &Table, const LangOptions& LangOpts);
@@ -76,39 +77,39 @@ public:
void GetBuiltinNames(SmallVectorImpl<const char *> &Names,
bool NoBuiltins);
- /// Builtin::GetName - Return the identifier name for the specified builtin,
+ /// \brief Return the identifier name for the specified builtin,
/// e.g. "__builtin_abs".
const char *GetName(unsigned ID) const {
return GetRecord(ID).Name;
}
- /// GetTypeString - Get the type descriptor string for the specified builtin.
+ /// \brief Get the type descriptor string for the specified builtin.
const char *GetTypeString(unsigned ID) const {
return GetRecord(ID).Type;
}
- /// isConst - Return true if this function has no side effects and doesn't
+ /// \brief Return true if this function has no side effects and doesn't
/// read memory.
bool isConst(unsigned ID) const {
return strchr(GetRecord(ID).Attributes, 'c') != 0;
}
- /// isNoThrow - Return true if we know this builtin never throws an exception.
+ /// \brief Return true if we know this builtin never throws an exception.
bool isNoThrow(unsigned ID) const {
return strchr(GetRecord(ID).Attributes, 'n') != 0;
}
- /// isNoReturn - Return true if we know this builtin never returns.
+ /// \brief Return true if we know this builtin never returns.
bool isNoReturn(unsigned ID) const {
return strchr(GetRecord(ID).Attributes, 'r') != 0;
}
- /// isReturnsTwice - Return true if we know this builtin can return twice.
+ /// \brief Return true if we know this builtin can return twice.
bool isReturnsTwice(unsigned ID) const {
return strchr(GetRecord(ID).Attributes, 'j') != 0;
}
- /// isLibFunction - Return true if this is a builtin for a libc/libm function,
+ /// \brief Return true if this is a builtin for a libc/libm function,
/// with a "__builtin_" prefix (e.g. __builtin_abs).
bool isLibFunction(unsigned ID) const {
return strchr(GetRecord(ID).Attributes, 'F') != 0;
@@ -146,10 +147,10 @@ public:
/// argument and whether this function as a va_list argument.
bool isScanfLike(unsigned ID, unsigned &FormatIdx, bool &HasVAListArg);
- /// isConstWithoutErrno - Return true if this function has no side
- /// effects and doesn't read memory, except for possibly errno. Such
- /// functions can be const when the MathErrno lang option is
- /// disabled.
+ /// \brief Return true if this function has no side effects and doesn't
+ /// read memory, except for possibly errno.
+ ///
+ /// Such functions can be const when the MathErrno lang option is disabled.
bool isConstWithoutErrno(unsigned ID) const {
return strchr(GetRecord(ID).Attributes, 'e') != 0;
}
diff --git a/include/clang/Basic/BuiltinsHexagon.def b/include/clang/Basic/BuiltinsHexagon.def
index 334224f..c071a46 100644
--- a/include/clang/Basic/BuiltinsHexagon.def
+++ b/include/clang/Basic/BuiltinsHexagon.def
@@ -1,4 +1,4 @@
-//==--- BuiltinsHexagon.def - Hexagon Builtin function database --*- C++ -*-==//
+//===-- BuiltinsHexagon.def - Hexagon Builtin function database --*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
@@ -7,683 +7,872 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines the X86-specific builtin function database. Users of
+// This file defines the Hexagon-specific builtin function database. Users of
// this file must define the BUILTIN macro to make use of this information.
//
//===----------------------------------------------------------------------===//
-BUILTIN(__builtin_HEXAGON_C2_cmpeq, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgt, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgtu, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpeqp, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgtp, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgtup, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_C2_bitsset, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_bitsclr, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpeqi, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgti, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgtui, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgei, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpgeui, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmplt, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_cmpltu, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_bitsclri, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_and, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_or, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_xor, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_andn, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_not, "bi", "")
-BUILTIN(__builtin_HEXAGON_C2_orn, "bii", "")
-BUILTIN(__builtin_HEXAGON_C2_pxfer_map, "bi", "")
-BUILTIN(__builtin_HEXAGON_C2_any8, "bi", "")
-BUILTIN(__builtin_HEXAGON_C2_all8, "bi", "")
-BUILTIN(__builtin_HEXAGON_C2_vitpack, "iii", "")
-BUILTIN(__builtin_HEXAGON_C2_mux, "iiii", "")
-BUILTIN(__builtin_HEXAGON_C2_muxii, "iiii", "")
-BUILTIN(__builtin_HEXAGON_C2_muxir, "iiii", "")
-BUILTIN(__builtin_HEXAGON_C2_muxri, "iiii", "")
-BUILTIN(__builtin_HEXAGON_C2_vmux, "LLiiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_C2_mask, "LLii", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmpbeq, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmpbgtu, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmpheq, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmphgt, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmphgtu, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmpweq, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmpwgt, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vcmpwgtu, "bLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_C2_tfrpr, "ii", "")
-BUILTIN(__builtin_HEXAGON_C2_tfrrp, "bi", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hl_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hl_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_lh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_lh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_ll_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_ll_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hl_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hl_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_lh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_lh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_ll_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_ll_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hl_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hl_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_lh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_lh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_ll_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_ll_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hl_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hl_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_lh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_lh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_ll_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_ll_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_hh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_hh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_hl_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_hl_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_lh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_lh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_ll_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_ll_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hl_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hl_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_lh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_lh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_ll_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_ll_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hl_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hl_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_lh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_lh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_ll_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_ll_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hl_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hl_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_lh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_lh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_ll_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_ll_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hl_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hl_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_lh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_lh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_ll_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_ll_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_hh_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_hh_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_hl_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_hl_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_lh_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_lh_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_ll_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_ll_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hh_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hh_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hl_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hl_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_lh_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_lh_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_ll_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_ll_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hl_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hl_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_lh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_lh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_ll_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_ll_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hl_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hl_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_lh_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_lh_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_ll_s0, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_ll_s1, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_hh_s0, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_hh_s1, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_hl_s0, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_hl_s1, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_lh_s0, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_lh_s1, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_ll_s0, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_ll_s1, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hl_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hl_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_lh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_lh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_ll_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_ll_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hl_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hl_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_lh_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_lh_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_ll_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_ll_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_hh_s0, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_hh_s1, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_hl_s0, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_hl_s1, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_lh_s0, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_lh_s1, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_ll_s0, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyud_ll_s1, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpysmi, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_macsip, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_macsin, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyss_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyss_acc_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyss_nac_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyuu_s0, "ULLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyuu_acc_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyuu_nac_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpy_up, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyu_up, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_M2_dpmpyss_rnd_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyi, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mpyui, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_maci, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_acci, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_accii, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_nacci, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_naccii, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_subacc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmac2s_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmac2s_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s0pack, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s1pack, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmac2, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vmpy2es_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vmpy2es_s1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vmac2es_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vmac2es_s1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vmac2es, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrmac_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrmpy_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vdmpyrs_s0, "iLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vdmpyrs_s1, "iLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vdmacs_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vdmacs_s1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vdmpys_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vdmpys_s1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpyrs_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpyrs_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpyrsc_s0, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpyrsc_s1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmacs_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmacs_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmacsc_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmacsc_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpys_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpys_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpysc_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpysc_s1, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cnacs_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cnacs_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cnacsc_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cnacsc_s1, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpys_s1, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpys_acc_s1, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpys_s1rp, "iLLii", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacls_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacls_s1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmachs_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmachs_s1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyl_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyl_s1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyh_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyh_s1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacls_rs0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacls_rs1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmachs_rs0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmachs_rs1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyl_rs0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyl_rs1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyh_rs0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyh_rs1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_hmmpyl_rs1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_hmmpyh_rs1, "iii", "")
-BUILTIN(__builtin_HEXAGON_M2_mmaculs_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmaculs_s1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacuhs_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacuhs_s1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyul_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyul_s1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyuh_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyuh_s1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmaculs_rs0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmaculs_rs1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacuhs_rs0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmacuhs_rs1, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyul_rs0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyul_rs1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyuh_rs0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_mmpyuh_rs1, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmaci_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmacr_s0, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmaci_s0c, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmacr_s0c, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_cmaci_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmacr_s0, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpyi_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpyr_s0, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpyi_s0c, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vrcmpyr_s0c, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpyi_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_cmpyr_s0, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_M2_vcmpy_s0_sat_i, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vcmpy_s0_sat_r, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vcmpy_s1_sat_i, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vcmpy_s1_sat_r, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vcmac_s0_sat_i, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vcmac_s0_sat_r, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vcrotate, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_A2_add, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_sub, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addsat, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subsat, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addi, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_l16_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_l16_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_l16_sat_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_l16_sat_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_l16_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_l16_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_l16_sat_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_l16_sat_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_lh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_hh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_lh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_hh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_lh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_hh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_lh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_hh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_aslh, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_asrh, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_addp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_addpsat, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_addsp, "LLiiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_subp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_neg, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_negsat, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_abs, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_abssat, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_vconj, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_negp, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_absp, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_max, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_maxu, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_A2_min, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_minu, "Uiii", "")
-BUILTIN(__builtin_HEXAGON_A2_maxp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_maxup, "ULLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_minp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_minup, "ULLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_tfr, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_tfrsi, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_tfrp, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_tfrpi, "LLii", "")
-BUILTIN(__builtin_HEXAGON_A2_zxtb, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_sxtb, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_zxth, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_sxth, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_combinew, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_A2_combineii, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_A2_combine_hh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_combine_hl, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_combine_lh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_combine_ll, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_tfril, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_tfrih, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_and, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_or, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_xor, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_not, "ii", "")
-BUILTIN(__builtin_HEXAGON_M2_xor_xacc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_A2_subri, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_andir, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_orir, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_andp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_orp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_xorp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_notp, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_sxtw, "LLii", "")
-BUILTIN(__builtin_HEXAGON_A2_sat, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_sath, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_satuh, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_satub, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_satb, "ii", "")
-BUILTIN(__builtin_HEXAGON_A2_vaddub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vaddubs, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vaddh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vaddhs, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vadduhs, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vaddw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vaddws, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_svavgh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svavghs, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svnavgh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svaddh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svaddhs, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svadduhs, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svsubh, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svsubhs, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_svsubuhs, "iii", "")
-BUILTIN(__builtin_HEXAGON_A2_vraddub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vraddub_acc, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vradduh, "iLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsubub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsububs, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsubh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsubhs, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsubuhs, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsubw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vsubws, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vabsh, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vabshsat, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vabsw, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vabswsat, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vabsdiffw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_M2_vabsdiffh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vrsadub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vrsadub_acc, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavgub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavguh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavgh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vnavgh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavgw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vnavgw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavgwr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vnavgwr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavgwcr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vnavgwcr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavghcr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vnavghcr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavguw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavguwr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavgubr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavguhr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vavghr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vnavghr, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vminh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vmaxh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vminub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vmaxub, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vminuh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vmaxuh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vminw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vmaxw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vminuw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A2_vmaxuw, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_r_sat, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_r_sat, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_p, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r_acc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_p_acc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r_nac, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_p_nac, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_xacc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r_xacc, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_xacc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_p_xacc, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_p_and, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_p_or, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_r_sat, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r_rnd, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_r_rnd_goodsyntax, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_addasl_rrri, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_valignib, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_valignrb, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vspliceib, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsplicerb, "LLiLLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsplatrh, "LLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsplatrb, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_insert, "iiiii", "")
-BUILTIN(__builtin_HEXAGON_S2_tableidxb_goodsyntax, "iiiii", "")
-BUILTIN(__builtin_HEXAGON_S2_tableidxh_goodsyntax, "iiiii", "")
-BUILTIN(__builtin_HEXAGON_S2_tableidxw_goodsyntax, "iiiii", "")
-BUILTIN(__builtin_HEXAGON_S2_tableidxd_goodsyntax, "iiiii", "")
-BUILTIN(__builtin_HEXAGON_S2_extractu, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S2_insertp, "LLiLLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_S2_extractup, "LLiLLiii", "")
-BUILTIN(__builtin_HEXAGON_S2_insert_rp, "iiiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_extractu_rp, "iiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_insertp_rp, "LLiLLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_extractup_rp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_tstbit_i, "bii", "")
-BUILTIN(__builtin_HEXAGON_S2_setbit_i, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_togglebit_i, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_clrbit_i, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_tstbit_r, "bii", "")
-BUILTIN(__builtin_HEXAGON_S2_setbit_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_togglebit_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_clrbit_r, "iii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_vh, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_i_svw_trun, "iLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_svw_trun, "iLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_i_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_i_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asr_r_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_asl_r_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsr_r_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_lsl_r_vw, "LLiLLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vrndpackwh, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vrndpackwhs, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vsxtbh, "LLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vzxtbh, "LLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsathub, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_svsathub, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_svsathb, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsathb, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vtrunohb, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vtrunewh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vtrunowh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vtrunehb, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vsxthw, "LLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vzxthw, "LLii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsatwh, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vsatwuh, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_packhl, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_A2_swiz, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_vsathub_nopack, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vsathb_nopack, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vsatwh_nopack, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_vsatwuh_nopack, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_shuffob, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_shuffeb, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_shuffoh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_shuffeh, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_parityp, "iLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_lfsp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_clbnorm, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_clb, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_cl0, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_cl1, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_clbp, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_cl0p, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_cl1p, "iLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_brev, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_ct0, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_ct1, "ii", "")
-BUILTIN(__builtin_HEXAGON_S2_interleave, "LLiLLi", "")
-BUILTIN(__builtin_HEXAGON_S2_deinterleave, "LLiLLi", "")
+// The format of this database matches clang/Basic/Builtins.def.
-BUILTIN(__builtin_SI_to_SXTHI_asrh, "ii", "")
-
-BUILTIN(__builtin_M2_vrcmpys_s1, "LLiLLii", "")
-BUILTIN(__builtin_M2_vrcmpys_acc_s1, "LLiLLiLLii", "")
-BUILTIN(__builtin_M2_vrcmpys_s1rp, "iLLii", "")
-
-BUILTIN(__builtin_M2_vradduh, "iLLiLLi", "")
-BUILTIN(__builtin_A2_addsp, "LLiiLLi", "")
-BUILTIN(__builtin_A2_addpsat, "LLiLLiLLi", "")
+// The builtins below are not autogenerated from iset.py.
+// Make sure you do not overwrite these.
-BUILTIN(__builtin_A2_maxp, "LLiLLiLLi", "")
-BUILTIN(__builtin_A2_maxup, "LLiLLiLLi", "")
-
-BUILTIN(__builtin_HEXAGON_A4_orn, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_andn, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_ornp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A4_andnp, "LLiLLiLLi", "")
-BUILTIN(__builtin_HEXAGON_A4_combineir, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_A4_combineri, "LLiii", "")
-BUILTIN(__builtin_HEXAGON_C4_cmpneqi, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_cmpneq, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_cmpltei, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_cmplte, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_cmplteui, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_cmplteu, "bii", "")
-BUILTIN(__builtin_HEXAGON_A4_rcmpneq, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_rcmpneqi, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_rcmpeq, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_rcmpeqi, "iii", "")
-BUILTIN(__builtin_HEXAGON_C4_fastcorner9, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_fastcorner9_not, "bii", "")
-BUILTIN(__builtin_HEXAGON_C4_and_andn, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_and_and, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_and_orn, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_and_or, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_or_andn, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_or_and, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_or_orn, "biii", "")
-BUILTIN(__builtin_HEXAGON_C4_or_or, "biii", "")
-BUILTIN(__builtin_HEXAGON_S4_addaddi, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S4_subaddi, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_xor_xacc, "LLiLLiLLiLLi", "")
-
-BUILTIN(__builtin_HEXAGON_M4_and_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_and_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_and_xor, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_and_andn, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_xor_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_xor_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_xor_andn, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_or_and, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_or_or, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_or_xor, "iiii", "")
-BUILTIN(__builtin_HEXAGON_M4_or_andn, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S4_or_andix, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S4_or_andi, "iiii", "")
-BUILTIN(__builtin_HEXAGON_S4_or_ori, "iiii", "")
+BUILTIN(__builtin_SI_to_SXTHI_asrh, "ii", "")
+BUILTIN(__builtin_circ_ldd, "LLi*LLi*LLi*ii", "")
-BUILTIN(__builtin_HEXAGON_A4_modwrapu, "iii", "")
+// The builtins above are not autogenerated from iset.py.
+// Make sure you do not overwrite these.
-BUILTIN(__builtin_HEXAGON_A4_cround_ri, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_cround_rr, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_round_ri, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_round_rr, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_round_ri_sat, "iii", "")
-BUILTIN(__builtin_HEXAGON_A4_round_rr_sat, "iii", "")
+BUILTIN(__builtin_HEXAGON_C2_cmpeq,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgt,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgtu,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpeqp,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgtp,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgtup,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_rcmpeqi,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_rcmpneqi,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_rcmpeq,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_rcmpneq,"iii","")
+BUILTIN(__builtin_HEXAGON_C2_bitsset,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_bitsclr,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_nbitsset,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_nbitsclr,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpeqi,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgti,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgtui,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgei,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpgeui,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmplt,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_cmpltu,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_bitsclri,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_nbitsclri,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_cmpneqi,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_cmpltei,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_cmplteui,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_cmpneq,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_cmplte,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_cmplteu,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_and,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_or,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_xor,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_andn,"bii","")
+BUILTIN(__builtin_HEXAGON_C2_not,"bi","")
+BUILTIN(__builtin_HEXAGON_C2_orn,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_and_and,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_and_or,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_or_and,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_or_or,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_and_andn,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_and_orn,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_or_andn,"biii","")
+BUILTIN(__builtin_HEXAGON_C4_or_orn,"biii","")
+BUILTIN(__builtin_HEXAGON_C2_pxfer_map,"bi","")
+BUILTIN(__builtin_HEXAGON_C2_any8,"bi","")
+BUILTIN(__builtin_HEXAGON_C2_all8,"bi","")
+BUILTIN(__builtin_HEXAGON_C2_vitpack,"iii","")
+BUILTIN(__builtin_HEXAGON_C2_mux,"iiii","")
+BUILTIN(__builtin_HEXAGON_C2_muxii,"iiii","")
+BUILTIN(__builtin_HEXAGON_C2_muxir,"iiii","")
+BUILTIN(__builtin_HEXAGON_C2_muxri,"iiii","")
+BUILTIN(__builtin_HEXAGON_C2_vmux,"LLiiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_C2_mask,"LLii","")
+BUILTIN(__builtin_HEXAGON_A2_vcmpbeq,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpbeqi,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpbeq_any,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vcmpbgtu,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpbgtui,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpbgt,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpbgti,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpbeq,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpbeqi,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpbgtu,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpbgtui,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpbgt,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpbgti,"bii","")
+BUILTIN(__builtin_HEXAGON_A2_vcmpheq,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vcmphgt,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vcmphgtu,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpheqi,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vcmphgti,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vcmphgtui,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpheq,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmphgt,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmphgtu,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmpheqi,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmphgti,"bii","")
+BUILTIN(__builtin_HEXAGON_A4_cmphgtui,"bii","")
+BUILTIN(__builtin_HEXAGON_A2_vcmpweq,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vcmpwgt,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vcmpwgtu,"bLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpweqi,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpwgti,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vcmpwgtui,"bLLii","")
+BUILTIN(__builtin_HEXAGON_A4_boundscheck,"biLLi","")
+BUILTIN(__builtin_HEXAGON_A4_tlbmatch,"bLLii","")
+BUILTIN(__builtin_HEXAGON_C2_tfrpr,"ii","")
+BUILTIN(__builtin_HEXAGON_C2_tfrrp,"bi","")
+BUILTIN(__builtin_HEXAGON_C4_fastcorner9,"bii","")
+BUILTIN(__builtin_HEXAGON_C4_fastcorner9_not,"bii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hl_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_hl_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_lh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_lh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_ll_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_ll_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hl_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_hl_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_lh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_lh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_ll_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_ll_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hl_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_hl_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_lh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_lh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_ll_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_acc_sat_ll_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hl_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_hl_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_lh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_lh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_ll_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_nac_sat_ll_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_hh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_hh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_hl_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_hl_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_lh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_lh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_ll_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_ll_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hl_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_hl_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_lh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_lh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_ll_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_ll_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hl_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_hl_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_lh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_lh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_ll_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_rnd_ll_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hl_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_hl_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_lh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_lh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_ll_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_acc_ll_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hl_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_hl_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_lh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_lh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_ll_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_nac_ll_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_hh_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_hh_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_hl_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_hl_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_lh_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_lh_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_ll_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_ll_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hh_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hh_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hl_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_hl_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_lh_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_lh_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_ll_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyd_rnd_ll_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hl_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_hl_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_lh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_lh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_ll_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_acc_ll_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hl_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_hl_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_lh_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_lh_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_ll_s0,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_nac_ll_s1,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_hh_s0,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_hh_s1,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_hl_s0,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_hl_s1,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_lh_s0,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_lh_s1,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_ll_s0,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_ll_s1,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hl_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_hl_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_lh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_lh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_ll_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_acc_ll_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hl_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_hl_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_lh_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_lh_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_ll_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_nac_ll_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_hh_s0,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_hh_s1,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_hl_s0,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_hl_s1,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_lh_s0,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_lh_s1,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_ll_s0,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyud_ll_s1,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpysmi,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_macsip,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_macsin,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyss_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyss_acc_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyss_nac_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyuu_s0,"ULLiii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyuu_acc_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyuu_nac_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_up,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_up_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpy_up_s1_sat,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyu_up,"Uiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpysu_up,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_dpmpyss_rnd_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M4_mac_up_s1_sat,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_nac_up_s1_sat,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyi,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mpyui,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_maci,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_acci,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_accii,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_nacci,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_naccii,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_subacc,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_mpyrr_addr,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_mpyri_addr_u2,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_mpyri_addr,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_mpyri_addi,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_mpyrr_addi,"iiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2s_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2s_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2su_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2su_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2su_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2su_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s0pack,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2s_s1pack,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2es_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vmpy2es_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2es_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2es_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vmac2es,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrmac_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrmpy_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vdmpyrs_s0,"iLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vdmpyrs_s1,"iLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M5_vrmpybuu,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M5_vrmacbuu,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M5_vrmpybsu,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M5_vrmacbsu,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M5_vmpybuu,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M5_vmpybsu,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M5_vmacbuu,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M5_vmacbsu,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M5_vdmpybsu,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M5_vdmacbsu,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vdmacs_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vdmacs_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vdmpys_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vdmpys_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_cmpyrs_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpyrs_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpyrsc_s0,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpyrsc_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_cmacs_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmacs_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmacsc_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmacsc_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpys_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpys_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpysc_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpysc_s1,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cnacs_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cnacs_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cnacsc_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cnacsc_s1,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpys_s1,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpys_acc_s1,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpys_s1rp,"iLLii","")
+BUILTIN(__builtin_HEXAGON_M2_mmacls_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacls_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmachs_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmachs_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyl_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyl_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyh_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyh_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacls_rs0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacls_rs1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmachs_rs0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmachs_rs1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyl_rs0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyl_rs1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyh_rs0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyh_rs1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyeh_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyeh_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyeh_acc_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyeh_acc_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyoh_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyoh_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyoh_acc_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M4_vrmpyoh_acc_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_hmmpyl_rs1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_hmmpyh_rs1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_hmmpyl_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_hmmpyh_s1,"iii","")
+BUILTIN(__builtin_HEXAGON_M2_mmaculs_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmaculs_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacuhs_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacuhs_s1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyul_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyul_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyuh_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyuh_s1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmaculs_rs0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmaculs_rs1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacuhs_rs0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmacuhs_rs1,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyul_rs0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyul_rs1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyuh_rs0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_mmpyuh_rs1,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmaci_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmacr_s0,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmaci_s0c,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmacr_s0c,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_cmaci_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmacr_s0,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpyi_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpyr_s0,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpyi_s0c,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vrcmpyr_s0c,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_cmpyi_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M2_cmpyr_s0,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M4_cmpyi_wh,"iLLii","")
+BUILTIN(__builtin_HEXAGON_M4_cmpyr_wh,"iLLii","")
+BUILTIN(__builtin_HEXAGON_M4_cmpyi_whc,"iLLii","")
+BUILTIN(__builtin_HEXAGON_M4_cmpyr_whc,"iLLii","")
+BUILTIN(__builtin_HEXAGON_M2_vcmpy_s0_sat_i,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vcmpy_s0_sat_r,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vcmpy_s1_sat_i,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vcmpy_s1_sat_r,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vcmac_s0_sat_i,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vcmac_s0_sat_r,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vcrotate,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S4_vrcrotate_acc,"LLiLLiLLiii","")
+BUILTIN(__builtin_HEXAGON_S4_vrcrotate,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_S2_vcnegh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_vrcnegh,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_M4_pmpyw,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M4_vpmpyh,"LLiii","")
+BUILTIN(__builtin_HEXAGON_M4_pmpyw_acc,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_M4_vpmpyh_acc,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_A2_add,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_sub,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addsat,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subsat,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addi,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_l16_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_l16_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_l16_sat_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_l16_sat_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_l16_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_l16_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_l16_sat_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_l16_sat_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_lh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_hh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_lh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_addh_h16_sat_hh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_lh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_hh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_lh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_subh_h16_sat_hh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_aslh,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_asrh,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_addp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_addpsat,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_addsp,"LLiiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_subp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_neg,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_negsat,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_abs,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_abssat,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_vconj,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_negp,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_absp,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_max,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_maxu,"Uiii","")
+BUILTIN(__builtin_HEXAGON_A2_min,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_minu,"Uiii","")
+BUILTIN(__builtin_HEXAGON_A2_maxp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_maxup,"ULLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_minp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_minup,"ULLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_tfr,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_tfrsi,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_tfrp,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_tfrpi,"LLii","")
+BUILTIN(__builtin_HEXAGON_A2_zxtb,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_sxtb,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_zxth,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_sxth,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_combinew,"LLiii","")
+BUILTIN(__builtin_HEXAGON_A4_combineri,"LLiii","")
+BUILTIN(__builtin_HEXAGON_A4_combineir,"LLiii","")
+BUILTIN(__builtin_HEXAGON_A2_combineii,"LLiii","")
+BUILTIN(__builtin_HEXAGON_A2_combine_hh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_combine_hl,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_combine_lh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_combine_ll,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_tfril,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_tfrih,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_and,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_or,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_xor,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_not,"ii","")
+BUILTIN(__builtin_HEXAGON_M2_xor_xacc,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_xor_xacc,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_andn,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_orn,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_andnp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_ornp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_addaddi,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_subaddi,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_and_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_and_andn,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_and_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_and_xor,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_or_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_or_andn,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_or_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_or_xor,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_or_andix,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_or_andi,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_or_ori,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_xor_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_xor_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_M4_xor_andn,"iiii","")
+BUILTIN(__builtin_HEXAGON_A2_subri,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_andir,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_orir,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_andp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_orp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_xorp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_notp,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_sxtw,"LLii","")
+BUILTIN(__builtin_HEXAGON_A2_sat,"iLLi","")
+BUILTIN(__builtin_HEXAGON_A2_roundsat,"iLLi","")
+BUILTIN(__builtin_HEXAGON_A2_sath,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_satuh,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_satub,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_satb,"ii","")
+BUILTIN(__builtin_HEXAGON_A2_vaddub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vaddb_map,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vaddubs,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vaddh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vaddhs,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vadduhs,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A5_vaddhubs,"iLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vaddw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vaddws,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_vxaddsubw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_vxsubaddw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_vxaddsubh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_vxsubaddh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_vxaddsubhr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_vxsubaddhr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_svavgh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svavghs,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svnavgh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svaddh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svaddhs,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svadduhs,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svsubh,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svsubhs,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_svsubuhs,"iii","")
+BUILTIN(__builtin_HEXAGON_A2_vraddub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vraddub_acc,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vraddh,"iLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vradduh,"iLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubb_map,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsububs,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubhs,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubuhs,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vsubws,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vabsh,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vabshsat,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vabsw,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vabswsat,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vabsdiffw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_M2_vabsdiffh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vrsadub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vrsadub_acc,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavgub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavguh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavgh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vnavgh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavgw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vnavgw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavgwr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vnavgwr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavgwcr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vnavgwcr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavghcr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vnavghcr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavguw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavguwr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavgubr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavguhr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vavghr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vnavghr,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_round_ri,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_round_rr,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_round_ri_sat,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_round_rr_sat,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_cround_ri,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_cround_rr,"iii","")
+BUILTIN(__builtin_HEXAGON_A4_vrminh,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrmaxh,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrminuh,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrmaxuh,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrminw,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrmaxw,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrminuw,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A4_vrmaxuw,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_A2_vminb,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vmaxb,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vminub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vmaxub,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vminh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vmaxh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vminuh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vmaxuh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vminw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vmaxw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vminuw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A2_vmaxuw,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_A4_modwrapu,"iii","")
+BUILTIN(__builtin_HEXAGON_F2_sfadd,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sfsub,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sfmpy,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sffma,"ffff","")
+BUILTIN(__builtin_HEXAGON_F2_sffma_sc,"ffffi","")
+BUILTIN(__builtin_HEXAGON_F2_sffms,"ffff","")
+BUILTIN(__builtin_HEXAGON_F2_sffma_lib,"ffff","")
+BUILTIN(__builtin_HEXAGON_F2_sffms_lib,"ffff","")
+BUILTIN(__builtin_HEXAGON_F2_sfcmpeq,"bff","")
+BUILTIN(__builtin_HEXAGON_F2_sfcmpgt,"bff","")
+BUILTIN(__builtin_HEXAGON_F2_sfcmpge,"bff","")
+BUILTIN(__builtin_HEXAGON_F2_sfcmpuo,"bff","")
+BUILTIN(__builtin_HEXAGON_F2_sfmax,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sfmin,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sfclass,"bfi","")
+BUILTIN(__builtin_HEXAGON_F2_sfimm_p,"fi","")
+BUILTIN(__builtin_HEXAGON_F2_sfimm_n,"fi","")
+BUILTIN(__builtin_HEXAGON_F2_sffixupn,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sffixupd,"fff","")
+BUILTIN(__builtin_HEXAGON_F2_sffixupr,"ff","")
+BUILTIN(__builtin_HEXAGON_F2_dfadd,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dfsub,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dfmpy,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffma,"dddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffms,"dddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffma_lib,"dddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffms_lib,"dddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffma_sc,"ddddi","")
+BUILTIN(__builtin_HEXAGON_F2_dfmax,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dfmin,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dfcmpeq,"bdd","")
+BUILTIN(__builtin_HEXAGON_F2_dfcmpgt,"bdd","")
+BUILTIN(__builtin_HEXAGON_F2_dfcmpge,"bdd","")
+BUILTIN(__builtin_HEXAGON_F2_dfcmpuo,"bdd","")
+BUILTIN(__builtin_HEXAGON_F2_dfclass,"bdi","")
+BUILTIN(__builtin_HEXAGON_F2_dfimm_p,"di","")
+BUILTIN(__builtin_HEXAGON_F2_dfimm_n,"di","")
+BUILTIN(__builtin_HEXAGON_F2_dffixupn,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffixupd,"ddd","")
+BUILTIN(__builtin_HEXAGON_F2_dffixupr,"dd","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2df,"df","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2sf,"fd","")
+BUILTIN(__builtin_HEXAGON_F2_conv_uw2sf,"fi","")
+BUILTIN(__builtin_HEXAGON_F2_conv_uw2df,"di","")
+BUILTIN(__builtin_HEXAGON_F2_conv_w2sf,"fi","")
+BUILTIN(__builtin_HEXAGON_F2_conv_w2df,"di","")
+BUILTIN(__builtin_HEXAGON_F2_conv_ud2sf,"fLLi","")
+BUILTIN(__builtin_HEXAGON_F2_conv_ud2df,"dLLi","")
+BUILTIN(__builtin_HEXAGON_F2_conv_d2sf,"fLLi","")
+BUILTIN(__builtin_HEXAGON_F2_conv_d2df,"dLLi","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2uw,"if","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2w,"if","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2ud,"LLif","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2d,"LLif","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2uw,"id","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2w,"id","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2ud,"LLid","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2d,"LLid","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2uw_chop,"if","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2w_chop,"if","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2ud_chop,"LLif","")
+BUILTIN(__builtin_HEXAGON_F2_conv_sf2d_chop,"LLif","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2uw_chop,"id","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2w_chop,"id","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2ud_chop,"LLid","")
+BUILTIN(__builtin_HEXAGON_F2_conv_df2d_chop,"LLid","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_p_xor,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_p_xor,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_p_xor,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_p_xor,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_r_sat,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_r_sat,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_p,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r_acc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_p_acc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r_nac,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_p_nac,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_xacc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r_xacc,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_xacc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_p_xacc,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r_and,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r_or,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_p_and,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_p_or,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_r_sat,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r_rnd,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_r_rnd_goodsyntax,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p_rnd,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_p_rnd_goodsyntax,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S4_lsli,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_addasl_rrri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_andi_asl_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_ori_asl_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_addi_asl_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_subi_asl_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_andi_lsr_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_ori_lsr_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_addi_lsr_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S4_subi_lsr_ri,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_valignib,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_valignrb,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_vspliceib,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_vsplicerb,"LLiLLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_vsplatrh,"LLii","")
+BUILTIN(__builtin_HEXAGON_S2_vsplatrb,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_insert,"iiiii","")
+BUILTIN(__builtin_HEXAGON_S2_tableidxb_goodsyntax,"iiiii","")
+BUILTIN(__builtin_HEXAGON_S2_tableidxh_goodsyntax,"iiiii","")
+BUILTIN(__builtin_HEXAGON_S2_tableidxw_goodsyntax,"iiiii","")
+BUILTIN(__builtin_HEXAGON_S2_tableidxd_goodsyntax,"iiiii","")
+BUILTIN(__builtin_HEXAGON_A4_bitspliti,"LLiii","")
+BUILTIN(__builtin_HEXAGON_A4_bitsplit,"LLiii","")
+BUILTIN(__builtin_HEXAGON_S4_extract,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_extractu,"iiii","")
+BUILTIN(__builtin_HEXAGON_S2_insertp,"LLiLLiLLiii","")
+BUILTIN(__builtin_HEXAGON_S4_extractp,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_S2_extractup,"LLiLLiii","")
+BUILTIN(__builtin_HEXAGON_S2_insert_rp,"iiiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_extract_rp,"iiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_extractu_rp,"iiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_insertp_rp,"LLiLLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S4_extractp_rp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_extractup_rp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_tstbit_i,"bii","")
+BUILTIN(__builtin_HEXAGON_S4_ntstbit_i,"bii","")
+BUILTIN(__builtin_HEXAGON_S2_setbit_i,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_togglebit_i,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_clrbit_i,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_tstbit_r,"bii","")
+BUILTIN(__builtin_HEXAGON_S4_ntstbit_r,"bii","")
+BUILTIN(__builtin_HEXAGON_S2_setbit_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_togglebit_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_clrbit_r,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S5_asrhub_rnd_sat_goodsyntax,"iLLii","")
+BUILTIN(__builtin_HEXAGON_S5_asrhub_sat,"iLLii","")
+BUILTIN(__builtin_HEXAGON_S5_vasrhrnd_goodsyntax,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_vh,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_i_svw_trun,"iLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_svw_trun,"iLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_i_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_i_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asr_r_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_asl_r_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsr_r_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_lsl_r_vw,"LLiLLii","")
+BUILTIN(__builtin_HEXAGON_S2_vrndpackwh,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vrndpackwhs,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vsxtbh,"LLii","")
+BUILTIN(__builtin_HEXAGON_S2_vzxtbh,"LLii","")
+BUILTIN(__builtin_HEXAGON_S2_vsathub,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_svsathub,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_svsathb,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_vsathb,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vtrunohb,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vtrunewh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vtrunowh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vtrunehb,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vsxthw,"LLii","")
+BUILTIN(__builtin_HEXAGON_S2_vzxthw,"LLii","")
+BUILTIN(__builtin_HEXAGON_S2_vsatwh,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vsatwuh,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_packhl,"LLiii","")
+BUILTIN(__builtin_HEXAGON_A2_swiz,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_vsathub_nopack,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vsathb_nopack,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vsatwh_nopack,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_vsatwuh_nopack,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_shuffob,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_shuffeb,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_shuffoh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_shuffeh,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S5_popcountp,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S4_parity,"iii","")
+BUILTIN(__builtin_HEXAGON_S2_parityp,"iLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_lfsp,"LLiLLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_clbnorm,"ii","")
+BUILTIN(__builtin_HEXAGON_S4_clbaddi,"iii","")
+BUILTIN(__builtin_HEXAGON_S4_clbpnorm,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S4_clbpaddi,"iLLii","")
+BUILTIN(__builtin_HEXAGON_S2_clb,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_cl0,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_cl1,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_clbp,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_cl0p,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_cl1p,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_brev,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_brevp,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_ct0,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_ct1,"ii","")
+BUILTIN(__builtin_HEXAGON_S2_ct0p,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_ct1p,"iLLi","")
+BUILTIN(__builtin_HEXAGON_S2_interleave,"LLiLLi","")
+BUILTIN(__builtin_HEXAGON_S2_deinterleave,"LLiLLi","")
#undef BUILTIN
diff --git a/include/clang/Basic/BuiltinsMips.def b/include/clang/Basic/BuiltinsMips.def
new file mode 100644
index 0000000..d013715
--- /dev/null
+++ b/include/clang/Basic/BuiltinsMips.def
@@ -0,0 +1,125 @@
+//===-- BuiltinsMips.def - Mips Builtin function database --------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the MIPS-specific builtin function database. Users of
+// this file must define the BUILTIN macro to make use of this information.
+//
+//===----------------------------------------------------------------------===//
+
+// The format of this database matches clang/Basic/Builtins.def.
+
+// Add/subtract with optional saturation
+BUILTIN(__builtin_mips_addu_qb, "V4ScV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_addu_s_qb, "V4ScV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_subu_qb, "V4ScV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_subu_s_qb, "V4ScV4ScV4Sc", "n")
+
+BUILTIN(__builtin_mips_addq_ph, "V2sV2sV2s", "n")
+BUILTIN(__builtin_mips_addq_s_ph, "V2sV2sV2s", "n")
+BUILTIN(__builtin_mips_subq_ph, "V2sV2sV2s", "n")
+BUILTIN(__builtin_mips_subq_s_ph, "V2sV2sV2s", "n")
+
+BUILTIN(__builtin_mips_madd, "LLiLLiii", "nc")
+BUILTIN(__builtin_mips_maddu, "LLiLLiUiUi", "nc")
+BUILTIN(__builtin_mips_msub, "LLiLLiii", "nc")
+BUILTIN(__builtin_mips_msubu, "LLiLLiUiUi", "nc")
+
+BUILTIN(__builtin_mips_addq_s_w, "iii", "n")
+BUILTIN(__builtin_mips_subq_s_w, "iii", "n")
+
+BUILTIN(__builtin_mips_addsc, "iii", "n")
+BUILTIN(__builtin_mips_addwc, "iii", "n")
+
+BUILTIN(__builtin_mips_modsub, "iii", "nc")
+
+BUILTIN(__builtin_mips_raddu_w_qb, "iV4Sc", "nc")
+
+BUILTIN(__builtin_mips_absq_s_ph, "V2sV2s", "n")
+BUILTIN(__builtin_mips_absq_s_w, "ii", "n")
+
+BUILTIN(__builtin_mips_precrq_qb_ph, "V4ScV2sV2s", "nc")
+BUILTIN(__builtin_mips_precrqu_s_qb_ph, "V4ScV2sV2s", "n")
+BUILTIN(__builtin_mips_precrq_ph_w, "V2sii", "nc")
+BUILTIN(__builtin_mips_precrq_rs_ph_w, "V2sii", "n")
+BUILTIN(__builtin_mips_preceq_w_phl, "iV2s", "nc")
+BUILTIN(__builtin_mips_preceq_w_phr, "iV2s", "nc")
+BUILTIN(__builtin_mips_precequ_ph_qbl, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_precequ_ph_qbr, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_precequ_ph_qbla, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_precequ_ph_qbra, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_preceu_ph_qbl, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_preceu_ph_qbr, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_preceu_ph_qbla, "V2sV4Sc", "nc")
+BUILTIN(__builtin_mips_preceu_ph_qbra, "V2sV4Sc", "nc")
+
+BUILTIN(__builtin_mips_shll_qb, "V4ScV4Sci", "n")
+BUILTIN(__builtin_mips_shrl_qb, "V4ScV4Sci", "nc")
+BUILTIN(__builtin_mips_shll_ph, "V2sV2si", "n")
+BUILTIN(__builtin_mips_shll_s_ph, "V2sV2si", "n")
+BUILTIN(__builtin_mips_shra_ph, "V2sV2si", "nc")
+BUILTIN(__builtin_mips_shra_r_ph, "V2sV2si", "nc")
+BUILTIN(__builtin_mips_shll_s_w, "iii", "n")
+BUILTIN(__builtin_mips_shra_r_w, "iii", "nc")
+BUILTIN(__builtin_mips_shilo, "LLiLLii", "nc")
+
+BUILTIN(__builtin_mips_muleu_s_ph_qbl, "V2sV4ScV2s", "n")
+BUILTIN(__builtin_mips_muleu_s_ph_qbr, "V2sV4ScV2s", "n")
+BUILTIN(__builtin_mips_mulq_rs_ph, "V2sV2sV2s", "n")
+BUILTIN(__builtin_mips_muleq_s_w_phl, "iV2sV2s", "n")
+BUILTIN(__builtin_mips_muleq_s_w_phr, "iV2sV2s", "n")
+BUILTIN(__builtin_mips_mulsaq_s_w_ph, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_maq_s_w_phl, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_maq_s_w_phr, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_maq_sa_w_phl, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_maq_sa_w_phr, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_mult, "LLiii", "nc")
+BUILTIN(__builtin_mips_multu, "LLiUiUi", "nc")
+
+BUILTIN(__builtin_mips_dpau_h_qbl, "LLiLLiV4ScV4Sc", "nc")
+BUILTIN(__builtin_mips_dpau_h_qbr, "LLiLLiV4ScV4Sc", "nc")
+BUILTIN(__builtin_mips_dpsu_h_qbl, "LLiLLiV4ScV4Sc", "nc")
+BUILTIN(__builtin_mips_dpsu_h_qbr, "LLiLLiV4ScV4Sc", "nc")
+BUILTIN(__builtin_mips_dpaq_s_w_ph, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_dpsq_s_w_ph, "LLiLLiV2sV2s", "n")
+BUILTIN(__builtin_mips_dpaq_sa_l_w, "LLiLLiii", "n")
+BUILTIN(__builtin_mips_dpsq_sa_l_w, "LLiLLiii", "n")
+
+BUILTIN(__builtin_mips_cmpu_eq_qb, "vV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_cmpu_lt_qb, "vV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_cmpu_le_qb, "vV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_cmpgu_eq_qb, "iV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_cmpgu_lt_qb, "iV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_cmpgu_le_qb, "iV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_cmp_eq_ph, "vV2sV2s", "n")
+BUILTIN(__builtin_mips_cmp_lt_ph, "vV2sV2s", "n")
+BUILTIN(__builtin_mips_cmp_le_ph, "vV2sV2s", "n")
+
+BUILTIN(__builtin_mips_extr_s_h, "iLLii", "n")
+BUILTIN(__builtin_mips_extr_w, "iLLii", "n")
+BUILTIN(__builtin_mips_extr_rs_w, "iLLii", "n")
+BUILTIN(__builtin_mips_extr_r_w, "iLLii", "n")
+BUILTIN(__builtin_mips_extp, "iLLii", "n")
+BUILTIN(__builtin_mips_extpdp, "iLLii", "n")
+
+BUILTIN(__builtin_mips_wrdsp, "viIi", "n")
+BUILTIN(__builtin_mips_rddsp, "iIi", "n")
+BUILTIN(__builtin_mips_insv, "iii", "n")
+BUILTIN(__builtin_mips_bitrev, "ii", "nc")
+BUILTIN(__builtin_mips_packrl_ph, "V2sV2sV2s", "nc")
+BUILTIN(__builtin_mips_repl_qb, "V4Sci", "nc")
+BUILTIN(__builtin_mips_repl_ph, "V2si", "nc")
+BUILTIN(__builtin_mips_pick_qb, "V4ScV4ScV4Sc", "n")
+BUILTIN(__builtin_mips_pick_ph, "V2sV2sV2s", "n")
+BUILTIN(__builtin_mips_mthlip, "LLiLLii", "n")
+BUILTIN(__builtin_mips_bposge32, "i", "n")
+BUILTIN(__builtin_mips_lbux, "iv*i", "n")
+BUILTIN(__builtin_mips_lhx, "iv*i", "n")
+BUILTIN(__builtin_mips_lwx, "iv*i", "n")
+
+#undef BUILTIN
diff --git a/include/clang/Basic/BuiltinsPTX.def b/include/clang/Basic/BuiltinsNVPTX.def
index f90a43f..f90a43f 100644
--- a/include/clang/Basic/BuiltinsPTX.def
+++ b/include/clang/Basic/BuiltinsNVPTX.def
diff --git a/include/clang/Basic/BuiltinsX86.def b/include/clang/Basic/BuiltinsX86.def
index 4aea980..75e6074 100644
--- a/include/clang/Basic/BuiltinsX86.def
+++ b/include/clang/Basic/BuiltinsX86.def
@@ -303,8 +303,6 @@ BUILTIN(__builtin_ia32_lddqu, "V16ccC*", "")
BUILTIN(__builtin_ia32_palignr128, "V16cV16cV16cIc", "")
BUILTIN(__builtin_ia32_insertps128, "V4fV4fV4fi", "")
-BUILTIN(__builtin_ia32_storelv4si, "vV2i*V2LLi", "")
-
BUILTIN(__builtin_ia32_pblendvb128, "V16cV16cV16cV16c", "")
BUILTIN(__builtin_ia32_pblendw128, "V8sV8sV8sIi", "")
BUILTIN(__builtin_ia32_blendpd, "V2dV2dV2dIi", "")
@@ -354,23 +352,30 @@ BUILTIN(__builtin_ia32_pcmpistri128, "iV16cV16cIc", "")
BUILTIN(__builtin_ia32_pcmpestrm128, "V16cV16ciV16ciIc", "")
BUILTIN(__builtin_ia32_pcmpestri128, "iV16ciV16ciIc","")
-// FIXME: These builtins are horribly broken; reenable when PR11305 is fixed.
-//BUILTIN(__builtin_ia32_pcmpistria128, "iV16cV16cIc","")
-//BUILTIN(__builtin_ia32_pcmpistric128, "iV16cV16cIc","")
-//BUILTIN(__builtin_ia32_pcmpistrio128, "iV16cV16cIc","")
-//BUILTIN(__builtin_ia32_pcmpistris128, "iV16cV16cIc","")
-//BUILTIN(__builtin_ia32_pcmpistriz128, "iV16cV16cIc","")
-//BUILTIN(__builtin_ia32_pcmpestria128, "iV16ciV16ciIc","")
-//BUILTIN(__builtin_ia32_pcmpestric128, "iV16ciV16ciIc","")
-//BUILTIN(__builtin_ia32_pcmpestrio128, "iV16ciV16ciic","")
-//BUILTIN(__builtin_ia32_pcmpestris128, "iV16ciV16ciIc","")
-//BUILTIN(__builtin_ia32_pcmpestriz128, "iV16ciV16ciIc","")
+BUILTIN(__builtin_ia32_pcmpistria128, "iV16cV16cIc","")
+BUILTIN(__builtin_ia32_pcmpistric128, "iV16cV16cIc","")
+BUILTIN(__builtin_ia32_pcmpistrio128, "iV16cV16cIc","")
+BUILTIN(__builtin_ia32_pcmpistris128, "iV16cV16cIc","")
+BUILTIN(__builtin_ia32_pcmpistriz128, "iV16cV16cIc","")
+BUILTIN(__builtin_ia32_pcmpestria128, "iV16ciV16ciIc","")
+BUILTIN(__builtin_ia32_pcmpestric128, "iV16ciV16ciIc","")
+BUILTIN(__builtin_ia32_pcmpestrio128, "iV16ciV16ciIc","")
+BUILTIN(__builtin_ia32_pcmpestris128, "iV16ciV16ciIc","")
+BUILTIN(__builtin_ia32_pcmpestriz128, "iV16ciV16ciIc","")
BUILTIN(__builtin_ia32_crc32qi, "UiUiUc", "")
BUILTIN(__builtin_ia32_crc32hi, "UiUiUs", "")
BUILTIN(__builtin_ia32_crc32si, "UiUiUi", "")
BUILTIN(__builtin_ia32_crc32di, "ULLiULLiULLi", "")
+// SSE4a
+BUILTIN(__builtin_ia32_extrqi, "V2LLiV2LLiIcIc", "")
+BUILTIN(__builtin_ia32_extrq, "V2LLiV2LLiV16c", "")
+BUILTIN(__builtin_ia32_insertqi, "V2LLiV2LLiV2LLiIcIc", "")
+BUILTIN(__builtin_ia32_insertq, "V2LLiV2LLiV2LLi", "")
+BUILTIN(__builtin_ia32_movntsd, "vd*V2d", "")
+BUILTIN(__builtin_ia32_movntss, "vf*V4f", "")
+
// AES
BUILTIN(__builtin_ia32_aesenc128, "V2LLiV2LLiV2LLi", "")
BUILTIN(__builtin_ia32_aesenclast128, "V2LLiV2LLiV2LLi", "")
@@ -379,6 +384,9 @@ BUILTIN(__builtin_ia32_aesdeclast128, "V2LLiV2LLiV2LLi", "")
BUILTIN(__builtin_ia32_aesimc128, "V2LLiV2LLi", "")
BUILTIN(__builtin_ia32_aeskeygenassist128, "V2LLiV2LLiIc", "")
+// CLMUL
+BUILTIN(__builtin_ia32_pclmulqdq128, "V2LLiV2LLiV2LLiIc", "")
+
// AVX
BUILTIN(__builtin_ia32_addsubpd256, "V4dV4dV4d", "")
BUILTIN(__builtin_ia32_addsubps256, "V8fV8fV8f", "")
@@ -586,6 +594,30 @@ BUILTIN(__builtin_ia32_psrlv4si, "V4iV4iV4i", "")
BUILTIN(__builtin_ia32_psrlv4di, "V4LLiV4LLiV4LLi", "")
BUILTIN(__builtin_ia32_psrlv2di, "V2LLiV2LLiV2LLi", "")
+// GATHER
+BUILTIN(__builtin_ia32_gatherd_pd, "V2dV2dV2dC*V4iV2dIc", "")
+BUILTIN(__builtin_ia32_gatherd_pd256, "V4dV4dV4dC*V4iV4dIc", "")
+BUILTIN(__builtin_ia32_gatherq_pd, "V2dV2dV2dC*V2LLiV2dIc", "")
+BUILTIN(__builtin_ia32_gatherq_pd256, "V4dV4dV4dC*V4LLiV4dIc", "")
+BUILTIN(__builtin_ia32_gatherd_ps, "V4fV4fV4fC*V4iV4fIc", "")
+BUILTIN(__builtin_ia32_gatherd_ps256, "V8fV8fV8fC*V8iV8fIc", "")
+BUILTIN(__builtin_ia32_gatherq_ps, "V4fV4fV4fC*V2LLiV4fIc", "")
+BUILTIN(__builtin_ia32_gatherq_ps256, "V4fV4fV4fC*V4LLiV4fIc", "")
+
+BUILTIN(__builtin_ia32_gatherd_q, "V2LLiV2LLiV2LLiC*V4iV2LLiIc", "")
+BUILTIN(__builtin_ia32_gatherd_q256, "V4LLiV4LLiV4LLiC*V4iV4LLiIc", "")
+BUILTIN(__builtin_ia32_gatherq_q, "V2LLiV2LLiV2LLiC*V2LLiV2LLiIc", "")
+BUILTIN(__builtin_ia32_gatherq_q256, "V4LLiV4LLiV4LLiC*V4LLiV4LLiIc", "")
+BUILTIN(__builtin_ia32_gatherd_d, "V4iV4iV4iC*V4iV4iIc", "")
+BUILTIN(__builtin_ia32_gatherd_d256, "V8iV8iV8iC*V8iV8iIc", "")
+BUILTIN(__builtin_ia32_gatherq_d, "V4iV4iV4iC*V2LLiV4iIc", "")
+BUILTIN(__builtin_ia32_gatherq_d256, "V4iV4iV4iC*V4LLiV4iIc", "")
+
+// RDRAND
+BUILTIN(__builtin_ia32_rdrand16_step, "UiUs*", "")
+BUILTIN(__builtin_ia32_rdrand32_step, "UiUi*", "")
+BUILTIN(__builtin_ia32_rdrand64_step, "UiULLi*", "")
+
// BMI
BUILTIN(__builtin_ia32_bextr_u32, "UiUiUi", "")
BUILTIN(__builtin_ia32_bextr_u64, "ULLiULLiULLi", "")
@@ -632,4 +664,71 @@ BUILTIN(__builtin_ia32_vfmaddsubpd256, "V4dV4dV4dV4d", "")
BUILTIN(__builtin_ia32_vfmsubaddps256, "V8fV8fV8fV8f", "")
BUILTIN(__builtin_ia32_vfmsubaddpd256, "V4dV4dV4dV4d", "")
+// XOP
+BUILTIN(__builtin_ia32_vpmacssww, "V8sV8sV8sV8s", "")
+BUILTIN(__builtin_ia32_vpmacsww, "V8sV8sV8sV8s", "")
+BUILTIN(__builtin_ia32_vpmacsswd, "V4iV8sV8sV4i", "")
+BUILTIN(__builtin_ia32_vpmacswd, "V4iV8sV8sV4i", "")
+BUILTIN(__builtin_ia32_vpmacssdd, "V4iV4iV4iV4i", "")
+BUILTIN(__builtin_ia32_vpmacsdd, "V4iV4iV4iV4i", "")
+BUILTIN(__builtin_ia32_vpmacssdql, "V2LLiV4iV4iV2LLi", "")
+BUILTIN(__builtin_ia32_vpmacsdql, "V2LLiV4iV4iV2LLi", "")
+BUILTIN(__builtin_ia32_vpmacssdqh, "V2LLiV4iV4iV2LLi", "")
+BUILTIN(__builtin_ia32_vpmacsdqh, "V2LLiV4iV4iV2LLi", "")
+BUILTIN(__builtin_ia32_vpmadcsswd, "V4iV8sV8sV4i", "")
+BUILTIN(__builtin_ia32_vpmadcswd, "V4iV8sV8sV4i", "")
+
+BUILTIN(__builtin_ia32_vphaddbw, "V8sV16c", "")
+BUILTIN(__builtin_ia32_vphaddbd, "V4iV16c", "")
+BUILTIN(__builtin_ia32_vphaddbq, "V2LLiV16c", "")
+BUILTIN(__builtin_ia32_vphaddwd, "V4iV8s", "")
+BUILTIN(__builtin_ia32_vphaddwq, "V2LLiV8s", "")
+BUILTIN(__builtin_ia32_vphadddq, "V2LLiV4i", "")
+BUILTIN(__builtin_ia32_vphaddubw, "V8sV16c", "")
+BUILTIN(__builtin_ia32_vphaddubd, "V4iV16c", "")
+BUILTIN(__builtin_ia32_vphaddubq, "V2LLiV16c", "")
+BUILTIN(__builtin_ia32_vphadduwd, "V4iV8s", "")
+BUILTIN(__builtin_ia32_vphadduwq, "V2LLiV8s", "")
+BUILTIN(__builtin_ia32_vphaddudq, "V2LLiV4i", "")
+BUILTIN(__builtin_ia32_vphsubbw, "V8sV16c", "")
+BUILTIN(__builtin_ia32_vphsubwd, "V4iV8s", "")
+BUILTIN(__builtin_ia32_vphsubdq, "V2LLiV4i", "")
+BUILTIN(__builtin_ia32_vpcmov, "V2LLiV2LLiV2LLiV2LLi", "")
+BUILTIN(__builtin_ia32_vpcmov_256, "V4LLiV4LLiV4LLiV4LLi", "")
+BUILTIN(__builtin_ia32_vpperm, "V16cV16cV16cV16c", "")
+BUILTIN(__builtin_ia32_vprotb, "V16cV16cV16c", "")
+BUILTIN(__builtin_ia32_vprotw, "V8sV8sV8s", "")
+BUILTIN(__builtin_ia32_vprotd, "V4iV4iV4i", "")
+BUILTIN(__builtin_ia32_vprotq, "V2LLiV2LLiV2LLi", "")
+BUILTIN(__builtin_ia32_vprotbi, "V16cV16cIc", "")
+BUILTIN(__builtin_ia32_vprotwi, "V8sV8sIc", "")
+BUILTIN(__builtin_ia32_vprotdi, "V4iV4iIc", "")
+BUILTIN(__builtin_ia32_vprotqi, "V2LLiV2LLiIc", "")
+BUILTIN(__builtin_ia32_vpshlb, "V16cV16cV16c", "")
+BUILTIN(__builtin_ia32_vpshlw, "V8sV8sV8s", "")
+BUILTIN(__builtin_ia32_vpshld, "V4iV4iV4i", "")
+BUILTIN(__builtin_ia32_vpshlq, "V2LLiV2LLiV2LLi", "")
+BUILTIN(__builtin_ia32_vpshab, "V16cV16cV16c", "")
+BUILTIN(__builtin_ia32_vpshaw, "V8sV8sV8s", "")
+BUILTIN(__builtin_ia32_vpshad, "V4iV4iV4i", "")
+BUILTIN(__builtin_ia32_vpshaq, "V2LLiV2LLiV2LLi", "")
+BUILTIN(__builtin_ia32_vpcomub, "V16cV16cV16cIc", "")
+BUILTIN(__builtin_ia32_vpcomuw, "V8sV8sV8sIc", "")
+BUILTIN(__builtin_ia32_vpcomud, "V4iV4iV4iIc", "")
+BUILTIN(__builtin_ia32_vpcomuq, "V2LLiV2LLiV2LLiIc", "")
+BUILTIN(__builtin_ia32_vpcomb, "V16cV16cV16cIc", "")
+BUILTIN(__builtin_ia32_vpcomw, "V8sV8sV8sIc", "")
+BUILTIN(__builtin_ia32_vpcomd, "V4iV4iV4iIc", "")
+BUILTIN(__builtin_ia32_vpcomq, "V2LLiV2LLiV2LLiIc", "")
+BUILTIN(__builtin_ia32_vpermil2pd, "V2dV2dV2dV2LLiIc", "")
+BUILTIN(__builtin_ia32_vpermil2pd256, "V4dV4dV4dV4LLiIc", "")
+BUILTIN(__builtin_ia32_vpermil2ps, "V4fV4fV4fV4iIc", "")
+BUILTIN(__builtin_ia32_vpermil2ps256, "V8fV8fV8fV8iIc", "")
+BUILTIN(__builtin_ia32_vfrczss, "V4fV4f", "")
+BUILTIN(__builtin_ia32_vfrczsd, "V2dV2d", "")
+BUILTIN(__builtin_ia32_vfrczps, "V4fV4f", "")
+BUILTIN(__builtin_ia32_vfrczpd, "V2dV2d", "")
+BUILTIN(__builtin_ia32_vfrczps256, "V8fV8f", "")
+BUILTIN(__builtin_ia32_vfrczpd256, "V4dV4d", "")
+
#undef BUILTIN
diff --git a/include/clang/Basic/CMakeLists.txt b/include/clang/Basic/CMakeLists.txt
index 3df88c7..274b94d 100644
--- a/include/clang/Basic/CMakeLists.txt
+++ b/include/clang/Basic/CMakeLists.txt
@@ -7,6 +7,7 @@ endmacro(clang_diag_gen)
clang_diag_gen(Analysis)
clang_diag_gen(AST)
+clang_diag_gen(Comment)
clang_diag_gen(Common)
clang_diag_gen(Driver)
clang_diag_gen(Frontend)
diff --git a/include/clang/Basic/CommentNodes.td b/include/clang/Basic/CommentNodes.td
new file mode 100644
index 0000000..7bf32b7
--- /dev/null
+++ b/include/clang/Basic/CommentNodes.td
@@ -0,0 +1,27 @@
+class Comment<bit abstract = 0> {
+ bit Abstract = abstract;
+}
+
+class DComment<Comment base, bit abstract = 0> : Comment<abstract> {
+ Comment Base = base;
+}
+
+def InlineContentComment : Comment<1>;
+ def TextComment : DComment<InlineContentComment>;
+ def InlineCommandComment : DComment<InlineContentComment>;
+ def HTMLTagComment : DComment<InlineContentComment, 1>;
+ def HTMLStartTagComment : DComment<HTMLTagComment>;
+ def HTMLEndTagComment : DComment<HTMLTagComment>;
+
+def BlockContentComment : Comment<1>;
+ def ParagraphComment : DComment<BlockContentComment>;
+ def BlockCommandComment : DComment<BlockContentComment>;
+ def ParamCommandComment : DComment<BlockCommandComment>;
+ def TParamCommandComment : DComment<BlockCommandComment>;
+ def VerbatimBlockComment : DComment<BlockCommandComment>;
+ def VerbatimLineComment : DComment<BlockCommandComment>;
+
+def VerbatimBlockLineComment : Comment;
+
+def FullComment : Comment;
+
diff --git a/include/clang/Basic/ConvertUTF.h b/include/clang/Basic/ConvertUTF.h
index 7fb5874..e7cfa8a 100644
--- a/include/clang/Basic/ConvertUTF.h
+++ b/include/clang/Basic/ConvertUTF.h
@@ -110,6 +110,8 @@ typedef unsigned char Boolean; /* 0 or 1 */
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF8_BYTES_PER_CODE_POINT 4
+
typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
@@ -139,11 +141,13 @@ ConversionResult ConvertUTF8toUTF32 (
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+#endif
ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+#ifdef CLANG_NEEDS_THESE_ONE_DAY
ConversionResult ConvertUTF16toUTF32 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
@@ -159,6 +163,37 @@ Boolean isLegalUTF8String(const UTF8 *source, const UTF8 *sourceEnd);
#ifdef __cplusplus
}
+
+/*************************************************************************/
+/* Below are LLVM-specific wrappers of the functions above. */
+
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+
+/**
+ * Convert an UTF8 StringRef to UTF8, UTF16, or UTF32 depending on
+ * WideCharWidth. The converted data is written to ResultPtr, which needs to
+ * point to at least WideCharWidth * (Source.Size() + 1) bytes. On success,
+ * ResultPtr will point one after the end of the copied string.
+ * \return true on success.
+ */
+bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source,
+ char *&ResultPtr);
+
+/**
+ * Convert an Unicode code point to UTF8 sequence.
+ *
+ * \param Source a Unicode code point.
+ * \param [in,out] ResultPtr pointer to the output buffer, needs to be at least
+ * \c UNI_MAX_UTF8_BYTES_PER_CODE_POINT bytes. On success \c ResultPtr is
+ * updated one past end of the converted sequence.
+ *
+ * \returns true on success.
+ */
+bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr);
+
+}
#endif
#endif
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index e157178..3997fb8 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the Diagnostic-related interfaces.
-//
+///
+/// \file
+/// \brief Defines the Diagnostic-related interfaces.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_DIAGNOSTIC_H
@@ -82,7 +83,7 @@ public:
}
/// \brief Create a code modification hint that inserts the given
- /// code from \arg FromRange at a specific location.
+ /// code from \p FromRange at a specific location.
static FixItHint CreateInsertionFromRange(SourceLocation InsertionLoc,
CharSourceRange FromRange,
bool BeforePreviousInsertions = false) {
@@ -121,14 +122,15 @@ public:
}
};
-/// DiagnosticsEngine - This concrete class is used by the front-end to report
-/// problems and issues. It massages the diagnostics (e.g. handling things like
-/// "report warnings as errors" and passes them off to the DiagnosticConsumer
-/// for reporting to the user. DiagnosticsEngine is tied to one translation unit
-/// and one SourceManager.
+/// \brief Concrete class used by the front-end to report problems and issues.
+///
+/// This massages the diagnostics (e.g. handling things like "report warnings
+/// as errors" and passes them off to the DiagnosticConsumer for reporting to
+/// the user. DiagnosticsEngine is tied to one translation unit and one
+/// SourceManager.
class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
public:
- /// Level - The level of the diagnostic, after it has been through mapping.
+ /// \brief The level of the diagnostic, after it has been through mapping.
enum Level {
Ignored = DiagnosticIDs::Ignored,
Note = DiagnosticIDs::Note,
@@ -137,34 +139,36 @@ public:
Fatal = DiagnosticIDs::Fatal
};
- /// ExtensionHandling - How do we handle otherwise-unmapped extension? This
- /// is controlled by -pedantic and -pedantic-errors.
+ /// \brief How do we handle otherwise-unmapped extension?
+ ///
+ /// This is controlled by -pedantic and -pedantic-errors.
enum ExtensionHandling {
Ext_Ignore, Ext_Warn, Ext_Error
};
enum ArgumentKind {
- ak_std_string, // std::string
- ak_c_string, // const char *
- ak_sint, // int
- ak_uint, // unsigned
- ak_identifierinfo, // IdentifierInfo
- ak_qualtype, // QualType
- ak_declarationname, // DeclarationName
- ak_nameddecl, // NamedDecl *
- ak_nestednamespec, // NestedNameSpecifier *
- ak_declcontext // DeclContext *
+ ak_std_string, ///< std::string
+ ak_c_string, ///< const char *
+ ak_sint, ///< int
+ ak_uint, ///< unsigned
+ ak_identifierinfo, ///< IdentifierInfo
+ ak_qualtype, ///< QualType
+ ak_declarationname, ///< DeclarationName
+ ak_nameddecl, ///< NamedDecl *
+ ak_nestednamespec, ///< NestedNameSpecifier *
+ ak_declcontext, ///< DeclContext *
+ ak_qualtype_pair ///< pair<QualType, QualType>
};
- /// Specifies which overload candidates to display when overload resolution
- /// fails.
+ /// \brief Specifies which overload candidates to display when overload
+ /// resolution fails.
enum OverloadsShown {
Ovl_All, ///< Show all overloads.
Ovl_Best ///< Show just the "best" overload candidates.
};
- /// ArgumentValue - This typedef represents on argument value, which is a
- /// union discriminated by ArgumentKind, with a value.
+ /// \brief Represents on argument value, which is a union discriminated
+ /// by ArgumentKind, with a value.
typedef std::pair<ArgumentKind, intptr_t> ArgumentValue;
private:
@@ -175,6 +179,9 @@ private:
bool ErrorsAsFatal; // Treat errors like fatal errors.
bool SuppressSystemWarnings; // Suppress warnings in system headers.
bool SuppressAllDiagnostics; // Suppress all diagnostics.
+ bool ElideType; // Elide common types of templates.
+ bool PrintTemplateTree; // Print a tree when comparing templates.
+ bool ShowColors; // Color printing is enabled.
OverloadsShown ShowOverloads; // Which overload candidates to show.
unsigned ErrorLimit; // Cap of # errors emitted, 0 -> no limit.
unsigned TemplateBacktraceLimit; // Cap on depth of template backtrace stack,
@@ -187,12 +194,13 @@ private:
bool OwnsDiagClient;
SourceManager *SourceMgr;
- /// \brief Mapping information for diagnostics. Mapping info is
- /// packed into four bits per diagnostic. The low three bits are the mapping
- /// (an instance of diag::Mapping), or zero if unset. The high bit is set
- /// when the mapping was established as a user mapping. If the high bit is
- /// clear, then the low bits are set to the default value, and should be
- /// mapped with -pedantic, -Werror, etc.
+ /// \brief Mapping information for diagnostics.
+ ///
+ /// Mapping info is packed into four bits per diagnostic. The low three
+ /// bits are the mapping (an instance of diag::Mapping), or zero if unset.
+ /// The high bit is set when the mapping was established as a user mapping.
+ /// If the high bit is clear, then the low bits are set to the default
+ /// value, and should be mapped with -pedantic, -Werror, etc.
///
/// A new DiagState is created and kept around when diagnostic pragmas modify
/// the state so that we know what is the diagnostic state at any given
@@ -220,8 +228,10 @@ private:
std::list<DiagState> DiagStates;
/// \brief Represents a point in source where the diagnostic state was
- /// modified because of a pragma. 'Loc' can be null if the point represents
- /// the diagnostic state modifications done through the command-line.
+ /// modified because of a pragma.
+ ///
+ /// 'Loc' can be null if the point represents the diagnostic state
+ /// modifications done through the command-line.
struct DiagStatePoint {
DiagState *State;
FullSourceLoc Loc;
@@ -239,9 +249,11 @@ private:
}
};
- /// \brief A vector of all DiagStatePoints representing changes in diagnostic
- /// state due to diagnostic pragmas. The vector is always sorted according to
- /// the SourceLocation of the DiagStatePoint.
+ /// \brief A sorted vector of all DiagStatePoints representing changes in
+ /// diagnostic state due to diagnostic pragmas.
+ ///
+ /// The vector is always sorted according to the SourceLocation of the
+ /// DiagStatePoint.
typedef std::vector<DiagStatePoint> DiagStatePointsTy;
mutable DiagStatePointsTy DiagStatePoints;
@@ -255,25 +267,24 @@ private:
}
void PushDiagStatePoint(DiagState *State, SourceLocation L) {
- FullSourceLoc Loc(L, *SourceMgr);
+ FullSourceLoc Loc(L, getSourceManager());
// Make sure that DiagStatePoints is always sorted according to Loc.
- assert((Loc.isValid() || DiagStatePoints.empty()) &&
- "Adding invalid loc point after another point");
- assert((Loc.isInvalid() || DiagStatePoints.empty() ||
- DiagStatePoints.back().Loc.isInvalid() ||
+ assert(Loc.isValid() && "Adding invalid loc point");
+ assert(!DiagStatePoints.empty() &&
+ (DiagStatePoints.back().Loc.isInvalid() ||
DiagStatePoints.back().Loc.isBeforeInTranslationUnitThan(Loc)) &&
"Previous point loc comes after or is the same as new one");
- DiagStatePoints.push_back(DiagStatePoint(State,
- FullSourceLoc(Loc, *SourceMgr)));
+ DiagStatePoints.push_back(DiagStatePoint(State, Loc));
}
/// \brief Finds the DiagStatePoint that contains the diagnostic state of
/// the given source location.
DiagStatePointsTy::iterator GetDiagStatePointForLoc(SourceLocation Loc) const;
- /// ErrorOccurred / FatalErrorOccurred - This is set to true when an error or
- /// fatal error is emitted, and is sticky.
+ /// \brief Sticky flag set to \c true when an error is emitted.
bool ErrorOccurred;
+
+ /// \brief Sticky flag set to \c true when a fatal error is emitted.
bool FatalErrorOccurred;
/// \brief Indicates that an unrecoverable error has occurred.
@@ -284,18 +295,20 @@ private:
unsigned TrapNumErrorsOccurred;
unsigned TrapNumUnrecoverableErrorsOccurred;
- /// LastDiagLevel - This is the level of the last diagnostic emitted. This is
- /// used to emit continuation diagnostics with the same level as the
+ /// \brief The level of the last diagnostic emitted.
+ ///
+ /// This is used to emit continuation diagnostics with the same level as the
/// diagnostic that they follow.
DiagnosticIDs::Level LastDiagLevel;
- unsigned NumWarnings; // Number of warnings reported
- unsigned NumErrors; // Number of errors reported
- unsigned NumErrorsSuppressed; // Number of errors suppressed
+ unsigned NumWarnings; ///< Number of warnings reported
+ unsigned NumErrors; ///< Number of errors reported
+ unsigned NumErrorsSuppressed; ///< Number of errors suppressed
- /// ArgToStringFn - A function pointer that converts an opaque diagnostic
- /// argument to a strings. This takes the modifiers and argument that was
- /// present in the diagnostic.
+ /// \brief A function pointer that converts an opaque diagnostic
+ /// argument to a strings.
+ ///
+ /// This takes the modifiers and argument that was present in the diagnostic.
///
/// The PrevArgs array (whose length is NumPrevArgs) indicates the previous
/// arguments formatted for this diagnostic. Implementations of this function
@@ -361,14 +374,15 @@ public:
// how diagnostics are emitted.
//
- /// pushMappings - Copies the current DiagMappings and pushes the new copy
+ /// \brief Copies the current DiagMappings and pushes the new copy
/// onto the top of the stack.
void pushMappings(SourceLocation Loc);
- /// popMappings - Pops the current DiagMappings off the top of the stack
- /// causing the new top of the stack to be the active mappings. Returns
- /// true if the pop happens, false if there is only one DiagMapping on the
- /// stack.
+ /// \brief Pops the current DiagMappings off the top of the stack,
+ /// causing the new top of the stack to be the active mappings.
+ ///
+ /// \returns \c true if the pop happens, \c false if there is only one
+ /// DiagMapping on the stack.
bool popMappings(SourceLocation Loc);
/// \brief Set the diagnostic client associated with this diagnostic object.
@@ -377,8 +391,10 @@ public:
/// ownership of \c client.
void setClient(DiagnosticConsumer *client, bool ShouldOwnClient = true);
- /// setErrorLimit - Specify a limit for the number of errors we should
- /// emit before giving up. Zero disables the limit.
+ /// \brief Specify a limit for the number of errors we should
+ /// emit before giving up.
+ ///
+ /// Zero disables the limit.
void setErrorLimit(unsigned Limit) { ErrorLimit = Limit; }
/// \brief Specify the maximum number of template instantiation
@@ -405,29 +421,28 @@ public:
return ConstexprBacktraceLimit;
}
- /// setIgnoreAllWarnings - When set to true, any unmapped warnings are
- /// ignored. If this and WarningsAsErrors are both set, then this one wins.
+ /// \brief When set to true, any unmapped warnings are ignored.
+ ///
+ /// If this and WarningsAsErrors are both set, then this one wins.
void setIgnoreAllWarnings(bool Val) { IgnoreAllWarnings = Val; }
bool getIgnoreAllWarnings() const { return IgnoreAllWarnings; }
- /// setEnableAllWarnings - When set to true, any unmapped ignored warnings
- /// are no longer ignored. If this and IgnoreAllWarnings are both set,
- /// then that one wins.
+ /// \brief When set to true, any unmapped ignored warnings are no longer
+ /// ignored.
+ ///
+ /// If this and IgnoreAllWarnings are both set, then that one wins.
void setEnableAllWarnings(bool Val) { EnableAllWarnings = Val; }
bool getEnableAllWarnngs() const { return EnableAllWarnings; }
- /// setWarningsAsErrors - When set to true, any warnings reported are issued
- /// as errors.
+ /// \brief When set to true, any warnings reported are issued as errors.
void setWarningsAsErrors(bool Val) { WarningsAsErrors = Val; }
bool getWarningsAsErrors() const { return WarningsAsErrors; }
- /// setErrorsAsFatal - When set to true, any error reported is made a
- /// fatal error.
+ /// \brief When set to true, any error reported is made a fatal error.
void setErrorsAsFatal(bool Val) { ErrorsAsFatal = Val; }
bool getErrorsAsFatal() const { return ErrorsAsFatal; }
- /// setSuppressSystemWarnings - When set to true mask warnings that
- /// come from system headers.
+ /// \brief When set to true mask warnings that come from system headers.
void setSuppressSystemWarnings(bool Val) { SuppressSystemWarnings = Val; }
bool getSuppressSystemWarnings() const { return SuppressSystemWarnings; }
@@ -438,76 +453,105 @@ public:
SuppressAllDiagnostics = Val;
}
bool getSuppressAllDiagnostics() const { return SuppressAllDiagnostics; }
-
+
+ /// \brief Set type eliding, to skip outputting same types occurring in
+ /// template types.
+ void setElideType(bool Val = true) { ElideType = Val; }
+ bool getElideType() { return ElideType; }
+
+ /// \brief Set tree printing, to outputting the template difference in a
+ /// tree format.
+ void setPrintTemplateTree(bool Val = false) { PrintTemplateTree = Val; }
+ bool getPrintTemplateTree() { return PrintTemplateTree; }
+
+ /// \brief Set color printing, so the type diffing will inject color markers
+ /// into the output.
+ void setShowColors(bool Val = false) { ShowColors = Val; }
+ bool getShowColors() { return ShowColors; }
+
/// \brief Specify which overload candidates to show when overload resolution
- /// fails. By default, we show all candidates.
+ /// fails.
+ ///
+ /// By default, we show all candidates.
void setShowOverloads(OverloadsShown Val) {
ShowOverloads = Val;
}
OverloadsShown getShowOverloads() const { return ShowOverloads; }
- /// \brief Pretend that the last diagnostic issued was ignored. This can
- /// be used by clients who suppress diagnostics themselves.
+ /// \brief Pretend that the last diagnostic issued was ignored.
+ ///
+ /// This can be used by clients who suppress diagnostics themselves.
void setLastDiagnosticIgnored() {
LastDiagLevel = DiagnosticIDs::Ignored;
}
- /// setExtensionHandlingBehavior - This controls whether otherwise-unmapped
- /// extension diagnostics are mapped onto ignore/warning/error. This
- /// corresponds to the GCC -pedantic and -pedantic-errors option.
+ /// \brief Controls whether otherwise-unmapped extension diagnostics are
+ /// mapped onto ignore/warning/error.
+ ///
+ /// This corresponds to the GCC -pedantic and -pedantic-errors option.
void setExtensionHandlingBehavior(ExtensionHandling H) {
ExtBehavior = H;
}
ExtensionHandling getExtensionHandlingBehavior() const { return ExtBehavior; }
- /// AllExtensionsSilenced - This is a counter bumped when an __extension__
- /// block is encountered. When non-zero, all extension diagnostics are
- /// entirely silenced, no matter how they are mapped.
+ /// \brief Counter bumped when an __extension__ block is/ encountered.
+ ///
+ /// When non-zero, all extension diagnostics are entirely silenced, no
+ /// matter how they are mapped.
void IncrementAllExtensionsSilenced() { ++AllExtensionsSilenced; }
void DecrementAllExtensionsSilenced() { --AllExtensionsSilenced; }
bool hasAllExtensionsSilenced() { return AllExtensionsSilenced != 0; }
- /// \brief This allows the client to specify that certain
- /// warnings are ignored. Notes can never be mapped, errors can only be
- /// mapped to fatal, and WARNINGs and EXTENSIONs can be mapped arbitrarily.
+ /// \brief This allows the client to specify that certain warnings are
+ /// ignored.
+ ///
+ /// Notes can never be mapped, errors can only be mapped to fatal, and
+ /// WARNINGs and EXTENSIONs can be mapped arbitrarily.
///
/// \param Loc The source location that this change of diagnostic state should
/// take affect. It can be null if we are setting the latest state.
void setDiagnosticMapping(diag::kind Diag, diag::Mapping Map,
SourceLocation Loc);
- /// setDiagnosticGroupMapping - Change an entire diagnostic group (e.g.
- /// "unknown-pragmas" to have the specified mapping. This returns true and
- /// ignores the request if "Group" was unknown, false otherwise.
+ /// \brief Change an entire diagnostic group (e.g. "unknown-pragmas") to
+ /// have the specified mapping.
///
- /// 'Loc' is the source location that this change of diagnostic state should
+ /// \returns true (and ignores the request) if "Group" was unknown, false
+ /// otherwise.
+ ///
+ /// \param Loc The source location that this change of diagnostic state should
/// take affect. It can be null if we are setting the state from command-line.
bool setDiagnosticGroupMapping(StringRef Group, diag::Mapping Map,
SourceLocation Loc = SourceLocation());
- /// \brief Set the warning-as-error flag for the given diagnostic. This
- /// function always only operates on the current diagnostic state.
+ /// \brief Set the warning-as-error flag for the given diagnostic.
+ ///
+ /// This function always only operates on the current diagnostic state.
void setDiagnosticWarningAsError(diag::kind Diag, bool Enabled);
- /// \brief Set the warning-as-error flag for the given diagnostic group. This
- /// function always only operates on the current diagnostic state.
+ /// \brief Set the warning-as-error flag for the given diagnostic group.
+ ///
+ /// This function always only operates on the current diagnostic state.
///
/// \returns True if the given group is unknown, false otherwise.
bool setDiagnosticGroupWarningAsError(StringRef Group, bool Enabled);
- /// \brief Set the error-as-fatal flag for the given diagnostic. This function
- /// always only operates on the current diagnostic state.
+ /// \brief Set the error-as-fatal flag for the given diagnostic.
+ ///
+ /// This function always only operates on the current diagnostic state.
void setDiagnosticErrorAsFatal(diag::kind Diag, bool Enabled);
- /// \brief Set the error-as-fatal flag for the given diagnostic group. This
- /// function always only operates on the current diagnostic state.
+ /// \brief Set the error-as-fatal flag for the given diagnostic group.
+ ///
+ /// This function always only operates on the current diagnostic state.
///
/// \returns True if the given group is unknown, false otherwise.
bool setDiagnosticGroupErrorAsFatal(StringRef Group, bool Enabled);
- /// \brief Add the specified mapping to all diagnostics. Mainly to be used
- /// by -Wno-everything to disable all warnings but allow subsequent -W options
- /// to enable specific warnings.
+ /// \brief Add the specified mapping to all diagnostics.
+ ///
+ /// Mainly to be used by -Wno-everything to disable all warnings but allow
+ /// subsequent -W options to enable specific warnings.
void setMappingToAllDiagnostics(diag::Mapping Map,
SourceLocation Loc = SourceLocation());
@@ -525,15 +569,16 @@ public:
this->NumWarnings = NumWarnings;
}
- /// getCustomDiagID - Return an ID for a diagnostic with the specified message
- /// and level. If this is the first request for this diagnosic, it is
- /// registered and created, otherwise the existing ID is returned.
+ /// \brief Return an ID for a diagnostic with the specified message and level.
+ ///
+ /// If this is the first request for this diagnosic, it is registered and
+ /// created, otherwise the existing ID is returned.
unsigned getCustomDiagID(Level L, StringRef Message) {
return Diags->getCustomDiagID((DiagnosticIDs::Level)L, Message);
}
- /// ConvertArgToString - This method converts a diagnostic argument (as an
- /// intptr_t) into the string that represents it.
+ /// \brief Converts a diagnostic argument (as an intptr_t) into the string
+ /// that represents it.
void ConvertArgToString(ArgumentKind Kind, intptr_t Val,
const char *Modifier, unsigned ModLen,
const char *Argument, unsigned ArgLen,
@@ -568,12 +613,15 @@ public:
return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
}
- /// Report - Issue the message to the client. @c DiagID is a member of the
- /// @c diag::kind enum. This actually returns aninstance of DiagnosticBuilder
- /// which emits the diagnostics (through @c ProcessDiag) when it is destroyed.
- /// @c Pos represents the source location associated with the diagnostic,
+ /// \brief Issue the message to the client.
+ ///
+ /// This actually returns an instance of DiagnosticBuilder which emits the
+ /// diagnostics (through @c ProcessDiag) when it is destroyed.
+ ///
+ /// \param DiagID A member of the @c diag::kind enum.
+ /// \param Loc Represents the source location associated with the diagnostic,
/// which can be an invalid location if no position information is available.
- inline DiagnosticBuilder Report(SourceLocation Pos, unsigned DiagID);
+ inline DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID);
inline DiagnosticBuilder Report(unsigned DiagID);
void Report(const StoredDiagnostic &storedDiag);
@@ -624,55 +672,60 @@ private:
friend class PartialDiagnostic;
friend class DiagnosticErrorTrap;
- /// CurDiagLoc - This is the location of the current diagnostic that is in
- /// flight.
+ /// \brief The location of the current diagnostic that is in flight.
SourceLocation CurDiagLoc;
- /// CurDiagID - This is the ID of the current diagnostic that is in flight.
+ /// \brief The ID of the current diagnostic that is in flight.
+ ///
/// This is set to ~0U when there is no diagnostic in flight.
unsigned CurDiagID;
enum {
- /// MaxArguments - The maximum number of arguments we can hold. We currently
- /// only support up to 10 arguments (%0-%9). A single diagnostic with more
- /// than that almost certainly has to be simplified anyway.
+ /// \brief The maximum number of arguments we can hold.
+ ///
+ /// We currently only support up to 10 arguments (%0-%9). A single
+ /// diagnostic with more than that almost certainly has to be simplified
+ /// anyway.
MaxArguments = 10,
- /// MaxRanges - The maximum number of ranges we can hold.
+ /// \brief The maximum number of ranges we can hold.
MaxRanges = 10,
- /// MaxFixItHints - The maximum number of ranges we can hold.
+ /// \brief The maximum number of ranges we can hold.
MaxFixItHints = 10
};
- /// NumDiagArgs - This contains the number of entries in Arguments.
+ /// \brief The number of entries in Arguments.
signed char NumDiagArgs;
- /// NumDiagRanges - This is the number of ranges in the DiagRanges array.
+ /// \brief The number of ranges in the DiagRanges array.
unsigned char NumDiagRanges;
- /// NumDiagFixItHints - This is the number of hints in the DiagFixItHints
- /// array.
+ /// \brief The number of hints in the DiagFixItHints array.
unsigned char NumDiagFixItHints;
- /// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum
- /// values, with one for each argument. This specifies whether the argument
- /// is in DiagArgumentsStr or in DiagArguments.
+ /// \brief Specifies whether an argument is in DiagArgumentsStr or
+ /// in DiagArguments.
+ ///
+ /// This is an array of ArgumentKind::ArgumentKind enum values, one for each
+ /// argument.
unsigned char DiagArgumentsKind[MaxArguments];
- /// DiagArgumentsStr - This holds the values of each string argument for the
- /// current diagnostic. This value is only used when the corresponding
- /// ArgumentKind is ak_std_string.
+ /// \brief Holds the values of each string argument for the current
+ /// diagnostic.
+ ///
+ /// This is only used when the corresponding ArgumentKind is ak_std_string.
std::string DiagArgumentsStr[MaxArguments];
- /// DiagArgumentsVal - The values for the various substitution positions. This
- /// is used when the argument is not an std::string. The specific value is
- /// mangled into an intptr_t and the interpretation depends on exactly what
- /// sort of argument kind it is.
+ /// \brief The values for the various substitution positions.
+ ///
+ /// This is used when the argument is not an std::string. The specific
+ /// value is mangled into an intptr_t and the interpretation depends on
+ /// exactly what sort of argument kind it is.
intptr_t DiagArgumentsVal[MaxArguments];
- /// DiagRanges - The list of ranges added to this diagnostic.
+ /// \brief The list of ranges added to this diagnostic.
CharSourceRange DiagRanges[MaxRanges];
- /// FixItHints - If valid, provides a hint with some code to insert, remove,
+ /// \brief If valid, provides a hint with some code to insert, remove,
/// or modify at a particular position.
FixItHint DiagFixItHints[MaxFixItHints];
@@ -691,11 +744,9 @@ private:
return MappingInfo;
}
- /// ProcessDiag - This is the method used to report a diagnostic that is
- /// finally fully formed.
+ /// \brief Used to report a diagnostic that is finally fully formed.
///
- /// \returns true if the diagnostic was emitted, false if it was
- /// suppressed.
+ /// \returns true if the diagnostic was emitted, false if it was suppressed.
bool ProcessDiag() {
return Diags->ProcessDiag(*this);
}
@@ -710,7 +761,9 @@ protected:
friend class Sema;
/// \brief Emit the current diagnostic and clear the diagnostic state.
- bool EmitCurrentDiagnostic();
+ ///
+ /// \param Force Emit the diagnostic regardless of suppression settings.
+ bool EmitCurrentDiagnostic(bool Force = false);
unsigned getCurrentDiagID() const { return CurDiagID; }
@@ -746,7 +799,7 @@ public:
return Diag.TrapNumUnrecoverableErrorsOccurred > NumUnrecoverableErrors;
}
- // Set to initial state of "no errors occurred".
+ /// \brief Set to initial state of "no errors occurred".
void reset() {
NumErrors = Diag.TrapNumErrorsOccurred;
NumUnrecoverableErrors = Diag.TrapNumUnrecoverableErrorsOccurred;
@@ -757,11 +810,12 @@ public:
// DiagnosticBuilder
//===----------------------------------------------------------------------===//
-/// DiagnosticBuilder - This is a little helper class used to produce
-/// diagnostics. This is constructed by the DiagnosticsEngine::Report method,
-/// and allows insertion of extra information (arguments and source ranges) into
-/// the currently "in flight" diagnostic. When the temporary for the builder is
-/// destroyed, the diagnostic is issued.
+/// \brief A little helper class used to produce diagnostics.
+///
+/// This is constructed by the DiagnosticsEngine::Report method, and
+/// allows insertion of extra information (arguments and source ranges) into
+/// the currently "in flight" diagnostic. When the temporary for the builder
+/// is destroyed, the diagnostic is issued.
///
/// Note that many of these will be created as temporary objects (many call
/// sites), so we want them to be small and we never want their address taken.
@@ -779,15 +833,25 @@ class DiagnosticBuilder {
// Emit() would end up with if we used that as our status variable.
mutable bool IsActive;
+ /// \brief Flag indicating that this diagnostic is being emitted via a
+ /// call to ForceEmit.
+ mutable bool IsForceEmit;
+
void operator=(const DiagnosticBuilder&); // DO NOT IMPLEMENT
friend class DiagnosticsEngine;
+
+ DiagnosticBuilder()
+ : DiagObj(0), NumArgs(0), NumRanges(0), NumFixits(0), IsActive(false),
+ IsForceEmit(false) { }
+
explicit DiagnosticBuilder(DiagnosticsEngine *diagObj)
- : DiagObj(diagObj), NumArgs(0), NumRanges(0), NumFixits(0), IsActive(true) {
+ : DiagObj(diagObj), NumArgs(0), NumRanges(0), NumFixits(0), IsActive(true),
+ IsForceEmit(false) {
assert(diagObj && "DiagnosticBuilder requires a valid DiagnosticsEngine!");
}
friend class PartialDiagnostic;
-
+
protected:
void FlushCounts() {
DiagObj->NumDiagArgs = NumArgs;
@@ -799,9 +863,10 @@ protected:
void Clear() const {
DiagObj = 0;
IsActive = false;
+ IsForceEmit = false;
}
- /// isActive - Determine whether this diagnostic is still active.
+ /// \brief Determine whether this diagnostic is still active.
bool isActive() const { return IsActive; }
/// \brief Force the diagnostic builder to emit the diagnostic now.
@@ -821,7 +886,7 @@ protected:
FlushCounts();
// Process the diagnostic.
- bool Result = DiagObj->EmitCurrentDiagnostic();
+ bool Result = DiagObj->EmitCurrentDiagnostic(IsForceEmit);
// This diagnostic is dead.
Clear();
@@ -835,20 +900,36 @@ public:
DiagnosticBuilder(const DiagnosticBuilder &D) {
DiagObj = D.DiagObj;
IsActive = D.IsActive;
+ IsForceEmit = D.IsForceEmit;
D.Clear();
NumArgs = D.NumArgs;
NumRanges = D.NumRanges;
NumFixits = D.NumFixits;
}
- /// Destructor - The dtor emits the diagnostic.
+ /// \brief Retrieve an empty diagnostic builder.
+ static DiagnosticBuilder getEmpty() {
+ return DiagnosticBuilder();
+ }
+
+ /// \brief Emits the diagnostic.
~DiagnosticBuilder() {
Emit();
}
- /// Operator bool: conversion of DiagnosticBuilder to bool always returns
- /// true. This allows is to be used in boolean error contexts like:
+ /// \brief Forces the diagnostic to be emitted.
+ const DiagnosticBuilder &setForceEmit() const {
+ IsForceEmit = true;
+ return *this;
+ }
+
+ /// \brief Conversion of DiagnosticBuilder to bool always returns \c true.
+ ///
+ /// This allows is to be used in boolean error contexts (where \c true is
+ /// used to indicate that an error has occurred), like:
+ /// \code
/// return Diag(...);
+ /// \endcode
operator bool() const { return true; }
void AddString(StringRef S) const {
@@ -951,9 +1032,6 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
return DB;
}
-/// Report - Issue the message to the client. DiagID is a member of the
-/// diag::kind enum. This actually returns a new instance of DiagnosticBuilder
-/// which emits the diagnostics (through ProcessDiag) when it is destroyed.
inline DiagnosticBuilder DiagnosticsEngine::Report(SourceLocation Loc,
unsigned DiagID){
assert(CurDiagID == ~0U && "Multiple diagnostics in flight at once!");
@@ -969,9 +1047,9 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
// Diagnostic
//===----------------------------------------------------------------------===//
-/// Diagnostic - This is a little helper class (which is basically a smart
-/// pointer that forward info from DiagnosticsEngine) that allows clients to
-/// enquire about the currently in-flight diagnostic.
+/// A little helper class (which is basically a smart pointer that forwards
+/// info from DiagnosticsEngine) that allows clients to enquire about the
+/// currently in-flight diagnostic.
class Diagnostic {
const DiagnosticsEngine *DiagObj;
StringRef StoredDiagMessage;
@@ -988,62 +1066,71 @@ public:
unsigned getNumArgs() const { return DiagObj->NumDiagArgs; }
- /// getArgKind - Return the kind of the specified index. Based on the kind
- /// of argument, the accessors below can be used to get the value.
+ /// \brief Return the kind of the specified index.
+ ///
+ /// Based on the kind of argument, the accessors below can be used to get
+ /// the value.
+ ///
+ /// \pre Idx < getNumArgs()
DiagnosticsEngine::ArgumentKind getArgKind(unsigned Idx) const {
assert(Idx < getNumArgs() && "Argument index out of range!");
return (DiagnosticsEngine::ArgumentKind)DiagObj->DiagArgumentsKind[Idx];
}
- /// getArgStdStr - Return the provided argument string specified by Idx.
+ /// \brief Return the provided argument string specified by \p Idx.
+ /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_std_string
const std::string &getArgStdStr(unsigned Idx) const {
assert(getArgKind(Idx) == DiagnosticsEngine::ak_std_string &&
"invalid argument accessor!");
return DiagObj->DiagArgumentsStr[Idx];
}
- /// getArgCStr - Return the specified C string argument.
+ /// \brief Return the specified C string argument.
+ /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_c_string
const char *getArgCStr(unsigned Idx) const {
assert(getArgKind(Idx) == DiagnosticsEngine::ak_c_string &&
"invalid argument accessor!");
return reinterpret_cast<const char*>(DiagObj->DiagArgumentsVal[Idx]);
}
- /// getArgSInt - Return the specified signed integer argument.
+ /// \brief Return the specified signed integer argument.
+ /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_sint
int getArgSInt(unsigned Idx) const {
assert(getArgKind(Idx) == DiagnosticsEngine::ak_sint &&
"invalid argument accessor!");
return (int)DiagObj->DiagArgumentsVal[Idx];
}
- /// getArgUInt - Return the specified unsigned integer argument.
+ /// \brief Return the specified unsigned integer argument.
+ /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_uint
unsigned getArgUInt(unsigned Idx) const {
assert(getArgKind(Idx) == DiagnosticsEngine::ak_uint &&
"invalid argument accessor!");
return (unsigned)DiagObj->DiagArgumentsVal[Idx];
}
- /// getArgIdentifier - Return the specified IdentifierInfo argument.
+ /// \brief Return the specified IdentifierInfo argument.
+ /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_identifierinfo
const IdentifierInfo *getArgIdentifier(unsigned Idx) const {
assert(getArgKind(Idx) == DiagnosticsEngine::ak_identifierinfo &&
"invalid argument accessor!");
return reinterpret_cast<IdentifierInfo*>(DiagObj->DiagArgumentsVal[Idx]);
}
- /// getRawArg - Return the specified non-string argument in an opaque form.
+ /// \brief Return the specified non-string argument in an opaque form.
+ /// \pre getArgKind(Idx) != DiagnosticsEngine::ak_std_string
intptr_t getRawArg(unsigned Idx) const {
assert(getArgKind(Idx) != DiagnosticsEngine::ak_std_string &&
"invalid argument accessor!");
return DiagObj->DiagArgumentsVal[Idx];
}
-
- /// getNumRanges - Return the number of source ranges associated with this
- /// diagnostic.
+ /// \brief Return the number of source ranges associated with this diagnostic.
unsigned getNumRanges() const {
return DiagObj->NumDiagRanges;
}
+ /// \pre Idx < getNumRanges()
const CharSourceRange &getRange(unsigned Idx) const {
assert(Idx < DiagObj->NumDiagRanges && "Invalid diagnostic range index!");
return DiagObj->DiagRanges[Idx];
@@ -1067,13 +1154,14 @@ public:
return getNumFixItHints()? DiagObj->DiagFixItHints : 0;
}
- /// FormatDiagnostic - Format this diagnostic into a string, substituting the
- /// formal arguments into the %0 slots. The result is appended onto the Str
- /// array.
+ /// \brief Format this diagnostic into a string, substituting the
+ /// formal arguments into the %0 slots.
+ ///
+ /// The result is appended onto the \p OutStr array.
void FormatDiagnostic(SmallVectorImpl<char> &OutStr) const;
- /// FormatDiagnostic - Format the given format-string into the
- /// output buffer using the arguments stored in this diagnostic.
+ /// \brief Format the given format-string into the output buffer using the
+ /// arguments stored in this diagnostic.
void FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
SmallVectorImpl<char> &OutStr) const;
};
@@ -1131,12 +1219,12 @@ public:
}
};
-/// DiagnosticConsumer - This is an abstract interface implemented by clients of
-/// the front-end, which formats and prints fully processed diagnostics.
+/// \brief Abstract interface, implemented by clients of the front-end, which
+/// formats and prints fully processed diagnostics.
class DiagnosticConsumer {
protected:
- unsigned NumWarnings; // Number of warnings reported
- unsigned NumErrors; // Number of errors reported
+ unsigned NumWarnings; ///< Number of warnings reported
+ unsigned NumErrors; ///< Number of errors reported
public:
DiagnosticConsumer() : NumWarnings(0), NumErrors(0) { }
@@ -1147,7 +1235,7 @@ public:
virtual ~DiagnosticConsumer();
- /// BeginSourceFile - Callback to inform the diagnostic client that processing
+ /// \brief Callback to inform the diagnostic client that processing
/// of a source file is beginning.
///
/// Note that diagnostics may be emitted outside the processing of a source
@@ -1155,32 +1243,35 @@ public:
/// diagnostics with source range information are required to only be emitted
/// in between BeginSourceFile() and EndSourceFile().
///
- /// \arg LO - The language options for the source file being processed.
- /// \arg PP - The preprocessor object being used for the source; this optional
- /// and may not be present, for example when processing AST source files.
+ /// \param LangOpts The language options for the source file being processed.
+ /// \param PP The preprocessor object being used for the source; this is
+ /// optional, e.g., it may not be present when processing AST source files.
virtual void BeginSourceFile(const LangOptions &LangOpts,
const Preprocessor *PP = 0) {}
- /// EndSourceFile - Callback to inform the diagnostic client that processing
- /// of a source file has ended. The diagnostic client should assume that any
- /// objects made available via \see BeginSourceFile() are inaccessible.
+ /// \brief Callback to inform the diagnostic client that processing
+ /// of a source file has ended.
+ ///
+ /// The diagnostic client should assume that any objects made available via
+ /// BeginSourceFile() are inaccessible.
virtual void EndSourceFile() {}
/// \brief Callback to inform the diagnostic client that processing of all
/// source files has ended.
virtual void finish() {}
- /// IncludeInDiagnosticCounts - This method (whose default implementation
- /// returns true) indicates whether the diagnostics handled by this
+ /// \brief Indicates whether the diagnostics handled by this
/// DiagnosticConsumer should be included in the number of diagnostics
/// reported by DiagnosticsEngine.
+ ///
+ /// The default implementation returns true.
virtual bool IncludeInDiagnosticCounts() const;
- /// HandleDiagnostic - Handle this diagnostic, reporting it to the user or
+ /// \brief Handle this diagnostic, reporting it to the user or
/// capturing it to a log as needed.
///
- /// Default implementation just keeps track of the total number of warnings
- /// and errors.
+ /// The default implementation just keeps track of the total number of
+ /// warnings and errors.
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info);
@@ -1189,8 +1280,7 @@ public:
virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const = 0;
};
-/// IgnoringDiagConsumer - This is a diagnostic client that just ignores all
-/// diags.
+/// \brief A diagnostic client that ignores all diagnostics.
class IgnoringDiagConsumer : public DiagnosticConsumer {
virtual void anchor();
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
@@ -1202,6 +1292,22 @@ class IgnoringDiagConsumer : public DiagnosticConsumer {
}
};
+// Struct used for sending info about how a type should be printed.
+struct TemplateDiffTypes {
+ intptr_t FromType;
+ intptr_t ToType;
+ unsigned PrintTree : 1;
+ unsigned PrintFromType : 1;
+ unsigned ElideType : 1;
+ unsigned ShowColors : 1;
+ // The printer sets this variable to true if the template diff was used.
+ unsigned TemplateDiffUsed : 1;
+};
+
+/// Special character that the diagnostic printer will use to toggle the bold
+/// attribute. The character itself will be not be printed.
+const char ToggleHighlight = 127;
+
} // end namespace clang
#endif
diff --git a/include/clang/Basic/Diagnostic.td b/include/clang/Basic/Diagnostic.td
index 109cd08..6dfecdc 100644
--- a/include/clang/Basic/Diagnostic.td
+++ b/include/clang/Basic/Diagnostic.td
@@ -88,6 +88,7 @@ class AccessControl { bit AccessControl = 1; }
// Definitions for Diagnostics.
include "DiagnosticASTKinds.td"
include "DiagnosticAnalysisKinds.td"
+include "DiagnosticCommentKinds.td"
include "DiagnosticCommonKinds.td"
include "DiagnosticDriverKinds.td"
include "DiagnosticFrontendKinds.td"
diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td
new file mode 100644
index 0000000..235ca79
--- /dev/null
+++ b/include/clang/Basic/DiagnosticCommentKinds.td
@@ -0,0 +1,125 @@
+//==--- DiagnosticCommentKinds.td - diagnostics related to comments -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+let Component = "Comment" in {
+let CategoryName = "Documentation Issue" in {
+
+// HTML parsing errors. These are under -Wdocumentation to make sure the user
+// knows that we didn't parse something as he might expect.
+
+def warn_doc_html_start_tag_expected_quoted_string : Warning<
+ "expected quoted string after equals sign">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_html_start_tag_expected_ident_or_greater : Warning<
+ "HTML start tag prematurely ended, expected attribute name or '>'">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_html_tag_started_here : Note<
+ "HTML tag started here">;
+
+// HTML semantic errors
+
+def warn_doc_html_end_forbidden : Warning<
+ "HTML end tag '%0' is forbidden">,
+ InGroup<DocumentationHTML>, DefaultIgnore;
+
+def warn_doc_html_end_unbalanced : Warning<
+ "HTML end tag does not match any start tag">,
+ InGroup<DocumentationHTML>, DefaultIgnore;
+
+def warn_doc_html_start_end_mismatch : Warning<
+ "HTML start tag '%0' closed by '%1'">,
+ InGroup<DocumentationHTML>, DefaultIgnore;
+
+def note_doc_html_end_tag : Note<
+ "end tag">;
+
+// Commands
+
+def warn_doc_block_command_empty_paragraph : Warning<
+ "empty paragraph passed to '\\%0' command">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_block_command_duplicate : Warning<
+ "duplicated command '\\%0'">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_block_command_previous : Note<
+ "previous command '\\%0' here">;
+
+def note_doc_block_command_previous_alias : Note<
+ "previous command '\\%0' (an alias of '\\%1') here">;
+
+// \param command
+
+def warn_doc_param_invalid_direction : Warning<
+ "unrecognized parameter passing direction, "
+ "valid directions are '[in]', '[out]' and '[in,out]'">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_param_spaces_in_direction : Warning<
+ "whitespace is not allowed in parameter passing direction">,
+ InGroup<DocumentationPedantic>, DefaultIgnore;
+
+def warn_doc_param_not_attached_to_a_function_decl : Warning<
+ "'\\param' command used in a comment that is not attached to "
+ "a function declaration">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_param_duplicate : Warning<
+ "parameter '%0' is already documented">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_param_previous : Note<
+ "previous documentation">;
+
+def warn_doc_param_not_found : Warning<
+ "parameter '%0' not found in the function declaration">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_param_name_suggestion : Note<
+ "did you mean '%0'?">;
+
+// \tparam command
+
+def warn_doc_tparam_not_attached_to_a_template_decl : Warning<
+ "'\\tparam' command used in a comment that is not attached to "
+ "a template declaration">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_tparam_duplicate : Warning<
+ "template parameter '%0' is already documented">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_tparam_previous : Note<
+ "previous documentation">;
+
+def warn_doc_tparam_not_found : Warning<
+ "template parameter '%0' not found in the template declaration">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_tparam_name_suggestion : Note<
+ "did you mean '%0'?">;
+
+// \returns command
+
+def warn_doc_returns_not_attached_to_a_function_decl : Warning<
+ "'\\%0' command used in a comment that is not attached to "
+ "a function or method declaration">,
+ InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_returns_attached_to_a_void_function : Warning<
+ "'\\%0' command used in a comment that is attached to a "
+ "%select{function returning void|constructor|destructor|"
+ "method returning void}1">,
+ InGroup<Documentation>, DefaultIgnore;
+
+} // end of documentation issue category
+} // end of AST component
diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td
index 103fc00..f859287 100644
--- a/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/include/clang/Basic/DiagnosticCommonKinds.td
@@ -74,6 +74,8 @@ def err_module_cycle : Error<"cyclic dependency in module '%0': %1">,
def warn_module_build : Warning<"building module '%0' from source">,
InGroup<ModuleBuild>, DefaultIgnore;
def note_pragma_entered_here : Note<"#pragma entered here">;
+def note_decl_hiding_tag_type : Note<
+ "%1 %0 is hidden by a non-type declaration of %0 here">;
// Sema && Lex
def ext_longlong : Extension<
@@ -106,4 +108,8 @@ def err_file_modified : Error<
"file '%0' modified since it was first processed">, DefaultFatal;
def err_unsupported_bom : Error<"%0 byte order mark detected in '%1', but "
"encoding is not supported">, DefaultFatal;
+def err_unable_to_rename_temp : Error<
+ "unable to rename temporary '%0' to output file '%1': '%2'">;
+def err_unable_to_make_temp : Error<
+ "unable to make temporary file: %0">;
}
diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td
index b443159..583b234 100644
--- a/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/include/clang/Basic/DiagnosticDriverKinds.td
@@ -33,8 +33,6 @@ def err_drv_use_of_Z_option : Error<
"unsupported use of internal gcc -Z option '%0'">;
def err_drv_output_argument_with_multiple_files : Error<
"cannot specify -o when generating multiple output files">;
-def err_drv_unable_to_make_temp : Error<
- "unable to make temporary file: %0">;
def err_drv_unable_to_remove_file : Error<
"unable to remove file: %0">;
def err_drv_command_failure : Error<
@@ -94,23 +92,32 @@ def err_drv_invalid_arch_for_deployment_target : Error<
def err_drv_objc_gc_arr : Error<
"cannot specify both '-fobjc-arc' and '%0'">;
def err_arc_nonfragile_abi : Error<
- "-fobjc-arc is not supported with fragile abi">;
+ "-fobjc-arc is not supported with legacy abi">;
def err_arc_unsupported : Error<
"-fobjc-arc is not supported on current deployment target">;
def err_drv_mg_requires_m_or_mm : Error<
"option '-MG' requires '-M' or '-MM'">;
+def err_drv_asan_android_requires_pie : Error<
+ "AddressSanitizer on Android requires '-pie'">;
+def err_drv_unknown_objc_runtime : Error<
+ "unknown or ill-formed Objective-C runtime '%0'">;
def warn_c_kext : Warning<
- "ignoring -fapple-kext which is valid for c++ and objective-c++ only">;
+ "ignoring -fapple-kext which is valid for C++ and Objective-C++ only">;
def warn_drv_input_file_unused : Warning<
- "%0: '%1' input unused when '%2' is present">;
+ "%0: '%1' input unused%select{ when '%3' is present|}2">,
+ InGroup<DiagGroup<"unused-command-line-argument">>;
+def warn_drv_input_file_unused_by_cpp : Warning<
+ "%0: '%1' input unused in cpp mode">,
+ InGroup<DiagGroup<"unused-command-line-argument">>;
def warn_drv_preprocessed_input_file_unused : Warning<
- "%0: previously preprocessed input unused when '%1' is present">;
+ "%0: previously preprocessed input%select{ unused when '%2' is present|}1">,
+ InGroup<DiagGroup<"unused-command-line-argument">>;
def warn_drv_unused_argument : Warning<
"argument unused during compilation: '%0'">,
InGroup<DiagGroup<"unused-command-line-argument">>;
def warn_drv_empty_joined_argument : Warning<
- "joined argument expects addition arg: '%0'">,
+ "joined argument expects additional value: '%0'">,
InGroup<DiagGroup<"unused-command-line-argument">>;
def warn_drv_not_using_clang_cpp : Warning<
"not using the clang preprocessor due to user override">;
diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td
index 5d6b887..417a22c 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -48,14 +48,12 @@ def err_fe_unable_to_interface_with_target : Error<
"unable to interface with target machine">;
def err_fe_unable_to_open_output : Error<
"unable to open output file '%0': '%1'">;
-def err_fe_unable_to_rename_temp : Error<
- "unable to rename temporary '%0' to output file '%1': '%2'">;
def err_fe_unable_to_open_logfile : Error<
"unable to open logfile file '%0': '%1'">;
def err_fe_pth_file_has_no_source_header : Error<
"PTH file '%0' does not designate an original source header file for -include-pth">;
def warn_fe_macro_contains_embedded_newline : Warning<
- "macro '%0' contains embedded newline, text after the newline is ignored.">;
+ "macro '%0' contains embedded newline; text after the newline is ignored">;
def warn_fe_cc_print_header_failure : Warning<
"unable to open CC_PRINT_HEADERS file: %0 (using stderr)">;
def warn_fe_cc_log_diagnostics_failure : Warning<
@@ -65,6 +63,10 @@ def warn_fe_serialized_diag_failure : Warning<
"unable to open file %0 for serializing diagnostics (%1)">,
InGroup<DiagGroup<"serialized-diagnostics">>;
+def err_verify_missing_line : Error<
+ "missing or invalid line number following '@' in expected %0">;
+def err_verify_invalid_range : Error<
+ "invalid range following '-' in expected %0">;
def err_verify_missing_start : Error<
"cannot find start ('{{') of expected %0">;
def err_verify_missing_end : Error<
@@ -93,19 +95,19 @@ def warn_unknown_warning_option : Warning<
"unknown warning option '%0'">,
InGroup<DiagGroup<"unknown-warning-option"> >;
def warn_unknown_negative_warning_option : Warning<
- "unknown warning option '%0'?">,
- InGroup<DiagGroup<"unknown-warning-option"> >, DefaultIgnore;
+ "unknown warning option '%0'">,
+ InGroup<DiagGroup<"unknown-warning-option"> >;
def warn_unknown_warning_option_suggest : Warning<
"unknown warning option '%0'; did you mean '%1'?">,
InGroup<DiagGroup<"unknown-warning-option"> >;
def warn_unknown_negative_warning_option_suggest : Warning<
"unknown warning option '%0'; did you mean '%1'?">,
- InGroup<DiagGroup<"unknown-warning-option"> >, DefaultIgnore;
+ InGroup<DiagGroup<"unknown-warning-option"> >;
def warn_unknown_warning_specifier : Warning<
"unknown %0 warning specifier: '%1'">,
InGroup<DiagGroup<"unknown-warning-option"> >;
-def warn_unknown_analyzer_checker : Warning<
+def err_unknown_analyzer_checker : Error<
"no analyzer checkers are associated with '%0'">;
def warn_incompatible_analyzer_plugin_api : Warning<
"checker plugin '%0' is not compatible with this version of the analyzer">,
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index c839853..b95a90b 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -25,6 +25,7 @@ def AmbigMemberTemplate : DiagGroup<"ambiguous-member-template">;
def : DiagGroup<"attributes">;
def : DiagGroup<"bad-function-cast">;
def Availability : DiagGroup<"availability">;
+def Section : DiagGroup<"section">;
def AutoImport : DiagGroup<"auto-import">;
def ConstantConversion : DiagGroup<"constant-conversion">;
def LiteralConversion : DiagGroup<"literal-conversion">;
@@ -32,6 +33,7 @@ def StringConversion : DiagGroup<"string-conversion">;
def SignConversion : DiagGroup<"sign-conversion">;
def BoolConversion : DiagGroup<"bool-conversion">;
def IntConversion : DiagGroup<"int-conversion">;
+def NonLiteralNullConversion : DiagGroup<"non-literal-null-conversion">;
def NullConversion : DiagGroup<"null-conversion">;
def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">;
def CXXCompat: DiagGroup<"c++-compat">;
@@ -56,8 +58,13 @@ def DeprecatedImplementations :DiagGroup<"deprecated-implementations">;
def : DiagGroup<"disabled-optimization">;
def : DiagGroup<"discard-qual">;
def : DiagGroup<"div-by-zero">;
+def DocumentationHTML : DiagGroup<"documentation-html">;
+def DocumentationPedantic : DiagGroup<"documentation-pedantic">;
+def Documentation : DiagGroup<"documentation", [DocumentationHTML]>;
def EmptyBody : DiagGroup<"empty-body">;
def ExtraTokens : DiagGroup<"extra-tokens">;
+def CXX11ExtraSemi : DiagGroup<"c++11-extra-semi">;
+def ExtraSemi : DiagGroup<"extra-semi", [CXX11ExtraSemi]>;
def FormatExtraArgs : DiagGroup<"format-extra-args">;
def FormatZeroLength : DiagGroup<"format-zero-length">;
@@ -96,6 +103,7 @@ def CXX11Compat : DiagGroup<"c++11-compat",
def : DiagGroup<"c++0x-compat", [CXX11Compat]>;
def : DiagGroup<"effc++">;
+def DivZero : DiagGroup<"division-by-zero">;
def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
def FourByteMultiChar : DiagGroup<"four-char-constants">;
@@ -142,9 +150,13 @@ def : DiagGroup<"old-style-cast">;
def : DiagGroup<"old-style-definition">;
def OutOfLineDeclaration : DiagGroup<"out-of-line-declaration">;
def : DiagGroup<"overflow">;
+def ForwardClassReceiver : DiagGroup<"receiver-forward-class">;
+def MethodAccess : DiagGroup<"objc-method-access">;
+def ObjCReceiver : DiagGroup<"receiver-expr">;
def OverlengthStrings : DiagGroup<"overlength-strings">;
def OverloadedVirtual : DiagGroup<"overloaded-virtual">;
def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
+def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
def ObjCMissingSuperCalls : DiagGroup<"objc-missing-super-calls">;
def ObjCRetainBlockProperty : DiagGroup<"objc-noncopy-retain-block-property">;
def ObjCReadonlyPropertyHasSetter : DiagGroup<"objc-readonly-with-setter-property">;
@@ -162,7 +174,8 @@ def ReturnTypeCLinkage : DiagGroup<"return-type-c-linkage">;
def ReturnType : DiagGroup<"return-type", [ReturnTypeCLinkage]>;
def BindToTemporaryCopy : DiagGroup<"bind-to-temporary-copy",
[CXX98CompatBindToTemporaryCopy]>;
-def SelfAssignment : DiagGroup<"self-assign">;
+def SelfAssignmentField : DiagGroup<"self-assign-field">;
+def SelfAssignment : DiagGroup<"self-assign", [SelfAssignmentField]>;
def SemiBeforeMethodBody : DiagGroup<"semicolon-before-method-body">;
def Sentinel : DiagGroup<"sentinel">;
def MissingMethodReturnType : DiagGroup<"missing-method-return-type">;
@@ -206,12 +219,18 @@ def MethodDuplicate : DiagGroup<"duplicate-method-match">;
def CoveredSwitchDefault : DiagGroup<"covered-switch-default">;
def SwitchEnum : DiagGroup<"switch-enum">;
def Switch : DiagGroup<"switch">;
+def ImplicitFallthroughPerFunction :
+ DiagGroup<"implicit-fallthrough-per-function">;
+def ImplicitFallthrough : DiagGroup<"implicit-fallthrough",
+ [ImplicitFallthroughPerFunction]>;
+def InvalidPPToken : DiagGroup<"invalid-pp-token">;
def Trigraphs : DiagGroup<"trigraphs">;
def : DiagGroup<"type-limits">;
def Unicode : DiagGroup<"unicode">;
-def Uninitialized : DiagGroup<"uninitialized">;
def UninitializedMaybe : DiagGroup<"conditional-uninitialized">;
+def UninitializedSometimes : DiagGroup<"sometimes-uninitialized">;
+def Uninitialized : DiagGroup<"uninitialized", [UninitializedSometimes]>;
def UnknownPragmas : DiagGroup<"unknown-pragmas">;
def NSobjectAttribute : DiagGroup<"NSObject-attribute">;
def UnknownAttributes : DiagGroup<"attributes">;
@@ -223,6 +242,7 @@ def UnusedComparison : DiagGroup<"unused-comparison">;
def UnusedExceptionParameter : DiagGroup<"unused-exception-parameter">;
def UnneededInternalDecl : DiagGroup<"unneeded-internal-declaration">;
def UnneededMemberFunction : DiagGroup<"unneeded-member-function">;
+def UnusedPrivateField : DiagGroup<"unused-private-field">;
def UnusedFunction : DiagGroup<"unused-function", [UnneededInternalDecl]>;
def UnusedMemberFunction : DiagGroup<"unused-member-function",
[UnneededMemberFunction]>;
@@ -250,7 +270,6 @@ def AutomaticReferenceCounting : DiagGroup<"arc",
ARCRetainCycles,
ARCNonPodMemAccess]>;
def Selector : DiagGroup<"selector">;
-def NonfragileAbi2 : DiagGroup<"nonfragile-abi2">;
def Protocol : DiagGroup<"protocol">;
def SuperSubClassMismatch : DiagGroup<"super-class-method-mismatch">;
def OverridingMethodMismatch : DiagGroup<"overriding-method-mismatch">;
@@ -296,7 +315,8 @@ def Conversion : DiagGroup<"conversion",
StringConversion,
SignConversion,
BoolConversion,
- NullConversion,
+ NullConversion, // NULL->non-pointer
+ NonLiteralNullConversion, // (1-1)->pointer (etc)
IntConversion]>,
DiagCategory<"Value Conversion Issue">;
@@ -304,6 +324,7 @@ def Unused : DiagGroup<"unused",
[UnusedArgument, UnusedFunction, UnusedLabel,
// UnusedParameter, (matches GCC's behavior)
// UnusedMemberFunction, (clean-up llvm before enabling)
+ UnusedPrivateField,
UnusedValue, UnusedVariable]>,
DiagCategory<"Unused Entity Issue">;
@@ -354,13 +375,19 @@ def Most : DiagGroup<"most", [
]>;
// Thread Safety warnings
-def ThreadSafety : DiagGroup<"thread-safety">;
+def ThreadSafetyAttributes : DiagGroup<"thread-safety-attributes">;
+def ThreadSafetyAnalysis : DiagGroup<"thread-safety-analysis">;
+def ThreadSafety : DiagGroup<"thread-safety",
+ [ThreadSafetyAttributes, ThreadSafetyAnalysis]>;
// Note that putting warnings in -Wall will not disable them by default. If a
// warning should be active _only_ when -Wall is passed in, mark it as
// DefaultIgnore in addition to putting it here.
def : DiagGroup<"all", [Most, Parentheses, Switch]>;
+// Warnings enabled by -pedantic. This is magically filled in by TableGen.
+def Pedantic : DiagGroup<"pedantic">;
+
// Aliases.
def : DiagGroup<"", [Extra]>; // -W = -Wextra
def : DiagGroup<"endif-labels", [ExtraTokens]>; // -Wendif-labels=-Wendif-tokens
@@ -381,7 +408,7 @@ def NonGCC : DiagGroup<"non-gcc",
// A warning group for warnings about using C++11 features as extensions in
// earlier C++ versions.
-def CXX11 : DiagGroup<"c++11-extensions">;
+def CXX11 : DiagGroup<"c++11-extensions", [CXX11ExtraSemi]>;
def : DiagGroup<"c++0x-extensions", [CXX11]>;
def DelegatingCtorCycles :
DiagGroup<"delegating-ctor-cycles">;
@@ -413,3 +440,8 @@ def ObjCRedundantAPIUse : DiagGroup<"objc-redundant-api-use", [
def ObjCCocoaAPI : DiagGroup<"objc-cocoa-api", [
ObjCRedundantAPIUse
]>;
+
+def ObjCStringComparison : DiagGroup<"objc-string-compare">;
+def ObjCLiteralComparison : DiagGroup<"objc-literal-compare", [
+ ObjCStringComparison
+ ]>;
diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h
index a6c22db..11552af 100644
--- a/include/clang/Basic/DiagnosticIDs.h
+++ b/include/clang/Basic/DiagnosticIDs.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the Diagnostic IDs-related interfaces.
-//
+///
+/// \file
+/// \brief Defines the Diagnostic IDs-related interfaces.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_DIAGNOSTICIDS_H
@@ -37,14 +38,15 @@ namespace clang {
DIAG_START_LEX = DIAG_START_SERIALIZATION + 120,
DIAG_START_PARSE = DIAG_START_LEX + 300,
DIAG_START_AST = DIAG_START_PARSE + 400,
- DIAG_START_SEMA = DIAG_START_AST + 100,
+ DIAG_START_COMMENT = DIAG_START_AST + 100,
+ DIAG_START_SEMA = DIAG_START_COMMENT + 100,
DIAG_START_ANALYSIS = DIAG_START_SEMA + 3000,
DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100
};
class CustomDiagInfo;
- /// diag::kind - All of the diagnostics that can be emitted by the frontend.
+ /// \brief All of the diagnostics that can be emitted by the frontend.
typedef unsigned kind;
// Get typedefs for common diagnostics.
@@ -63,10 +65,10 @@ namespace clang {
/// one).
enum Mapping {
// NOTE: 0 means "uncomputed".
- MAP_IGNORE = 1, //< Map this diagnostic to nothing, ignore it.
- MAP_WARNING = 2, //< Map this diagnostic to a warning.
- MAP_ERROR = 3, //< Map this diagnostic to an error.
- MAP_FATAL = 4 //< Map this diagnostic to a fatal error.
+ MAP_IGNORE = 1, ///< Map this diagnostic to nothing, ignore it.
+ MAP_WARNING = 2, ///< Map this diagnostic to a warning.
+ MAP_ERROR = 3, ///< Map this diagnostic to an error.
+ MAP_FATAL = 4 ///< Map this diagnostic to a fatal error.
};
}
@@ -111,83 +113,84 @@ public:
/// by multiple Diagnostics for multiple translation units.
class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
public:
- /// Level - The level of the diagnostic, after it has been through mapping.
+ /// Level The level of the diagnostic, after it has been through mapping.
enum Level {
Ignored, Note, Warning, Error, Fatal
};
private:
- /// CustomDiagInfo - Information for uniquing and looking up custom diags.
+ /// \brief Information for uniquing and looking up custom diags.
diag::CustomDiagInfo *CustomDiagInfo;
public:
DiagnosticIDs();
~DiagnosticIDs();
- /// getCustomDiagID - Return an ID for a diagnostic with the specified message
- /// and level. If this is the first request for this diagnosic, it is
- /// registered and created, otherwise the existing ID is returned.
+ /// \brief Return an ID for a diagnostic with the specified message and level.
+ ///
+ /// If this is the first request for this diagnosic, it is registered and
+ /// created, otherwise the existing ID is returned.
unsigned getCustomDiagID(Level L, StringRef Message);
//===--------------------------------------------------------------------===//
// Diagnostic classification and reporting interfaces.
//
- /// getDescription - Given a diagnostic ID, return a description of the
- /// issue.
+ /// \brief Given a diagnostic ID, return a description of the issue.
StringRef getDescription(unsigned DiagID) const;
- /// isBuiltinWarningOrExtension - Return true if the unmapped diagnostic level
- /// of the specified diagnostic ID is a Warning or Extension. This only works
- /// on builtin diagnostics, not custom ones, and is not legal to call on
- /// NOTEs.
+ /// \brief Return true if the unmapped diagnostic levelof the specified
+ /// diagnostic ID is a Warning or Extension.
+ ///
+ /// This only works on builtin diagnostics, not custom ones, and is not
+ /// legal to call on NOTEs.
static bool isBuiltinWarningOrExtension(unsigned DiagID);
/// \brief Return true if the specified diagnostic is mapped to errors by
/// default.
static bool isDefaultMappingAsError(unsigned DiagID);
- /// \brief Determine whether the given built-in diagnostic ID is a
- /// Note.
+ /// \brief Determine whether the given built-in diagnostic ID is a Note.
static bool isBuiltinNote(unsigned DiagID);
- /// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic
- /// ID is for an extension of some sort.
- ///
+ /// \brief Determine whether the given built-in diagnostic ID is for an
+ /// extension of some sort.
static bool isBuiltinExtensionDiag(unsigned DiagID) {
bool ignored;
return isBuiltinExtensionDiag(DiagID, ignored);
}
- /// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic
- /// ID is for an extension of some sort. This also returns EnabledByDefault,
- /// which is set to indicate whether the diagnostic is ignored by default (in
- /// which case -pedantic enables it) or treated as a warning/error by default.
+ /// \brief Determine whether the given built-in diagnostic ID is for an
+ /// extension of some sort, and whether it is enabled by default.
+ ///
+ /// This also returns EnabledByDefault, which is set to indicate whether the
+ /// diagnostic is ignored by default (in which case -pedantic enables it) or
+ /// treated as a warning/error by default.
///
static bool isBuiltinExtensionDiag(unsigned DiagID, bool &EnabledByDefault);
- /// getWarningOptionForDiag - Return the lowest-level warning option that
- /// enables the specified diagnostic. If there is no -Wfoo flag that controls
- /// the diagnostic, this returns null.
+ /// \brief Return the lowest-level warning option that enables the specified
+ /// diagnostic.
+ ///
+ /// If there is no -Wfoo flag that controls the diagnostic, this returns null.
static StringRef getWarningOptionForDiag(unsigned DiagID);
- /// getCategoryNumberForDiag - Return the category number that a specified
- /// DiagID belongs to, or 0 if no category.
+ /// \brief Return the category number that a specified \p DiagID belongs to,
+ /// or 0 if no category.
static unsigned getCategoryNumberForDiag(unsigned DiagID);
- /// getNumberOfCategories - Return the number of categories
+ /// \brief Return the number of diagnostic categories.
static unsigned getNumberOfCategories();
- /// getCategoryNameFromID - Given a category ID, return the name of the
- /// category.
+ /// \brief Given a category ID, return the name of the category.
static StringRef getCategoryNameFromID(unsigned CategoryID);
- /// isARCDiagnostic - Return true if a given diagnostic falls into an
- /// ARC diagnostic category;
+ /// \brief Return true if a given diagnostic falls into an ARC diagnostic
+ /// category.
static bool isARCDiagnostic(unsigned DiagID);
- /// \brief Enumeration describing how the the emission of a diagnostic should
+ /// \brief Enumeration describing how the emission of a diagnostic should
/// be treated when it occurs during C++ template argument deduction.
enum SFINAEResponse {
/// \brief The diagnostic should not be reported, but it should cause
@@ -253,20 +256,23 @@ private:
DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
const DiagnosticsEngine &Diag) const;
- /// getDiagnosticLevel - This is an internal implementation helper used when
- /// DiagClass is already known.
+ /// \brief An internal implementation helper used when \p DiagClass is
+ /// already known.
DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID,
unsigned DiagClass,
SourceLocation Loc,
const DiagnosticsEngine &Diag) const;
- /// ProcessDiag - This is the method used to report a diagnostic that is
- /// finally fully formed.
+ /// \brief Used to report a diagnostic that is finally fully formed.
///
- /// \returns true if the diagnostic was emitted, false if it was
+ /// \returns \c true if the diagnostic was emitted, \c false if it was
/// suppressed.
bool ProcessDiag(DiagnosticsEngine &Diag) const;
+ /// \brief Used to emit a diagnostic that is finally fully formed,
+ /// ignoring suppression.
+ void EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const;
+
/// \brief Whether the diagnostic may leave the AST in a state where some
/// invariants can break.
bool isUnrecoverable(unsigned DiagID) const;
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 670283e..cc958db 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -64,9 +64,12 @@ def ext_token_used : Extension<"extension used">,
def warn_cxx11_keyword : Warning<"'%0' is a keyword in C++11">,
InGroup<CXX11Compat>, DefaultIgnore;
-def warn_unterminated_string : ExtWarn<"missing terminating '\"' character">;
-def warn_unterminated_char : ExtWarn<"missing terminating ' character">;
-def err_empty_character : Error<"empty character constant">;
+def ext_unterminated_string : ExtWarn<"missing terminating '\"' character">,
+ InGroup<InvalidPPToken>;
+def ext_unterminated_char : ExtWarn<"missing terminating ' character">,
+ InGroup<InvalidPPToken>;
+def ext_empty_character : ExtWarn<"empty character constant">,
+ InGroup<InvalidPPToken>;
def err_unterminated_block_comment : Error<"unterminated /* comment">;
def err_invalid_character_to_charify : Error<
"invalid argument to convert to character">;
@@ -177,6 +180,7 @@ def err_bad_character_encoding : Error<
def warn_bad_character_encoding : ExtWarn<
"illegal character encoding in character literal">,
InGroup<DiagGroup<"invalid-source-encoding">>;
+def err_lexing_string : Error<"failure when lexing a string">;
//===----------------------------------------------------------------------===//
@@ -251,7 +255,7 @@ def ext_pp_comma_expr : Extension<"comma operator in operand of #if">;
def ext_pp_bad_vaargs_use : Extension<
"__VA_ARGS__ can only appear in the expansion of a C99 variadic macro">;
def ext_pp_macro_redef : ExtWarn<"%0 macro redefined">;
-def ext_variadic_macro : Extension<"variadic macros were introduced in C99">,
+def ext_variadic_macro : Extension<"variadic macros are a C99 feature">,
InGroup<VariadicMacros>;
def warn_cxx98_compat_variadic_macro : Warning<
"variadic macros are incompatible with C++98">,
@@ -264,15 +268,19 @@ def ext_embedded_directive : Extension<
"embedding a directive within macro arguments has undefined behavior">,
InGroup<DiagGroup<"embedded-directive">>;
def ext_missing_varargs_arg : Extension<
- "varargs argument missing, but tolerated as an extension">;
+ "must specify at least one argument for '...' parameter of variadic macro">,
+ InGroup<GNU>;
def ext_empty_fnmacro_arg : Extension<
- "empty macro arguments were standardized in C99">;
+ "empty macro arguments are a C99 feature">, InGroup<C99>;
def warn_cxx98_compat_empty_fnmacro_arg : Warning<
- "empty macro argument list is incompatible with C++98">,
+ "empty macro arguments are incompatible with C++98">,
InGroup<CXX98CompatPedantic>, DefaultIgnore;
+def note_macro_here : Note<"macro %0 defined here">;
def err_pp_invalid_directive : Error<"invalid preprocessing directive">;
def err_pp_file_not_found : Error<"'%0' file not found">, DefaultFatal;
+def err_pp_file_not_found_not_fatal : Error<
+ "'%0' file not found with <angled> include; use \"quotes\" instead">;
def err_pp_error_opening_file : Error<
"error opening file '%0': %1">, DefaultFatal;
def err_pp_empty_filename : Error<"empty filename">;
@@ -324,8 +332,7 @@ def err_feature_check_malformed : Error<
"builtin feature check macro requires a parenthesized identifier">;
def err_warning_check_malformed : Error<
- "builtin warning check macro requires a parenthesized string">,
- InGroup<MalformedWarningCheck>;
+ "builtin warning check macro requires a parenthesized string">;
def warn_has_warning_invalid_option :
ExtWarn<"__has_warning expected option name (e.g. \"-Wundef\")">,
InGroup<MalformedWarningCheck>;
@@ -394,7 +401,7 @@ def err_paste_at_start : Error<
"'##' cannot appear at start of macro expansion">;
def err_paste_at_end : Error<"'##' cannot appear at end of macro expansion">;
def ext_paste_comma : Extension<
- "Use of comma pasting extension is non-portable">;
+ "token pasting of ',' and __VA_ARGS__ is a GNU extension">, InGroup<GNU>;
def err_unterm_macro_invoc : Error<
"unterminated function-like macro invocation">;
def err_too_many_args_in_macro_invoc : Error<
@@ -412,6 +419,9 @@ def err_pp_illegal_floating_literal : Error<
"floating point literal in preprocessor expression">;
def err_pp_line_requires_integer : Error<
"#line directive requires a positive integer argument">;
+def ext_pp_line_zero : Extension<
+ "#line directive with zero argument is a GNU extension">,
+ InGroup<GNU>;
def err_pp_line_invalid_filename : Error<
"invalid filename for #line directive">;
def warn_pp_line_decimal : Warning<
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index c183da7..8cb82fd 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -20,23 +20,32 @@ def warn_file_asm_volatile : Warning<
let CategoryName = "Parse Issue" in {
-def ext_empty_source_file : Extension<"ISO C forbids an empty source file">;
-def ext_top_level_semi : Extension<
- "extra ';' outside of a function">;
+def ext_empty_translation_unit : Extension<
+ "ISO C requires a translation unit to contain at least one declaration.">,
+ InGroup<DiagGroup<"empty-translation-unit">>;
def warn_cxx98_compat_top_level_semi : Warning<
"extra ';' outside of a function is incompatible with C++98">,
InGroup<CXX98CompatPedantic>, DefaultIgnore;
-def ext_extra_struct_semi : Extension<
- "extra ';' inside a %0">;
-def ext_extra_ivar_semi : Extension<
- "extra ';' inside instance variable list">;
+def ext_extra_semi : Extension<
+ "extra ';' %select{"
+ "outside of a function|"
+ "inside a %1|"
+ "inside instance variable list|"
+ "after member function definition}0">,
+ InGroup<ExtraSemi>;
+def ext_extra_semi_cxx11 : Extension<
+ "extra ';' outside of a function is a C++11 extension">,
+ InGroup<CXX11ExtraSemi>;
+def warn_extra_semi_after_mem_fn_def : Warning<
+ "extra ';' after member function definition">,
+ InGroup<ExtraSemi>, DefaultIgnore;
def ext_duplicate_declspec : Extension<"duplicate '%0' declaration specifier">;
def ext_plain_complex : ExtWarn<
"plain '_Complex' requires a type specifier; assuming '_Complex double'">;
def ext_integer_complex : Extension<
"complex integer types are an extension">;
-def ext_thread_before : Extension<"'__thread' before 'static'">;
+def ext_thread_before : Extension<"'__thread' before '%0'">;
def ext_empty_struct_union : Extension<
"empty %select{struct|union}0 is a GNU extension">, InGroup<GNU>;
@@ -58,9 +67,12 @@ def ext_c99_compound_literal : Extension<
"compound literals are a C99-specific feature">, InGroup<C99>;
def ext_c99_flexible_array_member : Extension<
"Flexible array members are a C99-specific feature">, InGroup<C99>;
-def ext_enumerator_list_comma : Extension<
- "commas at the end of enumerator lists are a %select{C99|C++11}0-specific "
- "feature">;
+def ext_enumerator_list_comma_c : Extension<
+ "commas at the end of enumerator lists are a C99-specific "
+ "feature">, InGroup<C99>;
+def ext_enumerator_list_comma_cxx : Extension<
+ "commas at the end of enumerator lists are a C++11 extension">,
+ InGroup<CXX11>;
def warn_cxx98_compat_enumerator_list_comma : Warning<
"commas at the end of enumerator lists are incompatible with C++98">,
InGroup<CXX98CompatPedantic>, DefaultIgnore;
@@ -89,8 +101,8 @@ def err_duplicate_default_assoc : Error<
def note_previous_default_assoc : Note<
"previous default generic association is here">;
-def ext_c11_alignas : Extension<
- "_Alignas is a C11-specific feature">, InGroup<C11>;
+def ext_c11_alignment : Extension<
+ "%0 is a C11-specific feature">, InGroup<C11>;
def ext_gnu_indirect_goto : Extension<
"use of GNU indirect-goto extension">, InGroup<GNU>;
@@ -311,7 +323,7 @@ def err_templated_using_directive : Error<
def err_templated_using_declaration : Error<
"cannot template a using declaration">;
def err_unexected_colon_in_nested_name_spec : Error<
- "unexpected ':' in nested name specifier">;
+ "unexpected ':' in nested name specifier; did you mean '::'?">;
def err_bool_redeclaration : Error<
"redeclaration of C++ built-in type 'bool'">;
def ext_c11_static_assert : Extension<
@@ -336,6 +348,9 @@ def err_illegal_super_cast : Error<
"cannot cast 'super' (it isn't an expression)">;
def err_nsnumber_nonliteral_unary : Error<
"@%0 must be followed by a number to form an NSNumber object">;
+def warn_cstyle_param : Warning<
+ "use of C-style parameters in Objective-C method declarations"
+ " is deprecated">, InGroup<DeprecatedDeclarations>;
let CategoryName = "ARC Parse Issue" in {
def err_arc_bridge_retain : Error<
@@ -377,6 +392,8 @@ def err_synthesized_property_name : Error<
def warn_semicolon_before_method_body : Warning<
"semicolon before method body is ignored">,
InGroup<DiagGroup<"semicolon-before-method-body">>, DefaultIgnore;
+def note_extra_comma_message_arg : Note<
+ "comma separating Objective-C messaging arguments">;
def err_expected_field_designator : Error<
"expected a field designator, such as '.field = 4'">;
@@ -396,9 +413,8 @@ def err_expected_init_in_condition : Error<
"variable declaration in condition must have an initializer">;
def err_expected_init_in_condition_lparen : Error<
"variable declaration in condition cannot have a parenthesized initializer">;
-def warn_parens_disambiguated_as_function_decl : Warning<
- "parentheses were disambiguated as a function declarator">,
- InGroup<VexingParse>;
+def err_extraneous_rparen_in_condition : Error<
+ "extraneous ')' after condition, expected a statement">;
def warn_dangling_else : Warning<
"add explicit braces to avoid dangling else">,
InGroup<DanglingElse>;
@@ -474,6 +490,10 @@ def err_l_square_l_square_not_attribute : Error<
"introducing an attribute">;
def err_alignas_pack_exp_unsupported : Error<
"pack expansions in alignment specifiers are not supported yet">;
+def err_ms_declspec_type : Error<
+ "__declspec attributes must be an identifier or string literal">;
+def warn_ms_declspec_unknown : Warning<
+ "unknown __declspec attribute %0 ignored">, InGroup<UnknownAttributes>;
/// C++ Templates
def err_expected_template : Error<"expected template">;
@@ -491,9 +511,12 @@ def err_id_after_template_in_nested_name_spec : Error<
"expected template name after 'template' keyword in nested name specifier">;
def err_two_right_angle_brackets_need_space : Error<
"a space is required between consecutive right angle brackets (use '> >')">;
+def err_right_angle_bracket_equal_needs_space : Error<
+ "a space is required between a right angle bracket and an equals sign "
+ "(use '> =')">;
def warn_cxx0x_right_shift_in_template_arg : Warning<
"use of right-shift operator ('>>') in template argument will require "
- "parentheses in C++11">;
+ "parentheses in C++11">, InGroup<CXX11Compat>;
def warn_cxx98_compat_two_right_angle_brackets : Warning<
"consecutive right angle brackets are incompatible with C++98 (use '> >')">,
InGroup<CXX98Compat>, DefaultIgnore;
@@ -648,9 +671,11 @@ def err_availability_unknown_change : Error<
"%0 is not an availability stage; use 'introduced', 'deprecated', or "
"'obsoleted'">;
def err_availability_redundant : Error<
- "redundant %0 availability change; only the last specified change will " "be used">;
+ "redundant %0 availability change; only the last specified change will "
+ "be used">;
def warn_availability_and_unavailable : Warning<
- "'unavailable' availability overrides all other availability information">;
+ "'unavailable' availability overrides all other availability information">,
+ InGroup<Availability>;
// Language specific pragmas
// - Generic warnings
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 0614ade..6d7b52e 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -14,6 +14,19 @@
let Component = "Sema" in {
let CategoryName = "Semantic Issue" in {
+// For loop analysis
+def warn_variables_not_in_loop_body : Warning<
+ "variable%select{s| %1|s %1 and %2|s %1, %2, and %3|s %1, %2, %3, and %4}0 "
+ "used in loop condition not modified in loop body">,
+ InGroup<DiagGroup<"loop-analysis">>, DefaultIgnore;
+
+def warn_identical_enum_values : Warning<
+ "all elements of %0 are initialized with literals to value %1">,
+ InGroup<DiagGroup<"unique-enum">>;
+def note_identical_enum_values : Note<
+ "initialize the last element with the previous element to silence "
+ "this warning">;
+
// Constant expressions
def err_expr_not_ice : Error<
"expression is not an %select{integer|integral}0 constant expression">;
@@ -145,6 +158,11 @@ def warn_redefinition_in_param_list : Warning<
def warn_empty_parens_are_function_decl : Warning<
"empty parentheses interpreted as a function declaration">,
InGroup<VexingParse>;
+def warn_parens_disambiguated_as_function_declaration : Warning<
+ "parentheses were disambiguated as a function declaration">,
+ InGroup<VexingParse>;
+def note_additional_parens_for_variable_declaration : Note<
+ "add a pair of parentheses to declare a variable">;
def note_empty_parens_function_call : Note<
"change this ',' to a ';' to call %0">;
def note_empty_parens_default_ctor : Note<
@@ -160,9 +178,15 @@ def warn_used_but_marked_unused: Warning<"%0 was marked unused but was used">,
def warn_unneeded_internal_decl : Warning<
"%select{function|variable}0 %1 is not needed and will not be emitted">,
InGroup<UnneededInternalDecl>, DefaultIgnore;
+def warn_unneeded_static_internal_decl : Warning<
+ "'static' function %0 declared in header file "
+ "should be declared 'static inline'">,
+ InGroup<UnneededInternalDecl>, DefaultIgnore;
def warn_unneeded_member_function : Warning<
"member function %0 is not needed and will not be emitted">,
InGroup<UnneededMemberFunction>, DefaultIgnore;
+def warn_unused_private_field: Warning<"private field %0 is not used">,
+ InGroup<UnusedPrivateField>, DefaultIgnore;
def warn_parameter_size: Warning<
"%0 is a large (%1 bytes) pass-by-value argument; "
@@ -173,6 +197,9 @@ def warn_return_value_size: Warning<
def warn_return_value_udt: Warning<
"%0 has C-linkage specified, but returns user-defined type %1 which is "
"incompatible with C">, InGroup<ReturnTypeCLinkage>;
+def warn_return_value_udt_incomplete: Warning<
+ "%0 has C-linkage specified, but returns incomplete type %1 which could be "
+ "incompatible with C">, InGroup<ReturnTypeCLinkage>;
def warn_implicit_function_decl : Warning<
"implicit declaration of function %0">,
InGroup<ImplicitFunctionDeclare>, DefaultIgnore;
@@ -325,10 +352,14 @@ def warn_dyn_class_memaccess : Warning<
def note_bad_memaccess_silence : Note<
"explicitly cast the pointer to silence this warning">;
def warn_sizeof_pointer_expr_memaccess : Warning<
- "argument to 'sizeof' in %0 call is the same expression as the "
- "%select{destination|source}1; did you mean to "
- "%select{dereference it|remove the addressof|provide an explicit length}2?">,
+ "'%0' call operates on objects of type %1 while the size is based on a "
+ "different type %2">,
InGroup<DiagGroup<"sizeof-pointer-memaccess">>;
+def warn_sizeof_pointer_expr_memaccess_note : Note<
+ "did you mean to %select{dereference the argument to 'sizeof' (and multiply "
+ "it by the number of elements)|remove the addressof in the argument to "
+ "'sizeof' (and multiply it by the number of elements)|provide an explicit "
+ "length}0?">;
def warn_sizeof_pointer_type_memaccess : Warning<
"argument to 'sizeof' in %0 call is the same pointer type %1 as the "
"%select{destination|source}2; expected %3 or an explicit length">,
@@ -342,9 +373,11 @@ def note_strlcpycat_wrong_size : Note<
def warn_strncat_large_size : Warning<
"the value of the size argument in 'strncat' is too large, might lead to a "
- "buffer overflow">, InGroup<StrncatSize>, DefaultIgnore;
+ "buffer overflow">, InGroup<StrncatSize>;
def warn_strncat_src_size : Warning<"size argument in 'strncat' call appears "
- "to be size of the source">, InGroup<StrncatSize>, DefaultIgnore;
+ "to be size of the source">, InGroup<StrncatSize>;
+def warn_strncat_wrong_size : Warning<
+ "the value of the size argument to 'strncat' is wrong">, InGroup<StrncatSize>;
def note_strncat_wrong_size : Note<
"change the argument to be the free space in the destination buffer minus "
"the terminating null byte">;
@@ -442,6 +475,8 @@ def err_class_extension_after_impl : Error<
"cannot declare class extension for %0 after class implementation">;
def note_implementation_declared : Note<
"class implementation is declared here">;
+def note_while_in_implementation : Note<
+ "detected while default synthesizing properties in class implementation">;
def note_class_declared : Note<
"class is declared here">;
def note_receiver_is_id : Note<
@@ -452,7 +487,7 @@ def err_objc_root_class_subclass : Error<
"objc_root_class attribute may only be specified on a root class declaration">;
def warn_objc_root_class_missing : Warning<
"class %0 defined without specifying a base class">,
- InGroup<ObjCRootClass>, DefaultIgnore;
+ InGroup<ObjCRootClass>;
def note_objc_needs_superclass : Note<
"add a super class to fix this problem">;
def warn_dup_category_def : Warning<
@@ -462,7 +497,7 @@ def err_dup_implementation_class : Error<"reimplementation of class %0">;
def err_dup_implementation_category : Error<
"reimplementation of category %1 for class %0">;
def err_conflicting_ivar_type : Error<
- "instance variable %0 has conflicting type: %1 vs %2">;
+ "instance variable %0 has conflicting type%diff{: $ vs $|}1,2">;
def err_duplicate_ivar_declaration : Error<
"instance variable is already declared">;
def warn_on_superclass_use : Warning<
@@ -481,12 +516,12 @@ def note_required_for_protocol_at :
def warn_conflicting_overriding_ret_types : Warning<
"conflicting return type in "
- "declaration of %0: %1 vs %2">,
+ "declaration of %0%diff{: $ vs $|}1,2">,
InGroup<OverridingMethodMismatch>, DefaultIgnore;
def warn_conflicting_ret_types : Warning<
"conflicting return type in "
- "implementation of %0: %1 vs %2">;
+ "implementation of %0%diff{: $ vs $|}1,2">;
def warn_conflicting_overriding_ret_type_modifiers : Warning<
"conflicting distributed object modifiers on return type "
@@ -510,12 +545,12 @@ def warn_non_covariant_ret_types : Warning<
def warn_conflicting_overriding_param_types : Warning<
"conflicting parameter types in "
- "declaration of %0: %1 vs %2">,
+ "declaration of %0%diff{: $ vs $|}1,2">,
InGroup<OverridingMethodMismatch>, DefaultIgnore;
def warn_conflicting_param_types : Warning<
"conflicting parameter types in "
- "implementation of %0: %1 vs %2">;
+ "implementation of %0%diff{: $ vs $|}1,2">;
def warn_conflicting_param_modifiers : Warning<
"conflicting distributed object modifiers on parameter type "
"in implementation of %0">,
@@ -575,15 +610,17 @@ def err_objc_property_requires_object : Error<
"property with '%0' attribute must be of object type">;
def warn_objc_property_no_assignment_attribute : Warning<
"no 'assign', 'retain', or 'copy' attribute is specified - "
- "'assign' is assumed">;
+ "'assign' is assumed">,
+ InGroup<ObjCPropertyNoAttribute>;
def warn_objc_isa_use : Warning<
"direct access to objective-c's isa is deprecated "
"in favor of object_setClass() and object_getClass()">,
InGroup<DiagGroup<"deprecated-objc-isa-usage">>;
def warn_objc_property_default_assign_on_object : Warning<
- "default property attribute 'assign' not appropriate for non-gc object">;
+ "default property attribute 'assign' not appropriate for non-GC object">,
+ InGroup<ObjCPropertyNoAttribute>;
def warn_property_attr_mismatch : Warning<
- "property attribute in continuation class does not match the primary class">;
+ "property attribute in class extension does not match the primary class">;
def warn_objc_property_copy_missing_on_block : Warning<
"'copy' attribute must be specified for the block property "
"when -fobjc-gc-only is specified">;
@@ -610,9 +647,19 @@ def warn_auto_synthesizing_protocol_property :Warning<
"auto property synthesis will not synthesize property"
" declared in a protocol">,
InGroup<DiagGroup<"objc-protocol-property-synthesis">>;
+def warn_autosynthesis_property_ivar_match :Warning<
+ "autosynthesized property %0 will use %select{|synthesized}1 instance variable "
+ "%2, not existing instance variable %3">,
+ InGroup<DiagGroup<"objc-autosynthesis-property-ivar-name-match">>;
+def warn_missing_explicit_synthesis : Warning <
+ "auto property synthesis is synthesizing property not explicitly synthesized">,
+ InGroup<DiagGroup<"objc-missing-property-synthesis">>, DefaultIgnore;
def warn_property_getter_owning_mismatch : Warning<
"property declared as returning non-retained objects"
"; getter returning retained objects">;
+def error_property_setter_ambiguous_use : Error<
+ "synthesized properties '%0' and '%1' both claim setter %2 -"
+ " use of this setter will cause unexpected behavior">;
def err_ownin_getter_rule : Error<
"property's synthesized getter follows Cocoa naming"
" convention for returning 'owned' objects">;
@@ -621,16 +668,16 @@ def warn_default_atomic_custom_getter_setter : Warning<
"(property should be marked 'atomic' if this is intended)">,
InGroup<CustomAtomic>, DefaultIgnore;
def err_use_continuation_class : Error<
- "illegal redeclaration of property in continuation class %0"
+ "illegal redeclaration of property in class extension %0"
" (attribute must be 'readwrite', while its primary must be 'readonly')">;
def err_type_mismatch_continuation_class : Error<
- "type of property %0 in continuation class does not match "
+ "type of property %0 in class extension does not match "
"property type in primary class">;
def err_use_continuation_class_redeclaration_readwrite : Error<
- "illegal redeclaration of 'readwrite' property in continuation class %0"
+ "illegal redeclaration of 'readwrite' property in class extension %0"
" (perhaps you intended this to be a 'readwrite' redeclaration of a "
"'readonly' public property?)">;
-def err_continuation_class : Error<"continuation class has no primary class">;
+def err_continuation_class : Error<"class extension has no primary class">;
def err_property_type : Error<"property cannot have array or function type %0">;
def error_missing_property_context : Error<
"missing context for property implementation declaration">;
@@ -664,15 +711,16 @@ def warn_arc_perform_selector_leaks : Warning<
def err_gc_weak_property_strong_type : Error<
"weak attribute declared on a __strong type property in GC mode">;
def warn_receiver_is_weak : Warning <
- "weak receiver may be unpredictably null in ARC mode">,
+ "weak %select{receiver|property|implicit property}0 may be "
+ "unpredictably null in ARC mode">,
InGroup<DiagGroup<"receiver-is-weak">>, DefaultIgnore;
-
-def error_synthesized_ivar_yet_not_supported : Error<
- "instance variable synthesis not yet supported"
- " (need to declare %0 explicitly)">;
+def err_incomplete_synthesized_property : Error<
+ "cannot synthesize property %0 with incomplete type %1">;
def error_property_ivar_type : Error<
"type of property %0 (%1) does not match type of ivar %2 (%3)">;
+def error_property_accessor_type : Error<
+ "type of property %0 (%1) does not match type of accessor %2 (%3)">;
def error_ivar_in_superclass_use : Error<
"property %0 attempting to use ivar %1 declared in super class %2">;
def error_weak_property : Error<
@@ -690,6 +738,9 @@ def warn_objc_property_attr_mutually_exclusive : Warning<
def warn_objc_missing_super_dealloc : Warning<
"method possibly missing a [super dealloc] call">,
InGroup<ObjCMissingSuperCalls>;
+def error_dealloc_bad_result_type : Error<
+ "dealloc return type must be correctly specified as 'void' under ARC, "
+ "instead of %0">;
def warn_objc_missing_super_finalize : Warning<
"method possibly missing a [super finalize] call">,
InGroup<ObjCMissingSuperCalls>;
@@ -697,6 +748,12 @@ def warn_undeclared_selector : Warning<
"undeclared selector %0">, InGroup<UndeclaredSelector>, DefaultIgnore;
def warn_implicit_atomic_property : Warning<
"property is assumed atomic by default">, InGroup<ImplicitAtomic>, DefaultIgnore;
+def note_auto_readonly_iboutlet_fixup_suggest : Note<
+ "readonly IBOutlet property should be changed to be readwrite">;
+def warn_auto_readonly_iboutlet_property : Warning<
+ "readonly IBOutlet property when auto-synthesized may "
+ "not work correctly with 'nib' loader">,
+ InGroup<DiagGroup<"readonly-iboutlet-property">>;
def warn_auto_implicit_atomic_property : Warning<
"property is assumed atomic when auto-synthesizing the property">,
InGroup<ImplicitAtomic>, DefaultIgnore;
@@ -823,8 +880,6 @@ def warn_missing_exception_specification : Warning<
"%0 is missing exception specification '%1'">;
def err_noexcept_needs_constant_expression : Error<
"argument to noexcept specifier must be a constant expression">;
-def err_exception_spec_unknown : Error<
- "exception specification is not available until end of class definition">;
// C++ access checking
def err_class_redeclared_with_different_access : Error<
@@ -854,6 +909,9 @@ def err_access_field_ctor : Error<
"field of type %0 has %select{private|protected}2 "
"%select{default |copy |move |*ERROR* |*ERROR* |*ERROR* |}1constructor">,
AccessControl;
+def err_access_friend_function : Error<
+ "friend function %1 is a %select{private|protected}0 member of %3">,
+ AccessControl;
def err_access_dtor : Error<
"calling a %select{private|protected}1 destructor of class %0">,
@@ -1015,8 +1073,9 @@ def ext_anonymous_struct_union_qualified : Extension<
"anonymous %select{struct|union}0 cannot be '%select{const|volatile|"
"restrict}1'">;
def err_different_return_type_for_overriding_virtual_function : Error<
- "virtual function %0 has a different return type (%1) than the "
- "function it overrides (which has return type %2)">;
+ "virtual function %0 has a different return type "
+ "%diff{($) than the function it overrides (which has return type $)|"
+ "than the function it overrides}1,2">;
def note_overridden_virtual_function : Note<
"overridden virtual function is here">;
@@ -1094,36 +1153,43 @@ def err_destructor_template : Error<
def err_init_conversion_failed : Error<
"cannot initialize %select{a variable|a parameter|return object|an "
"exception object|a member subobject|an array element|a new value|a value|a "
- "base class|a constructor delegation|a vector element}0 of type %1 with an "
- "%select{rvalue|lvalue}2 of type %3"
- "%select{|: different classes (%5 vs %6)"
+ "base class|a constructor delegation|a vector element}0 "
+ "%diff{of type $ with an %select{rvalue|lvalue}2 of type $|"
+ "with an %select{rvalue|lvalue}2 of incompatible type}1,3"
+ "%select{|: different classes%diff{ ($ vs $)|}5,6"
"|: different number of parameters (%5 vs %6)"
- "|: type mismatch at %ordinal5 parameter (%6 vs %7)"
- "|: different return type (%5 vs %6)"
+ "|: type mismatch at %ordinal5 parameter%diff{ ($ vs $)|}6,7"
+ "|: different return type%diff{ ($ vs $)|}5,6"
"|: different qualifiers ("
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}5 vs "
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}6)}4">;
-def err_lvalue_to_rvalue_ref : Error<"rvalue reference to type %0 cannot bind "
- "to lvalue of type %1">;
+def err_lvalue_to_rvalue_ref : Error<"rvalue reference %diff{to type $ cannot "
+ "bind to lvalue of type $|cannot bind to incompatible lvalue}0,1">;
def err_lvalue_reference_bind_to_initlist : Error<
"%select{non-const|volatile}0 lvalue reference to type %1 cannot bind to an "
"initializer list temporary">;
def err_lvalue_reference_bind_to_temporary : Error<
- "%select{non-const|volatile}0 lvalue reference to type %1 cannot bind to a "
- "temporary of type %2">;
+ "%select{non-const|volatile}0 lvalue reference %diff{to type $ cannot bind "
+ "to a temporary of type $|cannot bind to incompatible temporary}1,2">;
def err_lvalue_reference_bind_to_unrelated : Error<
- "%select{non-const|volatile}0 lvalue reference to type %1 cannot bind to a "
- "value of unrelated type %2">;
+ "%select{non-const|volatile}0 lvalue reference "
+ "%diff{to type $ cannot bind to a value of unrelated type $|"
+ "cannot bind to a value of unrelated type}1,2">;
def err_reference_bind_drops_quals : Error<
- "binding of reference to type %0 to a value of type %1 drops qualifiers">;
+ "binding of reference %diff{to type $ to a value of type $ drops qualifiers|"
+ "drops qualifiers}0,1">;
def err_reference_bind_failed : Error<
- "reference to type %0 could not bind to an %select{rvalue|lvalue}1 of type "
- "%2">;
+ "reference %diff{to type $ could not bind to an %select{rvalue|lvalue}1 of "
+ "type $|could not bind to %select{rvalue|lvalue}1 of incompatible type}0,2">;
def err_reference_bind_init_list : Error<
"reference to type %0 cannot bind to an initializer list">;
+def warn_temporary_array_to_pointer_decay : Warning<
+ "pointer is initialized by a temporary array, which will be destroyed at the "
+ "end of the full-expression">,
+ InGroup<DiagGroup<"address-of-array-temporary">>;
def err_init_list_bad_dest_type : Error<
"%select{|non-aggregate }0type %1 cannot be initialized with an initializer "
"list">;
@@ -1154,19 +1220,21 @@ def warn_uninit_self_reference_in_init : Warning<
"variable %0 is uninitialized when used within its own initialization">,
InGroup<Uninitialized>;
def warn_uninit_var : Warning<
- "variable %0 is uninitialized when used here">,
- InGroup<Uninitialized>, DefaultIgnore;
-def warn_maybe_uninit_var :
- Warning<"variable %0 may be uninitialized when used here">,
- InGroup<UninitializedMaybe>, DefaultIgnore;
-def note_uninit_var_def : Note<
- "variable %0 is declared here">;
-def warn_uninit_var_captured_by_block : Warning<
- "variable %0 is uninitialized when captured by block">,
+ "variable %0 is uninitialized when %select{used here|captured by block}1">,
InGroup<Uninitialized>, DefaultIgnore;
-def warn_maybe_uninit_var_captured_by_block : Warning<
- "variable %0 may be uninitialized when captured by block">,
+def warn_sometimes_uninit_var : Warning<
+ "variable %0 is %select{used|captured}1 uninitialized whenever "
+ "%select{'%3' condition is %select{true|false}4|"
+ "'%3' loop %select{is entered|exits because its condition is false}4|"
+ "'%3' loop %select{condition is true|exits because its condition is false}4|"
+ "switch %3 is taken}2">, InGroup<UninitializedSometimes>, DefaultIgnore;
+def warn_maybe_uninit_var : Warning<
+ "variable %0 may be uninitialized when "
+ "%select{used here|captured by block}1">,
InGroup<UninitializedMaybe>, DefaultIgnore;
+def note_uninit_var_def : Note<"variable %0 is declared here">;
+def note_uninit_var_use : Note<
+ "%select{uninitialized use occurs|variable is captured by block}0 here">;
def warn_uninit_byref_blockvar_captured_by_block : Warning<
"block pointer variable %0 is uninitialized when captured by block">,
InGroup<Uninitialized>, DefaultIgnore;
@@ -1174,6 +1242,9 @@ def note_block_var_fixit_add_initialization : Note<
"maybe you meant to use __block %0">;
def note_var_fixit_add_initialization : Note<
"initialize the variable %0 to silence this warning">;
+def note_uninit_fixit_remove_cond : Note<
+ "remove the %select{'%1' if its condition|condition if it}0 "
+ "is always %select{false|true}2">;
def err_init_incomplete_type : Error<"initialization of incomplete type %0">;
def err_temp_copy_no_viable : Error<
@@ -1316,10 +1387,8 @@ def err_delegating_initializer_alone : Error<
def warn_delegating_ctor_cycle : Warning<
"constructor for %0 creates a delegation cycle">, DefaultError,
InGroup<DelegatingCtorCycles>;
-def note_it_delegates_to : Note<
- "it delegates to">, InGroup<DelegatingCtorCycles>;
-def note_which_delegates_to : Note<
- "which delegates to">, InGroup<DelegatingCtorCycles>;
+def note_it_delegates_to : Note<"it delegates to">;
+def note_which_delegates_to : Note<"which delegates to">;
// C++11 range-based for loop
def err_for_range_decl_must_be_var : Error<
@@ -1370,6 +1439,8 @@ def err_constexpr_virtual : Error<"virtual function cannot be constexpr">;
def err_constexpr_virtual_base : Error<
"constexpr %select{member function|constructor}0 not allowed in "
"%select{class|struct}1 with virtual base %plural{1:class|:classes}2">;
+def note_non_literal_incomplete : Note<
+ "incomplete type %0 is not a literal type">;
def note_non_literal_virtual_base : Note<"%select{class|struct}0 with virtual "
"base %plural{1:class|:classes}1 is not a literal type">;
def note_constexpr_virtual_base_here : Note<"virtual base class declared here">;
@@ -1427,6 +1498,10 @@ def warn_cxx98_compat_unicode_type : Warning<
// Objective-C++
def err_objc_decls_may_only_appear_in_global_scope : Error<
"Objective-C declarations may only appear in global scope">;
+def warn_auto_var_is_id : Warning<
+ "'auto' deduced as 'id' in declaration of %0">,
+ InGroup<DiagGroup<"auto-var-id">>;
+
// Attributes
def err_nsobject_attribute : Error<
"__attribute ((NSObject)) is for pointer types only">;
@@ -1451,6 +1526,8 @@ def err_attribute_bad_neon_vector_size : Error<
"Neon vector size must be 64 or 128 bits">;
def err_attribute_argument_not_int : Error<
"'%0' attribute requires integer constant">;
+def err_aligned_attribute_argument_not_int : Error<
+ "'aligned' attribute requires integer constant">;
def err_attribute_argument_not_class : Error<
"%0 attribute requires arguments that are class type or point to class type">;
def err_attribute_first_argument_not_int_or_bool : Error<
@@ -1522,17 +1599,19 @@ def err_undeclared_nsnumber : Error<
"NSNumber must be available to use Objective-C literals">;
def err_invalid_nsnumber_type : Error<
"%0 is not a valid literal type for NSNumber">;
+def err_undeclared_nsstring : Error<
+ "cannot box a string value because NSString has not been declared">;
+def err_objc_illegal_boxed_expression_type : Error<
+ "illegal type %0 used in a boxed expression">;
+def err_objc_incomplete_boxed_expression_type : Error<
+ "incomplete type %0 used in a boxed expression">;
def err_undeclared_nsarray : Error<
"NSArray must be available to use Objective-C array literals">;
def err_undeclared_nsdictionary : Error<
"NSDictionary must be available to use Objective-C dictionary "
"literals">;
-def err_undeclared_arraywithobjects : Error<
- "declaration of %0 is missing in NSArray class">;
-def err_undeclared_dictwithobjects : Error<
- "declaration of %0 is missing in NSDictionary class">;
-def err_undeclared_nsnumber_method : Error<
- "declaration of %0 is missing in NSNumber class">;
+def err_undeclared_boxing_method : Error<
+ "declaration of %0 is missing in %1 class">;
def err_objc_literal_method_sig : Error<
"literal construction method %0 has incompatible signature">;
def note_objc_literal_method_param : Note<
@@ -1545,50 +1624,81 @@ def err_invalid_collection_element : Error<
def err_box_literal_collection : Error<
"%select{string|character|boolean|numeric}0 literal must be prefixed by '@' "
"in a collection">;
+def warn_objc_literal_comparison : Warning<
+ "direct comparison of %select{an array literal|a dictionary literal|"
+ "a numeric literal|a boxed expression|}0 has undefined behavior">,
+ InGroup<ObjCLiteralComparison>;
+def warn_objc_string_literal_comparison : Warning<
+ "direct comparison of a string literal has undefined behavior">,
+ InGroup<ObjCStringComparison>;
+def note_objc_literal_comparison_isequal : Note<
+ "use 'isEqual:' instead">;
let CategoryName = "Cocoa API Issue" in {
def warn_objc_redundant_literal_use : Warning<
"using %0 with a literal is redundant">, InGroup<ObjCRedundantLiteralUse>;
}
+def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", "
+ "\"local-dynamic\", \"initial-exec\" or \"local-exec\"">;
+
def err_only_annotate_after_access_spec : Error<
"access specifier can only have annotation attributes">;
+
def err_attribute_section_invalid_for_target : Error<
"argument to 'section' attribute is not valid for this target: %0">;
def err_attribute_section_local_variable : Error<
"'section' attribute is not valid on local variables">;
+def warn_mismatched_section : Warning<
+ "section does not match previous declaration">, InGroup<Section>;
+
def err_attribute_aligned_not_power_of_two : Error<
"requested alignment is not a power of 2">;
+def err_attribute_aligned_greater_than_8192 : Error<
+ "requested alignment must be 8192 bytes or smaller">;
def warn_redeclaration_without_attribute_prev_attribute_ignored : Warning<
"'%0' redeclared without %1 attribute: previous %1 ignored">;
-def warn_attribute_ignored : Warning<"%0 attribute ignored">;
+def warn_attribute_ignored : Warning<"%0 attribute ignored">,
+ InGroup<IgnoredAttributes>;
+def warn_attribute_after_definition_ignored : Warning<
+ "attribute %0 after definition is ignored">,
+ InGroup<IgnoredAttributes>;
def warn_unknown_attribute_ignored : Warning<
"unknown attribute %0 ignored">, InGroup<UnknownAttributes>;
+def warn_unhandled_ms_attribute_ignored : Warning<
+ "__declspec attribute %0 is not supported">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_invalid_on_stmt : Warning<
"attribute %0 cannot be specified on a statement">,
InGroup<IgnoredAttributes>;
def warn_declspec_attribute_ignored : Warning<
"attribute %0 is ignored, place it after \"%select{class|struct|union|enum}1\" to apply attribute to type declaration">, InGroup<IgnoredAttributes>;
def warn_attribute_precede_definition : Warning<
- "attribute declaration must precede definition">;
+ "attribute declaration must precede definition">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_void_function_method : Warning<
"attribute %0 cannot be applied to "
- "%select{functions|Objective-C method}1 without return value">;
+ "%select{functions|Objective-C method}1 without return value">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_weak_on_field : Warning<
- "__weak attribute cannot be specified on a field declaration">;
+ "__weak attribute cannot be specified on a field declaration">,
+ InGroup<IgnoredAttributes>;
def warn_gc_attribute_weak_on_local : Warning<
- "Objective-C GC does not allow weak variables on the stack">;
+ "Objective-C GC does not allow weak variables on the stack">,
+ InGroup<IgnoredAttributes>;
def warn_nsobject_attribute : Warning<
"__attribute ((NSObject)) may be put on a typedef only, "
"attribute is ignored">, InGroup<NSobjectAttribute>;
def warn_attribute_weak_on_local : Warning<
- "__weak attribute cannot be specified on an automatic variable">;
+ "__weak attribute cannot be specified on an automatic variable">,
+ InGroup<IgnoredAttributes>;
def warn_weak_identifier_undeclared : Warning<
"weak identifier %0 never declared">;
def err_attribute_weak_static : Error<
"weak declaration cannot have internal linkage">;
def warn_attribute_weak_import_invalid_on_definition : Warning<
- "'weak_import' attribute cannot be specified on a definition">;
+ "'weak_import' attribute cannot be specified on a definition">,
+ InGroup<IgnoredAttributes>;
def err_attribute_weakref_not_static : Error<
"weakref declaration must have internal linkage">;
def err_attribute_weakref_not_global_context : Error<
@@ -1602,22 +1712,31 @@ def warn_attribute_wrong_decl_type : Warning<
"variables and functions|functions and methods|parameters|"
"functions, methods and blocks|functions, methods, and parameters|"
"classes|variables|methods|variables, functions and labels|"
- "fields and global variables|structs}1">;
+ "fields and global variables|structs|"
+ "variables, functions and tag types|thread-local variables}1">,
+ InGroup<IgnoredAttributes>;
def err_attribute_wrong_decl_type : Error<
"%0 attribute only applies to %select{functions|unions|"
"variables and functions|functions and methods|parameters|"
"functions, methods and blocks|functions, methods, and parameters|"
"classes|variables|methods|variables, functions and labels|"
- "fields and global variables|structs}1">;
+ "fields and global variables|structs|thread-local variables}1">;
def warn_function_attribute_wrong_type : Warning<
- "'%0' only applies to function types; type here is %1">;
+ "'%0' only applies to function types; type here is %1">,
+ InGroup<IgnoredAttributes>;
def warn_pointer_attribute_wrong_type : Warning<
- "'%0' only applies to pointer types; type here is %1">;
+ "'%0' only applies to pointer types; type here is %1">,
+ InGroup<IgnoredAttributes>;
def warn_objc_object_attribute_wrong_type : Warning<
- "'%0' only applies to objective-c object or block pointer types; type here is %1">;
+ "'%0' only applies to Objective-C object or block pointer types; type here is %1">,
+ InGroup<IgnoredAttributes>;
+def warn_attribute_requires_functions_or_static_globals : Warning<
+ "%0 only applies to variables with static storage duration and functions">,
+ InGroup<IgnoredAttributes>;
def warn_gnu_inline_attribute_requires_inline : Warning<
"'gnu_inline' attribute requires function to be marked 'inline',"
- " attribute ignored">;
+ " attribute ignored">,
+ InGroup<IgnoredAttributes>;
def err_attribute_vecreturn_only_vector_member : Error<
"the vecreturn attribute can only be used on a class or structure with one member, which must be a vector">;
def err_attribute_vecreturn_only_pod_record : Error<
@@ -1649,79 +1768,95 @@ def warn_attribute_not_on_decl : Error<
// Availability attribute
def warn_availability_unknown_platform : Warning<
- "unknown platform %0 in availability macro">;
+ "unknown platform %0 in availability macro">, InGroup<Availability>;
def warn_availability_version_ordering : Warning<
"feature cannot be %select{introduced|deprecated|obsoleted}0 in %1 version "
"%2 before it was %select{introduced|deprecated|obsoleted}3 in version %4; "
- "attribute ignored">;
-
+ "attribute ignored">, InGroup<Availability>;
+def warn_mismatched_availability: Warning<
+ "availability does not match previous declaration">, InGroup<Availability>;
+
// Thread Safety Attributes
-// Errors when parsing the attributes
+def warn_thread_attribute_ignored : Warning<
+ "ignoring %0 attribute because its argument is invalid">,
+ InGroup<ThreadSafetyAttributes>, DefaultIgnore;
+def warn_thread_attribute_argument_not_lockable : Warning<
+ "%0 attribute requires arguments whose type is annotated "
+ "with 'lockable' attribute; type here is '%1'">,
+ InGroup<ThreadSafetyAttributes>, DefaultIgnore;
+def warn_thread_attribute_argument_not_class : Warning<
+ "%0 attribute requires arguments that are class type or point to"
+ " class type; type here is '%1'">,
+ InGroup<ThreadSafetyAttributes>, DefaultIgnore;
+def warn_thread_attribute_decl_not_lockable : Warning<
+ "%0 attribute can only be applied in a context annotated "
+ "with 'lockable' attribute">,
+ InGroup<ThreadSafetyAttributes>, DefaultIgnore;
+def warn_thread_attribute_decl_not_pointer : Warning<
+ "'%0' only applies to pointer types; type here is %1">,
+ InGroup<ThreadSafetyAttributes>, DefaultIgnore;
+def warn_thread_attribute_wrong_decl_type : Warning<
+ "%0 attribute only applies to %select{"
+ "fields and global variables|functions and methods|"
+ "classes and structs}1">,
+ InGroup<ThreadSafetyAttributes>, DefaultIgnore;
def err_attribute_argument_out_of_range : Error<
"%0 attribute parameter %1 is out of bounds: "
"%plural{0:no parameters to index into|"
"1:can only be 1, since there is one parameter|"
":must be between 1 and %2}2">;
-def warn_attribute_argument_not_lockable : Warning<
- "%0 attribute requires arguments whose type is annotated "
- "with 'lockable' attribute; type here is '%1'">,
- InGroup<ThreadSafety>, DefaultIgnore;
-def warn_attribute_decl_not_lockable : Warning<
- "%0 attribute can only be applied in a context annotated "
- "with 'lockable' attribute">,
- InGroup<ThreadSafety>, DefaultIgnore;
-def warn_attribute_argument_not_class : Warning<
- "%0 attribute requires arguments that are class type or point to"
- " class type; type here is '%1'">,
- InGroup<ThreadSafety>, DefaultIgnore;
+
+// Thread Safety Analysis
def warn_unlock_but_no_lock : Warning<
"unlocking '%0' that was not locked">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_double_lock : Warning<
"locking '%0' that is already locked">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_no_unlock : Warning<
"mutex '%0' is still locked at the end of function">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
+def warn_expecting_locked : Warning<
+ "expecting mutex '%0' to be locked at the end of function">,
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
// FIXME: improve the error message about locks not in scope
def warn_lock_some_predecessors : Warning<
"mutex '%0' is not locked on every path through here">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_expecting_lock_held_on_loop : Warning<
"expecting mutex '%0' to be locked at start of each loop">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def note_locked_here : Note<"mutex acquired here">;
def warn_lock_exclusive_and_shared : Warning<
"mutex '%0' is locked exclusively and shared in the same scope">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def note_lock_exclusive_and_shared : Note<
- "the other lock of mutex '%0' is here">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ "the other lock of mutex '%0' is here">;
def warn_variable_requires_lock : Warning<
"%select{reading|writing}2 variable '%0' requires locking "
"%select{'%1'|'%1' exclusively}2">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_var_deref_requires_lock : Warning<
"%select{reading|writing}2 the value pointed to by '%0' requires locking "
"%select{'%1'|'%1' exclusively}2">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_variable_requires_any_lock : Warning<
"%select{reading|writing}1 variable '%0' requires locking "
"%select{any mutex|any mutex exclusively}1">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_var_deref_requires_any_lock : Warning<
"%select{reading|writing}1 the value pointed to by '%0' requires locking "
"%select{any mutex|any mutex exclusively}1">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_fun_requires_lock : Warning<
"calling function '%0' requires %select{shared|exclusive}2 lock on '%1'">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_fun_excludes_mutex : Warning<
"cannot call function '%0' while mutex '%1' is locked">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_cannot_resolve_lock : Warning<
"cannot resolve lock expression">,
- InGroup<ThreadSafety>, DefaultIgnore;
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_impcast_vector_scalar : Warning<
@@ -1755,8 +1890,7 @@ def warn_impcast_bitfield_precision_constant : Warning<
"implicit truncation from %2 to bitfield changes value from %0 to %1">,
InGroup<ConstantConversion>;
def warn_impcast_literal_float_to_integer : Warning<
- "implicit conversion turns literal floating-point number into integer: "
- "%0 to %1">,
+ "implicit conversion from %0 to %1 changes value from %2 to %3">,
InGroup<LiteralConversion>;
def warn_impcast_string_literal_to_bool : Warning<
"implicit conversion turns string literal into bool: %0 to %1">,
@@ -1767,6 +1901,9 @@ def warn_impcast_different_enum_types : Warning<
def warn_impcast_bool_to_null_pointer : Warning<
"initialization of pointer of type %0 to null from a constant boolean "
"expression">, InGroup<BoolConversion>;
+def warn_non_literal_null_pointer : Warning<
+ "expression which evaluates to zero treated as a null pointer constant of "
+ "type %0">, InGroup<NonLiteralNullConversion>;
def warn_impcast_null_pointer_to_integer : Warning<
"implicit conversion of NULL constant to %0">,
InGroup<NullConversion>;
@@ -1783,28 +1920,37 @@ def warn_cast_align : Warning<
InGroup<CastAlign>, DefaultIgnore;
def warn_attribute_ignored_for_field_of_type : Warning<
- "%0 attribute ignored for field of type %1">;
+ "%0 attribute ignored for field of type %1">,
+ InGroup<IgnoredAttributes>;
def warn_transparent_union_attribute_field_size_align : Warning<
"%select{alignment|size}0 of field %1 (%2 bits) does not match the "
"%select{alignment|size}0 of the first field in transparent union; "
- "transparent_union attribute ignored">;
+ "transparent_union attribute ignored">,
+ InGroup<IgnoredAttributes>;
def note_transparent_union_first_field_size_align : Note<
"%select{alignment|size}0 of first field is %1 bits">;
def warn_transparent_union_attribute_not_definition : Warning<
"transparent_union attribute can only be applied to a union definition; "
- "attribute ignored">;
+ "attribute ignored">,
+ InGroup<IgnoredAttributes>;
def warn_transparent_union_attribute_floating : Warning<
"first field of a transparent union cannot have %select{floating point|"
- "vector}0 type %1; transparent_union attribute ignored">;
+ "vector}0 type %1; transparent_union attribute ignored">,
+ InGroup<IgnoredAttributes>;
def warn_transparent_union_attribute_zero_fields : Warning<
"transparent union definition must contain at least one field; "
- "transparent_union attribute ignored">;
+ "transparent_union attribute ignored">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_type_not_supported : Warning<
- "'%0' attribute argument not supported: %1">;
-def warn_attribute_unknown_visibility : Warning<"unknown visibility '%0'">;
+ "'%0' attribute argument not supported: %1">,
+ InGroup<IgnoredAttributes>;
+def warn_attribute_unknown_visibility : Warning<"unknown visibility '%0'">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_protected_visibility :
Warning<"target does not support 'protected' visibility; using 'default'">,
InGroup<DiagGroup<"unsupported-visibility">>;
+def err_mismatched_visibility: Error<"visibility does not match previous declaration">;
+def note_previous_attribute : Note<"previous attribute is here">;
def err_unknown_machine_mode : Error<"unknown machine mode %0">;
def err_unsupported_machine_mode : Error<"unsupported machine mode %0">;
def err_mode_not_primitive : Error<
@@ -1814,13 +1960,17 @@ def err_mode_wrong_type : Error<
def err_attr_wrong_decl : Error<
"'%0' attribute invalid on this declaration, requires typedef or value">;
def warn_attribute_nonnull_no_pointers : Warning<
- "'nonnull' attribute applied to function with no pointer arguments">;
+ "'nonnull' attribute applied to function with no pointer arguments">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_malloc_pointer_only : Warning<
- "'malloc' attribute only applies to functions returning a pointer type">;
+ "'malloc' attribute only applies to functions returning a pointer type">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_sentinel_named_arguments : Warning<
- "'sentinel' attribute requires named arguments">;
+ "'sentinel' attribute requires named arguments">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_sentinel_not_variadic : Warning<
- "'sentinel' attribute only supported for variadic %select{functions|blocks}0">;
+ "'sentinel' attribute only supported for variadic %select{functions|blocks}0">,
+ InGroup<IgnoredAttributes>;
def err_attribute_sentinel_less_than_zero : Error<
"'sentinel' parameter 1 less than zero">;
def err_attribute_sentinel_not_zero_or_one : Error<
@@ -1832,8 +1982,8 @@ def err_attribute_cleanup_arg_not_function : Error<
def err_attribute_cleanup_func_must_take_one_arg : Error<
"'cleanup' function %0 must take 1 parameter">;
def err_attribute_cleanup_func_arg_incompatible_type : Error<
- "'cleanup' function %0 parameter has type %1 which is incompatible with "
- "type %2">;
+ "'cleanup' function %0 parameter has "
+ "%diff{type $ which is incompatible with type $|incompatible type}1,2">;
def err_attribute_regparm_wrong_platform : Error<
"'regparm' is not valid on this platform">;
def err_attribute_regparm_invalid_number : Error<
@@ -1842,9 +1992,11 @@ def err_attribute_regparm_invalid_number : Error<
// Clang-Specific Attributes
def warn_attribute_iboutlet : Warning<
- "%0 attribute can only be applied to instance variables or properties">;
+ "%0 attribute can only be applied to instance variables or properties">,
+ InGroup<IgnoredAttributes>;
def warn_attribute_ibaction: Warning<
- "ibaction attribute can only be applied to Objective-C instance methods">;
+ "ibaction attribute can only be applied to Objective-C instance methods">,
+ InGroup<IgnoredAttributes>;
def err_iboutletcollection_type : Error<
"invalid type %0 as argument of iboutletcollection attribute">;
def warn_iboutlet_object_type : Warning<
@@ -1862,10 +2014,12 @@ def err_attribute_overloadable_no_prototype : Error<
"'overloadable' function %0 must have a prototype">;
def warn_ns_attribute_wrong_return_type : Warning<
"%0 attribute only applies to %select{functions|methods}1 that "
- "return %select{an Objective-C object|a pointer|a non-retainable pointer}2">;
+ "return %select{an Objective-C object|a pointer|a non-retainable pointer}2">,
+ InGroup<IgnoredAttributes>;
def warn_ns_attribute_wrong_parameter_type : Warning<
"%0 attribute only applies to %select{Objective-C object|pointer}1 "
- "parameters">;
+ "parameters">,
+ InGroup<IgnoredAttributes>;
def err_ns_bridged_not_interface : Error<
"parameter of 'ns_bridged' attribute does not name an Objective-C class">;
@@ -1928,8 +2082,9 @@ def note_default_argument_declared_here : Note<
"default argument declared here">;
def ext_param_promoted_not_compatible_with_prototype : ExtWarn<
- "promoted type %0 of K&R function parameter is not compatible with the "
- "parameter type %1 declared in a previous prototype">,
+ "%diff{promoted type $ of K&R function parameter is not compatible with the "
+ "parameter type $|promoted type of K&R function parameter is not compatible "
+ "with parameter type}0,1 declared in a previous prototype">,
InGroup<KNRPromotedParameter>;
@@ -1964,10 +2119,11 @@ def note_ovl_candidate : Note<"candidate "
"is the implicit copy assignment operator|"
"is the implicit move assignment operator|"
"is an inherited constructor}0%1"
- "%select{| has different class (expected %3 but has %4)"
+ "%select{| has different class%diff{ (expected $ but has $)|}3,4"
"| has different number of parameters (expected %3 but has %4)"
- "| has type mismatch at %ordinal3 parameter (expected %4 but has %5)"
- "| has different return type (%3 expected but has %4)"
+ "| has type mismatch at %ordinal3 parameter"
+ "%diff{ (expected $ but has $)|}4,5"
+ "| has different return type%diff{ ($ expected but has $)|}3,4"
"| has different qualifiers (expected "
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
"|volatile and restrict|const, volatile, and restrict}3 but found "
@@ -1981,7 +2137,7 @@ def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored:
"couldn't infer template argument %0">;
def note_ovl_candidate_inconsistent_deduction : Note<
"candidate template ignored: deduced conflicting %select{types|values|"
- "templates}0 for parameter %1 (%2 vs. %3)">;
+ "templates}0 for parameter %1%diff{ ($ vs. $)|}2,3">;
def note_ovl_candidate_explicit_arg_mismatch_named : Note<
"candidate template ignored: invalid explicitly-specified argument "
"for template parameter %0">;
@@ -1995,7 +2151,9 @@ def note_ovl_candidate_underqualified : Note<
"candidate template ignored: can't deduce a type for %0 which would "
"make %2 equal %1">;
def note_ovl_candidate_substitution_failure : Note<
- "candidate template ignored: substitution failure %0">;
+ "candidate template ignored: substitution failure%0%1">;
+def note_ovl_candidate_disabled_by_enable_if : Note<
+ "candidate template ignored: disabled by %0%1">;
// Note that we don't treat templates differently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
@@ -2009,6 +2167,17 @@ def note_ovl_candidate_arity : Note<"candidate "
"not viable: requires%select{ at least| at most|}2 %3 argument%s3, but %4 "
"%plural{1:was|:were}4 provided">;
+def note_ovl_candidate_arity_one : Note<"candidate "
+ "%select{function|function|constructor|function|function|constructor|"
+ "constructor (the implicit default constructor)|"
+ "constructor (the implicit copy constructor)|"
+ "constructor (the implicit move constructor)|"
+ "function (the implicit copy assignment operator)|"
+ "function (the implicit move assignment operator)|"
+ "constructor (inherited)}0 %select{|template }1not viable: "
+ "%select{requires at least|allows at most single|requires single}2 "
+ "argument %3, but %plural{0:no|:%4}4 arguments were provided">;
+
def note_ovl_candidate_deleted : Note<
"candidate %select{function|function|constructor|"
"function |function |constructor |"
@@ -2035,7 +2204,8 @@ def note_ovl_candidate_bad_conv_incomplete : Note<"candidate "
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"constructor (inherited)}0%1 "
- "not viable: cannot convert argument of incomplete type %2 to %3">;
+ "not viable: cannot convert argument of incomplete type "
+ "%diff{$ to $|to parameter type}2,3">;
def note_ovl_candidate_bad_list_argument : Note<"candidate "
"%select{function|function|constructor|"
"function |function |constructor |"
@@ -2065,12 +2235,13 @@ def note_ovl_candidate_bad_conv : Note<"candidate "
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"constructor (inherited)}0%1"
- " not viable: no known conversion from %2 to %3 for "
- "%select{%ordinal5 argument|object argument}4; "
- "%select{|dereference the argument with *|"
- "take the address of the argument with &|"
- "remove *|"
- "remove &}6">;
+ " not viable: no known conversion "
+ "%diff{from $ to $|from argument type to parameter type}2,3 for "
+ "%select{%ordinal5 argument|object argument}4"
+ "%select{|; dereference the argument with *|"
+ "; take the address of the argument with &|"
+ "; remove *|"
+ "; remove &}6">;
def note_ovl_candidate_bad_arc_conv : Note<"candidate "
"%select{function|function|constructor|"
"function |function |constructor |"
@@ -2080,8 +2251,20 @@ def note_ovl_candidate_bad_arc_conv : Note<"candidate "
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"constructor (inherited)}0%1"
- " not viable: cannot implicitly convert argument of type %2 to %3 for "
+ " not viable: cannot implicitly convert argument "
+ "%diff{of type $ to $|type to parameter type}2,3 for "
"%select{%ordinal5 argument|object argument}4 under ARC">;
+def note_ovl_candidate_bad_lvalue : Note<"candidate "
+ "%select{function|function|constructor|"
+ "function |function |constructor |"
+ "constructor (the implicit default constructor)|"
+ "constructor (the implicit copy constructor)|"
+ "constructor (the implicit move constructor)|"
+ "function (the implicit copy assignment operator)|"
+ "function (the implicit move assignment operator)|"
+ "constructor (inherited)}0%1"
+ " not viable: expects an l-value for "
+ "%select{%ordinal3 argument|object argument}2">;
def note_ovl_candidate_bad_addrspace : Note<"candidate "
"%select{function|function|constructor|"
"function |function |constructor |"
@@ -2163,7 +2346,7 @@ def note_ovl_candidate_bad_target : Note<
" %select{__device__|__global__|__host__|__host__ __device__}2 function">;
def note_ambiguous_type_conversion: Note<
- "because of ambiguity in conversion of %0 to %1">;
+ "because of ambiguity in conversion %diff{of $ to $|between types}0,1">;
def note_ovl_builtin_binary_candidate : Note<
"built-in candidate %0">;
def note_ovl_builtin_unary_candidate : Note<
@@ -2317,6 +2500,8 @@ def note_template_decl_here : Note<"template is declared here">;
def note_member_of_template_here : Note<"member is declared here">;
def err_template_arg_must_be_type : Error<
"template argument for template type parameter must be a type">;
+def err_template_arg_must_be_type_suggest : Error<
+ "template argument for template type parameter must be a type; did you forget 'typename'?">;
def err_template_arg_must_be_expr : Error<
"template argument for non-type template parameter must be an expression">;
def err_template_arg_nontype_ambig : Error<
@@ -2353,6 +2538,9 @@ def err_template_arg_not_ice : Error<
"expression">;
def err_template_arg_not_address_constant : Error<
"non-type template argument of type %0 is not a constant expression">;
+def warn_cxx98_compat_template_arg_null : Warning<
+ "use of null pointer as non-type template argument is incompatible with "
+ "C++98">, InGroup<CXX98Compat>, DefaultIgnore;
def err_template_arg_untyped_null_constant : Error<
"null non-type template argument must be cast to template parameter type %0">;
def err_template_arg_wrongtype_null_constant : Error<
@@ -2360,7 +2548,7 @@ def err_template_arg_wrongtype_null_constant : Error<
"of type %1">;
def err_deduced_non_type_template_arg_type_mismatch : Error<
"deduced non-type template argument does not have the same type as the "
- "its corresponding template parameter (%0 vs %1)">;
+ "its corresponding template parameter%diff{ ($ vs $)|}0,1">;
def err_template_arg_not_convertible : Error<
"non-type template argument of type %0 cannot be converted to a value "
"of type %1">;
@@ -2371,11 +2559,13 @@ def warn_template_arg_too_large : Warning<
"non-type template argument value '%0' truncated to '%1' for "
"template parameter of type %2">, InGroup<Conversion>, DefaultIgnore;
def err_template_arg_no_ref_bind : Error<
- "non-type template parameter of reference type %0 cannot bind to template "
- "argument of type %1">;
+ "non-type template parameter of reference type "
+ "%diff{$ cannot bind to template argument of type $"
+ "|cannot bind to template of incompatible argument type}0,1">;
def err_template_arg_ref_bind_ignores_quals : Error<
- "reference binding of non-type template parameter of type %0 to template "
- "argument of type %1 ignores qualifiers">;
+ "reference binding of non-type template parameter "
+ "%diff{of type $ to template argument of type $|to template argument}0,1 "
+ "ignores qualifiers">;
def err_template_arg_not_decl_ref : Error<
"non-type template argument does not refer to any declaration">;
def err_template_arg_not_object_or_func_form : Error<
@@ -2701,6 +2891,9 @@ def note_explicit_instantiation_definition_here : Note<
// C++ typename-specifiers
def err_typename_nested_not_found : Error<"no type named %0 in %1">;
+def err_typename_nested_not_found_enable_if : Error<
+ "no type named 'type' in %0; 'enable_if' cannot be used to disable "
+ "this declaration">;
def err_typename_nested_not_type : Error<
"typename specifier refers to non-type member %0 in %1">;
def note_typename_refers_here : Note<
@@ -2768,28 +2961,28 @@ def err_unexpanded_parameter_pack_0 : Error<
"size|static assertion|fixed underlying type|enumerator value|"
"using declaration|friend declaration|qualifier|initializer|default argument|"
"non-type template parameter type|exception type|partial specialization|"
- "__if_exists name|__if_not_exists name}0 "
+ "__if_exists name|__if_not_exists name|lambda|block}0 "
"contains an unexpanded parameter pack">;
def err_unexpanded_parameter_pack_1 : Error<
"%select{expression|base type|declaration type|data member type|bit-field "
"size|static assertion|fixed underlying type|enumerator value|"
"using declaration|friend declaration|qualifier|initializer|default argument|"
"non-type template parameter type|exception type|partial specialization|"
- "__if_exists name|__if_not_exists name}0 "
+ "__if_exists name|__if_not_exists name|lambda|block}0 "
"contains unexpanded parameter pack %1">;
def err_unexpanded_parameter_pack_2 : Error<
"%select{expression|base type|declaration type|data member type|bit-field "
"size|static assertion|fixed underlying type|enumerator value|"
"using declaration|friend declaration|qualifier|initializer|default argument|"
"non-type template parameter type|exception type|partial specialization|"
- "__if_exists name|__if_not_exists name}0 "
+ "__if_exists name|__if_not_exists name|lambda|block}0 "
"contains unexpanded parameter packs %1 and %2">;
def err_unexpanded_parameter_pack_3_or_more : Error<
"%select{expression|base type|declaration type|data member type|bit-field "
"size|static assertion|fixed underlying type|enumerator value|"
"using declaration|friend declaration|qualifier|initializer|default argument|"
"non-type template parameter type|exception type|partial specialization|"
- "__if_exists name|__if_not_exists name}0 "
+ "__if_exists name|__if_not_exists name|lambda|block}0 "
"contains unexpanded parameter packs %1, %2, ...">;
def err_pack_expansion_without_parameter_packs : Error<
@@ -2878,8 +3071,8 @@ def note_deleted_dtor_no_operator_delete : Note<
def note_deleted_special_member_class_subobject : Note<
"%select{default constructor|copy constructor|move constructor|"
"copy assignment operator|move assignment operator|destructor}0 of "
- "%select{||||union }4%1 is implicitly deleted because "
- "%select{base class %3|field %3}2 has "
+ "%1 is implicitly deleted because "
+ "%select{base class %3|%select{||||variant }4field %3}2 has "
"%select{no|a deleted|multiple|an inaccessible|a non-trivial}4 "
"%select{%select{default constructor|copy constructor|move constructor|copy "
"assignment operator|move assignment operator|destructor}0|destructor}5"
@@ -2898,8 +3091,8 @@ def note_deleted_copy_user_declared_move : Note<
"copy %select{constructor|assignment operator}0 is implicitly deleted because"
" %1 has a user-declared move %select{constructor|assignment operator}2">;
def note_deleted_assign_field : Note<
- "%select{copy|move}0 assignment operator of %0 is implicitly deleted "
- "because field %1 is of %select{reference|const-qualified}3 type %2">;
+ "%select{copy|move}0 assignment operator of %1 is implicitly deleted "
+ "because field %2 is of %select{reference|const-qualified}4 type %3">;
// This should eventually be an error.
def warn_undefined_internal : Warning<
@@ -2907,6 +3100,17 @@ def warn_undefined_internal : Warning<
DiagGroup<"undefined-internal">;
def note_used_here : Note<"used here">;
+def warn_internal_in_extern_inline : ExtWarn<
+ "static %select{function|variable}0 %1 is used in an inline function with "
+ "external linkage">, InGroup<DiagGroup<"static-in-inline"> >;
+def ext_internal_in_extern_inline : Extension<
+ "static %select{function|variable}0 %1 is used in an inline function with "
+ "external linkage">, InGroup<DiagGroup<"static-in-inline"> >;
+def note_convert_inline_to_static : Note<
+ "use 'static' to give inline function %0 internal linkage">;
+def note_internal_decl_declared_here : Note<
+ "%0 declared here">;
+
def warn_redefinition_of_typedef : ExtWarn<
"redefinition of typedef %0 is a C11 feature">,
InGroup<DiagGroup<"typedef-redefinition"> >;
@@ -2939,7 +3143,8 @@ def warn_forward_class_redefinition : Warning<
"redefinition of forward class %0 of a typedef name of an object type is ignored">,
InGroup<DiagGroup<"objc-forward-class-redefinition">>;
def err_redefinition_different_typedef : Error<
- "%select{typedef|type alias|type alias template}0 redefinition with different types (%1 vs %2)">;
+ "%select{typedef|type alias|type alias template}0 "
+ "redefinition with different types%diff{ ($ vs $)|}1,2">;
def err_tag_reference_non_tag : Error<
"elaborated type refers to %select{a non-tag type|a typedef|a type alias|a template|a type alias template}0">;
def err_tag_reference_conflict : Error<
@@ -2977,6 +3182,9 @@ def err_redefinition_of_enumerator : Error<"redefinition of enumerator %0">;
def err_duplicate_member : Error<"duplicate member %0">;
def err_misplaced_ivar : Error<
"ivars may not be placed in %select{categories|class extension}0">;
+def warn_ivars_in_interface : Warning<
+ "declaration of ivars in the interface is deprecated">,
+ InGroup<DiagGroup<"objc-interface-ivars">>, DefaultIgnore;
def ext_enum_value_not_int : Extension<
"ISO C restricts enumerator values to range of 'int' (%0 is too "
"%select{small|large}1)">;
@@ -3041,7 +3249,8 @@ def err_local_cant_init : Error<
"'__local' variable cannot have an initializer">;
def err_block_extern_cant_init : Error<
"'extern' variable cannot have an initializer">;
-def warn_extern_init : Warning<"'extern' variable has an initializer">;
+def warn_extern_init : Warning<"'extern' variable has an initializer">,
+ InGroup<DiagGroup<"extern-initializer">>;
def err_variable_object_no_init : Error<
"variable-sized object may not be initialized">;
def err_excess_initializers : Error<
@@ -3070,6 +3279,15 @@ def err_empty_scalar_initializer : Error<"scalar initializer cannot be empty">;
def warn_cxx98_compat_empty_scalar_initializer : Warning<
"scalar initialized from empty initializer list is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
+def warn_cxx98_compat_reference_list_init : Warning<
+ "reference initialized from initializer list is incompatible with C++98">,
+ InGroup<CXX98Compat>, DefaultIgnore;
+def warn_cxx98_compat_initializer_list_init : Warning<
+ "initialization of initializer_list object is incompatible with C++98">,
+ InGroup<CXX98Compat>, DefaultIgnore;
+def warn_cxx98_compat_ctor_list_init : Warning<
+ "constructor call from initializer list is incompatible with C++98">,
+ InGroup<CXX98Compat>, DefaultIgnore;
def err_illegal_initializer : Error<
"illegal initializer (only variables can be initialized)">;
def err_illegal_initializer_type : Error<"illegal initializer type %0">;
@@ -3190,7 +3408,7 @@ def note_protected_by___block : Note<
def note_protected_by_objc_ownership : Note<
"jump bypasses initialization of retaining variable">;
def note_enters_block_captures_cxx_obj : Note<
- "jump enters lifetime of block which captures a destructible c++ object">;
+ "jump enters lifetime of block which captures a destructible C++ object">;
def note_enters_block_captures_strong : Note<
"jump enters lifetime of block which strongly captures a variable">;
def note_enters_block_captures_weak : Note<
@@ -3219,7 +3437,7 @@ def note_exits_objc_autoreleasepool : Note<
def note_exits_objc_ownership : Note<
"jump exits scope of retaining variable">;
def note_exits_block_captures_cxx_obj : Note<
- "jump exits lifetime of block which captures a destructible c++ object">;
+ "jump exits lifetime of block which captures a destructible C++ object">;
def note_exits_block_captures_strong : Note<
"jump exits lifetime of block which strongly captures a variable">;
def note_exits_block_captures_weak : Note<
@@ -3329,7 +3547,8 @@ def warn_arc_non_pod_class_with_object_member : Warning<
"to make it ABI-compatible">, InGroup<AutomaticReferenceCountingABI>,
DefaultIgnore;
def warn_arc_retained_assign : Warning<
- "assigning retained object to %select{weak|unsafe_unretained}0 variable"
+ "assigning retained object to %select{weak|unsafe_unretained}0 "
+ "%select{property|variable}1"
"; object will be released after assignment">,
InGroup<ARCUnsafeRetainedAssign>;
def warn_arc_retained_property_assign : Warning<
@@ -3356,8 +3575,7 @@ def err_arc_autoreleasing_capture : Error<
def err_arc_thread_ownership : Error<
"thread-local variable has non-trivial ownership: type is %0">;
def err_arc_indirect_no_ownership : Error<
- "%select{pointer|reference}1 to non-const type %0 with no explicit ownership">,
- InGroup<AutomaticReferenceCounting>;
+ "%select{pointer|reference}1 to non-const type %0 with no explicit ownership">;
def err_arc_array_param_no_ownership : Error<
"must explicitly describe intended ownership of an object array parameter">;
def err_arc_pseudo_dtor_inconstant_quals : Error<
@@ -3378,7 +3596,7 @@ def err_arc_receiver_forward_instance : Error<
"receiver type %0 for instance message is a forward declaration">;
def warn_receiver_forward_instance : Warning<
"receiver type %0 for instance message is a forward declaration">,
- InGroup<DiagGroup<"receiver-forward-class">>, DefaultIgnore;
+ InGroup<ForwardClassReceiver>, DefaultIgnore;
def err_arc_collection_forward : Error<
"collection expression type %0 is a forward declaration">;
def err_arc_multiple_method_decl : Error<
@@ -3454,7 +3672,7 @@ def err_illegal_decl_array_of_functions : Error<
def err_illegal_decl_array_incomplete_type : Error<
"array has incomplete element type %0">;
def err_illegal_message_expr_incomplete_type : Error<
- "objective-c message has incomplete result type %0">;
+ "Objective-C message has incomplete result type %0">;
def err_illegal_decl_array_of_references : Error<
"'%0' declared as array of references of type %1">;
def err_decl_negative_array_size : Error<
@@ -3507,14 +3725,18 @@ def ext_offsetof_extended_field_designator : Extension<
InGroup<DiagGroup<"extended-offsetof">>;
def warn_offsetof_non_pod_type : ExtWarn<"offset of on non-POD type %0">,
InGroup<InvalidOffsetof>;
+def warn_offsetof_non_standardlayout_type : ExtWarn<
+ "offset of on non-standard-layout type %0">, InGroup<InvalidOffsetof>;
def err_offsetof_bitfield : Error<"cannot compute offset of bit-field %0">;
def warn_floatingpoint_eq : Warning<
"comparing floating point with == or != is unsafe">,
InGroup<DiagGroup<"float-equal">>, DefaultIgnore;
-def warn_division_by_zero : Warning<"division by zero is undefined">;
-def warn_remainder_by_zero : Warning<"remainder by zero is undefined">;
+def warn_division_by_zero : Warning<"division by zero is undefined">,
+ InGroup<DivZero>;
+def warn_remainder_by_zero : Warning<"remainder by zero is undefined">,
+ InGroup<DivZero>;
def warn_shift_negative : Warning<"shift count is negative">,
InGroup<DiagGroup<"shift-count-negative">>;
def warn_shift_gt_typewidth : Warning<"shift count >= width of type">,
@@ -3576,17 +3798,17 @@ def warn_sizeof_array_param : Warning<
InGroup<SizeofArrayArgument>;
def err_sizeof_nonfragile_interface : Error<
- "invalid application of '%select{alignof|sizeof}1' to interface %0 in "
- "non-fragile ABI">;
+ "application of '%select{alignof|sizeof}1' to interface %0 is "
+ "not supported on this architecture and platform">;
def err_atdef_nonfragile_interface : Error<
- "invalid application of @defs in non-fragile ABI">;
+ "use of @defs is not supported on this architecture and platform">;
def err_subscript_nonfragile_interface : Error<
- "subscript requires size of interface %0, which is not constant in "
- "non-fragile ABI">;
+ "subscript requires size of interface %0, which is not constant for "
+ "this architecture and platform">;
def err_arithmetic_nonfragile_interface : Error<
- "arithmetic on pointer to interface %0, which is not a constant size in "
- "non-fragile ABI">;
+ "arithmetic on pointer to interface %0, which is not a constant size for "
+ "this architecture and platform">;
def ext_subscript_non_lvalue : Extension<
@@ -3624,6 +3846,8 @@ def warn_subscript_is_char : Warning<"array subscript is of type 'char'">,
def err_typecheck_incomplete_tag : Error<"incomplete definition of type %0">;
def err_no_member : Error<"no member named %0 in %1">;
+def err_no_member_overloaded_arrow : Error<
+ "no member named %0 in %1; did you mean to use '->' instead of '.'?">;
def err_member_not_yet_instantiated : Error<
"no member %0 in %1; it has not yet been instantiated">;
@@ -3636,11 +3860,15 @@ def note_enum_specialized_here : Note<
"enum %0 was explicitly specialized here">;
def err_member_redeclared : Error<"class member cannot be redeclared">;
+def err_member_redeclared_in_instantiation : Error<
+ "multiple overloads of %0 instantiate to the same signature %1">;
def err_member_name_of_class : Error<"member %0 has the same name as its class">;
def err_member_def_undefined_record : Error<
"out-of-line definition of %0 from class %1 without definition">;
def err_member_def_does_not_match : Error<
"out-of-line definition of %0 does not match any declaration in %1">;
+def err_friend_decl_does_not_match : Error<
+ "friend declaration of %0 does not match any declaration in %1">;
def err_member_def_does_not_match_suggest : Error<
"out-of-line definition of %0 does not match any declaration in %1; "
"did you mean %2?">;
@@ -3664,8 +3892,8 @@ def note_member_def_close_const_match : Note<
"member declaration does not match because "
"it %select{is|is not}0 const qualified">;
def note_member_def_close_param_match : Note<
- "type of %ordinal0 parameter of member declaration does not match "
- "definition (%1 vs %2)">;
+ "type of %ordinal0 parameter of member declaration does not match definition"
+ "%diff{ ($ vs $)|}1,2">;
def err_typecheck_ivar_variable_size : Error<
"instance variables must have a constant size">;
def err_ivar_reference_type : Error<
@@ -3697,12 +3925,15 @@ def err_array_init_not_init_list : Error<
"array initializer must be an initializer "
"list%select{| or string literal}0">;
def err_array_init_different_type : Error<
- "cannot initialize array of type %0 with array of type %1">;
+ "cannot initialize array %diff{of type $ with array of type $|"
+ "with different type of array}0,1">;
def err_array_init_non_constant_array : Error<
- "cannot initialize array of type %0 with non-constant array of type %1">;
+ "cannot initialize array %diff{of type $ with non-constant array of type $|"
+ "with different type of array}0,1">;
def ext_array_init_copy : Extension<
- "initialization of an array of type %0 from a compound literal of type %1 is "
- "a GNU extension">, InGroup<GNU>;
+ "initialization of an array "
+ "%diff{of type $ from a compound literal of type $|"
+ "from a compound literal}0,1 is a GNU extension">, InGroup<GNU>;
// This is intentionally not disabled by -Wno-gnu.
def ext_array_init_parens : ExtWarn<
"parenthesized initialization of a member array is a GNU extension">,
@@ -3745,7 +3976,7 @@ def note_indirection_through_null : Note<
"consider using __builtin_trap() or qualifying pointer with 'volatile'">;
def warn_pointer_indirection_from_incompatible_type : Warning<
"dereference of type %1 that was reinterpret_cast from type %0 has undefined "
- "behavior.">,
+ "behavior">,
InGroup<DiagGroup<"undefined-reinterpret-cast">>, DefaultIgnore;
def err_objc_object_assignment : Error<
@@ -3753,7 +3984,8 @@ def err_objc_object_assignment : Error<
def err_typecheck_invalid_operands : Error<
"invalid operands to binary expression (%0 and %1)">;
def err_typecheck_sub_ptr_compatible : Error<
- "%0 and %1 are not pointers to compatible types">;
+ "%diff{$ and $ are not pointers to compatible types|"
+ "pointers to incompatible types}0,1">;
def ext_typecheck_ordered_comparison_of_pointer_integer : ExtWarn<
"ordered comparison between pointer and integer (%0 and %1)">;
def ext_typecheck_ordered_comparison_of_pointer_and_zero : Extension<
@@ -3769,13 +4001,14 @@ def ext_typecheck_comparison_of_pointer_integer : ExtWarn<
def err_typecheck_comparison_of_pointer_integer : Error<
"comparison between pointer and integer (%0 and %1)">;
def ext_typecheck_comparison_of_distinct_pointers : ExtWarn<
- "comparison of distinct pointer types (%0 and %1)">;
+ "comparison of distinct pointer types%diff{ ($ and $)|}0,1">;
def ext_typecheck_cond_incompatible_operands : ExtWarn<
"incompatible operand types (%0 and %1)">;
def err_cond_voidptr_arc : Error <
- "operands to conditional of types %0 and %1 are incompatible in ARC mode">;
+ "operands to conditional of types%diff{ $ and $|}0,1 are incompatible "
+ "in ARC mode">;
def err_typecheck_comparison_of_distinct_pointers : Error<
- "comparison of distinct pointer types (%0 and %1)">;
+ "comparison of distinct pointer types%diff{ ($ and $)|}0,1">;
def ext_typecheck_comparison_of_distinct_pointers_nonstandard : ExtWarn<
"comparison of distinct pointer types (%0 and %1) uses non-standard "
"composite pointer type %2">;
@@ -3792,7 +4025,8 @@ def warn_runsigned_always_true_comparison : Warning<
"comparison of %0 unsigned%select{| enum}2 expression is always %1">,
InGroup<TautologicalCompare>;
def warn_comparison_of_mixed_enum_types : Warning<
- "comparison of two values with different enumeration types (%0 and %1)">,
+ "comparison of two values with different enumeration types"
+ "%diff{ ($ and $)|}0,1">,
InGroup<DiagGroup<"enum-compare">>;
def warn_null_in_arithmetic_operation : Warning<
"use of NULL in arithmetic operation">,
@@ -3865,8 +4099,8 @@ def err_objc_subscript_key_type : Error<
def err_objc_subscript_dic_object_type : Error<
"method object parameter type %0 is not object type">;
def err_objc_subscript_object_type : Error<
- "cannot assign to this %select{dictionary|array}1 because assigning method's 2nd parameter"
- " of type %0 is not an objective-C pointer type">;
+ "cannot assign to this %select{dictionary|array}1 because assigning method's "
+ "2nd parameter of type %0 is not an Objective-C pointer type">;
def err_objc_subscript_base_type : Error<
"%select{dictionary|array}1 subscript base type %0 is not an Objective-C object">;
def err_objc_multiple_subscript_type_conversion : Error<
@@ -3874,17 +4108,17 @@ def err_objc_multiple_subscript_type_conversion : Error<
"multiple type conversion functions">;
def err_objc_subscript_type_conversion : Error<
"indexing expression is invalid because subscript type %0 is not an integral"
- " or objective-C pointer type">;
+ " or Objective-C pointer type">;
def err_objc_subscript_pointer : Error<
"indexing expression is invalid because subscript type %0 is not an"
- " objective-C pointer">;
+ " Objective-C pointer">;
def err_objc_indexing_method_result_type : Error<
"method for accessing %select{dictionary|array}1 element must have Objective-C"
" object return type instead of %0">;
def err_objc_index_incomplete_class_type : Error<
- "objective-C index expression has incomplete class type %0">;
+ "Objective-C index expression has incomplete class type %0">;
def err_illegal_container_subscripting_op : Error<
- "illegal operation on objective-c container subscripting">;
+ "illegal operation on Objective-C container subscripting">;
def err_property_not_found_forward_class : Error<
"property %0 cannot be found in forward class object %1">;
def err_property_not_as_forward_class : Error<
@@ -3902,7 +4136,7 @@ def ext_gnu_ptr_func_arith : Extension<
"type%select{|s}2 %1%select{| and %3}2 is a GNU extension">,
InGroup<PointerArith>;
def error_readonly_message_assignment : Error<
- "assigning to 'readonly' return result of an objective-c message not allowed">;
+ "assigning to 'readonly' return result of an Objective-C message not allowed">;
def ext_integer_increment_complex : Extension<
"ISO C does not support '++'/'--' on complex integer type %0">;
def ext_integer_complement_complex : Extension<
@@ -3930,13 +4164,17 @@ def err_imaginary_not_supported : Error<"imaginary types are not supported">;
// Obj-c expressions
def warn_root_inst_method_not_found : Warning<
- "instance method %0 is being used on 'Class' which is not in the root class">;
+ "instance method %0 is being used on 'Class' which is not in the root class">,
+ InGroup<MethodAccess>;
def warn_class_method_not_found : Warning<
- "class method %objcclass0 not found (return type defaults to 'id')">;
+ "class method %objcclass0 not found (return type defaults to 'id')">,
+ InGroup<MethodAccess>;
def warn_instance_method_on_class_found : Warning<
- "instance method %0 found instead of class method %1">;
+ "instance method %0 found instead of class method %1">,
+ InGroup<MethodAccess>;
def warn_inst_method_not_found : Warning<
- "instance method %objcinstance0 not found (return type defaults to 'id')">;
+ "instance method %objcinstance0 not found (return type defaults to 'id')">,
+ InGroup<MethodAccess>;
def error_no_super_class_message : Error<
"no @interface declaration found in class messaging of %0">;
def error_root_class_cannot_use_super : Error<
@@ -3951,7 +4189,7 @@ def err_missing_open_square_message_send : Error<
"missing '[' at start of message send expression">;
def warn_bad_receiver_type : Warning<
"receiver type %0 is not 'id' or interface pointer, consider "
- "casting it to 'id'">;
+ "casting it to 'id'">,InGroup<ObjCReceiver>;
def err_bad_receiver_type : Error<"bad receiver type %0">;
def err_unknown_receiver_suggest : Error<
"unknown receiver %0; did you mean %1?">;
@@ -3978,7 +4216,7 @@ def warn_objc_pointer_cxx_catch_fragile : Warning<
"can not catch an exception thrown with @throw in C++ in the non-unified "
"exception model">, InGroup<ObjCNonUnifiedException>;
def err_objc_object_catch : Error<
- "can't catch an Objective C object by value">;
+ "can't catch an Objective-C object by value">;
def err_incomplete_type_objc_at_encode : Error<
"'@encode' of incomplete type %0">;
@@ -4050,7 +4288,7 @@ def err_bad_cxx_cast_member_pointer_size : Error<
def err_bad_reinterpret_cast_reference : Error<
"reinterpret_cast of a %0 to %1 needs its address which is not allowed">;
def warn_undefined_reinterpret_cast : Warning<
- "reinterpret_cast from %0 to %1 has undefined behavior.">,
+ "reinterpret_cast from %0 to %1 has undefined behavior">,
InGroup<DiagGroup<"undefined-reinterpret-cast">>, DefaultIgnore;
// These messages don't adhere to the pattern.
@@ -4191,11 +4429,12 @@ def err_conditional_void_nonvoid : Error<
"%select{left|right}1 operand to ? is void, but %select{right|left}1 operand "
"is of type %0">;
def err_conditional_ambiguous : Error<
- "conditional expression is ambiguous; %0 can be converted to %1 "
- "and vice versa">;
+ "conditional expression is ambiguous; "
+ "%diff{$ can be converted to $ and vice versa|"
+ "types can be convert to each other}0,1">;
def err_conditional_ambiguous_ovl : Error<
- "conditional expression is ambiguous; %0 and %1 can be converted to several "
- "common types">;
+ "conditional expression is ambiguous; %diff{$ and $|types}0,1 "
+ "can be converted to several common types">;
def err_throw_incomplete : Error<
"cannot throw object of incomplete type %0">;
@@ -4233,10 +4472,6 @@ let CategoryName = "Lambda Issue" in {
def note_lambda_decl : Note<"lambda expression begins here">;
def err_lambda_unevaluated_operand : Error<
"lambda expression in an unevaluated operand">;
- def ext_lambda_implies_void_return : ExtWarn<
- "C++11 requires lambda with omitted result type to consist of a single "
- "return statement">,
- InGroup<LambdaExtensions>;
def err_lambda_return_init_list : Error<
"cannot deduce lambda return type from initializer list">;
def err_lambda_capture_default_arg : Error<
@@ -4278,8 +4513,10 @@ def ext_pseudo_dtor_on_void : ExtWarn<
"pseudo-destructors on type void are a Microsoft extension">,
InGroup<Microsoft>;
def err_pseudo_dtor_type_mismatch : Error<
- "the type of object expression (%0) does not match the type being destroyed "
- "(%1) in pseudo-destructor expression">;
+ "the type of object expression "
+ "%diff{($) does not match the type being destroyed ($)|"
+ "does not match the type being destroyed}0,1 "
+ "in pseudo-destructor expression">;
def err_pseudo_dtor_call_with_args : Error<
"call to pseudo-destructor cannot have any arguments">;
def err_dtor_expr_without_call : Error<
@@ -4296,11 +4533,12 @@ def err_type_defined_in_condition : Error<
def err_typecheck_bool_condition : Error<
"value of type %0 is not contextually convertible to 'bool'">;
def err_typecheck_ambiguous_condition : Error<
- "conversion from %0 to %1 is ambiguous">;
+ "conversion %diff{from $ to $|between types}0,1 is ambiguous">;
def err_typecheck_nonviable_condition : Error<
- "no viable conversion from %0 to %1">;
+ "no viable conversion%diff{ from $ to $|}0,1">;
def err_typecheck_deleted_function : Error<
- "conversion function from %0 to %1 invokes a deleted function">;
+ "conversion function %diff{from $ to $|between types}0,1 "
+ "invokes a deleted function">;
def err_expected_class_or_namespace : Error<"expected a class or namespace">;
def err_expected_class : Error<"%0 is not a class%select{ or namespace|, "
@@ -4314,6 +4552,9 @@ def err_invalid_declarator_in_function : Error<
def err_not_tag_in_scope : Error<
"no %select{struct|union|class|enum}0 named %1 in %2">;
+def err_no_typeid_with_fno_rtti : Error<
+ "cannot use typeid with -fno-rtti">;
+
def err_cannot_form_pointer_to_member_of_reference_type : Error<
"cannot form a pointer-to-member to member %0 of reference type %1">;
def err_incomplete_object_call : Error<
@@ -4344,42 +4585,66 @@ def note_equality_comparison_silence : Note<
// In most of these diagnostics the %2 is a value from the
// Sema::AssignmentAction enumeration
def err_typecheck_convert_incompatible : Error<
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from incompatible type|to parameter of incompatible type|"
- "from a function with incompatible result type|to incompatible type|"
- "with an expression of incompatible type|to parameter of incompatible type|"
- "to incompatible type}2 %1"
+ "%select{%diff{assigning to $ from incompatible type $|"
+ "assigning to type from incompatible type}0,1"
+ "|%diff{passing $ to parameter of incompatible type $|"
+ "passing type to parameter of incompatible type}0,1"
+ "|%diff{returning $ from a function with incompatible result type $|"
+ "returning type from a function with incompatible result type}0,1"
+ "|%diff{converting $ to incompatible type $|"
+ "converting type to incompatible type}0,1"
+ "|%diff{initializing $ with an expression of incompatible type $|"
+ "initializing type with an expression of incompatible type}0,1"
+ "|%diff{sending $ to parameter of incompatible type $|"
+ "sending type to parameter of incompatible type}0,1"
+ "|%diff{casting $ to incompatible type $|"
+ "casting type to incompatible type}0,1}2"
"%select{|; dereference with *|"
"; take the address with &|"
"; remove *|"
"; remove &}3"
- "%select{|: different classes (%5 vs %6)"
+ "%select{|: different classes%diff{ ($ vs $)|}5,6"
"|: different number of parameters (%5 vs %6)"
- "|: type mismatch at %ordinal5 parameter (%6 vs %7)"
- "|: different return type (%5 vs %6)"
+ "|: type mismatch at %ordinal5 parameter%diff{ ($ vs $)|}6,7"
+ "|: different return type%diff{ ($ vs $)|}5,6"
"|: different qualifiers ("
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}5 vs "
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}6)}4">;
def err_typecheck_missing_return_type_incompatible : Error<
- "return type %0 must match previous return type %1 when %select{block "
+ "%diff{return type $ must match previous return type $|"
+ "return type must match previous return type}0,1 when %select{block "
"literal|lambda expression}2 has unspecified explicit return type">;
def warn_incompatible_qualified_id : Warning<
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from incompatible type|to parameter of incompatible type|"
- "from a function with incompatible result type|to incompatible type|"
- "with an expression of incompatible type|to parameter of incompatible type|"
- "to incompatible type}2 %1">;
+ "%select{%diff{assigning to $ from incompatible type $|"
+ "assigning to type from incompatible type}0,1"
+ "|%diff{passing $ to parameter of incompatible type $|"
+ "passing type to parameter of incompatible type}0,1"
+ "|%diff{returning $ from a function with incompatible result type $|"
+ "returning type from a function with incompatible result type}0,1"
+ "|%diff{converting $ to incompatible type $|"
+ "converting type to incompatible type}0,1"
+ "|%diff{initializing $ with an expression of incompatible type $|"
+ "initializing type with an expression of incompatible type}0,1"
+ "|%diff{sending $ to parameter of incompatible type $|"
+ "sending type to parameter of incompatible type}0,1"
+ "|%diff{casting $ to incompatible type $|"
+ "casting type to incompatible type}0,1}2">;
def ext_typecheck_convert_pointer_int : ExtWarn<
"incompatible pointer to integer conversion "
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1"
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
"%select{|; dereference with *|"
"; take the address with &|"
"; remove *|"
@@ -4387,92 +4652,163 @@ def ext_typecheck_convert_pointer_int : ExtWarn<
InGroup<IntConversion>;
def ext_typecheck_convert_int_pointer : ExtWarn<
"incompatible integer to pointer conversion "
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1"
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
"%select{|; dereference with *|"
"; take the address with &|"
"; remove *|"
"; remove &}3">,
InGroup<IntConversion>;
def ext_typecheck_convert_pointer_void_func : Extension<
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1 "
- "converts between void pointer and function pointer">;
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " converts between void pointer and function pointer">;
def ext_typecheck_convert_incompatible_pointer_sign : ExtWarn<
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1 "
- "converts between pointers to integer types with different sign">,
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " converts between pointers to integer types with different sign">,
InGroup<DiagGroup<"pointer-sign">>;
def ext_typecheck_convert_incompatible_pointer : ExtWarn<
"incompatible pointer types "
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1"
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
"%select{|; dereference with *|"
"; take the address with &|"
"; remove *|"
"; remove &}3">,
InGroup<IncompatiblePointerTypes>;
def ext_typecheck_convert_discards_qualifiers : ExtWarn<
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1 discards "
- "qualifiers">,
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " discards qualifiers">,
InGroup<IncompatiblePointerTypes>;
def ext_nested_pointer_qualifier_mismatch : ExtWarn<
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1 discards "
- "qualifiers in nested pointer types">,
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " discards qualifiers in nested pointer types">,
InGroup<IncompatiblePointerTypes>;
def warn_incompatible_vectors : Warning<
"incompatible vector types "
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1">,
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2">,
InGroup<VectorConversion>, DefaultIgnore;
def err_int_to_block_pointer : Error<
"invalid block pointer conversion "
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1">;
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2">;
def err_typecheck_convert_incompatible_block_pointer : Error<
"incompatible block pointer types "
- "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
- " %0 "
- "%select{from|to parameter of type|from a function with result type|to type|"
- "with an expression of type|to parameter of type|to type}2 %1">;
+ "%select{%diff{assigning to $ from $|assigning to different types}0,1"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2">;
def err_typecheck_incompatible_address_space : Error<
- "%select{assigning %1 to %0"
- "|passing %0 to parameter of type %1"
- "|returning %0 from a function with result type %1"
- "|converting %0 to type %1"
- "|initializing %0 with an expression of type %1"
- "|sending %0 to parameter of type %1"
- "|casting %0 to type %1}2"
+ "%select{%diff{assigning $ to $|assigning to different types}1,0"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
" changes address space of pointer">;
def err_typecheck_incompatible_ownership : Error<
- "%select{assigning %1 to %0"
- "|passing %0 to parameter of type %1"
- "|returning %0 from a function with result type %1"
- "|converting %0 to type %1"
- "|initializing %0 with an expression of type %1"
- "|sending %0 to parameter of type %1"
- "|casting %0 to type %1}2"
+ "%select{%diff{assigning $ to $|assigning to different types}1,0"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
" changes retain/release properties of pointer">;
def err_typecheck_comparison_of_distinct_blocks : Error<
- "comparison of distinct block types (%0 and %1)">;
+ "comparison of distinct block types%diff{ ($ and $)|}0,1">;
def err_typecheck_array_not_modifiable_lvalue : Error<
"array type %0 is not assignable">;
@@ -4505,18 +4841,34 @@ def err_typecheck_call_too_few_args : Error<
"too few %select{|||execution configuration }0arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2">;
+def err_typecheck_call_too_few_args_one : Error<
+ "too few %select{|||execution configuration }0arguments to "
+ "%select{function|block|method|kernel function}0 call, "
+ "single argument %1 was not specified">;
def err_typecheck_call_too_few_args_at_least : Error<
"too few %select{|||execution configuration }0arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at least %1, have %2">;
+def err_typecheck_call_too_few_args_at_least_one : Error<
+ "too few %select{|||execution configuration }0arguments to "
+ "%select{function|block|method|kernel function}0 call, "
+ "at least argument %1 must be specified">;
def err_typecheck_call_too_many_args : Error<
"too many %select{|||execution configuration }0arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2">;
+def err_typecheck_call_too_many_args_one : Error<
+ "too many %select{|||execution configuration }0arguments to "
+ "%select{function|block|method|kernel function}0 call, "
+ "expected single argument %1, have %2 arguments">;
def err_typecheck_call_too_many_args_at_most : Error<
"too many %select{|||execution configuration }0arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at most %1, have %2">;
+def err_typecheck_call_too_many_args_at_most_one : Error<
+ "too many %select{|||execution configuration }0arguments to "
+ "%select{function|block|method|kernel function}0 call, "
+ "expected at most single argument %1, have %2 arguments">;
def note_callee_decl : Note<
"%0 declared here">;
def note_defined_here : Note<"%0 defined here">;
@@ -4558,11 +4910,19 @@ def err_ref_bad_target : Error<
"reference to %select{__device__|__global__|__host__|__host__ __device__}0 "
"function %1 in %select{__device__|__global__|__host__|__host__ __device__}2 function">;
+def warn_non_pod_vararg_with_format_string : Warning<
+ "cannot pass %select{non-POD|non-trivial}0 object of type %1 to variadic "
+ "%select{function|block|method|constructor}2; expected type from format "
+ "string was %3">, InGroup<DiagGroup<"non-pod-varargs">>, DefaultError;
+// The arguments to this diagnostic should match the warning above.
+def err_cannot_pass_objc_interface_to_vararg_format : Error<
+ "cannot pass object with interface type %1 by value to variadic "
+ "%select{function|block|method|constructor}2; expected type from format "
+ "string was %3">;
def err_cannot_pass_objc_interface_to_vararg : Error<
- "cannot pass object with interface type %0 by-value through variadic "
- "%select{function|block|method}1">;
-
+ "cannot pass object with interface type %0 by value through variadic "
+ "%select{function|block|method|constructor}1">;
def warn_cannot_pass_non_pod_arg_to_vararg : Warning<
"cannot pass object of %select{non-POD|non-trivial}0 type %1 through variadic"
" %select{function|block|method|constructor}2; call will abort at runtime">,
@@ -4573,7 +4933,8 @@ def warn_cxx98_compat_pass_non_pod_arg_to_vararg : Warning<
InGroup<CXX98Compat>, DefaultIgnore;
def err_typecheck_call_invalid_ordered_compare : Error<
- "ordered compare requires two args of floating point type (%0 and %1)">;
+ "ordered compare requires two args of floating point type"
+ "%diff{ ($ and $)|}0,1">;
def err_typecheck_call_invalid_unary_fp : Error<
"floating point classification requires argument of floating point type "
"(passed in %0)">;
@@ -4597,17 +4958,18 @@ def err_cast_pointer_to_non_pointer_int : Error<
def err_typecheck_expect_scalar_operand : Error<
"operand of type %0 where arithmetic or pointer type is required">;
def err_typecheck_cond_incompatible_operands : Error<
- "incompatible operand types (%0 and %1)">;
+ "incompatible operand types%diff{ ($ and $)|}0,1">;
def ext_typecheck_cond_incompatible_operands_nonstandard : ExtWarn<
- "incompatible operand types (%0 and %1) use non-standard composite pointer "
- "type %2">;
+ "incompatible operand types%diff{ ($ and $)|}0,1 use non-standard composite "
+ "pointer type %2">;
def err_cast_selector_expr : Error<
"cannot type cast @selector expression">;
def warn_typecheck_cond_incompatible_pointers : ExtWarn<
- "pointer type mismatch (%0 and %1)">,
+ "pointer type mismatch%diff{ ($ and $)|}0,1">,
InGroup<DiagGroup<"pointer-type-mismatch">>;
def warn_typecheck_cond_pointer_integer_mismatch : ExtWarn<
- "pointer/integer type mismatch in conditional expression (%0 and %1)">,
+ "pointer/integer type mismatch in conditional expression"
+ "%diff{ ($ and $)|}0,1">,
InGroup<DiagGroup<"conditional-type-mismatch">>;
def err_typecheck_choose_expr_requires_constant : Error<
"'__builtin_choose_expr' requires a constant expression">;
@@ -4628,6 +4990,10 @@ def warn_unused_call : Warning<
def warn_unused_result : Warning<
"ignoring return value of function declared with warn_unused_result "
"attribute">, InGroup<DiagGroup<"unused-result">>;
+def warn_unused_volatile : Warning<
+ "expression result unused; assign into a variable to force a volatile load">,
+ InGroup<DiagGroup<"unused-volatile-lvalue">>;
+
def warn_unused_comparison : Warning<
"%select{equality|inequality}0 comparison result unused">,
InGroup<UnusedComparison>;
@@ -4661,7 +5027,8 @@ let CategoryName = "Inline Assembly Issue" in {
def err_asm_invalid_type_in_input : Error<
"invalid type %0 in asm input for constraint '%1'">;
def err_asm_tying_incompatible_types : Error<
- "unsupported inline asm: input with type %0 matching output with type %1">;
+ "unsupported inline asm: input with type "
+ "%diff{$ matching output with type $|}0,1">;
def err_asm_unknown_register_name : Error<"unknown register name '%0' in asm">;
def warn_asm_label_on_auto_decl : Warning<
"ignored asm label '%0' on automatic variable">;
@@ -4673,12 +5040,16 @@ let CategoryName = "Inline Assembly Issue" in {
"invalid use of a cast in a inline asm context requiring an l-value: "
"accepted due to -fheinous-gnu-extensions, but clang may remove support "
"for this in the future">;
+
+ def warn_unsupported_msasm : ExtWarn<
+ "MS-style inline assembly is not supported">, InGroup<Microsoft>;
}
let CategoryName = "Semantic Issue" in {
def err_invalid_conversion_between_vectors : Error<
- "invalid conversion between vector type %0 and %1 of different size">;
+ "invalid conversion between vector type%diff{ $ and $|}0,1 of different "
+ "size">;
def err_invalid_conversion_between_vector_and_integer : Error<
"invalid conversion between vector type %0 and integer type %1 "
"of different size">;
@@ -4730,6 +5101,9 @@ def err_in_class_initializer_literal_type : Error<
"'constexpr' specifier">;
def err_in_class_initializer_non_constant : Error<
"in-class initializer for static data member is not a constant expression">;
+def err_in_class_initializer_references_def_ctor : Error<
+ "defaulted default constructor of %0 cannot be used by non-static data "
+ "member initializer which appears before end of class definition">;
def ext_in_class_initializer_non_constant : Extension<
"in-class initializer for static data member is not a constant expression; "
@@ -4846,9 +5220,9 @@ def err_operator_new_delete_invalid_result_type : Error<
def err_operator_new_delete_dependent_result_type : Error<
"%0 cannot have a dependent return type; use %1 instead">;
def err_operator_new_delete_too_few_parameters : Error<
- "%0 must have at least one parameter.">;
+ "%0 must have at least one parameter">;
def err_operator_new_delete_template_too_few_parameters : Error<
- "%0 template must have at least two parameters.">;
+ "%0 template must have at least two parameters">;
def err_operator_new_dependent_param_type : Error<
"%0 cannot take a dependent type as first parameter; "
@@ -4912,60 +5286,33 @@ def warn_cxx98_compat_explicit_conversion_functions : Warning<
InGroup<CXX98Compat>, DefaultIgnore;
// C++11 defaulted functions
-def err_defaulted_default_ctor_params : Error<
- "an explicitly-defaulted default constructor must have no parameters">;
-def err_defaulted_copy_ctor_params : Error<
- "an explicitly-defaulted copy constructor must have exactly one parameter">;
-def err_defaulted_copy_ctor_volatile_param : Error<
- "the parameter for an explicitly-defaulted copy constructor may not be "
- "volatile">;
-def err_defaulted_copy_ctor_const_param : Error<
- "the parameter for this explicitly-defaulted copy constructor is const, but "
- "a member or base requires it to be non-const">;
-def err_defaulted_copy_assign_params : Error<
- "an explicitly-defaulted copy assignment operator must have exactly one "
- "parameter">;
-def err_defaulted_copy_assign_return_type : Error<
- "an explicitly-defaulted copy assignment operator must return an unqualified "
- "lvalue reference to its class type">;
+def err_defaulted_special_member_params : Error<
+ "an explicitly-defaulted %select{|copy |move }0constructor cannot "
+ "have default arguments">;
+def err_defaulted_special_member_return_type : Error<
+ "explicitly-defaulted %select{copy|move}0 assignment operator must "
+ "return %1">;
+def err_defaulted_special_member_quals : Error<
+ "an explicitly-defaulted %select{copy|move}0 assignment operator may not "
+ "have 'const', 'constexpr' or 'volatile' qualifiers">;
+def err_defaulted_special_member_volatile_param : Error<
+ "the parameter for an explicitly-defaulted %select{<<ERROR>>|"
+ "copy constructor|move constructor|copy assignment operator|"
+ "move assignment operator|<<ERROR>>}0 may not be volatile">;
+def err_defaulted_special_member_move_const_param : Error<
+ "the parameter for an explicitly-defaulted move "
+ "%select{constructor|assignment operator}0 may not be const">;
+def err_defaulted_special_member_copy_const_param : Error<
+ "the parameter for this explicitly-defaulted copy "
+ "%select{constructor|assignment operator}0 is const, but a member or base "
+ "requires it to be non-const">;
+def err_defaulted_special_member_copy_non_const_param : Error<
+ "explicitly-defaulted copy %select{constructor|assignment operator}0 with "
+ "a non-const parameter must be defaulted outside the class, unless a base or "
+ "member requires the parameter to be non-const">;
def err_defaulted_copy_assign_not_ref : Error<
"the parameter for an explicitly-defaulted copy assignment operator must be an "
"lvalue reference type">;
-def err_defaulted_copy_assign_volatile_param : Error<
- "the parameter for an explicitly-defaulted copy assignment operator may not "
- "be volatile">;
-def err_defaulted_copy_assign_const_param : Error<
- "the parameter for this explicitly-defaulted copy assignment operator is "
- "const, but a member or base requires it to be non-const">;
-def err_defaulted_copy_assign_quals : Error<
- "an explicitly-defaulted copy assignment operator may not have 'const', "
- "'constexpr' or 'volatile' qualifiers">;
-def err_defaulted_move_ctor_params : Error<
- "an explicitly-defaulted move constructor must have exactly one parameter">;
-def err_defaulted_move_ctor_volatile_param : Error<
- "the parameter for an explicitly-defaulted move constructor may not be "
- "volatile">;
-def err_defaulted_move_ctor_const_param : Error<
- "the parameter for an explicitly-defaulted move constructor may not be "
- "const">;
-def err_defaulted_move_assign_params : Error<
- "an explicitly-defaulted move assignment operator must have exactly one "
- "parameter">;
-def err_defaulted_move_assign_return_type : Error<
- "an explicitly-defaulted move assignment operator must return an unqualified "
- "lvalue reference to its class type">;
-def err_defaulted_move_assign_not_ref : Error<
- "the parameter for an explicitly-defaulted move assignment operator must be an "
- "rvalue reference type">;
-def err_defaulted_move_assign_volatile_param : Error<
- "the parameter for an explicitly-defaulted move assignment operator may not "
- "be volatile">;
-def err_defaulted_move_assign_const_param : Error<
- "the parameter for an explicitly-defaulted move assignment operator may not "
- "be const">;
-def err_defaulted_move_assign_quals : Error<
- "an explicitly-defaulted move assignment operator may not have 'const', "
- "'constexpr' or 'volatile' qualifiers">;
def err_incorrect_defaulted_exception_spec : Error<
"exception specification of explicitly defaulted %select{default constructor|"
"copy constructor|move constructor|copy assignment operator|move assignment "
@@ -4995,9 +5342,6 @@ def warn_array_index_exceeds_bounds : Warning<
def note_array_index_out_of_bounds : Note<
"array %0 declared here">;
-def warn_printf_write_back : Warning<
- "use of '%%n' in format string discouraged (potentially insecure)">,
- InGroup<FormatSecurity>;
def warn_printf_insufficient_data_args : Warning<
"more '%%' conversions than data arguments">, InGroup<Format>;
def warn_printf_data_arg_not_used : Warning<
@@ -5066,7 +5410,8 @@ def warn_scanf_scanlist_incomplete : Warning<
"no closing ']' for '%%[' in scanf format string">,
InGroup<Format>;
def note_format_string_defined : Note<"format string is defined here">;
-
+def note_printf_c_str: Note<"did you mean to call the %0 method?">;
+
def warn_null_arg : Warning<
"null passed to a callee which requires a non-null argument">,
InGroup<NonNull>;
@@ -5118,6 +5463,10 @@ def warn_stringcompare : Warning<
"unspecified (use strncmp instead)">,
InGroup<DiagGroup<"string-compare">>;
+def warn_identity_field_assign : Warning<
+ "assigning %select{field|instance variable}0 to itself">,
+ InGroup<SelfAssignmentField>;
+
// Generic selections.
def err_assoc_type_incomplete : Error<
"type %0 in generic association incomplete">;
@@ -5141,14 +5490,15 @@ def err_blocks_disable : Error<"blocks support disabled - compile with -fblocks"
def err_block_returning_array_function : Error<
"block cannot return %select{array|function}0 type %1">;
-// Builtin annotation string.
-def err_builtin_annotation_not_string_constant : Error<
- "__builtin_annotation requires a non wide string constant">;
+// Builtin annotation
+def err_builtin_annotation_first_arg : Error<
+ "first argument to __builtin_annotation must be an integer">;
+def err_builtin_annotation_second_arg : Error<
+ "second argument to __builtin_annotation must be a non-wide string constant">;
// CFString checking
def err_cfstring_literal_not_string_constant : Error<
- "CFString literal is not a string constant">,
- InGroup<DiagGroup<"CFString-literal">>;
+ "CFString literal is not a string constant">;
def warn_cfstring_truncated : Warning<
"input conversion stopped due to an input byte that does not "
"belong to the input codeset UTF-8">,
@@ -5168,6 +5518,8 @@ def warn_case_value_overflow : Warning<
"overflow converting case value to switch condition type (%0 to %1)">,
InGroup<DiagGroup<"switch">>;
def err_duplicate_case : Error<"duplicate case value '%0'">;
+def err_duplicate_case_differing_expr : Error<
+ "duplicate case value: '%0' and '%1' both equal '%2'">;
def warn_case_empty_range : Warning<"empty case range specified">;
def warn_missing_case_for_condition :
Warning<"no case matching constant switch condition '%0'">;
@@ -5197,11 +5549,35 @@ def warn_missing_cases : Warning<
"%0 enumeration values not handled in switch: %1, %2, %3...">,
InGroup<Switch>;
+def warn_unannotated_fallthrough : Warning<
+ "unannotated fall-through between switch labels">,
+ InGroup<ImplicitFallthrough>, DefaultIgnore;
+def warn_unannotated_fallthrough_per_function : Warning<
+ "unannotated fall-through between switch labels in partly-annotated "
+ "function">, InGroup<ImplicitFallthroughPerFunction>, DefaultIgnore;
+def note_insert_fallthrough_fixit : Note<
+ "insert '[[clang::fallthrough]];' to silence this warning">;
+def note_insert_break_fixit : Note<
+ "insert 'break;' to avoid fall-through">;
+def err_fallthrough_attr_wrong_target : Error<
+ "clang::fallthrough attribute is only allowed on empty statements">;
+def note_fallthrough_insert_semi_fixit : Note<"did you forget ';'?">;
+def err_fallthrough_attr_outside_switch : Error<
+ "fallthrough annotation is outside switch statement">;
+def warn_fallthrough_attr_invalid_placement : Warning<
+ "fallthrough annotation does not directly precede switch label">,
+ InGroup<ImplicitFallthrough>;
+def warn_fallthrough_attr_unreachable : Warning<
+ "fallthrough annotation in unreachable code">,
+ InGroup<ImplicitFallthrough>;
+
def warn_unreachable_default : Warning<
"default label in switch which covers all enumeration values">,
InGroup<CoveredSwitchDefault>, DefaultIgnore;
def warn_not_in_enum : Warning<"case value not in enumerated type %0">,
InGroup<Switch>;
+def warn_not_in_enum_assignement : Warning<"integer constant not in range "
+ "of enumerated type %0">, InGroup<DiagGroup<"assign-enum">>, DefaultIgnore;
def err_typecheck_statement_requires_scalar : Error<
"statement requires expression of scalar type (%0 invalid)">;
def err_typecheck_statement_requires_integer : Error<
@@ -5229,8 +5605,7 @@ def warn_empty_while_body : Warning<
def warn_empty_switch_body : Warning<
"switch statement has empty body">, InGroup<EmptyBody>;
def note_empty_body_on_separate_line : Note<
- "put the semicolon on a separate line to silence this warning">,
- InGroup<EmptyBody>;
+ "put the semicolon on a separate line to silence this warning">;
def err_va_start_used_in_non_variadic_function : Error<
"'va_start' used in function with fixed args">;
@@ -5318,17 +5693,22 @@ def err_selector_element_not_lvalue : Error<
def err_selector_element_type : Error<
"selector element type %0 is not a valid object">;
def err_collection_expr_type : Error<
- "collection expression type %0 is not a valid object">;
+ "the type %0 is not a pointer to a fast-enumerable object">;
def warn_collection_expr_type : Warning<
"collection expression type %0 may not respond to %1">;
def err_invalid_conversion_between_ext_vectors : Error<
"invalid conversion between ext-vector type %0 and %1">;
+def warn_duplicate_attribute : Warning<
+ "attribute %0 is already applied with different parameters">,
+ InGroup<IgnoredAttributes>;
+
// Type
def ext_invalid_sign_spec : Extension<"'%0' cannot be signed or unsigned">;
def warn_receiver_forward_class : Warning<
- "receiver %0 is a forward class and corresponding @interface may not exist">;
+ "receiver %0 is a forward class and corresponding @interface may not exist">,
+ InGroup<ForwardClassReceiver>;
def note_method_sent_forward_class : Note<"method %0 is used for the forward class">;
def ext_missing_declspec : ExtWarn<
"declaration specifier missing, defaulting to 'int'">;
@@ -5376,13 +5756,19 @@ def warn_attribute_method_def : Warning<
"attributes on method implementation and its declaration must match">,
InGroup<DiagGroup<"mismatched-method-attributes">>;
def ext_typecheck_base_super : Warning<
- "method parameter type %0 does not match "
- "super class method parameter type %1">, InGroup<SuperSubClassMismatch>, DefaultIgnore;
+ "method parameter type "
+ "%diff{$ does not match super class method parameter type $|"
+ "does not match super class method parameter type}0,1">,
+ InGroup<SuperSubClassMismatch>, DefaultIgnore;
def warn_missing_method_return_type : Warning<
"method has no return type specified; defaults to 'id'">,
InGroup<MissingMethodReturnType>, DefaultIgnore;
+def warn_direct_ivar_access : Warning<"instance variable %0 is being "
+ "directly accessed">, InGroup<DiagGroup<"direct-ivar-access">>, DefaultIgnore;
// Spell-checking diagnostics
+def err_unknown_type_or_class_name_suggest : Error<
+ "unknown %select{type|class}2 name %0; did you mean %1?">;
def err_unknown_typename_suggest : Error<
"unknown type name %0; did you mean %1?">;
def err_unknown_nested_typename_suggest : Error<
@@ -5457,14 +5843,19 @@ def err_filter_expression_integral : Error<
// OpenCL warnings and errors.
def err_invalid_astype_of_different_size : Error<
"invalid reinterpretation: sizes of %0 and %1 must match">;
+def err_static_kernel : Error<
+ "kernel functions cannot be declared static">;
+def err_static_function_scope : Error<
+ "variables in function scope cannot be declared static">;
} // end of sema category
let CategoryName = "Related Result Type Issue" in {
// Objective-C related result type compatibility
def warn_related_result_type_compatibility_class : Warning<
- "method is expected to return an instance of its class type %0, but "
- "is declared to return %1">;
+ "method is expected to return an instance of its class type "
+ "%diff{$, but is declared to return $|"
+ ", but is declared to return different type}0,1">;
def warn_related_result_type_compatibility_protocol : Warning<
"protocol method is expected to return an instance of the implementing "
"class, but is declared to return %0">;
@@ -5494,5 +5885,9 @@ def err_module_private_definition : Error<
"definition of %0 must be imported before it is required">;
}
-} // end of sema component.
+let CategoryName = "Documentation Issue" in {
+def warn_not_a_doxygen_trailing_member_comment : Warning<
+ "not a Doxygen trailing comment">, InGroup<Documentation>, DefaultIgnore;
+} // end of documentation issue category
+} // end of sema component.
diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td
index 7f9fe26..a440e80 100644
--- a/include/clang/Basic/DiagnosticSerializationKinds.td
+++ b/include/clang/Basic/DiagnosticSerializationKinds.td
@@ -10,7 +10,7 @@
let Component = "Serialization" in {
def err_fe_unable_to_read_pch_file : Error<
- "unable to read PCH file: '%0'">;
+ "unable to read PCH file %0: '%1'">;
def err_fe_not_a_pch_file : Error<
"input is not a PCH file: '%0'">;
def err_fe_pch_malformed : Error<
@@ -22,6 +22,8 @@ def err_fe_pch_error_at_end_block : Error<
def err_fe_pch_file_modified : Error<
"file '%0' has been modified since the precompiled header was built">,
DefaultFatal;
+def err_fe_pch_file_overridden : Error<
+ "file '%0' from the precompiled header has been overridden">;
def warn_pch_target_triple : Error<
"PCH file was compiled for the target '%0' but the current translation "
diff --git a/include/clang/Basic/ExceptionSpecificationType.h b/include/clang/Basic/ExceptionSpecificationType.h
index e911bde..edd89ec 100644
--- a/include/clang/Basic/ExceptionSpecificationType.h
+++ b/include/clang/Basic/ExceptionSpecificationType.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the ExceptionSpecificationType enumeration and various
-// utility functions.
-//
+///
+/// \file
+/// \brief Defines the ExceptionSpecificationType enumeration and various
+/// utility functions.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_EXCEPTIONSPECIFICATIONTYPE_H
#define LLVM_CLANG_BASIC_EXCEPTIONSPECIFICATIONTYPE_H
@@ -24,7 +25,7 @@ enum ExceptionSpecificationType {
EST_MSAny, ///< Microsoft throw(...) extension
EST_BasicNoexcept, ///< noexcept
EST_ComputedNoexcept, ///< noexcept(expression)
- EST_Delayed, ///< not known yet
+ EST_Unevaluated, ///< not evaluated yet, for special member function
EST_Uninstantiated ///< not instantiated yet
};
@@ -36,6 +37,10 @@ inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
return ESpecType == EST_BasicNoexcept || ESpecType == EST_ComputedNoexcept;
}
+inline bool isUnresolvedExceptionSpec(ExceptionSpecificationType ESpecType) {
+ return ESpecType == EST_Unevaluated || ESpecType == EST_Uninstantiated;
+}
+
/// \brief Possible results from evaluation of a noexcept expression.
enum CanThrowResult {
CT_Cannot,
diff --git a/include/clang/Basic/ExpressionTraits.h b/include/clang/Basic/ExpressionTraits.h
index c4e6a1c..e877715 100644
--- a/include/clang/Basic/ExpressionTraits.h
+++ b/include/clang/Basic/ExpressionTraits.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines enumerations for expression traits intrinsics.
-//
+///
+/// \file
+/// \brief Defines enumerations for expression traits intrinsics.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_EXPRESSIONTRAITS_H
diff --git a/include/clang/Basic/FileManager.h b/include/clang/Basic/FileManager.h
index 5c7d9eb..b00f2b7 100644
--- a/include/clang/Basic/FileManager.h
+++ b/include/clang/Basic/FileManager.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the FileManager interface.
-//
+///
+/// \file
+/// \brief Defines the clang::FileManager interface and associated types.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_FILEMANAGER_H
@@ -40,9 +41,8 @@ namespace clang {
class FileManager;
class FileSystemStatCache;
-/// DirectoryEntry - Cached information about one directory (either on
-/// the disk or in the virtual file system).
-///
+/// \brief Cached information about one directory (either on disk or in
+/// the virtual file system).
class DirectoryEntry {
const char *Name; // Name of the directory.
friend class FileManager;
@@ -51,10 +51,11 @@ public:
const char *getName() const { return Name; }
};
-/// FileEntry - Cached information about one file (either on the disk
-/// or in the virtual file system). If the 'FD' member is valid, then
-/// this FileEntry has an open file descriptor for the file.
+/// \brief Cached information about one file (either on disk
+/// or in the virtual file system).
///
+/// If the 'FD' member is valid, then this FileEntry has an open file
+/// descriptor for the file.
class FileEntry {
const char *Name; // Name of the file.
off_t Size; // File size in bytes.
@@ -96,8 +97,7 @@ public:
time_t getModificationTime() const { return ModTime; }
mode_t getFileMode() const { return FileMode; }
- /// getDir - Return the directory the file lives in.
- ///
+ /// \brief Return the directory the file lives in.
const DirectoryEntry *getDir() const { return Dir; }
bool operator<(const FileEntry &RHS) const {
@@ -105,10 +105,12 @@ public:
}
};
-/// FileManager - Implements support for file system lookup, file system
-/// caching, and directory search management. This also handles more advanced
-/// properties, such as uniquing files based on "inode", so that a file with two
-/// names (e.g. symlinked) will be treated as a single file.
+/// \brief Implements support for file system lookup, file system caching,
+/// and directory search management.
+///
+/// This also handles more advanced properties, such as uniquing files based
+/// on "inode", so that a file with two names (e.g. symlinked) will be treated
+/// as a single file.
///
class FileManager : public RefCountedBase<FileManager> {
FileSystemOptions FileSystemOpts;
@@ -116,30 +118,37 @@ class FileManager : public RefCountedBase<FileManager> {
class UniqueDirContainer;
class UniqueFileContainer;
- /// UniqueRealDirs/UniqueRealFiles - Cache for existing real
- /// directories/files.
- ///
+ /// \brief Cache for existing real directories.
UniqueDirContainer &UniqueRealDirs;
+
+ /// \brief Cache for existing real files.
UniqueFileContainer &UniqueRealFiles;
- /// \brief The virtual directories that we have allocated. For each
- /// virtual file (e.g. foo/bar/baz.cpp), we add all of its parent
+ /// \brief The virtual directories that we have allocated.
+ ///
+ /// For each virtual file (e.g. foo/bar/baz.cpp), we add all of its parent
/// directories (foo/ and foo/bar/) here.
SmallVector<DirectoryEntry*, 4> VirtualDirectoryEntries;
/// \brief The virtual files that we have allocated.
SmallVector<FileEntry*, 4> VirtualFileEntries;
- /// SeenDirEntries/SeenFileEntries - This is a cache that maps paths
- /// to directory/file entries (either real or virtual) we have
- /// looked up. The actual Entries for real directories/files are
+ /// \brief A cache that maps paths to directory entries (either real or
+ /// virtual) we have looked up
+ ///
+ /// The actual Entries for real directories/files are
/// owned by UniqueRealDirs/UniqueRealFiles above, while the Entries
/// for virtual directories/files are owned by
/// VirtualDirectoryEntries/VirtualFileEntries above.
///
llvm::StringMap<DirectoryEntry*, llvm::BumpPtrAllocator> SeenDirEntries;
+
+ /// \brief A cache that maps paths to file entries (either real or
+ /// virtual) we have looked up.
+ ///
+ /// \see SeenDirEntries
llvm::StringMap<FileEntry*, llvm::BumpPtrAllocator> SeenFileEntries;
- /// NextFileUID - Each FileEntry we create is assigned a unique ID #.
+ /// \brief Each FileEntry we create is assigned a unique ID #.
///
unsigned NextFileUID;
@@ -177,8 +186,13 @@ public:
/// \brief Removes the specified FileSystemStatCache object from the manager.
void removeStatCache(FileSystemStatCache *statCache);
- /// getDirectory - Lookup, cache, and verify the specified directory
- /// (real or virtual). This returns NULL if the directory doesn't exist.
+ /// \brief Removes all FileSystemStatCache objects from the manager.
+ void clearStatCaches();
+
+ /// \brief Lookup, cache, and verify the specified directory (real or
+ /// virtual).
+ ///
+ /// This returns NULL if the directory doesn't exist.
///
/// \param CacheFailure If true and the file does not exist, we'll cache
/// the failure to find this file.
@@ -186,7 +200,9 @@ public:
bool CacheFailure = true);
/// \brief Lookup, cache, and verify the specified file (real or
- /// virtual). This returns NULL if the file doesn't exist.
+ /// virtual).
+ ///
+ /// This returns NULL if the file doesn't exist.
///
/// \param OpenFile if true and the file exists, it will be opened.
///
@@ -199,23 +215,29 @@ public:
const FileSystemOptions &getFileSystemOptions() { return FileSystemOpts; }
/// \brief Retrieve a file entry for a "virtual" file that acts as
- /// if there were a file with the given name on disk. The file
- /// itself is not accessed.
+ /// if there were a file with the given name on disk.
+ ///
+ /// The file itself is not accessed.
const FileEntry *getVirtualFile(StringRef Filename, off_t Size,
time_t ModificationTime);
/// \brief Open the specified file as a MemoryBuffer, returning a new
/// MemoryBuffer if successful, otherwise returning null.
llvm::MemoryBuffer *getBufferForFile(const FileEntry *Entry,
- std::string *ErrorStr = 0);
+ std::string *ErrorStr = 0,
+ bool isVolatile = false);
llvm::MemoryBuffer *getBufferForFile(StringRef Filename,
std::string *ErrorStr = 0);
- // getNoncachedStatValue - Will get the 'stat' information for the given path.
- // If the path is relative, it will be resolved against the WorkingDir of the
- // FileManager's FileSystemOptions.
+ /// \brief Get the 'stat' information for the given \p Path.
+ ///
+ /// If the path is relative, it will be resolved against the WorkingDir of the
+ /// FileManager's FileSystemOptions.
bool getNoncachedStatValue(StringRef Path, struct stat &StatBuf);
+ /// \brief Remove the real file \p Entry from the cache.
+ void invalidateCache(const FileEntry *Entry);
+
/// \brief If path is not absolute and FileSystemOptions set the working
/// directory, the path is modified to be relative to the given
/// working directory.
@@ -226,6 +248,11 @@ public:
void GetUniqueIDMapping(
SmallVectorImpl<const FileEntry *> &UIDToFiles) const;
+ /// \brief Modifies the size and modification time of a previously created
+ /// FileEntry. Use with caution.
+ static void modifyFileEntry(FileEntry *File, off_t Size,
+ time_t ModificationTime);
+
void PrintStats() const;
};
diff --git a/include/clang/Basic/FileSystemOptions.h b/include/clang/Basic/FileSystemOptions.h
index 81e928d..38f1346 100644
--- a/include/clang/Basic/FileSystemOptions.h
+++ b/include/clang/Basic/FileSystemOptions.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the FileSystemOptions interface.
-//
+///
+/// \file
+/// \brief Defines the clang::FileSystemOptions interface.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_FILESYSTEMOPTIONS_H
diff --git a/include/clang/Basic/FileSystemStatCache.h b/include/clang/Basic/FileSystemStatCache.h
index 96a2f90..a802c7c 100644
--- a/include/clang/Basic/FileSystemStatCache.h
+++ b/include/clang/Basic/FileSystemStatCache.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the FileSystemStatCache interface.
-//
+///
+/// \file
+/// \brief Defines the FileSystemStatCache interface.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_FILESYSTEMSTATCACHE_H
@@ -34,13 +35,14 @@ public:
virtual ~FileSystemStatCache() {}
enum LookupResult {
- CacheExists, //< We know the file exists and its cached stat data.
- CacheMissing //< We know that the file doesn't exist.
+ CacheExists, ///< We know the file exists and its cached stat data.
+ CacheMissing ///< We know that the file doesn't exist.
};
- /// FileSystemStatCache::get - Get the 'stat' information for the specified
- /// path, using the cache to accellerate it if possible. This returns true if
- /// the path does not exist or false if it exists.
+ /// \brief Get the 'stat' information for the specified path, using the cache
+ /// to accelerate it if possible.
+ ///
+ /// \returns \c true if the path does not exist or \c false if it exists.
///
/// If FileDescriptor is non-null, then this lookup should only return success
/// for files (not directories). If it is null this lookup should only return
diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h
index cc0080b..dc6acda 100644
--- a/include/clang/Basic/IdentifierTable.h
+++ b/include/clang/Basic/IdentifierTable.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the IdentifierInfo, IdentifierTable, and Selector
-// interfaces.
-//
+///
+/// \file
+/// \brief Defines the clang::IdentifierInfo, clang::IdentifierTable, and
+/// clang::Selector interfaces.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_IDENTIFIERTABLE_H
@@ -37,12 +38,12 @@ namespace clang {
class MultiKeywordSelector; // private class used by Selector
class DeclarationName; // AST class that stores declaration names
- /// IdentifierLocPair - A simple pair of identifier info and location.
+ /// \brief A simple pair of identifier info and location.
typedef std::pair<IdentifierInfo*, SourceLocation> IdentifierLocPair;
-/// IdentifierInfo - One of these records is kept for each identifier that
-/// is lexed. This contains information about whether the token was #define'd,
+/// One of these records is kept for each identifier that
+/// is lexed. This contains information about whether the token was \#define'd,
/// is a language keyword, or if it is a front-end token of some sort (e.g. a
/// variable or function name). The preprocessor keeps this information in a
/// set, and all tok::identifier tokens have a pointer to one of these.
@@ -67,7 +68,7 @@ class IdentifierInfo {
bool OutOfDate : 1; // True if there may be additional
// information about this identifier
// stored externally.
- bool IsModulesImport : 1; // True if this is the 'import' contextual
+ bool IsModulesImport : 1; // True if this is the 'import' contextual
// keyword.
// 1 bit left in 32-bit word.
@@ -83,15 +84,16 @@ public:
IdentifierInfo();
- /// isStr - Return true if this is the identifier for the specified string.
+ /// \brief Return true if this is the identifier for the specified string.
+ ///
/// This is intended to be used for string literals only: II->isStr("foo").
template <std::size_t StrLen>
bool isStr(const char (&Str)[StrLen]) const {
return getLength() == StrLen-1 && !memcmp(getNameStart(), Str, StrLen-1);
}
- /// getNameStart - Return the beginning of the actual string for this
- /// identifier. The returned string is properly null terminated.
+ /// \brief Return the beginning of the actual null-terminated string for this
+ /// identifier.
///
const char *getNameStart() const {
if (Entry) return Entry->getKeyData();
@@ -104,7 +106,7 @@ public:
return ((const actualtype*) this)->second;
}
- /// getLength - Efficiently return the length of this identifier info.
+ /// \brief Efficiently return the length of this identifier info.
///
unsigned getLength() const {
if (Entry) return Entry->getKeyLength();
@@ -118,13 +120,12 @@ public:
return (((unsigned) p[0]) | (((unsigned) p[1]) << 8)) - 1;
}
- /// getName - Return the actual identifier string.
+ /// \brief Return the actual identifier string.
StringRef getName() const {
return StringRef(getNameStart(), getLength());
}
- /// hasMacroDefinition - Return true if this identifier is #defined to some
- /// other value.
+ /// \brief Return true if this identifier is \#defined to some other value.
bool hasMacroDefinition() const {
return HasMacro;
}
@@ -158,13 +159,14 @@ public:
RevertedTokenID = true;
}
- /// getPPKeywordID - Return the preprocessor keyword ID for this identifier.
+ /// \brief Return the preprocessor keyword ID for this identifier.
+ ///
/// For example, "define" will return tok::pp_define.
tok::PPKeywordKind getPPKeywordID() const;
- /// getObjCKeywordID - Return the Objective-C keyword ID for the this
- /// identifier. For example, 'class' will return tok::objc_class if ObjC is
- /// enabled.
+ /// \brief Return the Objective-C keyword ID for the this identifier.
+ ///
+ /// For example, 'class' will return tok::objc_class if ObjC is enabled.
tok::ObjCKeywordKind getObjCKeywordID() const {
if (ObjCOrBuiltinID < tok::NUM_OBJC_KEYWORDS)
return tok::ObjCKeywordKind(ObjCOrBuiltinID);
@@ -400,10 +402,11 @@ public:
virtual IdentifierInfo *GetIdentifier(unsigned ID) = 0;
};
-/// IdentifierTable - This table implements an efficient mapping from strings to
-/// IdentifierInfo nodes. It has no other purpose, but this is an
-/// extremely performance-critical piece of the code, as each occurrence of
-/// every identifier goes through here when lexed.
+/// \brief Implements an efficient mapping from strings to IdentifierInfo nodes.
+///
+/// This has no other purpose, but this is an extremely performance-critical
+/// piece of the code, as each occurrence of every identifier goes through
+/// here when lexed.
class IdentifierTable {
// Shark shows that using MallocAllocator is *much* slower than using this
// BumpPtrAllocator!
@@ -413,8 +416,8 @@ class IdentifierTable {
IdentifierInfoLookup* ExternalLookup;
public:
- /// IdentifierTable ctor - Create the identifier table, populating it with
- /// info about the language keywords for the language specified by LangOpts.
+ /// \brief Create the identifier table, populating it with info about the
+ /// language keywords for the language specified by \p LangOpts.
IdentifierTable(const LangOptions &LangOpts,
IdentifierInfoLookup* externalLookup = 0);
@@ -432,8 +435,8 @@ public:
return HashTable.getAllocator();
}
- /// get - Return the identifier token info for the specified named identifier.
- ///
+ /// \brief Return the identifier token info for the specified named
+ /// identifier.
IdentifierInfo &get(StringRef Name) {
llvm::StringMapEntry<IdentifierInfo*> &Entry =
HashTable.GetOrCreateValue(Name);
@@ -507,15 +510,16 @@ public:
iterator end() const { return HashTable.end(); }
unsigned size() const { return HashTable.size(); }
- /// PrintStats - Print some statistics to stderr that indicate how well the
+ /// \brief Print some statistics to stderr that indicate how well the
/// hashing is doing.
void PrintStats() const;
void AddKeywords(const LangOptions &LangOpts);
};
-/// ObjCMethodFamily - A family of Objective-C methods. These
-/// families have no inherent meaning in the language, but are
+/// \brief A family of Objective-C methods.
+///
+/// These families have no inherent meaning in the language, but are
/// nonetheless central enough in the existing implementations to
/// merit direct AST support. While, in theory, arbitrary methods can
/// be considered to form families, we focus here on the methods
@@ -562,11 +566,13 @@ enum ObjCMethodFamily {
/// InvalidObjCMethodFamily.
enum { ObjCMethodFamilyBitWidth = 4 };
-/// An invalid value of ObjCMethodFamily.
+/// \brief An invalid value of ObjCMethodFamily.
enum { InvalidObjCMethodFamily = (1 << ObjCMethodFamilyBitWidth) - 1 };
-/// Selector - This smart pointer class efficiently represents Objective-C
-/// method names. This class will either point to an IdentifierInfo or a
+/// \brief Smart pointer class that efficiently represents Objective-C method
+/// names.
+///
+/// This class will either point to an IdentifierInfo or a
/// MultiKeywordSelector (which is private). This enables us to optimize
/// selectors that take no arguments and selectors that take 1 argument, which
/// accounts for 78% of all selectors in Cocoa.h.
@@ -574,9 +580,10 @@ class Selector {
friend class Diagnostic;
enum IdentifierInfoFlag {
- // MultiKeywordSelector = 0.
+ // Empty selector = 0.
ZeroArg = 0x1,
OneArg = 0x2,
+ MultiArg = 0x3,
ArgFlags = ZeroArg|OneArg
};
uintptr_t InfoPtr; // a pointer to the MultiKeywordSelector or IdentifierInfo.
@@ -590,13 +597,18 @@ class Selector {
Selector(MultiKeywordSelector *SI) {
InfoPtr = reinterpret_cast<uintptr_t>(SI);
assert((InfoPtr & ArgFlags) == 0 &&"Insufficiently aligned IdentifierInfo");
+ InfoPtr |= MultiArg;
}
IdentifierInfo *getAsIdentifierInfo() const {
- if (getIdentifierInfoFlag())
+ if (getIdentifierInfoFlag() < MultiArg)
return reinterpret_cast<IdentifierInfo *>(InfoPtr & ~ArgFlags);
return 0;
}
+ MultiKeywordSelector *getMultiKeywordSelector() const {
+ return reinterpret_cast<MultiKeywordSelector *>(InfoPtr & ~ArgFlags);
+ }
+
unsigned getIdentifierInfoFlag() const {
return InfoPtr & ArgFlags;
}
@@ -661,11 +673,12 @@ public:
/// name was supplied.
StringRef getNameForSlot(unsigned argIndex) const;
- /// getAsString - Derive the full selector name (e.g. "foo:bar:") and return
+ /// \brief Derive the full selector name (e.g. "foo:bar:") and return
/// it as an std::string.
+ // FIXME: Add a print method that uses a raw_ostream.
std::string getAsString() const;
- /// getMethodFamily - Derive the conventional family of this method.
+ /// \brief Derive the conventional family of this method.
ObjCMethodFamily getMethodFamily() const {
return getMethodFamilyImpl(*this);
}
@@ -678,7 +691,7 @@ public:
}
};
-/// SelectorTable - This table allows us to fully hide how we implement
+/// \brief This table allows us to fully hide how we implement
/// multi-keyword caching.
class SelectorTable {
void *Impl; // Actually a SelectorTableImpl
@@ -688,9 +701,10 @@ public:
SelectorTable();
~SelectorTable();
- /// getSelector - This can create any sort of selector. NumArgs indicates
- /// whether this is a no argument selector "foo", a single argument selector
- /// "foo:" or multi-argument "foo:bar:".
+ /// \brief Can create any sort of selector.
+ ///
+ /// \p NumArgs indicates whether this is a no argument selector "foo", a
+ /// single argument selector "foo:" or multi-argument "foo:bar:".
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV);
Selector getUnarySelector(IdentifierInfo *ID) {
@@ -700,11 +714,12 @@ public:
return Selector(ID, 0);
}
- /// Return the total amount of memory allocated for managing selectors.
+ /// \brief Return the total amount of memory allocated for managing selectors.
size_t getTotalMemory() const;
- /// constructSetterName - Return the setter name for the given
- /// identifier, i.e. "set" + Name where the initial character of Name
+ /// \brief Return the setter name for the given identifier.
+ ///
+ /// This is "set" + \p Name where the initial character of \p Name
/// has been capitalized.
static Selector constructSetterName(IdentifierTable &Idents,
SelectorTable &SelTable,
diff --git a/include/clang/Basic/LLVM.h b/include/clang/Basic/LLVM.h
index 813b49e..13c5b44 100644
--- a/include/clang/Basic/LLVM.h
+++ b/include/clang/Basic/LLVM.h
@@ -7,9 +7,10 @@
//
//===----------------------------------------------------------------------===//
//
-// This file forward declares and imports various common LLVM datatypes that
-// clang wants to use unqualified.
-//
+/// \file
+/// \brief Forward declares and imports various common LLVM datatypes that
+/// clang wants to use unqualified.
+///
//===----------------------------------------------------------------------===//
#ifndef CLANG_BASIC_LLVM_H
diff --git a/include/clang/Basic/Lambda.h b/include/clang/Basic/Lambda.h
index df50d94..b1ad6ac 100644
--- a/include/clang/Basic/Lambda.h
+++ b/include/clang/Basic/Lambda.h
@@ -6,9 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines several types used to describe C++ lambda
-// expressions that are shared between the parser and AST.
+///
+/// \file
+/// \brief Defines several types used to describe C++ lambda expressions
+/// that are shared between the parser and AST.
+///
//===----------------------------------------------------------------------===//
@@ -17,16 +19,15 @@
namespace clang {
-/// LambdaCaptureDefault - The default, if any, capture method for a
-/// lambda expression.
+/// \brief The default, if any, capture method for a lambda expression.
enum LambdaCaptureDefault {
LCD_None,
LCD_ByCopy,
LCD_ByRef
};
-/// LambdaCaptureKind - The different capture forms in a lambda
-/// introducer: 'this' or a copied or referenced variable.
+/// \brief The different capture forms in a lambda introducer: 'this' or a
+/// copied or referenced variable.
enum LambdaCaptureKind {
LCK_This,
LCK_ByCopy,
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index d2ce7c0..fab17a2 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -50,8 +50,6 @@ LANGOPT(CPlusPlus , 1, 0, "C++")
LANGOPT(CPlusPlus0x , 1, 0, "C++0x")
LANGOPT(ObjC1 , 1, 0, "Objective-C 1")
LANGOPT(ObjC2 , 1, 0, "Objective-C 2")
-LANGOPT(ObjCNonFragileABI , 1, 0, "Objective-C modern abi")
-LANGOPT(ObjCNonFragileABI2 , 1, 0, "Objective-C enhanced modern abi")
BENIGN_LANGOPT(ObjCDefaultSynthProperties , 1, 0,
"Objective-C auto-synthesized properties")
BENIGN_LANGOPT(ObjCInferRelatedResultType , 1, 1,
@@ -80,7 +78,6 @@ LANGOPT(SjLjExceptions , 1, 0, "setjmp-longjump exception handling")
LANGOPT(TraditionalCPP , 1, 0, "traditional CPP emulation")
LANGOPT(RTTI , 1, 1, "run-time type information")
LANGOPT(MSBitfields , 1, 0, "Microsoft-compatible structure layout")
-LANGOPT(NeXTRuntime , 1, 1, "NeXT Objective-C runtime")
LANGOPT(Freestanding, 1, 0, "freestanding implementation")
LANGOPT(NoBuiltin , 1, 0, "disable builtin functions")
@@ -102,6 +99,7 @@ LANGOPT(GNUInline , 1, 0, "GNU inline semantics")
LANGOPT(NoInlineDefine , 1, 0, "__NO_INLINE__ predefined macro")
LANGOPT(Deprecated , 1, 0, "__DEPRECATED predefined macro")
LANGOPT(FastMath , 1, 0, "__FAST_MATH__ predefined macro")
+LANGOPT(FiniteMathOnly , 1, 0, "__FINITE_MATH_ONLY__ predefined macro")
BENIGN_LANGOPT(ObjCGCBitmapPrint , 1, 0, "printing of GC's bitmap layout for __weak/__strong ivars")
@@ -112,6 +110,7 @@ LANGOPT(ShortWChar , 1, 0, "unsigned short wchar_t")
LANGOPT(ShortEnums , 1, 0, "short enum types")
LANGOPT(OpenCL , 1, 0, "OpenCL")
+LANGOPT(OpenCLVersion , 32, 0, "OpenCL version")
LANGOPT(CUDA , 1, 0, "CUDA")
LANGOPT(AssumeSaneOperatorNew , 1, 1, "implicit __attribute__((malloc)) for C++'s new operators")
@@ -125,7 +124,7 @@ BENIGN_LANGOPT(InlineVisibilityHidden , 1, 0, "hidden default visibility for inl
BENIGN_LANGOPT(ParseUnknownAnytype, 1, 0, "__unknown_anytype")
BENIGN_LANGOPT(DebuggerSupport , 1, 0, "debugger support")
BENIGN_LANGOPT(DebuggerCastResultToId, 1, 0, "for 'po' in the debugger, cast the result to id if it is of unknown type")
-BENIGN_LANGOPT(DebuggerObjCLiteral , 1, 0, "debugger objective-C literals and subscripting support")
+BENIGN_LANGOPT(DebuggerObjCLiteral , 1, 0, "debugger Objective-C literals and subscripting support")
BENIGN_LANGOPT(AddressSanitizer , 1, 0, "AddressSanitizer enabled")
BENIGN_LANGOPT(ThreadSanitizer , 1, 0, "ThreadSanitizer enabled")
@@ -150,8 +149,9 @@ ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff,
"stack protector mode")
ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined,
"signed integer overflow handling")
+ENUM_LANGOPT(FPContractMode, FPContractModeKind, 2, FPC_On, "FP_CONTRACT mode")
-BENIGN_LANGOPT(InstantiationDepth, 32, 1024,
+BENIGN_LANGOPT(InstantiationDepth, 32, 512,
"maximum template instantiation depth")
BENIGN_LANGOPT(ConstexprCallDepth, 32, 512,
"maximum constexpr call depth")
diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h
index ce4ff06..fbb014e 100644
--- a/include/clang/Basic/LangOptions.h
+++ b/include/clang/Basic/LangOptions.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the LangOptions interface.
-//
+///
+/// \file
+/// \brief Defines the clang::LangOptions interface.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LANGOPTIONS_H
@@ -16,6 +17,7 @@
#include <string>
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/Visibility.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -39,8 +41,8 @@ protected:
#include "clang/Basic/LangOptions.def"
};
-/// LangOptions - This class keeps track of the various options that can be
-/// enabled, which controls the dialect of C that is accepted.
+/// \brief Keeps track of the various options that can be
+/// enabled, which controls the dialect of C or C++ that is accepted.
class LangOptions : public RefCountedBase<LangOptions>, public LangOptionsBase {
public:
typedef clang::Visibility Visibility;
@@ -54,10 +56,20 @@ public:
SOB_Trapping // -ftrapv
};
+ enum FPContractModeKind {
+ FPC_Off, // Form fused FP ops only where result will not be affected.
+ FPC_On, // Form fused FP ops according to FP_CONTRACT rules.
+ FPC_Fast // Aggressively fuse FP ops (E.g. FMA).
+ };
+
public:
+ clang::ObjCRuntime ObjCRuntime;
+
std::string ObjCConstantStringClass;
- /// The name of the handler function to be called when -ftrapv is specified.
+ /// \brief The name of the handler function to be called when -ftrapv is
+ /// specified.
+ ///
/// If none is specified, abort (GCC-compatible behaviour).
std::string OverflowHandler;
@@ -82,7 +94,7 @@ public:
void resetNonModularOptions();
};
-/// Floating point control options
+/// \brief Floating point control options
class FPOptions {
public:
unsigned fp_contract : 1;
@@ -93,7 +105,7 @@ public:
fp_contract(LangOpts.DefaultFPContract) {}
};
-/// OpenCL volatile options
+/// \brief OpenCL volatile options
class OpenCLOptions {
public:
#define OPENCLEXT(nm) unsigned nm : 1;
@@ -116,7 +128,6 @@ enum TranslationUnitKind {
TU_Module
};
- /// \brief
} // end namespace clang
#endif
diff --git a/include/clang/Basic/Linkage.h b/include/clang/Basic/Linkage.h
index 09a5a0b..6bc1f5d 100644
--- a/include/clang/Basic/Linkage.h
+++ b/include/clang/Basic/Linkage.h
@@ -6,10 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the Linkage enumeration and various utility
-// functions.
-//
+///
+/// \file
+/// \brief Defines the Linkage enumeration and various utility functions.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_LINKAGE_H
#define LLVM_CLANG_BASIC_LINKAGE_H
@@ -28,8 +28,9 @@ enum Linkage {
/// translation units).
InternalLinkage,
- /// \brief External linkage within a unique namespace. From the
- /// language perspective, these entities have external
+ /// \brief External linkage within a unique namespace.
+ ///
+ /// From the language perspective, these entities have external
/// linkage. However, since they reside in an anonymous namespace,
/// their names are unique to this translation unit, which is
/// equivalent to having internal linkage from the code-generation
@@ -41,8 +42,9 @@ enum Linkage {
ExternalLinkage
};
-/// \brief A more specific kind of linkage. This is relevant to CodeGen and
-/// AST file reading.
+/// \brief A more specific kind of linkage than enum Linkage.
+///
+/// This is relevant to CodeGen and AST file reading.
enum GVALinkage {
GVA_Internal,
GVA_C99Inline,
@@ -52,14 +54,13 @@ enum GVALinkage {
GVA_ExplicitTemplateInstantiation
};
-/// \brief Determine whether the given linkage is semantically
-/// external.
+/// \brief Determine whether the given linkage is semantically external.
inline bool isExternalLinkage(Linkage L) {
return L == UniqueExternalLinkage || L == ExternalLinkage;
}
/// \brief Compute the minimum linkage given two linages.
-static inline Linkage minLinkage(Linkage L1, Linkage L2) {
+inline Linkage minLinkage(Linkage L1, Linkage L2) {
return L1 < L2? L1 : L2;
}
diff --git a/include/clang/Basic/MacroBuilder.h b/include/clang/Basic/MacroBuilder.h
index 1d0f1e8..6df3a38 100644
--- a/include/clang/Basic/MacroBuilder.h
+++ b/include/clang/Basic/MacroBuilder.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the MacroBuilder utility class.
-//
+///
+/// \file
+/// \brief Defines the clang::MacroBuilder utility class.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_MACROBUILDER_H
@@ -24,13 +25,13 @@ class MacroBuilder {
public:
MacroBuilder(raw_ostream &Output) : Out(Output) {}
- /// Append a #define line for macro of the form "#define Name Value\n".
+ /// Append a \#define line for macro of the form "\#define Name Value\n".
void defineMacro(const Twine &Name, const Twine &Value = "1") {
Out << "#define " << Name << ' ' << Value << '\n';
}
- /// Append a #undef line for Name. Name should be of the form XXX
- /// and we emit "#undef XXX".
+ /// Append a \#undef line for Name. Name should be of the form XXX
+ /// and we emit "\#undef XXX".
void undefineMacro(const Twine &Name) {
Out << "#undef " << Name << '\n';
}
diff --git a/include/clang/Basic/Makefile b/include/clang/Basic/Makefile
index 702afac..6a33133 100644
--- a/include/clang/Basic/Makefile
+++ b/include/clang/Basic/Makefile
@@ -1,6 +1,7 @@
CLANG_LEVEL := ../../..
BUILT_SOURCES = \
DiagnosticAnalysisKinds.inc DiagnosticASTKinds.inc \
+ DiagnosticCommentKinds.inc \
DiagnosticCommonKinds.inc DiagnosticDriverKinds.inc \
DiagnosticFrontendKinds.inc DiagnosticLexKinds.inc \
DiagnosticParseKinds.inc DiagnosticSemaKinds.inc \
diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h
index 82dbd5b..c8027f4 100644
--- a/include/clang/Basic/Module.h
+++ b/include/clang/Basic/Module.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the Module class, which describes a module in the source
-// code.
-//
+///
+/// \file
+/// \brief Defines the clang::Module class, which describes a module in the
+/// source code.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_MODULE_H
#define LLVM_CLANG_BASIC_MODULE_H
@@ -137,7 +138,7 @@ public:
llvm::SmallVector<ExportDecl, 2> Exports;
/// \brief Describes an exported module that has not yet been resolved
- /// (perhaps because tASThe module it refers to has not yet been loaded).
+ /// (perhaps because the module it refers to has not yet been loaded).
struct UnresolvedExportDecl {
/// \brief The location of the 'export' keyword in the module map file.
SourceLocation ExportLoc;
@@ -243,7 +244,7 @@ public:
return Umbrella && Umbrella.is<const DirectoryEntry *>();
}
- /// \briaf Add the given feature requirement to the list of features
+ /// \brief Add the given feature requirement to the list of features
/// required by this module.
///
/// \param Feature The feature that is required by this module (and
diff --git a/include/clang/Basic/ObjCRuntime.h b/include/clang/Basic/ObjCRuntime.h
new file mode 100644
index 0000000..b24fe7c
--- /dev/null
+++ b/include/clang/Basic/ObjCRuntime.h
@@ -0,0 +1,264 @@
+//===--- ObjCRuntime.h - Objective-C Runtime Configuration ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines types useful for describing an Objective-C runtime.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_OBJCRUNTIME_H
+#define LLVM_CLANG_OBJCRUNTIME_H
+
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+
+/// \brief The basic abstraction for the target Objective-C runtime.
+class ObjCRuntime {
+public:
+ /// \brief The basic Objective-C runtimes that we know about.
+ enum Kind {
+ /// 'macosx' is the Apple-provided NeXT-derived runtime on Mac OS
+ /// X platforms that use the non-fragile ABI; the version is a
+ /// release of that OS.
+ MacOSX,
+
+ /// 'macosx-fragile' is the Apple-provided NeXT-derived runtime on
+ /// Mac OS X platforms that use the fragile ABI; the version is a
+ /// release of that OS.
+ FragileMacOSX,
+
+ /// 'ios' is the Apple-provided NeXT-derived runtime on iOS or the iOS
+ /// simulator; it is always non-fragile. The version is a release
+ /// version of iOS.
+ iOS,
+
+ /// 'gcc' is the Objective-C runtime shipped with GCC, implementing a
+ /// fragile Objective-C ABI
+ GCC,
+
+ /// 'gnustep' is the modern non-fragile GNUstep runtime.
+ GNUstep,
+
+ /// 'objfw' is the Objective-C runtime included in ObjFW
+ ObjFW
+ };
+
+private:
+ Kind TheKind;
+ VersionTuple Version;
+
+public:
+ /// A bogus initialization of the runtime.
+ ObjCRuntime() : TheKind(MacOSX) {}
+
+ ObjCRuntime(Kind kind, const VersionTuple &version)
+ : TheKind(kind), Version(version) {}
+
+ void set(Kind kind, VersionTuple version) {
+ TheKind = kind;
+ Version = version;
+ }
+
+ Kind getKind() const { return TheKind; }
+ const VersionTuple &getVersion() const { return Version; }
+
+ /// \brief Does this runtime follow the set of implied behaviors for a
+ /// "non-fragile" ABI?
+ bool isNonFragile() const {
+ switch (getKind()) {
+ case FragileMacOSX: return false;
+ case GCC: return false;
+ case MacOSX: return true;
+ case GNUstep: return true;
+ case ObjFW: return false;
+ case iOS: return true;
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// The inverse of isNonFragile(): does this runtime follow the set of
+ /// implied behaviors for a "fragile" ABI?
+ bool isFragile() const { return !isNonFragile(); }
+
+ /// The default dispatch mechanism to use for the specified architecture
+ bool isLegacyDispatchDefaultForArch(llvm::Triple::ArchType Arch) {
+ // The GNUstep runtime uses a newer dispatch method by default from
+ // version 1.6 onwards
+ if (getKind() == GNUstep && getVersion() >= VersionTuple(1, 6)) {
+ if (Arch == llvm::Triple::arm ||
+ Arch == llvm::Triple::x86 ||
+ Arch == llvm::Triple::x86_64)
+ return false;
+ // Mac runtimes use legacy dispatch everywhere except x86-64
+ } else if (isNeXTFamily() && isNonFragile())
+ return Arch != llvm::Triple::x86_64;
+ return true;
+ }
+
+ /// \brief Is this runtime basically of the GNUstep family of runtimes?
+ bool isGNUFamily() const {
+ switch (getKind()) {
+ case FragileMacOSX:
+ case MacOSX:
+ case iOS:
+ return false;
+ case GCC:
+ case GNUstep:
+ case ObjFW:
+ return true;
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// \brief Is this runtime basically of the NeXT family of runtimes?
+ bool isNeXTFamily() const {
+ // For now, this is just the inverse of isGNUFamily(), but that's
+ // not inherently true.
+ return !isGNUFamily();
+ }
+
+ /// \brief Does this runtime natively provide the ARC entrypoints?
+ ///
+ /// ARC cannot be directly supported on a platform that does not provide
+ /// these entrypoints, although it may be supportable via a stub
+ /// library.
+ bool hasARC() const {
+ switch (getKind()) {
+ case FragileMacOSX: return false;
+ case MacOSX: return getVersion() >= VersionTuple(10, 7);
+ case iOS: return getVersion() >= VersionTuple(5);
+
+ case GCC: return false;
+ case GNUstep: return getVersion() >= VersionTuple(1, 6);
+ case ObjFW: return false; // XXX: this will change soon
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// \brief Does this runtime natively provide ARC-compliant 'weak'
+ /// entrypoints?
+ bool hasWeak() const {
+ // Right now, this is always equivalent to the ARC decision.
+ return hasARC();
+ }
+
+ /// \brief Does this runtime directly support the subscripting methods?
+ ///
+ /// This is really a property of the library, not the runtime.
+ bool hasSubscripting() const {
+ switch (getKind()) {
+ case FragileMacOSX: return false;
+ case MacOSX: return getVersion() >= VersionTuple(10, 8);
+ case iOS: return false;
+
+ // This is really a lie, because some implementations and versions
+ // of the runtime do not support ARC. Probably -fgnu-runtime
+ // should imply a "maximal" runtime or something?
+ case GCC: return true;
+ case GNUstep: return true;
+ case ObjFW: return true;
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// \brief Does this runtime allow sizeof or alignof on object types?
+ bool allowsSizeofAlignof() const {
+ return isFragile();
+ }
+
+ /// \brief Does this runtime allow pointer arithmetic on objects?
+ ///
+ /// This covers +, -, ++, --, and (if isSubscriptPointerArithmetic()
+ /// yields true) [].
+ bool allowsPointerArithmetic() const {
+ switch (getKind()) {
+ case FragileMacOSX:
+ case GCC:
+ return true;
+ case MacOSX:
+ case iOS:
+ case GNUstep:
+ case ObjFW:
+ return false;
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// \brief Is subscripting pointer arithmetic?
+ bool isSubscriptPointerArithmetic() const {
+ return allowsPointerArithmetic();
+ }
+
+ /// \brief Does this runtime provide an objc_terminate function?
+ ///
+ /// This is used in handlers for exceptions during the unwind process;
+ /// without it, abort() must be used in pure ObjC files.
+ bool hasTerminate() const {
+ switch (getKind()) {
+ case FragileMacOSX: return getVersion() >= VersionTuple(10, 8);
+ case MacOSX: return getVersion() >= VersionTuple(10, 8);
+ case iOS: return getVersion() >= VersionTuple(5);
+ case GCC: return false;
+ case GNUstep: return false;
+ case ObjFW: return false;
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// \brief Does this runtime support weakly importing classes?
+ bool hasWeakClassImport() const {
+ switch (getKind()) {
+ case MacOSX: return true;
+ case iOS: return true;
+ case FragileMacOSX: return false;
+ case GCC: return true;
+ case GNUstep: return true;
+ case ObjFW: return true;
+ }
+ llvm_unreachable("bad kind");
+ }
+ /// \brief Does this runtime use zero-cost exceptions?
+ bool hasUnwindExceptions() const {
+ switch (getKind()) {
+ case MacOSX: return true;
+ case iOS: return true;
+ case FragileMacOSX: return false;
+ case GCC: return true;
+ case GNUstep: return true;
+ case ObjFW: return true;
+ }
+ llvm_unreachable("bad kind");
+ }
+
+ /// \brief Try to parse an Objective-C runtime specification from the given
+ /// string.
+ ///
+ /// \return true on error.
+ bool tryParse(StringRef input);
+
+ std::string getAsString() const;
+
+ friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) {
+ return left.getKind() == right.getKind() &&
+ left.getVersion() == right.getVersion();
+ }
+
+ friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) {
+ return !(left == right);
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value);
+
+} // end namespace clang
+
+#endif
diff --git a/include/clang/Basic/OnDiskHashTable.h b/include/clang/Basic/OnDiskHashTable.h
index b92f1cf..79273fc 100644
--- a/include/clang/Basic/OnDiskHashTable.h
+++ b/include/clang/Basic/OnDiskHashTable.h
@@ -6,10 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines facilities for reading and writing on-disk hash
-// tables.
-//
+///
+/// \file
+/// \brief Defines facilities for reading and writing on-disk hash tables.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_ON_DISK_HASH_TABLE_H
#define LLVM_CLANG_BASIC_ON_DISK_HASH_TABLE_H
diff --git a/include/clang/Basic/OpenCL.h b/include/clang/Basic/OpenCL.h
index 6f9785f..3b3f259 100644
--- a/include/clang/Basic/OpenCL.h
+++ b/include/clang/Basic/OpenCL.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines some OpenCL-specific enums.
-//
+///
+/// \file
+/// \brief Defines some OpenCL-specific enums.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_OPENCL_H
@@ -16,7 +17,7 @@
namespace clang {
-/// Names for the OpenCL image access qualifiers (OpenCL 1.1 6.6).
+/// \brief Names for the OpenCL image access qualifiers (OpenCL 1.1 6.6).
enum OpenCLImageAccess {
CLIA_read_only = 1,
CLIA_write_only = 2,
diff --git a/include/clang/Basic/OperatorKinds.h b/include/clang/Basic/OperatorKinds.h
index c0a9505..108014f 100644
--- a/include/clang/Basic/OperatorKinds.h
+++ b/include/clang/Basic/OperatorKinds.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines C++ overloaded operators.
-//
+///
+/// \file
+/// \brief Defines an enumeration for C++ overloaded operators.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_OPERATOR_KINDS_H
@@ -16,10 +17,10 @@
namespace clang {
-/// OverloadedOperatorKind - Enumeration specifying the different kinds of
-/// C++ overloaded operators.
+/// \brief Enumeration specifying the different kinds of C++ overloaded
+/// operators.
enum OverloadedOperatorKind {
- OO_None, //< Not an overloaded operator
+ OO_None, ///< Not an overloaded operator
#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \
OO_##Name,
#include "clang/Basic/OperatorKinds.def"
diff --git a/include/clang/Basic/PartialDiagnostic.h b/include/clang/Basic/PartialDiagnostic.h
index 007e6a4..3f4626e 100644
--- a/include/clang/Basic/PartialDiagnostic.h
+++ b/include/clang/Basic/PartialDiagnostic.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file implements a partial diagnostic that can be emitted anwyhere
-// in a DiagnosticBuilder stream.
-//
+///
+/// \file
+/// \brief Implements a partial diagnostic that can be emitted anwyhere
+/// in a DiagnosticBuilder stream.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_PARTIALDIAGNOSTIC_H
@@ -37,25 +38,26 @@ public:
Storage() : NumDiagArgs(0), NumDiagRanges(0) { }
enum {
- /// MaxArguments - The maximum number of arguments we can hold. We
+ /// \brief The maximum number of arguments we can hold. We
/// currently only support up to 10 arguments (%0-%9).
+ ///
/// A single diagnostic with more than that almost certainly has to
/// be simplified anyway.
MaxArguments = PartialDiagnostic::MaxArguments
};
- /// NumDiagArgs - This contains the number of entries in Arguments.
+ /// \brief The number of entries in Arguments.
unsigned char NumDiagArgs;
- /// NumDiagRanges - This is the number of ranges in the DiagRanges array.
+ /// \brief This is the number of ranges in the DiagRanges array.
unsigned char NumDiagRanges;
- /// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum
- /// values, with one for each argument. This specifies whether the argument
- /// is in DiagArgumentsStr or in DiagArguments.
+ /// \brief Specifies for each argument whether it is in DiagArgumentsStr
+ /// or in DiagArguments.
unsigned char DiagArgumentsKind[MaxArguments];
- /// DiagArgumentsVal - The values for the various substitution positions.
+ /// \brief The values for the various substitution positions.
+ ///
/// This is used when the argument is not an std::string. The specific value
/// is mangled into an intptr_t and the interpretation depends on exactly
/// what sort of argument kind it is.
@@ -65,12 +67,13 @@ public:
/// string arguments.
std::string DiagArgumentsStr[MaxArguments];
- /// DiagRanges - The list of ranges added to this diagnostic. It currently
- /// only support 10 ranges, could easily be extended if needed.
+ /// \brief The list of ranges added to this diagnostic.
+ ///
+ /// It currently only support 10 ranges, could easily be extended if needed.
CharSourceRange DiagRanges[10];
- /// FixItHints - If valid, provides a hint with some code
- /// to insert, remove, or modify at a particular position.
+ /// \brief If valid, provides a hint with some code to insert, remove, or
+ /// modify at a particular position.
SmallVector<FixItHint, 6> FixItHints;
};
@@ -114,10 +117,10 @@ private:
// in the sense that its bits can be safely memcpy'ed and destructed
// in the new location.
- /// DiagID - The diagnostic ID.
+ /// \brief The diagnostic ID.
mutable unsigned DiagID;
- /// DiagStorage - Storage for args and ranges.
+ /// \brief Storage for args and ranges.
mutable Storage *DiagStorage;
/// \brief Allocator used to allocate storage for this diagnostic.
@@ -179,6 +182,12 @@ private:
}
public:
+ struct NullDiagnostic {};
+ /// \brief Create a null partial diagnostic, which cannot carry a payload,
+ /// and only exists to be swapped with a real partial diagnostic.
+ PartialDiagnostic(NullDiagnostic)
+ : DiagID(0), DiagStorage(0), Allocator(0) { }
+
PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
: DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { }
@@ -237,6 +246,12 @@ public:
freeStorage();
}
+ void swap(PartialDiagnostic &PD) {
+ std::swap(DiagID, PD.DiagID);
+ std::swap(DiagStorage, PD.DiagStorage);
+ std::swap(Allocator, PD.Allocator);
+ }
+
unsigned getDiagID() const { return DiagID; }
void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
@@ -283,6 +298,18 @@ public:
DB.AddFixItHint(DiagStorage->FixItHints[i]);
}
+ void EmitToString(DiagnosticsEngine &Diags,
+ llvm::SmallVectorImpl<char> &Buf) const {
+ // FIXME: It should be possible to render a diagnostic to a string without
+ // messing with the state of the diagnostics engine.
+ DiagnosticBuilder DB(Diags.Report(getDiagID()));
+ Emit(DB);
+ DB.FlushCounts();
+ Diagnostic(&Diags).FormatDiagnostic(Buf);
+ DB.Clear();
+ Diags.Clear();
+ }
+
/// \brief Clear out this partial diagnostic, giving it a new diagnostic ID
/// and removing all of its arguments, ranges, and fix-it hints.
void Reset(unsigned DiagID = 0) {
diff --git a/include/clang/Basic/PrettyStackTrace.h b/include/clang/Basic/PrettyStackTrace.h
index 06a1264..967d0d1 100644
--- a/include/clang/Basic/PrettyStackTrace.h
+++ b/include/clang/Basic/PrettyStackTrace.h
@@ -6,11 +6,12 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the PrettyStackTraceEntry class, which is used to make
-// crashes give more contextual information about what the program was doing
-// when it crashed.
-//
+///
+/// \file
+/// \brief Defines the PrettyStackTraceEntry class, which is used to make
+/// crashes give more contextual information about what the program was doing
+/// when it crashed.
+///
//===----------------------------------------------------------------------===//
#ifndef CLANG_BASIC_PRETTYSTACKTRACE_H
@@ -21,8 +22,8 @@
namespace clang {
- /// PrettyStackTraceLoc - If a crash happens while one of these objects are
- /// live, the message is printed out along with the specified source location.
+ /// If a crash happens while one of these objects are live, the message
+ /// is printed out along with the specified source location.
class PrettyStackTraceLoc : public llvm::PrettyStackTraceEntry {
SourceManager &SM;
SourceLocation Loc;
diff --git a/include/clang/Basic/SourceLocation.h b/include/clang/Basic/SourceLocation.h
index d5fa7e7..d6bba38 100644
--- a/include/clang/Basic/SourceLocation.h
+++ b/include/clang/Basic/SourceLocation.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the SourceLocation class.
-//
+///
+/// \file
+/// \brief Defines the clang::SourceLocation class and associated facilities.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SOURCELOCATION_H
@@ -31,12 +32,12 @@ namespace clang {
class SourceManager;
-/// FileID - This is an opaque identifier used by SourceManager which refers to
-/// a source file (MemoryBuffer) along with its #include path and #line data.
+/// \brief An opaque identifier used by SourceManager which refers to a
+/// source file (MemoryBuffer) along with its \#include path and \#line data.
///
class FileID {
- /// ID - Opaque identifier, 0 is "invalid". >0 is this module, <-1 is
- /// something loaded from another module.
+ /// \brief A mostly-opaque identifier, where 0 is "invalid", >0 is
+ /// this module, and <-1 is something loaded from another module.
int ID;
public:
FileID() : ID(0) {}
@@ -135,24 +136,28 @@ public:
return L;
}
- /// getRawEncoding - When a SourceLocation itself cannot be used, this returns
- /// an (opaque) 32-bit integer encoding for it. This should only be passed
- /// to SourceLocation::getFromRawEncoding, it should not be inspected
- /// directly.
+ /// \brief When a SourceLocation itself cannot be used, this returns
+ /// an (opaque) 32-bit integer encoding for it.
+ ///
+ /// This should only be passed to SourceLocation::getFromRawEncoding, it
+ /// should not be inspected directly.
unsigned getRawEncoding() const { return ID; }
- /// getFromRawEncoding - Turn a raw encoding of a SourceLocation object into
+ /// \brief Turn a raw encoding of a SourceLocation object into
/// a real SourceLocation.
+ ///
+ /// \see getRawEncoding.
static SourceLocation getFromRawEncoding(unsigned Encoding) {
SourceLocation X;
X.ID = Encoding;
return X;
}
- /// getPtrEncoding - When a SourceLocation itself cannot be used, this returns
- /// an (opaque) pointer encoding for it. This should only be passed
- /// to SourceLocation::getFromPtrEncoding, it should not be inspected
- /// directly.
+ /// \brief When a SourceLocation itself cannot be used, this returns
+ /// an (opaque) pointer encoding for it.
+ ///
+ /// This should only be passed to SourceLocation::getFromPtrEncoding, it
+ /// should not be inspected directly.
void* getPtrEncoding() const {
// Double cast to avoid a warning "cast to pointer from integer of different
// size".
@@ -161,7 +166,7 @@ public:
/// getFromPtrEncoding - Turn a pointer encoding of a SourceLocation object
/// into a real SourceLocation.
- static SourceLocation getFromPtrEncoding(void *Encoding) {
+ static SourceLocation getFromPtrEncoding(const void *Encoding) {
return getFromRawEncoding((unsigned)(uintptr_t)Encoding);
}
@@ -181,7 +186,7 @@ inline bool operator<(const SourceLocation &LHS, const SourceLocation &RHS) {
return LHS.getRawEncoding() < RHS.getRawEncoding();
}
-/// SourceRange - a trival tuple used to represent a source range.
+/// \brief A trival tuple used to represent a source range.
class SourceRange {
SourceLocation B;
SourceLocation E;
@@ -208,7 +213,8 @@ public:
}
};
-/// CharSourceRange - This class represents a character granular source range.
+/// \brief Represents a character-granular source range.
+///
/// The underlying SourceRange can either specify the starting/ending character
/// of the range, or it can specify the start or the range and the start of the
/// last token of the range (a "token range"). In the token range case, the
@@ -242,7 +248,7 @@ public:
return getCharRange(SourceRange(B, E));
}
- /// isTokenRange - Return true if the end of this range specifies the start of
+ /// \brief Return true if the end of this range specifies the start of
/// the last token. Return false if the end of this range specifies the last
/// character in the range.
bool isTokenRange() const { return IsTokenRange; }
@@ -259,17 +265,19 @@ public:
bool isInvalid() const { return !isValid(); }
};
-/// FullSourceLoc - A SourceLocation and its associated SourceManager. Useful
-/// for argument passing to functions that expect both objects.
+/// \brief A SourceLocation and its associated SourceManager.
+///
+/// This is useful for argument passing to functions that expect both objects.
class FullSourceLoc : public SourceLocation {
const SourceManager *SrcMgr;
public:
- /// Creates a FullSourceLoc where isValid() returns false.
+ /// \brief Creates a FullSourceLoc where isValid() returns \c false.
explicit FullSourceLoc() : SrcMgr(0) {}
explicit FullSourceLoc(SourceLocation Loc, const SourceManager &SM)
: SourceLocation(Loc), SrcMgr(&SM) {}
+ /// \pre This FullSourceLoc has an associated SourceManager.
const SourceManager &getManager() const {
assert(SrcMgr && "SourceManager is NULL.");
return *SrcMgr;
@@ -290,13 +298,14 @@ public:
const llvm::MemoryBuffer* getBuffer(bool *Invalid = 0) const;
- /// getBufferData - Return a StringRef to the source buffer data for the
+ /// \brief Return a StringRef to the source buffer data for the
/// specified FileID.
StringRef getBufferData(bool *Invalid = 0) const;
- /// getDecomposedLoc - Decompose the specified location into a raw FileID +
- /// Offset pair. The first element is the FileID, the second is the
- /// offset from the start of the buffer of the location.
+ /// \brief Decompose the specified location into a raw FileID + Offset pair.
+ ///
+ /// The first element is the FileID, the second is the offset from the
+ /// start of the buffer of the location.
std::pair<FileID, unsigned> getDecomposedLoc() const;
bool isInSystemHeader() const;
@@ -323,8 +332,9 @@ public:
}
};
- /// Prints information about this FullSourceLoc to stderr. Useful for
- /// debugging.
+ /// \brief Prints information about this FullSourceLoc to stderr.
+ ///
+ /// This is useful for debugging.
LLVM_ATTRIBUTE_USED void dump() const;
friend inline bool
@@ -340,10 +350,11 @@ public:
};
-/// PresumedLoc - This class represents an unpacked "presumed" location which
-/// can be presented to the user. A 'presumed' location can be modified by
-/// #line and GNU line marker directives and is always the expansion point of
-/// a normal location.
+/// \brief Represents an unpacked "presumed" location which can be presented
+/// to the user.
+///
+/// A 'presumed' location can be modified by \#line and GNU line marker
+/// directives and is always the expansion point of a normal location.
///
/// You can get a PresumedLoc from a SourceLocation with SourceManager.
class PresumedLoc {
@@ -356,25 +367,30 @@ public:
: Filename(FN), Line(Ln), Col(Co), IncludeLoc(IL) {
}
- /// isInvalid - Return true if this object is invalid or uninitialized. This
- /// occurs when created with invalid source locations or when walking off
- /// the top of a #include stack.
+ /// \brief Return true if this object is invalid or uninitialized.
+ ///
+ /// This occurs when created with invalid source locations or when walking
+ /// off the top of a \#include stack.
bool isInvalid() const { return Filename == 0; }
bool isValid() const { return Filename != 0; }
- /// getFilename - Return the presumed filename of this location. This can be
- /// affected by #line etc.
+ /// \brief Return the presumed filename of this location.
+ ///
+ /// This can be affected by \#line etc.
const char *getFilename() const { return Filename; }
- /// getLine - Return the presumed line number of this location. This can be
- /// affected by #line etc.
+ /// \brief Return the presumed line number of this location.
+ ///
+ /// This can be affected by \#line etc.
unsigned getLine() const { return Line; }
- /// getColumn - Return the presumed column number of this location. This can
- /// not be affected by #line, but is packaged here for convenience.
+ /// \brief Return the presumed column number of this location.
+ ///
+ /// This cannot be affected by \#line, but is packaged here for convenience.
unsigned getColumn() const { return Col; }
- /// getIncludeLoc - Return the presumed include location of this location.
+ /// \brief Return the presumed include location of this location.
+ ///
/// This can be affected by GNU linemarker directives.
SourceLocation getIncludeLoc() const { return IncludeLoc; }
};
diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h
index bcb2d56..32268d7 100644
--- a/include/clang/Basic/SourceManager.h
+++ b/include/clang/Basic/SourceManager.h
@@ -6,22 +6,46 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the SourceManager interface.
-//
+///
+/// \file
+/// \brief Defines the SourceManager interface.
+///
+/// There are three different types of locations in a file: a spelling
+/// location, an expansion location, and a presumed location.
+///
+/// Given an example of:
+/// \code
+/// #define min(x, y) x < y ? x : y
+/// \endcode
+///
+/// and then later on a use of min:
+/// \code
+/// #line 17
+/// return min(a, b);
+/// \endcode
+///
+/// The expansion location is the line in the source code where the macro
+/// was expanded (the return statement), the spelling location is the
+/// location in the source where the macro was originally defined,
+/// and the presumed location is where the line directive states that
+/// the line is 17, or any other line.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SOURCEMANAGER_H
#define LLVM_CLANG_SOURCEMANAGER_H
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/MemoryBuffer.h"
#include <map>
#include <vector>
@@ -38,38 +62,25 @@ class LangOptions;
class ASTWriter;
class ASTReader;
-/// There are three different types of locations in a file: a spelling
-/// location, an expansion location, and a presumed location.
-///
-/// Given an example of:
-/// #define min(x, y) x < y ? x : y
-///
-/// and then later on a use of min:
-/// #line 17
-/// return min(a, b);
-///
-/// The expansion location is the line in the source code where the macro
-/// was expanded (the return statement), the spelling location is the
-/// location in the source where the macro was originally defined,
-/// and the presumed location is where the line directive states that
-/// the line is 17, or any other line.
-
-/// SrcMgr - Public enums and private classes that are part of the
+/// \namespace
+/// \brief Public enums and private classes that are part of the
/// SourceManager implementation.
///
namespace SrcMgr {
- /// CharacteristicKind - This is used to represent whether a file or directory
- /// holds normal user code, system code, or system code which is implicitly
- /// 'extern "C"' in C++ mode. Entire directories can be tagged with this
- /// (this is maintained by DirectoryLookup and friends) as can specific
- /// FileInfos when a #pragma system_header is seen or various other cases.
+ /// \brief Indicates whether a file or directory holds normal user code,
+ /// system code, or system code which is implicitly 'extern "C"' in C++ mode.
+ ///
+ /// Entire directories can be tagged with this (this is maintained by
+ /// DirectoryLookup and friends) as can specific FileInfos when a \#pragma
+ /// system_header is seen or in various other cases.
///
enum CharacteristicKind {
C_User, C_System, C_ExternCSystem
};
- /// ContentCache - One instance of this struct is kept for every file
- /// loaded or used. This object owns the MemoryBuffer object.
+ /// \brief One instance of this struct is kept for every file loaded or used.
+ ////
+ /// This object owns the MemoryBuffer object.
class ContentCache {
enum CCFlags {
/// \brief Whether the buffer is invalid.
@@ -78,30 +89,37 @@ namespace SrcMgr {
DoNotFreeFlag = 0x02
};
- /// Buffer - The actual buffer containing the characters from the input
- /// file. This is owned by the ContentCache object.
- /// The bits indicate indicates whether the buffer is invalid.
+ /// \brief The actual buffer containing the characters from the input
+ /// file.
+ ///
+ /// This is owned by the ContentCache object. The bits indicate
+ /// whether the buffer is invalid.
mutable llvm::PointerIntPair<const llvm::MemoryBuffer *, 2> Buffer;
public:
- /// Reference to the file entry representing this ContentCache.
+ /// \brief Reference to the file entry representing this ContentCache.
+ ///
/// This reference does not own the FileEntry object.
- /// It is possible for this to be NULL if
- /// the ContentCache encapsulates an imaginary text buffer.
+ ///
+ /// It is possible for this to be NULL if the ContentCache encapsulates
+ /// an imaginary text buffer.
const FileEntry *OrigEntry;
/// \brief References the file which the contents were actually loaded from.
+ ///
/// Can be different from 'Entry' if we overridden the contents of one file
/// with the contents of another file.
const FileEntry *ContentsEntry;
- /// SourceLineCache - A bump pointer allocated array of offsets for each
- /// source line. This is lazily computed. This is owned by the
- /// SourceManager BumpPointerAllocator object.
+ /// \brief A bump pointer allocated array of offsets for each source line.
+ ///
+ /// This is lazily computed. This is owned by the SourceManager
+ /// BumpPointerAllocator object.
unsigned *SourceLineCache;
- /// NumLines - The number of lines in this ContentCache. This is only valid
- /// if SourceLineCache is non-null.
+ /// \brief The number of lines in this ContentCache.
+ ///
+ /// This is only valid if SourceLineCache is non-null.
unsigned NumLines : 31;
/// \brief Indicates whether the buffer itself was provided to override
@@ -110,22 +128,29 @@ namespace SrcMgr {
/// When true, the original entry may be a virtual file that does not
/// exist.
unsigned BufferOverridden : 1;
+
+ /// \brief True if this content cache was initially created for a source
+ /// file considered as a system one.
+ unsigned IsSystemFile : 1;
ContentCache(const FileEntry *Ent = 0)
: Buffer(0, false), OrigEntry(Ent), ContentsEntry(Ent),
- SourceLineCache(0), NumLines(0), BufferOverridden(false) {}
+ SourceLineCache(0), NumLines(0), BufferOverridden(false),
+ IsSystemFile(false) {}
ContentCache(const FileEntry *Ent, const FileEntry *contentEnt)
: Buffer(0, false), OrigEntry(Ent), ContentsEntry(contentEnt),
- SourceLineCache(0), NumLines(0), BufferOverridden(false) {}
+ SourceLineCache(0), NumLines(0), BufferOverridden(false),
+ IsSystemFile(false) {}
~ContentCache();
/// The copy ctor does not allow copies where source object has either
- /// a non-NULL Buffer or SourceLineCache. Ownership of allocated memory
- /// is not transferred, so this is a logical error.
+ /// a non-NULL Buffer or SourceLineCache. Ownership of allocated memory
+ /// is not transferred, so this is a logical error.
ContentCache(const ContentCache &RHS)
- : Buffer(0, false), SourceLineCache(0), BufferOverridden(false)
+ : Buffer(0, false), SourceLineCache(0), BufferOverridden(false),
+ IsSystemFile(false)
{
OrigEntry = RHS.OrigEntry;
ContentsEntry = RHS.ContentsEntry;
@@ -136,13 +161,13 @@ namespace SrcMgr {
NumLines = RHS.NumLines;
}
- /// getBuffer - Returns the memory buffer for the associated content.
+ /// \brief Returns the memory buffer for the associated content.
///
/// \param Diag Object through which diagnostics will be emitted if the
- /// buffer cannot be retrieved.
+ /// buffer cannot be retrieved.
///
/// \param Loc If specified, is the location that invalid file diagnostics
- /// will be emitted at.
+ /// will be emitted at.
///
/// \param Invalid If non-NULL, will be set \c true if an error occurred.
const llvm::MemoryBuffer *getBuffer(DiagnosticsEngine &Diag,
@@ -150,15 +175,18 @@ namespace SrcMgr {
SourceLocation Loc = SourceLocation(),
bool *Invalid = 0) const;
- /// getSize - Returns the size of the content encapsulated by this
- /// ContentCache. This can be the size of the source file or the size of an
- /// arbitrary scratch buffer. If the ContentCache encapsulates a source
- /// file this size is retrieved from the file's FileEntry.
+ /// \brief Returns the size of the content encapsulated by this
+ /// ContentCache.
+ ///
+ /// This can be the size of the source file or the size of an
+ /// arbitrary scratch buffer. If the ContentCache encapsulates a source
+ /// file this size is retrieved from the file's FileEntry.
unsigned getSize() const;
- /// getSizeBytesMapped - Returns the number of bytes actually mapped for
- /// this ContentCache. This can be 0 if the MemBuffer was not actually
- /// expanded.
+ /// \brief Returns the number of bytes actually mapped for this
+ /// ContentCache.
+ ///
+ /// This can be 0 if the MemBuffer was not actually expanded.
unsigned getSizeBytesMapped() const;
/// Returns the kind of memory used to back the memory buffer for
@@ -196,35 +224,37 @@ namespace SrcMgr {
ContentCache &operator=(const ContentCache& RHS);
};
- /// FileInfo - Information about a FileID, basically just the logical file
+ /// \brief Information about a FileID, basically just the logical file
/// that it represents and include stack information.
///
/// Each FileInfo has include stack information, indicating where it came
- /// from. This information encodes the #include chain that a token was
+ /// from. This information encodes the \#include chain that a token was
/// expanded from. The main include file has an invalid IncludeLoc.
///
/// FileInfos contain a "ContentCache *", with the contents of the file.
///
class FileInfo {
- /// IncludeLoc - The location of the #include that brought in this file.
- /// This is an invalid SLOC for the main file (top of the #include chain).
+ /// \brief The location of the \#include that brought in this file.
+ ///
+ /// This is an invalid SLOC for the main file (top of the \#include chain).
unsigned IncludeLoc; // Really a SourceLocation
/// \brief Number of FileIDs (files and macros) that were created during
- /// preprocessing of this #include, including this SLocEntry.
+ /// preprocessing of this \#include, including this SLocEntry.
+ ///
/// Zero means the preprocessor didn't provide such info for this SLocEntry.
unsigned NumCreatedFIDs;
- /// Data - This contains the ContentCache* and the bits indicating the
- /// characteristic of the file and whether it has #line info, all bitmangled
- /// together.
+ /// \brief Contains the ContentCache* and the bits indicating the
+ /// characteristic of the file and whether it has \#line info, all
+ /// bitmangled together.
uintptr_t Data;
friend class clang::SourceManager;
friend class clang::ASTWriter;
friend class clang::ASTReader;
public:
- /// get - Return a FileInfo object.
+ /// \brief Return a FileInfo object.
static FileInfo get(SourceLocation IL, const ContentCache *Con,
CharacteristicKind FileCharacter) {
FileInfo X;
@@ -244,36 +274,35 @@ namespace SrcMgr {
return reinterpret_cast<const ContentCache*>(Data & ~7UL);
}
- /// getCharacteristic - Return whether this is a system header or not.
+ /// \brief Return whether this is a system header or not.
CharacteristicKind getFileCharacteristic() const {
return (CharacteristicKind)(Data & 3);
}
- /// hasLineDirectives - Return true if this FileID has #line directives in
- /// it.
+ /// \brief Return true if this FileID has \#line directives in it.
bool hasLineDirectives() const { return (Data & 4) != 0; }
- /// setHasLineDirectives - Set the flag that indicates that this FileID has
+ /// \brief Set the flag that indicates that this FileID has
/// line table entries associated with it.
void setHasLineDirectives() {
Data |= 4;
}
};
- /// ExpansionInfo - Each ExpansionInfo encodes the expansion location - where
+ /// \brief Each ExpansionInfo encodes the expansion location - where
/// the token was ultimately expanded, and the SpellingLoc - where the actual
/// character data for the token came from.
class ExpansionInfo {
// Really these are all SourceLocations.
- /// SpellingLoc - Where the spelling for the token can be found.
+ /// \brief Where the spelling for the token can be found.
unsigned SpellingLoc;
- /// ExpansionLocStart/ExpansionLocEnd - In a macro expansion, these
+ /// In a macro expansion, ExpansionLocStart and ExpansionLocEnd
/// indicate the start and end of the expansion. In object-like macros,
- /// these will be the same. In a function-like macro expansion, the start
+ /// they will be the same. In a function-like macro expansion, the start
/// will be the identifier and the end will be the ')'. Finally, in
- /// macro-argument instantitions, the end will be 'SourceLocation()', an
+ /// macro-argument instantiations, the end will be 'SourceLocation()', an
/// invalid location.
unsigned ExpansionLocStart, ExpansionLocEnd;
@@ -305,11 +334,12 @@ namespace SrcMgr {
getExpansionLocStart() != getExpansionLocEnd();
}
- /// create - Return a ExpansionInfo for an expansion. Start and End specify
- /// the expansion range (where the macro is expanded), and SpellingLoc
- /// specifies the spelling location (where the characters from the token
- /// come from). All three can refer to normal File SLocs or expansion
- /// locations.
+ /// \brief Return a ExpansionInfo for an expansion.
+ ///
+ /// Start and End specify the expansion range (where the macro is
+ /// expanded), and SpellingLoc specifies the spelling location (where
+ /// the characters from the token come from). All three can refer to
+ /// normal File SLocs or expansion locations.
static ExpansionInfo create(SourceLocation SpellingLoc,
SourceLocation Start, SourceLocation End) {
ExpansionInfo X;
@@ -319,14 +349,15 @@ namespace SrcMgr {
return X;
}
- /// createForMacroArg - Return a special ExpansionInfo for the expansion of
- /// a macro argument into a function-like macro's body. ExpansionLoc
- /// specifies the expansion location (where the macro is expanded). This
- /// doesn't need to be a range because a macro is always expanded at
- /// a macro parameter reference, and macro parameters are always exactly
- /// one token. SpellingLoc specifies the spelling location (where the
- /// characters from the token come from). ExpansionLoc and SpellingLoc can
- /// both refer to normal File SLocs or expansion locations.
+ /// \brief Return a special ExpansionInfo for the expansion of
+ /// a macro argument into a function-like macro's body.
+ ///
+ /// ExpansionLoc specifies the expansion location (where the macro is
+ /// expanded). This doesn't need to be a range because a macro is always
+ /// expanded at a macro parameter reference, and macro parameters are
+ /// always exactly one token. SpellingLoc specifies the spelling location
+ /// (where the characters from the token come from). ExpansionLoc and
+ /// SpellingLoc can both refer to normal File SLocs or expansion locations.
///
/// Given the code:
/// \code
@@ -335,7 +366,7 @@ namespace SrcMgr {
/// \endcode
///
/// When expanding '\c F(42)', the '\c x' would call this with an
- /// SpellingLoc pointing at '\c 42' anad an ExpansionLoc pointing at its
+ /// SpellingLoc pointing at '\c 42' and an ExpansionLoc pointing at its
/// location in the definition of '\c F'.
static ExpansionInfo createForMacroArg(SourceLocation SpellingLoc,
SourceLocation ExpansionLoc) {
@@ -346,9 +377,10 @@ namespace SrcMgr {
}
};
- /// SLocEntry - This is a discriminated union of FileInfo and
- /// ExpansionInfo. SourceManager keeps an array of these objects, and
- /// they are uniquely identified by the FileID datatype.
+ /// \brief This is a discriminated union of FileInfo and ExpansionInfo.
+ ///
+ /// SourceManager keeps an array of these objects, and they are uniquely
+ /// identified by the FileID datatype.
class SLocEntry {
unsigned Offset; // low bit is set for expansion info.
union {
@@ -401,37 +433,43 @@ public:
};
-/// IsBeforeInTranslationUnitCache - This class holds the cache used by
-/// isBeforeInTranslationUnit. The cache structure is complex enough to be
-/// worth breaking out of SourceManager.
+/// \brief Holds the cache used by isBeforeInTranslationUnit.
+///
+/// The cache structure is complex enough to be worth breaking out of
+/// SourceManager.
class IsBeforeInTranslationUnitCache {
- /// L/R QueryFID - These are the FID's of the cached query. If these match up
- /// with a subsequent query, the result can be reused.
+ /// \brief The FileID's of the cached query.
+ ///
+ /// If these match up with a subsequent query, the result can be reused.
FileID LQueryFID, RQueryFID;
- /// \brief True if LQueryFID was created before RQueryFID. This is used
- /// to compare macro expansion locations.
+ /// \brief True if LQueryFID was created before RQueryFID.
+ ///
+ /// This is used to compare macro expansion locations.
bool IsLQFIDBeforeRQFID;
- /// CommonFID - This is the file found in common between the two #include
- /// traces. It is the nearest common ancestor of the #include tree.
+ /// \brief The file found in common between the two \#include traces, i.e.,
+ /// the nearest common ancestor of the \#include tree.
FileID CommonFID;
- /// L/R CommonOffset - This is the offset of the previous query in CommonFID.
- /// Usually, this represents the location of the #include for QueryFID, but if
- /// LQueryFID is a parent of RQueryFID (or vise versa) then these can be a
+ /// \brief The offset of the previous query in CommonFID.
+ ///
+ /// Usually, this represents the location of the \#include for QueryFID, but
+ /// if LQueryFID is a parent of RQueryFID (or vice versa) then these can be a
/// random token in the parent.
unsigned LCommonOffset, RCommonOffset;
public:
- /// isCacheValid - Return true if the currently cached values match up with
- /// the specified LHS/RHS query. If not, we can't use the cache.
+ /// \brief Return true if the currently cached values match up with
+ /// the specified LHS/RHS query.
+ ///
+ /// If not, we can't use the cache.
bool isCacheValid(FileID LHS, FileID RHS) const {
return LQueryFID == LHS && RQueryFID == RHS;
}
- /// getCachedResult - If the cache is valid, compute the result given the
- /// specified offsets in the LHS/RHS FID's.
+ /// \brief If the cache is valid, compute the result given the
+ /// specified offsets in the LHS/RHS FileID's.
bool getCachedResult(unsigned LOffset, unsigned ROffset) const {
// If one of the query files is the common file, use the offset. Otherwise,
// use the #include loc in the common file.
@@ -449,7 +487,7 @@ public:
return LOffset < ROffset;
}
- // Set up a new query.
+ /// \brief Set up a new query.
void setQueryFIDs(FileID LHS, FileID RHS, bool isLFIDBeforeRFID) {
assert(LHS != RHS);
LQueryFID = LHS;
@@ -474,7 +512,7 @@ public:
/// \brief This class handles loading and caching of source files into memory.
///
/// This object owns the MemoryBuffer objects for all of the loaded
-/// files and assigns unique FileID's for each unique #include chain.
+/// files and assigns unique FileID's for each unique \#include chain.
///
/// The SourceManager can be queried for information about SourceLocation
/// objects, turning them into either spelling or expansion locations. Spelling
@@ -491,8 +529,10 @@ class SourceManager : public RefCountedBase<SourceManager> {
mutable llvm::BumpPtrAllocator ContentCacheAlloc;
- /// FileInfos - Memoized information about all of the files tracked by this
- /// SourceManager. This set allows us to merge ContentCache entries based
+ /// \brief Memoized information about all of the files tracked by this
+ /// SourceManager.
+ ///
+ /// This map allows us to merge ContentCache entries based
/// on their FileEntry*. All ContentCache objects will thus have unique,
/// non-null, FileEntry pointers.
llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*> FileInfos;
@@ -501,11 +541,31 @@ class SourceManager : public RefCountedBase<SourceManager> {
/// files, should report the original file name. Defaults to true.
bool OverridenFilesKeepOriginalName;
- /// \brief Files that have been overriden with the contents from another file.
- llvm::DenseMap<const FileEntry *, const FileEntry *> OverriddenFiles;
+ /// \brief True if non-system source files should be treated as volatile
+ /// (likely to change while trying to use them). Defaults to false.
+ bool UserFilesAreVolatile;
+
+ struct OverriddenFilesInfoTy {
+ /// \brief Files that have been overriden with the contents from another
+ /// file.
+ llvm::DenseMap<const FileEntry *, const FileEntry *> OverriddenFiles;
+ /// \brief Files that were overridden with a memory buffer.
+ llvm::DenseSet<const FileEntry *> OverriddenFilesWithBuffer;
+ };
+
+ /// \brief Lazily create the object keeping overridden files info, since
+ /// it is uncommonly used.
+ OwningPtr<OverriddenFilesInfoTy> OverriddenFilesInfo;
+
+ OverriddenFilesInfoTy &getOverriddenFilesInfo() {
+ if (!OverriddenFilesInfo)
+ OverriddenFilesInfo.reset(new OverriddenFilesInfoTy);
+ return *OverriddenFilesInfo;
+ }
- /// MemBufferInfos - Information about various memory buffers that we have
- /// read in. All FileEntry* within the stored ContentCache objects are NULL,
+ /// \brief Information about various memory buffers that we have read in.
+ ///
+ /// All FileEntry* within the stored ContentCache objects are NULL,
/// as they do not refer to a file.
std::vector<SrcMgr::ContentCache*> MemBufferInfos;
@@ -545,23 +605,25 @@ class SourceManager : public RefCountedBase<SourceManager> {
/// \brief An external source for source location entries.
ExternalSLocEntrySource *ExternalSLocEntries;
- /// LastFileIDLookup - This is a one-entry cache to speed up getFileID.
+ /// \brief A one-entry cache to speed up getFileID.
+ ///
/// LastFileIDLookup records the last FileID looked up or created, because it
/// is very common to look up many tokens from the same file.
mutable FileID LastFileIDLookup;
- /// LineTable - This holds information for #line directives. It is referenced
- /// by indices from SLocEntryTable.
+ /// \brief Holds information for \#line directives.
+ ///
+ /// This is referenced by indices from SLocEntryTable.
LineTableInfo *LineTable;
- /// LastLineNo - These ivars serve as a cache used in the getLineNumber
+ /// \brief These ivars serve as a cache used in the getLineNumber
/// method which is used to speedup getLineNumber calls to nearby locations.
mutable FileID LastLineNoFileIDQuery;
mutable SrcMgr::ContentCache *LastLineNoContentCache;
mutable unsigned LastLineNoFilePos;
mutable unsigned LastLineNoResult;
- /// MainFileID - The file ID for the main source file of the translation unit.
+ /// \brief The file ID for the main source file of the translation unit.
FileID MainFileID;
/// \brief The file ID for the precompiled preamble there is one.
@@ -588,7 +650,8 @@ class SourceManager : public RefCountedBase<SourceManager> {
explicit SourceManager(const SourceManager&);
void operator=(const SourceManager&);
public:
- SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr);
+ SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr,
+ bool UserFilesAreVolatile = false);
~SourceManager();
void clearIDTables();
@@ -603,9 +666,15 @@ public:
OverridenFilesKeepOriginalName = value;
}
- /// createMainFileIDForMembuffer - Create the FileID for a memory buffer
- /// that will represent the FileID for the main source. One example
- /// of when this would be used is when the main source is read from STDIN.
+ /// \brief True if non-system source files should be treated as volatile
+ /// (likely to change while trying to use them).
+ bool userFilesAreVolatile() const { return UserFilesAreVolatile; }
+
+ /// \brief Create the FileID for a memory buffer that will represent the
+ /// FileID for the main source.
+ ///
+ /// One example of when this would be used is when the main source is read
+ /// from STDIN.
FileID createMainFileIDForMemBuffer(const llvm::MemoryBuffer *Buffer) {
assert(MainFileID.isInvalid() && "MainFileID already set!");
MainFileID = createFileIDForMemBuffer(Buffer);
@@ -616,10 +685,10 @@ public:
// MainFileID creation and querying methods.
//===--------------------------------------------------------------------===//
- /// getMainFileID - Returns the FileID of the main source file.
+ /// \brief Returns the FileID of the main source file.
FileID getMainFileID() const { return MainFileID; }
- /// createMainFileID - Create the FileID for the main source file.
+ /// \brief Create the FileID for the main source file.
FileID createMainFileID(const FileEntry *SourceFile,
SrcMgr::CharacteristicKind Kind = SrcMgr::C_User) {
assert(MainFileID.isInvalid() && "MainFileID already set!");
@@ -646,20 +715,24 @@ public:
// Methods to create new FileID's and macro expansions.
//===--------------------------------------------------------------------===//
- /// createFileID - Create a new FileID that represents the specified file
- /// being #included from the specified IncludePosition. This translates NULL
- /// into standard input.
+ /// \brief Create a new FileID that represents the specified file
+ /// being \#included from the specified IncludePosition.
+ ///
+ /// This translates NULL into standard input.
FileID createFileID(const FileEntry *SourceFile, SourceLocation IncludePos,
SrcMgr::CharacteristicKind FileCharacter,
int LoadedID = 0, unsigned LoadedOffset = 0) {
- const SrcMgr::ContentCache *IR = getOrCreateContentCache(SourceFile);
+ const SrcMgr::ContentCache *
+ IR = getOrCreateContentCache(SourceFile,
+ /*isSystemFile=*/FileCharacter != SrcMgr::C_User);
assert(IR && "getOrCreateContentCache() cannot return NULL");
return createFileID(IR, IncludePos, FileCharacter, LoadedID, LoadedOffset);
}
- /// createFileIDForMemBuffer - Create a new FileID that represents the
- /// specified memory buffer. This does no caching of the buffer and takes
- /// ownership of the MemoryBuffer, so only pass a MemoryBuffer to this once.
+ /// \brief Create a new FileID that represents the specified memory buffer.
+ ///
+ /// This does no caching of the buffer and takes ownership of the
+ /// MemoryBuffer, so only pass a MemoryBuffer to this once.
FileID createFileIDForMemBuffer(const llvm::MemoryBuffer *Buffer,
int LoadedID = 0, unsigned LoadedOffset = 0,
SourceLocation IncludeLoc = SourceLocation()) {
@@ -667,7 +740,7 @@ public:
SrcMgr::C_User, LoadedID, LoadedOffset);
}
- /// createMacroArgExpansionLoc - Return a new SourceLocation that encodes the
+ /// \brief Return a new SourceLocation that encodes the
/// fact that a token from SpellingLoc should actually be referenced from
/// ExpansionLoc, and that it represents the expansion of a macro argument
/// into the function-like macro body.
@@ -675,7 +748,7 @@ public:
SourceLocation ExpansionLoc,
unsigned TokLength);
- /// createExpansionLoc - Return a new SourceLocation that encodes the fact
+ /// \brief Return a new SourceLocation that encodes the fact
/// that a token from SpellingLoc should actually be referenced from
/// ExpansionLoc.
SourceLocation createExpansionLoc(SourceLocation Loc,
@@ -706,7 +779,7 @@ public:
const llvm::MemoryBuffer *Buffer,
bool DoNotFree = false);
- /// \brief Override the the given source file with another one.
+ /// \brief Override the given source file with another one.
///
/// \param SourceFile the source file which will be overriden.
///
@@ -715,13 +788,32 @@ public:
void overrideFileContents(const FileEntry *SourceFile,
const FileEntry *NewFile);
+ /// \brief Returns true if the file contents have been overridden.
+ bool isFileOverridden(const FileEntry *File) {
+ if (OverriddenFilesInfo) {
+ if (OverriddenFilesInfo->OverriddenFilesWithBuffer.count(File))
+ return true;
+ if (OverriddenFilesInfo->OverriddenFiles.find(File) !=
+ OverriddenFilesInfo->OverriddenFiles.end())
+ return true;
+ }
+ return false;
+ }
+
+ /// \brief Disable overridding the contents of a file, previously enabled
+ /// with #overrideFileContents.
+ ///
+ /// This should be called before parsing has begun.
+ void disableFileContentsOverride(const FileEntry *File);
+
//===--------------------------------------------------------------------===//
// FileID manipulation methods.
//===--------------------------------------------------------------------===//
- /// getBuffer - Return the buffer for the specified FileID. If there is an
- /// error opening this buffer the first time, this manufactures a temporary
- /// buffer and returns a non-empty error string.
+ /// \brief Return the buffer for the specified FileID.
+ ///
+ /// If there is an error opening this buffer the first time, this
+ /// manufactures a temporary buffer and returns a non-empty error string.
const llvm::MemoryBuffer *getBuffer(FileID FID, SourceLocation Loc,
bool *Invalid = 0) const {
bool MyInvalid = false;
@@ -752,7 +844,7 @@ public:
Invalid);
}
- /// getFileEntryForID - Returns the FileEntry record for the provided FileID.
+ /// \brief Returns the FileEntry record for the provided FileID.
const FileEntry *getFileEntryForID(FileID FID) const {
bool MyInvalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid);
@@ -765,7 +857,7 @@ public:
return Content->OrigEntry;
}
- /// Returns the FileEntry record for the provided SLocEntry.
+ /// \brief Returns the FileEntry record for the provided SLocEntry.
const FileEntry *getFileEntryForSLocEntry(const SrcMgr::SLocEntry &sloc) const
{
const SrcMgr::ContentCache *Content = sloc.getFile().getContentCache();
@@ -774,7 +866,7 @@ public:
return Content->OrigEntry;
}
- /// getBufferData - Return a StringRef to the source buffer data for the
+ /// \brief Return a StringRef to the source buffer data for the
/// specified FileID.
///
/// \param FID The file ID whose contents will be returned.
@@ -808,10 +900,11 @@ public:
// SourceLocation manipulation methods.
//===--------------------------------------------------------------------===//
- /// getFileID - Return the FileID for a SourceLocation. This is a very
- /// hot method that is used for all SourceManager queries that start with a
- /// SourceLocation object. It is responsible for finding the entry in
- /// SLocEntryTable which contains the specified location.
+ /// \brief Return the FileID for a SourceLocation.
+ ///
+ /// This is a very hot method that is used for all SourceManager queries
+ /// that start with a SourceLocation object. It is responsible for finding
+ /// the entry in SLocEntryTable which contains the specified location.
///
FileID getFileID(SourceLocation SpellingLoc) const {
unsigned SLocOffset = SpellingLoc.getOffset();
@@ -823,8 +916,15 @@ public:
return getFileIDSlow(SLocOffset);
}
- /// getLocForStartOfFile - Return the source location corresponding to the
- /// first byte of the specified file.
+ /// \brief Return the filename of the file containing a SourceLocation.
+ StringRef getFilename(SourceLocation SpellingLoc) const {
+ if (const FileEntry *F = getFileEntryForID(getFileID(SpellingLoc)))
+ return F->getName();
+ return StringRef();
+ }
+
+ /// \brief Return the source location corresponding to the first byte of
+ /// the specified file.
SourceLocation getLocForStartOfFile(FileID FID) const {
bool Invalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
@@ -847,7 +947,7 @@ public:
return SourceLocation::getFileLoc(FileOffset + getFileIDSize(FID) - 1);
}
- /// \brief Returns the include location if \p FID is a #include'd file
+ /// \brief Returns the include location if \p FID is a \#include'd file
/// otherwise it returns an invalid location.
SourceLocation getIncludeLoc(FileID FID) const {
bool Invalid = false;
@@ -858,7 +958,7 @@ public:
return Entry.getFile().getIncludeLoc();
}
- /// getExpansionLoc - Given a SourceLocation object, return the expansion
+ /// \brief Given a SourceLocation object \p Loc, return the expansion
/// location referenced by the ID.
SourceLocation getExpansionLoc(SourceLocation Loc) const {
// Handle the non-mapped case inline, defer to out of line code to handle
@@ -875,20 +975,24 @@ public:
return getFileLocSlowCase(Loc);
}
- /// getImmediateExpansionRange - Loc is required to be an expansion location.
- /// Return the start/end of the expansion information.
+ /// \brief Return the start/end of the expansion information for an
+ /// expansion location.
+ ///
+ /// \pre \p Loc is required to be an expansion location.
std::pair<SourceLocation,SourceLocation>
getImmediateExpansionRange(SourceLocation Loc) const;
- /// getExpansionRange - Given a SourceLocation object, return the range of
+ /// \brief Given a SourceLocation object, return the range of
/// tokens covered by the expansion the ultimate file.
std::pair<SourceLocation,SourceLocation>
getExpansionRange(SourceLocation Loc) const;
- /// getSpellingLoc - Given a SourceLocation object, return the spelling
- /// location referenced by the ID. This is the place where the characters
- /// that make up the lexed token can be found.
+ /// \brief Given a SourceLocation object, return the spelling
+ /// location referenced by the ID.
+ ///
+ /// This is the place where the characters that make up the lexed token
+ /// can be found.
SourceLocation getSpellingLoc(SourceLocation Loc) const {
// Handle the non-mapped case inline, defer to out of line code to handle
// expansions.
@@ -896,15 +1000,18 @@ public:
return getSpellingLocSlowCase(Loc);
}
- /// getImmediateSpellingLoc - Given a SourceLocation object, return the
- /// spelling location referenced by the ID. This is the first level down
- /// towards the place where the characters that make up the lexed token can be
- /// found. This should not generally be used by clients.
+ /// \brief Given a SourceLocation object, return the spelling location
+ /// referenced by the ID.
+ ///
+ /// This is the first level down towards the place where the characters
+ /// that make up the lexed token can be found. This should not generally
+ /// be used by clients.
SourceLocation getImmediateSpellingLoc(SourceLocation Loc) const;
- /// getDecomposedLoc - Decompose the specified location into a raw FileID +
- /// Offset pair. The first element is the FileID, the second is the
- /// offset from the start of the buffer of the location.
+ /// \brief Decompose the specified location into a raw FileID + Offset pair.
+ ///
+ /// The first element is the FileID, the second is the offset from the
+ /// start of the buffer of the location.
std::pair<FileID, unsigned> getDecomposedLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
bool Invalid = false;
@@ -914,9 +1021,10 @@ public:
return std::make_pair(FID, Loc.getOffset()-E.getOffset());
}
- /// getDecomposedExpansionLoc - Decompose the specified location into a raw
- /// FileID + Offset pair. If the location is an expansion record, walk
- /// through it until we find the final location expanded.
+ /// \brief Decompose the specified location into a raw FileID + Offset pair.
+ ///
+ /// If the location is an expansion record, walk through it until we find
+ /// the final location expanded.
std::pair<FileID, unsigned>
getDecomposedExpansionLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
@@ -932,9 +1040,10 @@ public:
return getDecomposedExpansionLocSlowCase(E);
}
- /// getDecomposedSpellingLoc - Decompose the specified location into a raw
- /// FileID + Offset pair. If the location is an expansion record, walk
- /// through it until we find its spelling record.
+ /// \brief Decompose the specified location into a raw FileID + Offset pair.
+ ///
+ /// If the location is an expansion record, walk through it until we find
+ /// its spelling record.
std::pair<FileID, unsigned>
getDecomposedSpellingLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
@@ -949,22 +1058,25 @@ public:
return getDecomposedSpellingLocSlowCase(E, Offset);
}
- /// getFileOffset - This method returns the offset from the start
- /// of the file that the specified SourceLocation represents. This is not very
- /// meaningful for a macro ID.
+ /// \brief Returns the offset from the start of the file that the
+ /// specified SourceLocation represents.
+ ///
+ /// This is not very meaningful for a macro ID.
unsigned getFileOffset(SourceLocation SpellingLoc) const {
return getDecomposedLoc(SpellingLoc).second;
}
- /// isMacroArgExpansion - This method tests whether the given source location
- /// represents a macro argument's expansion into the function-like macro
- /// definition. Such source locations only appear inside of the expansion
+ /// \brief Tests whether the given source location represents a macro
+ /// argument's expansion into the function-like macro definition.
+ ///
+ /// Such source locations only appear inside of the expansion
/// locations representing where a particular function-like macro was
/// expanded.
bool isMacroArgExpansion(SourceLocation Loc) const;
/// \brief Returns true if \p Loc is inside the [\p Start, +\p Length)
/// chunk of the source location address space.
+ ///
/// If it's true and \p RelativeOffset is non-null, it will be set to the
/// relative offset of \p Loc inside the chunk.
bool isInSLocAddrSpace(SourceLocation Loc,
@@ -988,9 +1100,10 @@ public:
}
/// \brief Return true if both \p LHS and \p RHS are in the local source
- /// location address space or the loaded one. If it's true and \p
- /// RelativeOffset is non-null, it will be set to the offset of \p RHS
- /// relative to \p LHS.
+ /// location address space or the loaded one.
+ ///
+ /// If it's true and \p RelativeOffset is non-null, it will be set to the
+ /// offset of \p RHS relative to \p LHS.
bool isInSameSLocAddrSpace(SourceLocation LHS, SourceLocation RHS,
int *RelativeOffset) const {
unsigned LHSOffs = LHS.getOffset(), RHSOffs = RHS.getOffset();
@@ -1010,13 +1123,14 @@ public:
// Queries about the code at a SourceLocation.
//===--------------------------------------------------------------------===//
- /// getCharacterData - Return a pointer to the start of the specified location
+ /// \brief Return a pointer to the start of the specified location
/// in the appropriate spelling MemoryBuffer.
///
/// \param Invalid If non-NULL, will be set \c true if an error occurs.
const char *getCharacterData(SourceLocation SL, bool *Invalid = 0) const;
- /// getColumnNumber - Return the column # for the specified file position.
+ /// \brief Return the column # for the specified file position.
+ ///
/// This is significantly cheaper to compute than the line number. This
/// returns zero if the column number isn't known. This may only be called
/// on a file sloc, so you must choose a spelling or expansion location
@@ -1029,34 +1143,41 @@ public:
unsigned getPresumedColumnNumber(SourceLocation Loc, bool *Invalid = 0) const;
- /// getLineNumber - Given a SourceLocation, return the spelling line number
- /// for the position indicated. This requires building and caching a table of
- /// line offsets for the MemoryBuffer, so this is not cheap: use only when
- /// about to emit a diagnostic.
+ /// \brief Given a SourceLocation, return the spelling line number
+ /// for the position indicated.
+ ///
+ /// This requires building and caching a table of line offsets for the
+ /// MemoryBuffer, so this is not cheap: use only when about to emit a
+ /// diagnostic.
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid = 0) const;
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid = 0) const;
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid = 0) const;
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid = 0) const;
- /// Return the filename or buffer identifier of the buffer the location is in.
- /// Note that this name does not respect #line directives. Use getPresumedLoc
- /// for normal clients.
+ /// \brief Return the filename or buffer identifier of the buffer the
+ /// location is in.
+ ///
+ /// Note that this name does not respect \#line directives. Use
+ /// getPresumedLoc for normal clients.
const char *getBufferName(SourceLocation Loc, bool *Invalid = 0) const;
- /// getFileCharacteristic - return the file characteristic of the specified
- /// source location, indicating whether this is a normal file, a system
+ /// \brief Return the file characteristic of the specified source
+ /// location, indicating whether this is a normal file, a system
/// header, or an "implicit extern C" system header.
///
/// This state can be modified with flags on GNU linemarker directives like:
+ /// \code
/// # 4 "foo.h" 3
+ /// \endcode
/// which changes all source locations in the current file after that to be
/// considered to be from a system header.
SrcMgr::CharacteristicKind getFileCharacteristic(SourceLocation Loc) const;
- /// getPresumedLoc - This method returns the "presumed" location of a
- /// SourceLocation specifies. A "presumed location" can be modified by #line
- /// or GNU line marker directives. This provides a view on the data that a
- /// user should see in diagnostics, for example.
+ /// \brief Returns the "presumed" location of a SourceLocation specifies.
+ ///
+ /// A "presumed location" can be modified by \#line or GNU line marker
+ /// directives. This provides a view on the data that a user should see
+ /// in diagnostics, for example.
///
/// Note that a presumed location is always given as the expansion point of
/// an expansion location, not at the spelling location.
@@ -1067,25 +1188,23 @@ public:
/// presumed location.
PresumedLoc getPresumedLoc(SourceLocation Loc) const;
- /// isFromSameFile - Returns true if both SourceLocations correspond to
- /// the same file.
+ /// \brief Returns true if both SourceLocations correspond to the same file.
bool isFromSameFile(SourceLocation Loc1, SourceLocation Loc2) const {
return getFileID(Loc1) == getFileID(Loc2);
}
- /// isFromMainFile - Returns true if the file of provided SourceLocation is
- /// the main file.
+ /// \brief Returns true if the file of provided SourceLocation is the main
+ /// file.
bool isFromMainFile(SourceLocation Loc) const {
return getFileID(Loc) == getMainFileID();
}
- /// isInSystemHeader - Returns if a SourceLocation is in a system header.
+ /// \brief Returns if a SourceLocation is in a system header.
bool isInSystemHeader(SourceLocation Loc) const {
return getFileCharacteristic(Loc) != SrcMgr::C_User;
}
- /// isInExternCSystemHeader - Returns if a SourceLocation is in an "extern C"
- /// system header.
+ /// \brief Returns if a SourceLocation is in an "extern C" system header.
bool isInExternCSystemHeader(SourceLocation Loc) const {
return getFileCharacteristic(Loc) == SrcMgr::C_ExternCSystem;
}
@@ -1117,13 +1236,14 @@ public:
// Line Table Manipulation Routines
//===--------------------------------------------------------------------===//
- /// getLineTableFilenameID - Return the uniqued ID for the specified filename.
+ /// \brief Return the uniqued ID for the specified filename.
///
unsigned getLineTableFilenameID(StringRef Str);
- /// AddLineNote - Add a line note to the line table for the FileID and offset
- /// specified by Loc. If FilenameID is -1, it is considered to be
- /// unspecified.
+ /// \brief Add a line note to the line table for the FileID and offset
+ /// specified by Loc.
+ ///
+ /// If FilenameID is -1, it is considered to be unspecified.
void AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID);
void AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID,
bool IsFileEntry, bool IsFileExit,
@@ -1139,7 +1259,7 @@ public:
// Queries for performance analysis.
//===--------------------------------------------------------------------===//
- /// Return the total amount of physical memory allocated by the
+ /// \brief Return the total amount of physical memory allocated by the
/// ContentCache allocator.
size_t getContentCacheSize() const {
return ContentCacheAlloc.getTotalMemory();
@@ -1153,12 +1273,12 @@ public:
: malloc_bytes(malloc_bytes), mmap_bytes(mmap_bytes) {}
};
- /// Return the amount of memory used by memory buffers, breaking down
+ /// \brief Return the amount of memory used by memory buffers, breaking down
/// by heap-backed versus mmap'ed memory.
MemoryBufferSizes getMemoryBufferSizes() const;
- // Return the amount of memory used for various side tables and
- // data structures in the SourceManager.
+ /// \brief Return the amount of memory used for various side tables and
+ /// data structures in the SourceManager.
size_t getDataStructureSizes() const;
//===--------------------------------------------------------------------===//
@@ -1199,19 +1319,6 @@ public:
/// \returns true if LHS source location comes before RHS, false otherwise.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const;
- /// \brief Comparison function class.
- class LocBeforeThanCompare : public std::binary_function<SourceLocation,
- SourceLocation, bool> {
- SourceManager &SM;
-
- public:
- explicit LocBeforeThanCompare(SourceManager &SM) : SM(SM) { }
-
- bool operator()(SourceLocation LHS, SourceLocation RHS) const {
- return SM.isBeforeInTranslationUnit(LHS, RHS);
- }
- };
-
/// \brief Determines the order of 2 source locations in the "source location
/// address space".
bool isBeforeInSLocAddrSpace(SourceLocation LHS, SourceLocation RHS) const {
@@ -1241,7 +1348,7 @@ public:
return FileInfos.find(File) != FileInfos.end();
}
- /// PrintStats - Print statistics to stderr.
+ /// \brief Print statistics to stderr.
///
void PrintStats() const;
@@ -1313,6 +1420,65 @@ public:
return !isLoadedFileID(FID);
}
+ /// Get a presumed location suitable for displaying in a diagnostic message,
+ /// taking into account macro arguments and expansions.
+ PresumedLoc getPresumedLocForDisplay(SourceLocation Loc) const {
+ // This is a condensed form of the algorithm used by emitCaretDiagnostic to
+ // walk to the top of the macro call stack.
+ while (Loc.isMacroID()) {
+ Loc = skipToMacroArgExpansion(Loc);
+ Loc = getImmediateMacroCallerLoc(Loc);
+ }
+
+ return getPresumedLoc(Loc);
+ }
+
+ /// Look through spelling locations for a macro argument expansion, and if
+ /// found skip to it so that we can trace the argument rather than the macros
+ /// in which that argument is used. If no macro argument expansion is found,
+ /// don't skip anything and return the starting location.
+ SourceLocation skipToMacroArgExpansion(SourceLocation StartLoc) const {
+ for (SourceLocation L = StartLoc; L.isMacroID();
+ L = getImmediateSpellingLoc(L)) {
+ if (isMacroArgExpansion(L))
+ return L;
+ }
+ // Otherwise just return initial location, there's nothing to skip.
+ return StartLoc;
+ }
+
+ /// Gets the location of the immediate macro caller, one level up the stack
+ /// toward the initial macro typed into the source.
+ SourceLocation getImmediateMacroCallerLoc(SourceLocation Loc) const {
+ if (!Loc.isMacroID()) return Loc;
+
+ // When we have the location of (part of) an expanded parameter, its
+ // spelling location points to the argument as typed into the macro call,
+ // and therefore is used to locate the macro caller.
+ if (isMacroArgExpansion(Loc))
+ return getImmediateSpellingLoc(Loc);
+
+ // Otherwise, the caller of the macro is located where this macro is
+ // expanded (while the spelling is part of the macro definition).
+ return getImmediateExpansionRange(Loc).first;
+ }
+
+ /// Gets the location of the immediate macro callee, one level down the stack
+ /// toward the leaf macro.
+ SourceLocation getImmediateMacroCalleeLoc(SourceLocation Loc) const {
+ if (!Loc.isMacroID()) return Loc;
+
+ // When we have the location of (part of) an expanded parameter, its
+ // expansion location points to the unexpanded parameter reference within
+ // the macro definition (or callee).
+ if (isMacroArgExpansion(Loc))
+ return getImmediateExpansionRange(Loc).first;
+
+ // Otherwise, the callee of the macro is located where this location was
+ // spelled inside the macro definition.
+ return getImmediateSpellingLoc(Loc);
+ }
+
private:
const llvm::MemoryBuffer *getFakeBufferForRecovery() const;
const SrcMgr::ContentCache *getFakeContentCacheForRecovery() const;
@@ -1332,15 +1498,14 @@ private:
return getLoadedSLocEntry(static_cast<unsigned>(-ID - 2), Invalid);
}
- /// createExpansionLoc - Implements the common elements of storing an
- /// expansion info struct into the SLocEntry table and producing a source
- /// location that refers to it.
+ /// Implements the common elements of storing an expansion info struct into
+ /// the SLocEntry table and producing a source location that refers to it.
SourceLocation createExpansionLocImpl(const SrcMgr::ExpansionInfo &Expansion,
unsigned TokLength,
int LoadedID = 0,
unsigned LoadedOffset = 0);
- /// isOffsetInFileID - Return true if the specified FileID contains the
+ /// \brief Return true if the specified FileID contains the
/// specified SourceLocation offset. This is a very hot method.
inline bool isOffsetInFileID(FileID FID, unsigned SLocOffset) const {
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID);
@@ -1352,28 +1517,29 @@ private:
return true;
// If it is the last local entry, then it does if the location is local.
- if (static_cast<unsigned>(FID.ID+1) == LocalSLocEntryTable.size()) {
+ if (FID.ID+1 == static_cast<int>(LocalSLocEntryTable.size()))
return SLocOffset < NextLocalOffset;
- }
// Otherwise, the entry after it has to not include it. This works for both
// local and loaded entries.
- return SLocOffset < getSLocEntry(FileID::get(FID.ID+1)).getOffset();
+ return SLocOffset < getSLocEntryByID(FID.ID+1).getOffset();
}
- /// createFileID - Create a new fileID for the specified ContentCache and
- /// include position. This works regardless of whether the ContentCache
- /// corresponds to a file or some other input source.
+ /// \brief Create a new fileID for the specified ContentCache and
+ /// include position.
+ ///
+ /// This works regardless of whether the ContentCache corresponds to a
+ /// file or some other input source.
FileID createFileID(const SrcMgr::ContentCache* File,
SourceLocation IncludePos,
SrcMgr::CharacteristicKind DirCharacter,
int LoadedID, unsigned LoadedOffset);
const SrcMgr::ContentCache *
- getOrCreateContentCache(const FileEntry *SourceFile);
+ getOrCreateContentCache(const FileEntry *SourceFile,
+ bool isSystemFile = false);
- /// createMemBufferContentCache - Create a new ContentCache for the specified
- /// memory buffer.
+ /// \brief Create a new ContentCache for the specified memory buffer.
const SrcMgr::ContentCache*
createMemBufferContentCache(const llvm::MemoryBuffer *Buf);
@@ -1396,6 +1562,35 @@ private:
friend class ASTWriter;
};
+/// \brief Comparison function object.
+template<typename T>
+class BeforeThanCompare;
+
+/// \brief Compare two source locations.
+template<>
+class BeforeThanCompare<SourceLocation> {
+ SourceManager &SM;
+
+public:
+ explicit BeforeThanCompare(SourceManager &SM) : SM(SM) { }
+
+ bool operator()(SourceLocation LHS, SourceLocation RHS) const {
+ return SM.isBeforeInTranslationUnit(LHS, RHS);
+ }
+};
+
+/// \brief Compare two non-overlapping source ranges.
+template<>
+class BeforeThanCompare<SourceRange> {
+ SourceManager &SM;
+
+public:
+ explicit BeforeThanCompare(SourceManager &SM) : SM(SM) { }
+
+ bool operator()(SourceRange LHS, SourceRange RHS) {
+ return SM.isBeforeInTranslationUnit(LHS.getBegin(), RHS.getBegin());
+ }
+};
} // end namespace clang
diff --git a/include/clang/Basic/SourceManagerInternals.h b/include/clang/Basic/SourceManagerInternals.h
index 1cb16b4..af95b78 100644
--- a/include/clang/Basic/SourceManagerInternals.h
+++ b/include/clang/Basic/SourceManagerInternals.h
@@ -6,15 +6,16 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the implementation details of the SourceManager
-// class.
-//
+///
+/// \file
+/// \brief Defines implementation details of the clang::SourceManager class.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SOURCEMANAGER_INTERNALS_H
#define LLVM_CLANG_SOURCEMANAGER_INTERNALS_H
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/StringMap.h"
#include <map>
@@ -26,22 +27,23 @@ namespace clang {
//===----------------------------------------------------------------------===//
struct LineEntry {
- /// FileOffset - The offset in this file that the line entry occurs at.
+ /// \brief The offset in this file that the line entry occurs at.
unsigned FileOffset;
- /// LineNo - The presumed line number of this line entry: #line 4.
+ /// \brief The presumed line number of this line entry: \#line 4.
unsigned LineNo;
- /// FilenameID - The ID of the filename identified by this line entry:
- /// #line 4 "foo.c". This is -1 if not specified.
+ /// \brief The ID of the filename identified by this line entry:
+ /// \#line 4 "foo.c". This is -1 if not specified.
int FilenameID;
- /// Flags - Set the 0 if no flags, 1 if a system header,
+ /// \brief Set the 0 if no flags, 1 if a system header,
SrcMgr::CharacteristicKind FileKind;
- /// IncludeOffset - This is the offset of the virtual include stack location,
- /// which is manipulated by GNU linemarker directives. If this is 0 then
- /// there is no virtual #includer.
+ /// \brief The offset of the virtual include stack location,
+ /// which is manipulated by GNU linemarker directives.
+ ///
+ /// If this is 0 then there is no virtual \#includer.
unsigned IncludeOffset;
static LineEntry get(unsigned Offs, unsigned Line, int Filename,
@@ -71,20 +73,20 @@ inline bool operator<(unsigned Offset, const LineEntry &E) {
return Offset < E.FileOffset;
}
-/// LineTableInfo - This class is used to hold and unique data used to
-/// represent #line information.
+/// \brief Used to hold and unique data used to represent \#line information.
class LineTableInfo {
- /// FilenameIDs - This map is used to assign unique IDs to filenames in
- /// #line directives. This allows us to unique the filenames that
+ /// \brief Map used to assign unique IDs to filenames in \#line directives.
+ ///
+ /// This allows us to unique the filenames that
/// frequently reoccur and reference them with indices. FilenameIDs holds
/// the mapping from string -> ID, and FilenamesByID holds the mapping of ID
/// to string.
llvm::StringMap<unsigned, llvm::BumpPtrAllocator> FilenameIDs;
std::vector<llvm::StringMapEntry<unsigned>*> FilenamesByID;
- /// LineEntries - This is a map from FileIDs to a list of line entries (sorted
- /// by the offset they occur in the file.
- std::map<int, std::vector<LineEntry> > LineEntries;
+ /// \brief Map from FileIDs to a list of line entries (sorted by the offset
+ /// at which they occur in the file).
+ std::map<FileID, std::vector<LineEntry> > LineEntries;
public:
LineTableInfo() {
}
@@ -104,25 +106,26 @@ public:
}
unsigned getNumFilenames() const { return FilenamesByID.size(); }
- void AddLineNote(int FID, unsigned Offset,
+ void AddLineNote(FileID FID, unsigned Offset,
unsigned LineNo, int FilenameID);
- void AddLineNote(int FID, unsigned Offset,
+ void AddLineNote(FileID FID, unsigned Offset,
unsigned LineNo, int FilenameID,
unsigned EntryExit, SrcMgr::CharacteristicKind FileKind);
- /// FindNearestLineEntry - Find the line entry nearest to FID that is before
- /// it. If there is no line entry before Offset in FID, return null.
- const LineEntry *FindNearestLineEntry(int FID, unsigned Offset);
+ /// \brief Find the line entry nearest to FID that is before it.
+ ///
+ /// If there is no line entry before \p Offset in \p FID, returns null.
+ const LineEntry *FindNearestLineEntry(FileID FID, unsigned Offset);
// Low-level access
- typedef std::map<int, std::vector<LineEntry> >::iterator iterator;
+ typedef std::map<FileID, std::vector<LineEntry> >::iterator iterator;
iterator begin() { return LineEntries.begin(); }
iterator end() { return LineEntries.end(); }
/// \brief Add a new line entry that has already been encoded into
/// the internal representation of the line table.
- void AddEntry(int FID, const std::vector<LineEntry> &Entries);
+ void AddEntry(FileID FID, const std::vector<LineEntry> &Entries);
};
} // end namespace clang
diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h
index 9e71827..96cada1 100644
--- a/include/clang/Basic/Specifiers.h
+++ b/include/clang/Basic/Specifiers.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines various enumerations that describe declaration and
-// type specifiers.
-//
+///
+/// \file
+/// \brief Defines various enumerations that describe declaration and
+/// type specifiers.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_SPECIFIERS_H
@@ -63,9 +64,8 @@ namespace clang {
TST_error // erroneous type
};
- /// WrittenBuiltinSpecs - Structure that packs information about the
- /// type specifiers that were written in a particular type specifier
- /// sequence.
+ /// \brief Structure that packs information about the type specifiers that
+ /// were written in a particular type specifier sequence.
struct WrittenBuiltinSpecs {
/*DeclSpec::TST*/ unsigned Type : 5;
/*DeclSpec::TSS*/ unsigned Sign : 2;
@@ -73,9 +73,8 @@ namespace clang {
bool ModeAttr : 1;
};
- /// AccessSpecifier - A C++ access specifier (public, private,
- /// protected), plus the special value "none" which means
- /// different things in different contexts.
+ /// \brief A C++ access specifier (public, private, protected), plus the
+ /// special value "none" which means different things in different contexts.
enum AccessSpecifier {
AS_public,
AS_protected,
@@ -83,24 +82,24 @@ namespace clang {
AS_none
};
- /// ExprValueKind - The categorization of expression values,
- /// currently following the C++0x scheme.
+ /// \brief The categorization of expression values, currently following the
+ /// C++11 scheme.
enum ExprValueKind {
- /// An r-value expression (a pr-value in the C++0x taxonomy)
+ /// \brief An r-value expression (a pr-value in the C++11 taxonomy)
/// produces a temporary value.
VK_RValue,
- /// An l-value expression is a reference to an object with
+ /// \brief An l-value expression is a reference to an object with
/// independent storage.
VK_LValue,
- /// An x-value expression is a reference to an object with
+ /// \brief An x-value expression is a reference to an object with
/// independent storage but which can be "moved", i.e.
/// efficiently cannibalized for its resources.
VK_XValue
};
- /// A further classification of the kind of object referenced by an
+ /// \brief A further classification of the kind of object referenced by an
/// l-value or x-value.
enum ExprObjectKind {
/// An ordinary object is located at an address in memory.
@@ -112,13 +111,13 @@ namespace clang {
/// A vector component is an element or range of elements on a vector.
OK_VectorComponent,
- /// An Objective C property is a logical field of an Objective-C
- /// object which is read and written via Objective C method calls.
+ /// An Objective-C property is a logical field of an Objective-C
+ /// object which is read and written via Objective-C method calls.
OK_ObjCProperty,
- /// An Objective C array/dictionary subscripting which reads an object
- /// or writes at the subscripted array/dictionary element via
- /// Objective C method calls.
+ /// An Objective-C array/dictionary subscripting which reads an
+ /// object or writes at the subscripted array/dictionary element via
+ /// Objective-C method calls.
OK_ObjCSubscript
};
@@ -159,15 +158,22 @@ namespace clang {
SC_Register
};
- /// Checks whether the given storage class is legal for functions.
+ /// \brief Checks whether the given storage class is legal for functions.
inline bool isLegalForFunction(StorageClass SC) {
return SC <= SC_PrivateExtern;
}
- /// Checks whether the given storage class is legal for variables.
+ /// \brief Checks whether the given storage class is legal for variables.
inline bool isLegalForVariable(StorageClass SC) {
return true;
}
+
+ /// \brief In-class initialization styles for non-static data members.
+ enum InClassInitStyle {
+ ICIS_NoInit, ///< No in-class initializer.
+ ICIS_CopyInit, ///< Copy initialization.
+ ICIS_ListInit ///< Direct list-initialization.
+ };
} // end namespace clang
#endif // LLVM_CLANG_BASIC_SPECIFIERS_H
diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td
index e7718cd..47738af 100644
--- a/include/clang/Basic/StmtNodes.td
+++ b/include/clang/Basic/StmtNodes.td
@@ -31,6 +31,9 @@ def DefaultStmt : DStmt<SwitchCase>;
// GNU Extensions
def AsmStmt : Stmt;
+// MS Extensions
+def MSAsmStmt : Stmt;
+
// Obj-C statements
def ObjCAtTryStmt : Stmt;
def ObjCAtCatchStmt : Stmt;
@@ -134,7 +137,7 @@ def LambdaExpr : DStmt<Expr>;
// Obj-C Expressions.
def ObjCStringLiteral : DStmt<Expr>;
-def ObjCNumericLiteral : DStmt<Expr>;
+def ObjCBoxedExpr : DStmt<Expr>;
def ObjCArrayLiteral : DStmt<Expr>;
def ObjCDictionaryLiteral : DStmt<Expr>;
def ObjCEncodeExpr : DStmt<Expr>;
diff --git a/include/clang/Basic/TargetBuiltins.h b/include/clang/Basic/TargetBuiltins.h
index 7c04bf7..1d5004c 100644
--- a/include/clang/Basic/TargetBuiltins.h
+++ b/include/clang/Basic/TargetBuiltins.h
@@ -6,6 +6,12 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Enumerates target-specific builtins in their own namespaces within
+/// namespace ::clang.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_TARGET_BUILTINS_H
#define LLVM_CLANG_BASIC_TARGET_BUILTINS_H
@@ -15,7 +21,7 @@
namespace clang {
- /// ARM builtins
+ /// \brief ARM builtins
namespace ARM {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
@@ -25,7 +31,7 @@ namespace clang {
};
}
- /// PPC builtins
+ /// \brief PPC builtins
namespace PPC {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
@@ -35,18 +41,18 @@ namespace clang {
};
}
- /// PTX builtins
- namespace PTX {
+ /// \brief NVPTX builtins
+ namespace NVPTX {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
#define BUILTIN(ID, TYPE, ATTRS) BI##ID,
-#include "clang/Basic/BuiltinsPTX.def"
+#include "clang/Basic/BuiltinsNVPTX.def"
LastTSBuiltin
};
}
- /// X86 builtins
+ /// \brief X86 builtins
namespace X86 {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
@@ -56,9 +62,9 @@ namespace clang {
};
}
- /// NeonTypeFlags - Flags to identify the types for overloaded Neon
- /// builtins. These must be kept in sync with the flags in
- /// utils/TableGen/NeonEmitter.h.
+ /// \brief Flags to identify the types for overloaded Neon builtins.
+ ///
+ /// These must be kept in sync with the flags in utils/TableGen/NeonEmitter.h.
class NeonTypeFlags {
enum {
EltTypeMask = 0xf,
@@ -96,7 +102,7 @@ namespace clang {
bool isQuad() const { return (Flags & QuadFlag) != 0; }
};
- /// Hexagon builtins
+ /// \brief Hexagon builtins
namespace Hexagon {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
@@ -105,6 +111,16 @@ namespace clang {
LastTSBuiltin
};
}
+
+ /// \brief MIPS builtins
+ namespace Mips {
+ enum {
+ LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
+#define BUILTIN(ID, TYPE, ATTRS) BI##ID,
+#include "clang/Basic/BuiltinsMips.def"
+ LastTSBuiltin
+ };
+ }
} // end namespace clang.
#endif
diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h
index a03cf83..54d49e6 100644
--- a/include/clang/Basic/TargetInfo.h
+++ b/include/clang/Basic/TargetInfo.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the TargetInfo interface.
-//
+///
+/// \file
+/// \brief Defines the clang::TargetInfo interface.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_TARGETINFO_H
@@ -41,7 +42,7 @@ class TargetOptions;
namespace Builtin { struct Info; }
-/// TargetCXXABI - The types of C++ ABIs for which we can generate code.
+/// \brief The types of C++ ABIs for which we can generate code.
enum TargetCXXABI {
/// The generic ("Itanium") C++ ABI, documented at:
/// http://www.codesourcery.com/public/cxx-abi/
@@ -57,7 +58,7 @@ enum TargetCXXABI {
CXXABI_Microsoft
};
-/// TargetInfo - This class exposes information about the current target.
+/// \brief Exposes information about the current target.
///
class TargetInfo : public RefCountedBase<TargetInfo> {
llvm::Triple Triple;
@@ -79,6 +80,7 @@ protected:
unsigned char LongLongWidth, LongLongAlign;
unsigned char SuitableAlign;
unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
+ unsigned short MaxVectorAlign;
const char *DescriptionString;
const char *UserLabelPrefix;
const char *MCountName;
@@ -99,7 +101,7 @@ protected:
TargetInfo(const std::string &T);
public:
- /// CreateTargetInfo - Construct a target for the given options.
+ /// \brief Construct a target for the given options.
///
/// \param Opts - The options to use to initialize the target. The target may
/// modify the options to canonicalize the target feature information to match
@@ -128,11 +130,35 @@ public:
LongDouble
};
+ /// \brief The different kinds of __builtin_va_list types defined by
+ /// the target implementation.
+ enum BuiltinVaListKind {
+ /// typedef char* __builtin_va_list;
+ CharPtrBuiltinVaList = 0,
+
+ /// typedef void* __builtin_va_list;
+ VoidPtrBuiltinVaList,
+
+ /// __builtin_va_list as defined by the PNaCl ABI:
+ /// http://www.chromium.org/nativeclient/pnacl/bitcode-abi#TOC-Machine-Types
+ PNaClABIBuiltinVaList,
+
+ /// __builtin_va_list as defined by the Power ABI:
+ /// https://www.power.org
+ /// /resources/downloads/Power-Arch-32-bit-ABI-supp-1.0-Embedded.pdf
+ PowerABIBuiltinVaList,
+
+ /// __builtin_va_list as defined by the x86-64 ABI:
+ /// http://www.x86-64.org/documentation/abi.pdf
+ X86_64ABIBuiltinVaList
+ };
+
protected:
IntType SizeType, IntMaxType, UIntMaxType, PtrDiffType, IntPtrType, WCharType,
WIntType, Char16Type, Char32Type, Int64Type, SigAtomicType;
- /// Flag whether the Objective-C built-in boolean type should be signed char.
+ /// \brief Whether Objective-C's built-in boolean type should be signed char.
+ ///
/// Otherwise, when this flag is not set, the normal built-in boolean type is
/// used.
unsigned UseSignedCharForObjCBool : 1;
@@ -144,10 +170,12 @@ protected:
/// boundary.
unsigned UseBitFieldTypeAlignment : 1;
- /// Control whether zero length bitfields (e.g., int : 0;) force alignment of
- /// the next bitfield. If the alignment of the zero length bitfield is
- /// greater than the member that follows it, `bar', `bar' will be aligned as
- /// the type of the zero-length bitfield.
+ /// \brief Whether zero length bitfields (e.g., int : 0;) force alignment of
+ /// the next bitfield.
+ ///
+ /// If the alignment of the zero length bitfield is greater than the member
+ /// that follows it, `bar', `bar' will be aligned as the type of the
+ /// zero-length bitfield.
unsigned UseZeroLengthBitfieldAlignment : 1;
/// If non-zero, specifies a fixed alignment value for bitfields that follow
@@ -170,19 +198,20 @@ public:
IntType getSigAtomicType() const { return SigAtomicType; }
- /// getTypeWidth - Return the width (in bits) of the specified integer type
- /// enum. For example, SignedInt -> getIntWidth().
+ /// \brief Return the width (in bits) of the specified integer type enum.
+ ///
+ /// For example, SignedInt -> getIntWidth().
unsigned getTypeWidth(IntType T) const;
- /// getTypeAlign - Return the alignment (in bits) of the specified integer
- /// type enum. For example, SignedInt -> getIntAlign().
+ /// \brief Return the alignment (in bits) of the specified integer type enum.
+ ///
+ /// For example, SignedInt -> getIntAlign().
unsigned getTypeAlign(IntType T) const;
- /// isTypeSigned - Return whether an integer types is signed. Returns true if
- /// the type is signed; false otherwise.
+ /// \brief Returns true if the type is signed; false otherwise.
static bool isTypeSigned(IntType T);
- /// getPointerWidth - Return the width of pointers on this target, for the
+ /// \brief Return the width of pointers on this target, for the
/// specified address space.
uint64_t getPointerWidth(unsigned AddrSpace) const {
return AddrSpace == 0 ? PointerWidth : getPointerWidthV(AddrSpace);
@@ -191,17 +220,21 @@ public:
return AddrSpace == 0 ? PointerAlign : getPointerAlignV(AddrSpace);
}
- /// getBoolWidth/Align - Return the size of '_Bool' and C++ 'bool' for this
- /// target, in bits.
+ /// \brief Return the size of '_Bool' and C++ 'bool' for this target, in bits.
unsigned getBoolWidth() const { return BoolWidth; }
+
+ /// \brief Return the alignment of '_Bool' and C++ 'bool' for this target.
unsigned getBoolAlign() const { return BoolAlign; }
unsigned getCharWidth() const { return 8; } // FIXME
unsigned getCharAlign() const { return 8; } // FIXME
- /// getShortWidth/Align - Return the size of 'signed short' and
- /// 'unsigned short' for this target, in bits.
+ /// \brief Return the size of 'signed short' and 'unsigned short' for this
+ /// target, in bits.
unsigned getShortWidth() const { return 16; } // FIXME
+
+ /// \brief Return the alignment of 'signed short' and 'unsigned short' for
+ /// this target.
unsigned getShortAlign() const { return 16; } // FIXME
/// getIntWidth/Align - Return the size of 'signed int' and 'unsigned int' for
@@ -219,7 +252,7 @@ public:
unsigned getLongLongWidth() const { return LongLongWidth; }
unsigned getLongLongAlign() const { return LongLongAlign; }
- /// getSuitableAlign - Return the alignment that is suitable for storing any
+ /// \brief Return the alignment that is suitable for storing any
/// object with a fundamental alignment requirement.
unsigned getSuitableAlign() const { return SuitableAlign; }
@@ -261,7 +294,7 @@ public:
return *LongDoubleFormat;
}
- /// getFloatEvalMethod - Return the value for the C99 FLT_EVAL_METHOD macro.
+ /// \brief Return the value for the C99 FLT_EVAL_METHOD macro.
virtual unsigned getFloatEvalMethod() const { return 0; }
// getLargeArrayMinWidth/Align - Return the minimum array size that is
@@ -269,21 +302,22 @@ public:
unsigned getLargeArrayMinWidth() const { return LargeArrayMinWidth; }
unsigned getLargeArrayAlign() const { return LargeArrayAlign; }
- /// getMaxAtomicPromoteWidth - Return the maximum width lock-free atomic
- /// operation which will ever be supported for the given target
+ /// \brief Return the maximum width lock-free atomic operation which will
+ /// ever be supported for the given target
unsigned getMaxAtomicPromoteWidth() const { return MaxAtomicPromoteWidth; }
- /// getMaxAtomicInlineWidth - Return the maximum width lock-free atomic
- /// operation which can be inlined given the supported features of the
- /// given target.
+ /// \brief Return the maximum width lock-free atomic operation which can be
+ /// inlined given the supported features of the given target.
unsigned getMaxAtomicInlineWidth() const { return MaxAtomicInlineWidth; }
- /// getIntMaxTWidth - Return the size of intmax_t and uintmax_t for this
- /// target, in bits.
+ /// \brief Return the maximum vector alignment supported for the given target.
+ unsigned getMaxVectorAlign() const { return MaxVectorAlign; }
+
+ /// \brief Return the size of intmax_t and uintmax_t for this target, in bits.
unsigned getIntMaxTWidth() const {
return getTypeWidth(IntMaxType);
}
- /// getRegisterWidth - Return the "preferred" register width on this target.
+ /// \brief Return the "preferred" register width on this target.
uint64_t getRegisterWidth() const {
// Currently we assume the register width on the target matches the pointer
// width, we can introduce a new variable for this if/when some target wants
@@ -291,22 +325,24 @@ public:
return LongWidth;
}
- /// getUserLabelPrefix - This returns the default value of the
- /// __USER_LABEL_PREFIX__ macro, which is the prefix given to user symbols by
- /// default. On most platforms this is "_", but it is "" on some, and "." on
- /// others.
+ /// \brief Returns the default value of the __USER_LABEL_PREFIX__ macro,
+ /// which is the prefix given to user symbols by default.
+ ///
+ /// On most platforms this is "_", but it is "" on some, and "." on others.
const char *getUserLabelPrefix() const {
return UserLabelPrefix;
}
- /// MCountName - This returns name of the mcount instrumentation function.
+ /// \brief Returns the name of the mcount instrumentation function.
const char *getMCountName() const {
return MCountName;
}
- /// useSignedCharForObjCBool - Check if the Objective-C built-in boolean
- /// type should be signed char. Otherwise, if this returns false, the
- /// normal built-in boolean type should also be used for Objective-C.
+ /// \brief Check if the Objective-C built-in boolean type should be signed
+ /// char.
+ ///
+ /// Otherwise, if this returns false, the normal built-in boolean type
+ /// should also be used for Objective-C.
bool useSignedCharForObjCBool() const {
return UseSignedCharForObjCBool;
}
@@ -314,87 +350,91 @@ public:
UseSignedCharForObjCBool = false;
}
- /// useBitFieldTypeAlignment() - Check whether the alignment of bit-field
- /// types is respected when laying out structures.
+ /// \brief Check whether the alignment of bit-field types is respected
+ /// when laying out structures.
bool useBitFieldTypeAlignment() const {
return UseBitFieldTypeAlignment;
}
- /// useZeroLengthBitfieldAlignment() - Check whether zero length bitfields
- /// should force alignment of the next member.
+ /// \brief Check whether zero length bitfields should force alignment of
+ /// the next member.
bool useZeroLengthBitfieldAlignment() const {
return UseZeroLengthBitfieldAlignment;
}
- /// getZeroLengthBitfieldBoundary() - Get the fixed alignment value in bits
- /// for a member that follows a zero length bitfield.
+ /// \brief Get the fixed alignment value in bits for a member that follows
+ /// a zero length bitfield.
unsigned getZeroLengthBitfieldBoundary() const {
return ZeroLengthBitfieldBoundary;
}
- /// hasAlignMac68kSupport - Check whether this target support '#pragma options
- /// align=mac68k'.
+ /// \brief Check whether this target support '\#pragma options align=mac68k'.
bool hasAlignMac68kSupport() const {
return HasAlignMac68kSupport;
}
- /// getTypeName - Return the user string for the specified integer type enum.
+ /// \brief Return the user string for the specified integer type enum.
+ ///
/// For example, SignedShort -> "short".
static const char *getTypeName(IntType T);
- /// getTypeConstantSuffix - Return the constant suffix for the specified
- /// integer type enum. For example, SignedLong -> "L".
+ /// \brief Return the constant suffix for the specified integer type enum.
+ ///
+ /// For example, SignedLong -> "L".
static const char *getTypeConstantSuffix(IntType T);
/// \brief Check whether the given real type should use the "fpret" flavor of
- /// Obj-C message passing on this target.
+ /// Objective-C message passing on this target.
bool useObjCFPRetForRealType(RealType T) const {
return RealTypeUsesObjCFPRet & (1 << T);
}
/// \brief Check whether _Complex long double should use the "fp2ret" flavor
- /// of Obj-C message passing on this target.
+ /// of Objective-C message passing on this target.
bool useObjCFP2RetForComplexLongDouble() const {
return ComplexLongDoubleUsesFP2Ret;
}
///===---- Other target property query methods --------------------------===//
- /// getTargetDefines - Appends the target-specific #define values for this
+ /// \brief Appends the target-specific \#define values for this
/// target set to the specified buffer.
virtual void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const = 0;
- /// getTargetBuiltins - Return information about target-specific builtins for
+ /// Return information about target-specific builtins for
/// the current primary target, and info about which builtins are non-portable
/// across the current set of primary and secondary targets.
virtual void getTargetBuiltins(const Builtin::Info *&Records,
unsigned &NumRecords) const = 0;
- /// isCLZForZeroUndef - The __builtin_clz* and __builtin_ctz* built-in
+ /// The __builtin_clz* and __builtin_ctz* built-in
/// functions are specified to have undefined results for zero inputs, but
/// on targets that support these operations in a way that provides
/// well-defined results for zero without loss of performance, it is a good
/// idea to avoid optimizing based on that undef behavior.
virtual bool isCLZForZeroUndef() const { return true; }
- /// getVAListDeclaration - Return the declaration to use for
- /// __builtin_va_list, which is target-specific.
- virtual const char *getVAListDeclaration() const = 0;
+ /// \brief Returns the kind of __builtin_va_list type that should be used
+ /// with this target.
+ virtual BuiltinVaListKind getBuiltinVaListKind() const = 0;
- /// isValidClobber - Returns whether the passed in string is
- /// a valid clobber in an inline asm statement. This is used by
- /// Sema.
+ /// \brief Returns whether the passed in string is a valid clobber in an
+ /// inline asm statement.
+ ///
+ /// This is used by Sema.
bool isValidClobber(StringRef Name) const;
- /// isValidGCCRegisterName - Returns whether the passed in string
- /// is a valid register name according to GCC. This is used by Sema for
- /// inline asm statements.
+ /// \brief Returns whether the passed in string is a valid register name
+ /// according to GCC.
+ ///
+ /// This is used by Sema for inline asm statements.
bool isValidGCCRegisterName(StringRef Name) const;
- // getNormalizedGCCRegisterName - Returns the "normalized" GCC register name.
- // For example, on x86 it will return "ax" when "eax" is passed in.
+ /// \brief Returns the "normalized" GCC register name.
+ ///
+ /// For example, on x86 it will return "ax" when "eax" is passed in.
StringRef getNormalizedGCCRegisterName(StringRef Name) const;
struct ConstraintInfo {
@@ -421,13 +461,15 @@ public:
bool allowsRegister() const { return (Flags & CI_AllowsRegister) != 0; }
bool allowsMemory() const { return (Flags & CI_AllowsMemory) != 0; }
- /// hasMatchingInput - Return true if this output operand has a matching
+ /// \brief Return true if this output operand has a matching
/// (tied) input operand.
bool hasMatchingInput() const { return (Flags & CI_HasMatchingInput) != 0; }
- /// hasTiedOperand() - Return true if this input operand is a matching
- /// constraint that ties it to an output operand. If this returns true,
- /// then getTiedOperand will indicate which output operand this is tied to.
+ /// \brief Return true if this input operand is a matching
+ /// constraint that ties it to an output operand.
+ ///
+ /// If this returns true then getTiedOperand will indicate which output
+ /// operand this is tied to.
bool hasTiedOperand() const { return TiedOperand != -1; }
unsigned getTiedOperand() const {
assert(hasTiedOperand() && "Has no tied operand!");
@@ -439,9 +481,10 @@ public:
void setAllowsRegister() { Flags |= CI_AllowsRegister; }
void setHasMatchingInput() { Flags |= CI_HasMatchingInput; }
- /// setTiedOperand - Indicate that this is an input operand that is tied to
- /// the specified output operand. Copy over the various constraint
- /// information from the output.
+ /// \brief Indicate that this is an input operand that is tied to
+ /// the specified output operand.
+ ///
+ /// Copy over the various constraint information from the output.
void setTiedOperand(unsigned N, ConstraintInfo &Output) {
Output.setHasMatchingInput();
Flags = Output.Flags;
@@ -471,11 +514,11 @@ public:
return std::string(1, *Constraint);
}
- // Returns a string of target-specific clobbers, in LLVM format.
+ /// \brief Returns a string of target-specific clobbers, in LLVM format.
virtual const char *getClobbers() const = 0;
- /// getTriple - Return the target triple of the primary target.
+ /// \brief Returns the target triple of the primary target.
const llvm::Triple &getTriple() const {
return Triple;
}
@@ -494,14 +537,13 @@ public:
const unsigned RegNum;
};
- /// hasProtectedVisibility - Does this target support "protected"
- /// visibility?
+ /// \brief Does this target support "protected" visibility?
///
/// Any target which dynamic libraries will naturally support
/// something like "default" (meaning that the symbol is visible
/// outside this shared object) and "hidden" (meaning that it isn't)
/// visibilities, but "protected" is really an ELF-specific concept
- /// with wierd semantics designed around the convenience of dynamic
+ /// with weird semantics designed around the convenience of dynamic
/// linker implementations. Which is not to suggest that there's
/// consistent target-independent semantics for "default" visibility
/// either; the entire thing is pretty badly mangled.
@@ -509,28 +551,29 @@ public:
virtual bool useGlobalsForAutomaticVariables() const { return false; }
- /// getCFStringSection - Return the section to use for CFString
- /// literals, or 0 if no special section is used.
+ /// \brief Return the section to use for CFString literals, or 0 if no
+ /// special section is used.
virtual const char *getCFStringSection() const {
return "__DATA,__cfstring";
}
- /// getNSStringSection - Return the section to use for NSString
- /// literals, or 0 if no special section is used.
+ /// \brief Return the section to use for NSString literals, or 0 if no
+ /// special section is used.
virtual const char *getNSStringSection() const {
return "__OBJC,__cstring_object,regular,no_dead_strip";
}
- /// getNSStringNonFragileABISection - Return the section to use for
- /// NSString literals, or 0 if no special section is used (NonFragile ABI).
+ /// \brief Return the section to use for NSString literals, or 0 if no
+ /// special section is used (NonFragile ABI).
virtual const char *getNSStringNonFragileABISection() const {
return "__DATA, __objc_stringobj, regular, no_dead_strip";
}
- /// isValidSectionSpecifier - This is an optional hook that targets can
- /// implement to perform semantic checking on attribute((section("foo")))
- /// specifiers. In this case, "foo" is passed in to be checked. If the
- /// section specifier is invalid, the backend should return a non-empty string
+ /// \brief An optional hook that targets can implement to perform semantic
+ /// checking on attribute((section("foo"))) specifiers.
+ ///
+ /// In this case, "foo" is passed in to be checked. If the section
+ /// specifier is invalid, the backend should return a non-empty string
/// that indicates the problem.
///
/// This hook is a simple quality of implementation feature to catch errors
@@ -541,43 +584,44 @@ public:
return "";
}
- /// setForcedLangOptions - Set forced language options.
+ /// \brief Set forced language options.
+ ///
/// Apply changes to the target information with respect to certain
/// language options which change the target configuration.
virtual void setForcedLangOptions(LangOptions &Opts);
- /// getDefaultFeatures - Get the default set of target features for the CPU;
+ /// \brief Get the default set of target features for the CPU;
/// this should include all legal feature strings on the target.
virtual void getDefaultFeatures(llvm::StringMap<bool> &Features) const {
}
- /// getABI - Get the ABI in use.
+ /// \brief Get the ABI currently in use.
virtual const char *getABI() const {
return "";
}
- /// getCXXABI - Get the C++ ABI in use.
+ /// \brief Get the C++ ABI currently in use.
virtual TargetCXXABI getCXXABI() const {
return CXXABI;
}
- /// setCPU - Target the specific CPU.
+ /// \brief Target the specified CPU.
///
- /// \return - False on error (invalid CPU name).
+ /// \return False on error (invalid CPU name).
virtual bool setCPU(const std::string &Name) {
return false;
}
- /// setABI - Use the specific ABI.
+ /// \brief Use the specified ABI.
///
- /// \return - False on error (invalid ABI name).
+ /// \return False on error (invalid ABI name).
virtual bool setABI(const std::string &Name) {
return false;
}
- /// setCXXABI - Use this specific C++ ABI.
+ /// \brief Use this specified C++ ABI.
///
- /// \return - False on error (invalid C++ ABI name).
+ /// \return False on error (invalid C++ ABI name).
bool setCXXABI(const std::string &Name) {
static const TargetCXXABI Unknown = static_cast<TargetCXXABI>(-1);
TargetCXXABI ABI = llvm::StringSwitch<TargetCXXABI>(Name)
@@ -589,27 +633,28 @@ public:
return setCXXABI(ABI);
}
- /// setCXXABI - Set the C++ ABI to be used by this implementation.
+ /// \brief Set the C++ ABI to be used by this implementation.
///
- /// \return - False on error (ABI not valid on this target)
+ /// \return False on error (ABI not valid on this target)
virtual bool setCXXABI(TargetCXXABI ABI) {
CXXABI = ABI;
return true;
}
- /// setFeatureEnabled - Enable or disable a specific target feature,
+ /// \brief Enable or disable a specific target feature;
/// the feature name must be valid.
///
- /// \return - False on error (invalid feature name).
+ /// \return False on error (invalid feature name).
virtual bool setFeatureEnabled(llvm::StringMap<bool> &Features,
StringRef Name,
bool Enabled) const {
return false;
}
- /// HandleTargetOptions - Perform initialization based on the user configured
- /// set of features (e.g., +sse4). The list is guaranteed to have at most one
- /// entry per feature.
+ /// \brief Perform initialization based on the user configured
+ /// set of features (e.g., +sse4).
+ ///
+ /// The list is guaranteed to have at most one entry per feature.
///
/// The target may modify the features list, to change which options are
/// passed onwards to the backend.
@@ -621,19 +666,20 @@ public:
return false;
}
- // getRegParmMax - Returns maximal number of args passed in registers.
+ // \brief Returns maximal number of args passed in registers.
unsigned getRegParmMax() const {
assert(RegParmMax < 7 && "RegParmMax value is larger than AST can handle");
return RegParmMax;
}
- /// isTLSSupported - Whether the target supports thread-local storage.
+ /// \brief Whether the target supports thread-local storage.
bool isTLSSupported() const {
return TLSSupported;
}
- /// hasNoAsmVariants - Return true if {|} are normal characters in the
- /// asm string. If this returns false (the default), then {abc|xyz} is syntax
+ /// \brief Return true if {|} are normal characters in the asm string.
+ ///
+ /// If this returns false (the default), then {abc|xyz} is syntax
/// that says that when compiling for asm variant #0, "abc" should be
/// generated, but when compiling for asm variant #1, "xyz" should be
/// generated.
@@ -641,14 +687,13 @@ public:
return NoAsmVariants;
}
- /// getEHDataRegisterNumber - Return the register number that
- /// __builtin_eh_return_regno would return with the specified argument.
+ /// \brief Return the register number that __builtin_eh_return_regno would
+ /// return with the specified argument.
virtual int getEHDataRegisterNumber(unsigned RegNo) const {
return -1;
}
- /// getStaticInitSectionSpecifier - Return the section to use for C++ static
- /// initialization functions.
+ /// \brief Return the section to use for C++ static initialization functions.
virtual const char *getStaticInitSectionSpecifier() const {
return 0;
}
diff --git a/include/clang/Basic/TargetOptions.h b/include/clang/Basic/TargetOptions.h
index f3c206f..15ececd 100644
--- a/include/clang/Basic/TargetOptions.h
+++ b/include/clang/Basic/TargetOptions.h
@@ -6,6 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines the clang::TargetOptions class.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_FRONTEND_TARGETOPTIONS_H
#define LLVM_CLANG_FRONTEND_TARGETOPTIONS_H
@@ -15,7 +20,7 @@
namespace clang {
-/// TargetOptions - Options for controlling the target.
+/// \brief Options for controlling the target.
class TargetOptions {
public:
/// If given, the name of the target triple to compile for. If not given the
diff --git a/include/clang/Basic/TemplateKinds.h b/include/clang/Basic/TemplateKinds.h
index c6ea05b..dda011a 100644
--- a/include/clang/Basic/TemplateKinds.h
+++ b/include/clang/Basic/TemplateKinds.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the TemplateNameKind enum.
-//
+///
+/// \file
+/// \brief Defines the clang::TemplateNameKind enum.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TEMPLATEKINDS_H
#define LLVM_CLANG_TEMPLATEKINDS_H
diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def
index 2e4d34d..fc03191 100644
--- a/include/clang/Basic/TokenKinds.def
+++ b/include/clang/Basic/TokenKinds.def
@@ -211,6 +211,8 @@ PUNCTUATOR(greatergreatergreater, ">>>")
// KEYCXX0X - This is a C++ keyword introduced to C++ in C++0x
// KEYGNU - This is a keyword if GNU extensions are enabled
// KEYMS - This is a keyword if Microsoft extensions are enabled
+// KEYNOMS - This is a keyword that must never be enabled under
+// Microsoft mode
// KEYOPENCL - This is a keyword in OpenCL
// KEYALTIVEC - This is a keyword in AltiVec
// KEYBORLAND - This is a keyword if Borland extensions are enabled
@@ -251,6 +253,7 @@ KEYWORD(void , KEYALL)
KEYWORD(volatile , KEYALL)
KEYWORD(while , KEYALL)
KEYWORD(_Alignas , KEYALL)
+KEYWORD(_Alignof , KEYALL)
KEYWORD(_Atomic , KEYALL)
KEYWORD(_Bool , KEYNOCXX)
KEYWORD(_Complex , KEYALL)
@@ -310,8 +313,8 @@ CXX_KEYWORD_OPERATOR(xor_eq , caretequal)
// C++0x keywords
KEYWORD(alignas , KEYCXX0X)
KEYWORD(alignof , KEYCXX0X)
-KEYWORD(char16_t , KEYCXX0X)
-KEYWORD(char32_t , KEYCXX0X)
+KEYWORD(char16_t , KEYCXX0X|KEYNOMS)
+KEYWORD(char32_t , KEYCXX0X|KEYNOMS)
KEYWORD(constexpr , KEYCXX0X)
KEYWORD(decltype , KEYCXX0X)
KEYWORD(noexcept , KEYCXX0X)
@@ -342,6 +345,9 @@ KEYWORD(__PRETTY_FUNCTION__ , KEYALL)
// GNU Extensions (outside impl-reserved namespace)
KEYWORD(typeof , KEYGNU)
+// MS Extensions
+KEYWORD(L__FUNCTION__ , KEYMS)
+
// GNU and MS Type Traits
KEYWORD(__has_nothrow_assign , KEYCXX)
KEYWORD(__has_nothrow_copy , KEYCXX)
@@ -486,28 +492,33 @@ ALIAS("__volatile" , volatile , KEYALL)
ALIAS("__volatile__" , volatile , KEYALL)
// Microsoft extensions which should be disabled in strict conformance mode
-KEYWORD(__ptr64 , KEYMS)
-KEYWORD(__ptr32 , KEYMS)
-KEYWORD(__w64 , KEYMS)
-KEYWORD(__uuidof , KEYMS | KEYBORLAND)
-KEYWORD(__try , KEYMS | KEYBORLAND)
-KEYWORD(__finally , KEYMS | KEYBORLAND)
-KEYWORD(__leave , KEYMS | KEYBORLAND)
-KEYWORD(__int64 , KEYMS)
-KEYWORD(__if_exists , KEYMS)
-KEYWORD(__if_not_exists , KEYMS)
-ALIAS("__int8" , char , KEYMS)
-ALIAS("__int16" , short , KEYMS)
-ALIAS("__int32" , int , KEYMS)
-ALIAS("_asm" , asm , KEYMS)
-ALIAS("_cdecl" , __cdecl , KEYMS | KEYBORLAND)
-ALIAS("_fastcall" , __fastcall , KEYMS | KEYBORLAND)
-ALIAS("_stdcall" , __stdcall , KEYMS | KEYBORLAND)
-ALIAS("_thiscall" , __thiscall , KEYMS)
-ALIAS("_uuidof" , __uuidof , KEYMS | KEYBORLAND)
-ALIAS("_inline" , inline , KEYMS)
-ALIAS("_declspec" , __declspec , KEYMS)
-ALIAS("__interface" , struct , KEYMS)
+KEYWORD(__ptr64 , KEYMS)
+KEYWORD(__ptr32 , KEYMS)
+KEYWORD(__w64 , KEYMS)
+KEYWORD(__uuidof , KEYMS | KEYBORLAND)
+KEYWORD(__try , KEYMS | KEYBORLAND)
+KEYWORD(__finally , KEYMS | KEYBORLAND)
+KEYWORD(__leave , KEYMS | KEYBORLAND)
+KEYWORD(__int64 , KEYMS)
+KEYWORD(__if_exists , KEYMS)
+KEYWORD(__if_not_exists , KEYMS)
+KEYWORD(__single_inheritance , KEYMS)
+KEYWORD(__multiple_inheritance , KEYMS)
+KEYWORD(__virtual_inheritance , KEYMS)
+ALIAS("__int8" , char , KEYMS)
+ALIAS("__int16" , short , KEYMS)
+ALIAS("__int32" , int , KEYMS)
+ALIAS("_asm" , asm , KEYMS)
+ALIAS("_alignof" , __alignof , KEYMS)
+ALIAS("__builtin_alignof", __alignof , KEYMS)
+ALIAS("_cdecl" , __cdecl , KEYMS | KEYBORLAND)
+ALIAS("_fastcall" , __fastcall , KEYMS | KEYBORLAND)
+ALIAS("_stdcall" , __stdcall , KEYMS | KEYBORLAND)
+ALIAS("_thiscall" , __thiscall , KEYMS)
+ALIAS("_uuidof" , __uuidof , KEYMS | KEYBORLAND)
+ALIAS("_inline" , inline , KEYMS)
+ALIAS("_declspec" , __declspec , KEYMS)
+ALIAS("__interface" , struct , KEYMS)
// Borland Extensions which should be disabled in strict conformance mode.
ALIAS("_pascal" , __pascal , KEYBORLAND)
@@ -584,6 +595,11 @@ ANNOTATION(pragma_vis)
// handles them.
ANNOTATION(pragma_pack)
+// Annotation for #pragma clang __debug parser_crash...
+// The lexer produces these so that they only take effect when the parser
+// handles them.
+ANNOTATION(pragma_parser_crash)
+
#undef ANNOTATION
#undef TESTING_KEYWORD
#undef OBJC2_AT_KEYWORD
diff --git a/include/clang/Basic/TokenKinds.h b/include/clang/Basic/TokenKinds.h
index 515390a..478add8 100644
--- a/include/clang/Basic/TokenKinds.h
+++ b/include/clang/Basic/TokenKinds.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the TokenKind enum and support functions.
-//
+///
+/// \file
+/// \brief Defines the clang::TokenKind enum and support functions.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOKENKINDS_H
@@ -18,24 +19,23 @@ namespace clang {
namespace tok {
-/// TokenKind - This provides a simple uniform namespace for tokens from all C
-/// languages.
+/// \brief Provides a simple uniform namespace for tokens from all C languages.
enum TokenKind {
#define TOK(X) X,
#include "clang/Basic/TokenKinds.def"
NUM_TOKENS
};
-/// PPKeywordKind - This provides a namespace for preprocessor keywords which
-/// start with a '#' at the beginning of the line.
+/// \brief Provides a namespace for preprocessor keywords which start with a
+/// '#' at the beginning of the line.
enum PPKeywordKind {
#define PPKEYWORD(X) pp_##X,
#include "clang/Basic/TokenKinds.def"
NUM_PP_KEYWORDS
};
-/// ObjCKeywordKind - This provides a namespace for Objective-C keywords which
-/// start with an '@'.
+/// \brief Provides a namespace for Objective-C keywords which start with
+/// an '@'.
enum ObjCKeywordKind {
#define OBJC1_AT_KEYWORD(X) objc_##X,
#define OBJC2_AT_KEYWORD(X) objc_##X,
@@ -43,8 +43,7 @@ enum ObjCKeywordKind {
NUM_OBJC_KEYWORDS
};
-/// OnOffSwitch - This defines the possible values of an on-off-switch
-/// (C99 6.10.6p2).
+/// \brief Defines the possible values of an on-off-switch (C99 6.10.6p2).
enum OnOffSwitch {
OOS_ON, OOS_OFF, OOS_DEFAULT
};
diff --git a/include/clang/Basic/TypeTraits.h b/include/clang/Basic/TypeTraits.h
index 721f44f..0a5a864 100644
--- a/include/clang/Basic/TypeTraits.h
+++ b/include/clang/Basic/TypeTraits.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines enumerations for the type traits support.
-//
+///
+/// \file
+/// \brief Defines enumerations for the type traits support.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TYPETRAITS_H
@@ -16,7 +17,7 @@
namespace clang {
- /// UnaryTypeTrait - Names for the unary type traits.
+ /// \brief Names for the unary type traits.
enum UnaryTypeTrait {
UTT_HasNothrowAssign,
UTT_HasNothrowCopy,
@@ -62,7 +63,7 @@ namespace clang {
UTT_IsVolatile
};
- /// BinaryTypeTrait - Names for the binary type traits.
+ /// \brief Names for the binary type traits.
enum BinaryTypeTrait {
BTT_IsBaseOf,
BTT_IsConvertible,
@@ -72,13 +73,13 @@ namespace clang {
BTT_IsTriviallyAssignable
};
- /// ArrayTypeTrait - Names for the array type traits.
+ /// \brief Names for the array type traits.
enum ArrayTypeTrait {
ATT_ArrayRank,
ATT_ArrayExtent
};
- /// UnaryExprOrTypeTrait - Names for the "expression or type" traits.
+ /// \brief Names for the "expression or type" traits.
enum UnaryExprOrTypeTrait {
UETT_SizeOf,
UETT_AlignOf,
diff --git a/include/clang/Basic/Version.h b/include/clang/Basic/Version.h
index f3f5b5a..3f1b4d8 100644
--- a/include/clang/Basic/Version.h
+++ b/include/clang/Basic/Version.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This header defines version macros and version-related utility functions
-// for Clang.
-//
+///
+/// \file
+/// \brief Defines version macros and version-related utility functions
+/// for Clang.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_VERSION_H
@@ -26,8 +27,7 @@
/// \brief Helper macro for CLANG_VERSION_STRING.
#define CLANG_MAKE_VERSION_STRING(X,Y,Z) CLANG_MAKE_VERSION_STRING2(X.Y.Z)
-/// \brief A string that describes the Clang version number, e.g.,
-/// "1.0".
+/// \brief A string that describes the Clang version number, e.g., "1.0".
#define CLANG_VERSION_STRING \
CLANG_MAKE_VERSION_STRING(CLANG_VERSION_MAJOR,CLANG_VERSION_MINOR, \
CLANG_VERSION_PATCHLEVEL)
@@ -35,8 +35,7 @@
/// \brief Helper macro for CLANG_VERSION_STRING.
#define CLANG_MAKE_VERSION_STRING(X,Y) CLANG_MAKE_VERSION_STRING2(X.Y)
-/// \brief A string that describes the Clang version number, e.g.,
-/// "1.0".
+/// \brief A string that describes the Clang version number, e.g., "1.0".
#define CLANG_VERSION_STRING \
CLANG_MAKE_VERSION_STRING(CLANG_VERSION_MAJOR,CLANG_VERSION_MINOR)
#endif
@@ -47,31 +46,34 @@ namespace clang {
/// Clang was built.
std::string getClangRepositoryPath();
- /// \brief Retrieves the repository path from which LLVM was built. Supports
- /// LLVM residing in a separate repository from clang.
+ /// \brief Retrieves the repository path from which LLVM was built.
+ ///
+ /// This supports LLVM residing in a separate repository from clang.
std::string getLLVMRepositoryPath();
/// \brief Retrieves the repository revision number (or identifer) from which
- /// this Clang was built.
+ /// this Clang was built.
std::string getClangRevision();
/// \brief Retrieves the repository revision number (or identifer) from which
- /// LLVM was built. If Clang and LLVM are in the same repository, this returns
- /// the same string as getClangRevision.
+ /// LLVM was built.
+ ///
+ /// If Clang and LLVM are in the same repository, this returns the same
+ /// string as getClangRevision.
std::string getLLVMRevision();
/// \brief Retrieves the full repository version that is an amalgamation of
- /// the information in getClangRepositoryPath() and getClangRevision().
+ /// the information in getClangRepositoryPath() and getClangRevision().
std::string getClangFullRepositoryVersion();
/// \brief Retrieves a string representing the complete clang version,
- /// which includes the clang version number, the repository version,
- /// and the vendor tag.
+ /// which includes the clang version number, the repository version,
+ /// and the vendor tag.
std::string getClangFullVersion();
/// \brief Retrieves a string representing the complete clang version suitable
- /// for use in the CPP __VERSION__ macro, which includes the clang version
- /// number, the repository version, and the vendor tag.
+ /// for use in the CPP __VERSION__ macro, which includes the clang version
+ /// number, the repository version, and the vendor tag.
std::string getClangFullCPPVersion();
}
diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h
index 30ef6641..a94f76c 100644
--- a/include/clang/Basic/VersionTuple.h
+++ b/include/clang/Basic/VersionTuple.h
@@ -6,10 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This header defines the VersionTuple class, which represents a version in
-// the form major[.minor[.subminor]].
-//
+///
+/// \file
+/// \brief Defines the clang::VersionTuple class, which represents a version in
+/// the form major[.minor[.subminor]].
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_VERSIONTUPLE_H
#define LLVM_CLANG_BASIC_VERSIONTUPLE_H
@@ -73,15 +74,18 @@ public:
return X.Major == Y.Major && X.Minor == Y.Minor && X.Subminor == Y.Subminor;
}
- /// \brief Determine if two version numbers are not equivalent. If
- /// not provided, minor and subminor version numbers are considered to be
+ /// \brief Determine if two version numbers are not equivalent.
+ ///
+ /// If not provided, minor and subminor version numbers are considered to be
/// zero.
friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) {
return !(X == Y);
}
- /// \brief Determine whether one version number precedes another. If not
- /// provided, minor and subminor version numbers are considered to be zero.
+ /// \brief Determine whether one version number precedes another.
+ ///
+ /// If not provided, minor and subminor version numbers are considered to be
+ /// zero.
friend bool operator<(const VersionTuple &X, const VersionTuple &Y) {
if (X.Major != Y.Major)
return X.Major < Y.Major;
@@ -92,28 +96,39 @@ public:
return X.Subminor < Y.Subminor;
}
- /// \brief Determine whether one version number follows another. If not
- /// provided, minor and subminor version numbers are considered to be zero.
+ /// \brief Determine whether one version number follows another.
+ ///
+ /// If not provided, minor and subminor version numbers are considered to be
+ /// zero.
friend bool operator>(const VersionTuple &X, const VersionTuple &Y) {
return Y < X;
}
/// \brief Determine whether one version number precedes or is
- /// equivalent to another. If not provided, minor and subminor
- /// version numbers are considered to be zero.
+ /// equivalent to another.
+ ///
+ /// If not provided, minor and subminor version numbers are considered to be
+ /// zero.
friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) {
return !(Y < X);
}
/// \brief Determine whether one version number follows or is
- /// equivalent to another. If not provided, minor and subminor
- /// version numbers are considered to be zero.
+ /// equivalent to another.
+ ///
+ /// If not provided, minor and subminor version numbers are considered to be
+ /// zero.
friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
return !(X < Y);
}
- /// \brief Retrieve a string representation of the version number/
+ /// \brief Retrieve a string representation of the version number.
std::string getAsString() const;
+
+ /// \brief Try to parse the given string as a version number.
+ /// \returns \c true if the string does not match the regular expression
+ /// [0-9]+(\.[0-9]+(\.[0-9]+))
+ bool tryParse(StringRef string);
};
/// \brief Print a version number.
diff --git a/include/clang/Basic/Visibility.h b/include/clang/Basic/Visibility.h
index 90e288a..e81ad91 100644
--- a/include/clang/Basic/Visibility.h
+++ b/include/clang/Basic/Visibility.h
@@ -6,20 +6,23 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the Visibility enumeration and various utility
-// functions.
-//
+///
+/// \file
+/// \brief Defines the clang::Visibility enumeration and various utility
+/// functions.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_VISIBILITY_H
#define LLVM_CLANG_BASIC_VISIBILITY_H
namespace clang {
-/// \link Describes the different kinds of visibility that a
-/// declaration may have. Visibility determines how a declaration
-/// interacts with the dynamic linker. It may also affect whether the
-/// symbol can be found by runtime symbol lookup APIs.
+/// \brief Describes the different kinds of visibility that a declaration
+/// may have.
+///
+/// Visibility determines how a declaration interacts with the dynamic
+/// linker. It may also affect whether the symbol can be found by runtime
+/// symbol lookup APIs.
///
/// Visibility is not described in any language standard and
/// (nonetheless) sometimes has odd behavior. Not all platforms
diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td
index 71a0aa2..451d562 100644
--- a/include/clang/Basic/arm_neon.td
+++ b/include/clang/Basic/arm_neon.td
@@ -15,6 +15,7 @@
class Op;
def OP_NONE : Op;
+def OP_UNAVAILABLE : Op;
def OP_ADD : Op;
def OP_ADDL : Op;
def OP_ADDW : Op;
diff --git a/include/clang/Driver/Arg.h b/include/clang/Driver/Arg.h
index e8625bb..e466cc3 100644
--- a/include/clang/Driver/Arg.h
+++ b/include/clang/Driver/Arg.h
@@ -6,6 +6,11 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines the clang::driver::Arg class for parsed arguments.
+///
+//===----------------------------------------------------------------------===//
#ifndef CLANG_DRIVER_ARG_H_
#define CLANG_DRIVER_ARG_H_
@@ -20,7 +25,7 @@ namespace driver {
class ArgList;
class Option;
- /// Arg - A concrete instance of a particular driver option.
+ /// \brief A concrete instance of a particular driver option.
///
/// The Arg class encodes just enough information to be able to
/// derive the argument values efficiently. In addition, Arg
@@ -32,25 +37,26 @@ namespace driver {
void operator=(const Arg &); // DO NOT IMPLEMENT
private:
- /// The option this argument is an instance of.
+ /// \brief The option this argument is an instance of.
const Option *Opt;
- /// The argument this argument was derived from (during tool chain
+ /// \brief The argument this argument was derived from (during tool chain
/// argument translation), if any.
const Arg *BaseArg;
- /// The index at which this argument appears in the containing
+ /// \brief The index at which this argument appears in the containing
/// ArgList.
unsigned Index;
- /// Was this argument used to effect compilation; used for generating
- /// "argument unused" diagnostics.
+ /// \brief Was this argument used to effect compilation?
+ ///
+ /// This is used for generating "argument unused" diagnostics.
mutable unsigned Claimed : 1;
- /// Does this argument own its values.
+ /// \brief Does this argument own its values?
mutable unsigned OwnsValues : 1;
- /// The argument values, as C strings.
+ /// \brief The argument values, as C strings.
SmallVector<const char *, 2> Values;
public:
@@ -64,8 +70,9 @@ namespace driver {
const Option &getOption() const { return *Opt; }
unsigned getIndex() const { return Index; }
- /// getBaseArg - Return the base argument which generated this
- /// arg; this is either the argument itself or the argument it was
+ /// \brief Return the base argument which generated this arg.
+ ///
+ /// This is either the argument itself or the argument it was
/// derived from during tool chain specific argument translation.
const Arg &getBaseArg() const {
return BaseArg ? *BaseArg : *this;
@@ -79,7 +86,7 @@ namespace driver {
bool isClaimed() const { return getBaseArg().Claimed; }
- /// claim - Set the Arg claimed bit.
+ /// \brief Set the Arg claimed bit.
void claim() const { getBaseArg().Claimed = true; }
unsigned getNumValues() const { return Values.size(); }
@@ -98,20 +105,21 @@ namespace driver {
return false;
}
- /// render - Append the argument onto the given array as strings.
+ /// \brief Append the argument onto the given array as strings.
void render(const ArgList &Args, ArgStringList &Output) const;
- /// renderAsInput - Append the argument, render as an input, onto
- /// the given array as strings. The distinction is that some
- /// options only render their values when rendered as a input
- /// (e.g., Xlinker).
+ /// \brief Append the argument, render as an input, onto the given
+ /// array as strings.
+ ///
+ /// The distinction is that some options only render their values
+ /// when rendered as a input (e.g., Xlinker).
void renderAsInput(const ArgList &Args, ArgStringList &Output) const;
static bool classof(const Arg *) { return true; }
void dump() const;
- /// getAsString - Return a formatted version of the argument and
+ /// \brief Return a formatted version of the argument and
/// its values, for debugging and diagnostics.
std::string getAsString(const ArgList &Args) const;
};
diff --git a/include/clang/Driver/ArgList.h b/include/clang/Driver/ArgList.h
index 3affb00..b7e490c 100644
--- a/include/clang/Driver/ArgList.h
+++ b/include/clang/Driver/ArgList.h
@@ -187,6 +187,14 @@ namespace driver {
OptSpecifier Id3) const;
Arg *getLastArg(OptSpecifier Id0, OptSpecifier Id1, OptSpecifier Id2,
OptSpecifier Id3, OptSpecifier Id4) const;
+ Arg *getLastArg(OptSpecifier Id0, OptSpecifier Id1, OptSpecifier Id2,
+ OptSpecifier Id3, OptSpecifier Id4, OptSpecifier Id5) const;
+ Arg *getLastArg(OptSpecifier Id0, OptSpecifier Id1, OptSpecifier Id2,
+ OptSpecifier Id3, OptSpecifier Id4, OptSpecifier Id5,
+ OptSpecifier Id6) const;
+ Arg *getLastArg(OptSpecifier Id0, OptSpecifier Id1, OptSpecifier Id2,
+ OptSpecifier Id3, OptSpecifier Id4, OptSpecifier Id5,
+ OptSpecifier Id6, OptSpecifier Id7) const;
/// getArgString - Return the input argument string at \arg Index.
virtual const char *getArgString(unsigned Index) const = 0;
diff --git a/include/clang/Driver/CC1Options.h b/include/clang/Driver/CC1Options.h
index 4a8bbe5..e69de29 100644
--- a/include/clang/Driver/CC1Options.h
+++ b/include/clang/Driver/CC1Options.h
@@ -1,32 +0,0 @@
-//===--- CC1Options.h - Clang CC1 Options Table -----------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_DRIVER_CC1OPTIONS_H
-#define CLANG_DRIVER_CC1OPTIONS_H
-
-namespace clang {
-namespace driver {
- class OptTable;
-
-namespace cc1options {
- enum ID {
- OPT_INVALID = 0, // This is not an option ID.
-#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
- HELPTEXT, METAVAR) OPT_##ID,
-#include "clang/Driver/CC1Options.inc"
- LastOption
-#undef OPTION
- };
-}
-
- OptTable *createCC1OptTable();
-}
-}
-
-#endif
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index 83c988a..6e4d7f2 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -11,8 +11,7 @@
//
//===----------------------------------------------------------------------===//
-// Include the common option parsing interfaces.
-include "OptParser.td"
+let Flags = [CC1Option] in {
//===----------------------------------------------------------------------===//
// Target Options
@@ -40,8 +39,6 @@ def analysis_UnoptimizedCFG : Flag<"-unoptimized-cfg">,
HelpText<"Generate unoptimized CFGs for all analyses">;
def analysis_CFGAddImplicitDtors : Flag<"-cfg-add-implicit-dtors">,
HelpText<"Add C++ implicit destructors to CFGs for all analyses">;
-def analysis_CFGAddInitializers : Flag<"-cfg-add-initializers">,
- HelpText<"Add C++ initializers to CFGs for all analyses">;
def analyzer_store : Separate<"-analyzer-store">,
HelpText<"Source Code Analysis - Abstract Memory Store Models">;
@@ -145,46 +142,12 @@ def fdebug_compilation_dir : Separate<"-fdebug-compilation-dir">,
HelpText<"The compilation directory to embed in the debug info.">;
def dwarf_debug_flags : Separate<"-dwarf-debug-flags">,
HelpText<"The string to embed in the Dwarf debug flags record.">;
-def faddress_sanitizer: Flag<"-faddress-sanitizer">,
- HelpText<"Enable AddressSanitizer instrumentation (memory error detection)">;
-def fthread_sanitizer: Flag<"-fthread-sanitizer">,
- HelpText<"Enable ThreadSanitizer instrumentation (race detection)">;
def fforbid_guard_variables : Flag<"-fforbid-guard-variables">,
HelpText<"Emit an error if a C++ static local initializer would need a guard variable">;
-def g : Flag<"-g">, HelpText<"Generate source level debug information">;
-def fno_dwarf2_cfi_asm : Flag<"-fno-dwarf2-cfi-asm">,
- HelpText<"Don't use the cfi directives">;
-def fno_dwarf_directory_asm : Flag<"-fno-dwarf-directory-asm">,
- HelpText<"Don't separate directory and filename in .file directives">;
-def fcatch_undefined_behavior : Flag<"-fcatch-undefined-behavior">,
- HelpText<"Generate runtime checks for undefined behavior.">;
-def flimit_debug_info : Flag<"-flimit-debug-info">,
- HelpText<"Limit debug information produced to reduce size of debug binary">;
-def fno_common : Flag<"-fno-common">,
- HelpText<"Compile common globals like normal definitions">;
def no_implicit_float : Flag<"-no-implicit-float">,
- HelpText<"Don't generate implicit floating point instructions (x86-only)">;
-def finstrument_functions : Flag<"-finstrument-functions">,
- HelpText<"Generate calls to instrument function entry and exit">;
-def fno_limit_debug_info : Flag<"-fno-limit-debug-info">,
- HelpText<"Do not limit debug information produced to reduce size of debug binary">;
-def fno_merge_all_constants : Flag<"-fno-merge-all-constants">,
- HelpText<"Disallow merging of constants.">;
-def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">,
- HelpText<"Do not emit code to make initialization of local statics thread safe">;
+ HelpText<"Don't generate implicit floating point instructions">;
def fdump_vtable_layouts : Flag<"-fdump-vtable-layouts">,
HelpText<"Dump the layouts of all vtables that will be emitted in a translation unit">;
-def ffunction_sections : Flag<"-ffunction-sections">,
- HelpText<"Place each function in its own section (ELF Only)">;
-def fdata_sections : Flag<"-fdata-sections">,
- HelpText<"Place each data in its own section (ELF Only)">;
-def fstrict_enums : Flag<"-fstrict-enums">,
- HelpText<"Enable optimizations based on the strict definition of an enum's "
- "value range.">;
-def ftrap_function_EQ : Joined<"-ftrap-function=">,
- HelpText<"Issue call to specified function rather than a trap instruction">;
-def funroll_loops : Flag<"-funroll-loops">,
- HelpText<"Turn on loop unroller">;
def femit_coverage_notes : Flag<"-femit-coverage-notes">,
HelpText<"Emit a gcov coverage notes file when compiling.">;
def femit_coverage_data: Flag<"-femit-coverage-data">,
@@ -215,66 +178,37 @@ def menable_unsafe_fp_math : Flag<"-menable-unsafe-fp-math">,
"precision">;
def mfloat_abi : Separate<"-mfloat-abi">,
HelpText<"The float ABI to use">;
-def mno_global_merge : Flag<"-mno-global-merge">,
- HelpText<"Disable merging of globals">;
def mlimit_float_precision : Separate<"-mlimit-float-precision">,
HelpText<"Limit float precision to the given value">;
def mno_exec_stack : Flag<"-mnoexecstack">,
HelpText<"Mark the file as not needing an executable stack">;
def mno_zero_initialized_in_bss : Flag<"-mno-zero-initialized-in-bss">,
HelpText<"Do not put zero initialized data in the BSS">;
-def momit_leaf_frame_pointer : Flag<"-momit-leaf-frame-pointer">,
- HelpText<"Omit frame pointer setup for leaf functions.">;
-def msoft_float : Flag<"-msoft-float">,
- HelpText<"Use software floating point">;
def backend_option : Separate<"-backend-option">,
HelpText<"Additional arguments to forward to LLVM backend (during code gen)">;
def mregparm : Separate<"-mregparm">,
HelpText<"Limit the number of registers available for integer arguments">;
-def mrelax_all : Flag<"-mrelax-all">,
- HelpText<"(integrated-as) Relax all machine instructions">;
def msave_temp_labels : Flag<"-msave-temp-labels">,
HelpText<"(integrated-as) Save temporary labels">;
-def mrtd: Flag<"-mrtd">,
- HelpText<"Make StdCall calling convention the default">;
def mrelocation_model : Separate<"-mrelocation-model">,
HelpText<"The relocation model to use">;
def munwind_tables : Flag<"-munwind-tables">,
HelpText<"Generate unwinding tables for all functions">;
+def fuse_init_array : Flag<"-fuse-init-array">,
+ HelpText<"Use .init_array instead of .ctors">;
def mconstructor_aliases : Flag<"-mconstructor-aliases">,
HelpText<"Emit complete constructors and destructors as aliases when possible">;
-def mms_bitfields : Flag<"-mms-bitfields">,
- HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard.">;
-def mstackrealign : Flag<"-mstackrealign">,
- HelpText<"Force realign the stack at entry to every function.">;
-def mstack_alignment : Joined<"-mstack-alignment=">,
- HelpText<"Set the stack alignment">;
def mlink_bitcode_file : Separate<"-mlink-bitcode-file">,
HelpText<"Link the given bitcode file before performing optimizations.">;
-def O : Joined<"-O">, HelpText<"Optimization level">;
-def Os : Flag<"-Os">, HelpText<"Optimize for size">;
-def Oz : Flag<"-Oz">, HelpText<"Optimize for size, regardless of performance">;
-def pg : Flag<"-pg">, HelpText<"Enable mcount instrumentation">;
//===----------------------------------------------------------------------===//
// Dependency Output Options
//===----------------------------------------------------------------------===//
-def dependency_file : Separate<"-dependency-file">,
- HelpText<"Filename (or -) to write dependency output to">;
-def dependency_dot : Separate<"-dependency-dot">,
- HelpText<"Filename to write DOT-formatted header dependencies to">;
def sys_header_deps : Flag<"-sys-header-deps">,
HelpText<"Include system headers in dependency output">;
def header_include_file : Separate<"-header-include-file">,
HelpText<"Filename (or -) to write header include output to">;
-def H : Flag<"-H">,
- HelpText<"Show header includes and nesting depth">;
-def MQ : Separate<"-MQ">, HelpText<"Specify target to quote for dependency">;
-def MT : Separate<"-MT">, HelpText<"Specify target for dependency">;
-def MP : Flag<"-MP">,
- HelpText<"Create phony target for each dependency (other than main file)">;
-def MG : Flag<"-MG">, HelpText<"Add missing headers to dependency list">;
//===----------------------------------------------------------------------===//
// Diagnostic Options
@@ -288,44 +222,11 @@ def diagnostic_log_file : Separate<"-diagnostic-log-file">,
def diagnostic_serialized_file : Separate<"-serialize-diagnostic-file">,
MetaVarName<"<filename>">,
HelpText<"File for serializing diagnostics in a binary format">;
-def fno_show_column : Flag<"-fno-show-column">,
- HelpText<"Do not include column number on diagnostics">;
-def fshow_column : Flag<"-fshow-column">,
- HelpText<"Include column number on diagnostics">;
-def fno_show_source_location : Flag<"-fno-show-source-location">,
- HelpText<"Do not include source location information with diagnostics">;
-def fshow_overloads_EQ : Joined<"-fshow-overloads=">,
- HelpText<"Which overload candidates to show when overload resolution fails: "
- "best|all; defaults to all">;
-def fno_caret_diagnostics : Flag<"-fno-caret-diagnostics">,
- HelpText<"Do not include source line and caret with diagnostics">;
-def fno_diagnostics_fixit_info : Flag<"-fno-diagnostics-fixit-info">,
- HelpText<"Do not include fixit information in diagnostics">;
-def fno_diagnostics_show_note_include_stack :
- Flag<"-fno-diagnostics-show-note-include-stack">,
- HelpText<"Display include stacks for diagnostic notes">;
-def w : Flag<"-w">, HelpText<"Suppress all warnings">;
-def pedantic : Flag<"-pedantic">;
-def pedantic_errors : Flag<"-pedantic-errors">;
-
-// This gets all -W options, including -Werror, -W[no-]system-headers, etc. The
-// driver has stripped off -Wa,foo etc. The driver has also translated -W to
-// -Wextra, so we don't need to worry about it.
-def W : Joined<"-W">;
-
-def fdiagnostics_print_source_range_info : Flag<"-fdiagnostics-print-source-range-info">,
- HelpText<"Print source range spans in numeric form">;
-def fdiagnostics_parseable_fixits : Flag<"-fdiagnostics-parseable-fixits">,
- HelpText<"Print fix-its in machine parseable form">;
-def fdiagnostics_show_option : Flag<"-fdiagnostics-show-option">,
- HelpText<"Print option name with mappable diagnostics">;
+
def fdiagnostics_format : Separate<"-fdiagnostics-format">,
HelpText<"Change diagnostic formatting to match IDE and command line tools">;
def fdiagnostics_show_category : Separate<"-fdiagnostics-show-category">,
HelpText<"Print diagnostic category">;
-def fdiagnostics_show_note_include_stack :
- Flag<"-fdiagnostics-show-note-include-stack">,
- HelpText<"Display include stacks for diagnostic notes">;
def ftabstop : Separate<"-ftabstop">, MetaVarName<"<N>">,
HelpText<"Set the tab stop distance.">;
def ferror_limit : Separate<"-ferror-limit">, MetaVarName<"<N>">,
@@ -338,12 +239,8 @@ def fconstexpr_backtrace_limit : Separate<"-fconstexpr-backtrace-limit">, MetaVa
HelpText<"Set the maximum number of entries to print in a constexpr evaluation backtrace (0 = no limit).">;
def fmessage_length : Separate<"-fmessage-length">, MetaVarName<"<N>">,
HelpText<"Format message diagnostics so that they fit within N columns or fewer, when possible.">;
-def fcolor_diagnostics : Flag<"-fcolor-diagnostics">,
- HelpText<"Use colors in diagnostics">;
def Wno_rewrite_macros : Flag<"-Wno-rewrite-macros">,
HelpText<"Silence ObjC rewriting warnings">;
-def verify : Flag<"-verify">,
- HelpText<"Verify emitted diagnostics and warnings">;
//===----------------------------------------------------------------------===//
// Frontend Options
@@ -370,13 +267,10 @@ def code_completion_patterns : Flag<"-code-completion-patterns">,
HelpText<"Include code patterns in code-completion results">;
def no_code_completion_globals : Flag<"-no-code-completion-globals">,
HelpText<"Do not include global declarations in code-completion results.">;
+def code_completion_brief_comments : Flag<"-code-completion-brief-comments">,
+ HelpText<"Include brief documentation comments in code-completion results.">;
def disable_free : Flag<"-disable-free">,
HelpText<"Disable freeing of memory on exit">;
-def help : Flag<"-help">,
- HelpText<"Print this help text">;
-def _help : Flag<"--help">, Alias<help>;
-def x : Separate<"-x">, HelpText<"Input language type">;
-def o : Separate<"-o">, MetaVarName<"<path>">, HelpText<"Specify output file">;
def load : Separate<"-load">, MetaVarName<"<dsopath>">,
HelpText<"Load the named plugin (dynamic shared object)">;
def plugin : Separate<"-plugin">, MetaVarName<"<name>">,
@@ -390,15 +284,16 @@ def resource_dir : Separate<"-resource-dir">,
HelpText<"The directory which holds the compiler resource files">;
def version : Flag<"-version">,
HelpText<"Print the compiler version">;
-def _version : Flag<"--version">, Alias<version>;
+def ast_dump_filter : Separate<"-ast-dump-filter">,
+ MetaVarName<"<dump_filter>">,
+ HelpText<"Use with -ast-dump or -ast-print to dump/print only AST declaration"
+ " nodes having a certain substring in a qualified name. Use"
+ " -ast-list to list all filterable declaration node names.">;
-def Action_Group : OptionGroup<"<action group>">;
let Group = Action_Group in {
def Eonly : Flag<"-Eonly">,
HelpText<"Just run preprocessor, no output (for timings)">;
-def E : Flag<"-E">,
- HelpText<"Run preprocessor, emit preprocessed file">;
def dump_raw_tokens : Flag<"-dump-raw-tokens">,
HelpText<"Lex file in raw mode and dump raw tokens">;
def analyze : Flag<"-analyze">,
@@ -407,8 +302,6 @@ def dump_tokens : Flag<"-dump-tokens">,
HelpText<"Run preprocessor, dump internal rep of tokens">;
def init_only : Flag<"-init-only">,
HelpText<"Only execute frontend initialization">;
-def fsyntax_only : Flag<"-fsyntax-only">,
- HelpText<"Run parser and perform semantic analysis">;
def fixit : Flag<"-fixit">,
HelpText<"Apply fix-it advice to the input source">;
def fixit_EQ : Joined<"-fixit=">,
@@ -420,6 +313,8 @@ def emit_html : Flag<"-emit-html">,
HelpText<"Output input source as HTML">;
def ast_print : Flag<"-ast-print">,
HelpText<"Build ASTs and then pretty-print them">;
+def ast_list : Flag<"-ast-list">,
+ HelpText<"Build ASTs and print the list of declaration node qualified names">;
def ast_dump : Flag<"-ast-dump">,
HelpText<"Build ASTs and then debug dump them">;
def ast_dump_xml : Flag<"-ast-dump-xml">,
@@ -434,10 +329,6 @@ def emit_pth : Flag<"-emit-pth">,
HelpText<"Generate pre-tokenized header file">;
def emit_pch : Flag<"-emit-pch">,
HelpText<"Generate pre-compiled header file">;
-def S : Flag<"-S">,
- HelpText<"Emit native assembly code">;
-def emit_llvm : Flag<"-emit-llvm">,
- HelpText<"Build ASTs then convert to LLVM, emit .ll file">;
def emit_llvm_bc : Flag<"-emit-llvm-bc">,
HelpText<"Build ASTs then convert to LLVM, emit .bc file">;
def emit_llvm_only : Flag<"-emit-llvm-only">,
@@ -448,8 +339,6 @@ def emit_obj : Flag<"-emit-obj">,
HelpText<"Emit native object files">;
def rewrite_test : Flag<"-rewrite-test">,
HelpText<"Rewriter playground">;
-def rewrite_objc : Flag<"-rewrite-objc">,
- HelpText<"Rewrite ObjC into C (code rewriter example)">;
def rewrite_macros : Flag<"-rewrite-macros">,
HelpText<"Expand macros without full preprocessing">;
def migrate : Flag<"-migrate">,
@@ -464,27 +353,11 @@ def arcmt_modify : Flag<"-arcmt-modify">,
HelpText<"Apply modifications to files to conform to ARC">;
def arcmt_migrate : Flag<"-arcmt-migrate">,
HelpText<"Apply modifications and produces temporary files that conform to ARC">;
-def arcmt_migrate_report_output : Separate<"-arcmt-migrate-report-output">,
- HelpText<"Output path for the plist report">;
-def arcmt_migrate_emit_arc_errors : Flag<"-arcmt-migrate-emit-errors">,
- HelpText<"Emit ARC errors even if the migrator can fix them">;
-
-def objcmt_migrate_literals : Flag<"-objcmt-migrate-literals">,
- HelpText<"Enable migration to modern ObjC literals">;
-def objcmt_migrate_subscripting : Flag<"-objcmt-migrate-subscripting">,
- HelpText<"Enable migration to modern ObjC subscripting">;
-
-def working_directory : JoinedOrSeparate<"-working-directory">,
- HelpText<"Resolve file paths relative to the specified directory">;
-def working_directory_EQ : Joined<"-working-directory=">,
- Alias<working_directory>;
def relocatable_pch : Flag<"-relocatable-pch">,
HelpText<"Whether to build a relocatable precompiled header">;
def print_stats : Flag<"-print-stats">,
HelpText<"Print performance metrics and statistics">;
-def ftime_report : Flag<"-ftime-report">,
- HelpText<"Print the amount of time each phase of compilation takes">;
def fdump_record_layouts : Flag<"-fdump-record-layouts">,
HelpText<"Dump record layout information">;
def fdump_record_layouts_simple : Flag<"-fdump-record-layouts-simple">,
@@ -498,11 +371,6 @@ def fixit_recompile : Flag<"-fixit-recompile">,
def fixit_to_temp : Flag<"-fixit-to-temporary">,
HelpText<"Apply fix-it changes to temporary files">;
-// Generic forwarding to LLVM options. This should only be used for debugging
-// and experimental features.
-def mllvm : Separate<"-mllvm">,
- HelpText<"Additional arguments to forward to LLVM's option processing">;
-
def foverride_record_layout_EQ : Joined<"-foverride-record-layout=">,
HelpText<"Override record layouts with those in the given file">;
@@ -510,135 +378,31 @@ def foverride_record_layout_EQ : Joined<"-foverride-record-layout=">,
// Language Options
//===----------------------------------------------------------------------===//
-def fno_builtin : Flag<"-fno-builtin">,
- HelpText<"Disable implicit builtin knowledge of functions">;
-def faltivec : Flag<"-faltivec">,
- HelpText<"Enable AltiVec vector initializer syntax">;
-def fno_access_control : Flag<"-fno-access-control">,
- HelpText<"Disable C++ access control">;
-def fno_assume_sane_operator_new : Flag<"-fno-assume-sane-operator-new">,
- HelpText<"Don't assume that C++'s global operator new can't alias any pointer">;
-def fgnu_keywords : Flag<"-fgnu-keywords">,
- HelpText<"Allow GNU-extension keywords regardless of language standard">;
-def fgnu89_inline : Flag<"-fgnu89-inline">,
- HelpText<"Use the gnu89 inline semantics">;
-def fno_inline : Flag<"-fno-inline">,
- HelpText<"Disable use of the inline keyword">;
-def fno_inline_functions : Flag<"-fno-inline-functions">,
- HelpText<"Disable automatic function inlining">;
-def fno_gnu_keywords : Flag<"-fno-gnu-keywords">,
- HelpText<"Disallow GNU-extension keywords regardless of language standard">;
-def fdollars_in_identifiers : Flag<"-fdollars-in-identifiers">,
- HelpText<"Allow '$' in identifiers">;
-def fno_dollars_in_identifiers : Flag<"-fno-dollars-in-identifiers">,
- HelpText<"Disallow '$' in identifiers">;
-def femit_all_decls : Flag<"-femit-all-decls">,
- HelpText<"Emit all declarations, even if unused">;
-def fblocks : Flag<"-fblocks">,
- HelpText<"Enable the 'blocks' language feature">;
def fblocks_runtime_optional : Flag<"-fblocks-runtime-optional">,
HelpText<"Weakly link in the blocks runtime">;
-def fheinous_gnu_extensions : Flag<"-fheinous-gnu-extensions">;
-def fexceptions : Flag<"-fexceptions">,
- HelpText<"Enable support for exception handling">;
-def fobjc_exceptions : Flag<"-fobjc-exceptions">,
- HelpText<"Enable Objective-C exceptions">;
-def fcxx_exceptions : Flag<"-fcxx-exceptions">,
- HelpText<"Enable C++ exceptions">;
def fsjlj_exceptions : Flag<"-fsjlj-exceptions">,
HelpText<"Use SjLj style exceptions">;
-def ffast_math : Flag<"-ffast-math">,
- HelpText<"Enable the *frontend*'s 'fast-math' mode. This has no effect on "
- "optimizations, but provides a preprocessor macro __FAST_MATH__ the "
- "same as GCC's -ffast-math flag.">;
-def ffreestanding : Flag<"-ffreestanding">,
- HelpText<"Assert that the compilation takes place in a freestanding environment">;
-def fgnu_runtime : Flag<"-fgnu-runtime">,
- HelpText<"Generate output compatible with the standard GNU Objective-C runtime">;
def fhidden_weak_vtables : Flag<"-fhidden-weak-vtables">,
HelpText<"Generate weak vtables and RTTI with hidden visibility">;
-def std_EQ : Joined<"-std=">,
- HelpText<"Language standard to compile for">;
-def stdlib_EQ : Joined<"-stdlib=">,
- HelpText<"C++ standard library to use">;
-def fmath_errno : Flag<"-fmath-errno">,
- HelpText<"Require math functions to indicate errors by setting errno">;
-def fms_extensions : Flag<"-fms-extensions">,
- HelpText<"Accept some non-standard constructs supported by the Microsoft compiler">;
-def fms_compatibility : Flag<"-fms-compatibility">,
- HelpText<"Enable Microsoft compatibility mode">;
-def fmsc_version : Joined<"-fmsc-version=">,
- HelpText<"Version of the Microsoft C/C++ compiler to report in _MSC_VER (0 = don't define it (default))">;
-def fborland_extensions : Flag<"-fborland-extensions">,
- HelpText<"Accept non-standard constructs supported by the Borland compiler">;
def main_file_name : Separate<"-main-file-name">,
HelpText<"Main file name to use for debug info">;
-def fno_elide_constructors : Flag<"-fno-elide-constructors">,
- HelpText<"Disable C++ copy constructor elision">;
-def fno_lax_vector_conversions : Flag<"-fno-lax-vector-conversions">,
- HelpText<"Disallow implicit conversions between vectors with a different number of elements or different element types">;
-def fno_operator_names : Flag<"-fno-operator-names">,
- HelpText<"Do not treat C++ operator name keywords as synonyms for operators">;
def fno_signed_char : Flag<"-fno-signed-char">,
HelpText<"Char is unsigned">;
-def fno_spell_checking : Flag<"-fno-spell-checking">,
- HelpText<"Disable spell-checking">;
-def fno_use_cxa_atexit : Flag<"-fno-use-cxa-atexit">,
- HelpText<"Don't use __cxa_atexit for calling destructors">;
def fconstant_string_class : Separate<"-fconstant-string-class">,
MetaVarName<"<class name>">,
HelpText<"Specify the class to use for constant Objective-C string objects.">;
-def fno_constant_cfstrings : Flag<"-fno-constant-cfstrings">,
- HelpText<"Enable creation of CodeFoundation-type constant strings">;
-def fobjc_arc : Flag<"-fobjc-arc">,
- HelpText<"Synthesize retain and release calls for Objective-C pointers">;
def fobjc_arc_cxxlib_EQ : Joined<"-fobjc-arc-cxxlib=">,
HelpText<"Objective-C++ Automatic Reference Counting standard library kind">;
-def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">,
- HelpText<"Use EH-safe code when synthesizing retains and releases in -fobjc-arc">;
-def fobjc_runtime_has_arc : Flag<"-fobjc-runtime-has-arc">,
- HelpText<"The target Objective-C runtime provides ARC entrypoints">;
def fobjc_runtime_has_weak : Flag<"-fobjc-runtime-has-weak">,
HelpText<"The target Objective-C runtime supports ARC weak operations">;
-def fobjc_runtime_has_terminate : Flag<"-fobjc-runtime-has-terminate">,
- HelpText<"The target Objective-C runtime provides an objc_terminate entrypoint">;
-def fobjc_gc : Flag<"-fobjc-gc">,
- HelpText<"Enable Objective-C garbage collection">;
-def fobjc_gc_only : Flag<"-fobjc-gc-only">,
- HelpText<"Use GC exclusively for Objective-C related memory management">;
-def fapple_kext : Flag<"-fapple-kext">,
- HelpText<"Use Apple's kernel extensions ABI">;
def fobjc_dispatch_method_EQ : Joined<"-fobjc-dispatch-method=">,
HelpText<"Objective-C dispatch method to use">;
def fobjc_default_synthesize_properties : Flag<"-fobjc-default-synthesize-properties">,
HelpText<"enable the default synthesis of Objective-C properties">;
-def print_ivar_layout : Flag<"-print-ivar-layout">,
- HelpText<"Enable Objective-C Ivar layout bitmap print trace">;
-def fobjc_fragile_abi : Flag<"-fobjc-fragile-abi">,
- HelpText<"Use Objective-C's fragile ABI">;
-def fno_objc_infer_related_result_type : Flag<
- "-fno-objc-infer-related-result-type">,
- HelpText<
- "do not infer Objective-C related result type based on method family">;
-def ftrapv : Flag<"-ftrapv">,
- HelpText<"Trap on integer overflow">;
-def ftrapv_handler : Separate<"-ftrapv-handler">,
- MetaVarName<"<function name>">,
- HelpText<"Specify the function to be called on overflow.">;
-def fwrapv : Flag<"-fwrapv">,
- HelpText<"Treat signed integer overflow as two's complement">;
def pic_level : Separate<"-pic-level">,
HelpText<"Value for __PIC__">;
def pie_level : Separate<"-pie-level">,
HelpText<"Value for __PIE__">;
-def pthread : Flag<"-pthread">,
- HelpText<"Support POSIX threads in generated code">;
-def fpack_struct : Separate<"-fpack-struct">,
- HelpText<"Specify the default maximum struct packing alignment">;
-def fpascal_strings : Flag<"-fpascal-strings">,
- HelpText<"Recognize and construct Pascal-style string literals">;
-def fno_rtti : Flag<"-fno-rtti">,
- HelpText<"Disable generation of rtti information">;
def fno_validate_pch : Flag<"-fno-validate-pch">,
HelpText<"Disable validation of precompiled headers">;
def dump_deserialized_pch_decls : Flag<"-dump-deserialized-decls">,
@@ -647,44 +411,24 @@ def error_on_deserialized_pch_decl : Separate<"-error-on-deserialized-decl">,
HelpText<"Emit error if a specific declaration is deserialized from PCH, for testing">;
def error_on_deserialized_pch_decl_EQ : Joined<"-error-on-deserialized-decl=">,
Alias<error_on_deserialized_pch_decl>;
-def fshort_wchar : Flag<"-fshort-wchar">,
- HelpText<"Force wchar_t to be a short unsigned int">;
-def fshort_enums : Flag<"-fshort-enums">,
- HelpText<"Allocate to an enum type only as many bytes as it needs for the declared range of possible values">;
def static_define : Flag<"-static-define">,
HelpText<"Should __STATIC__ be defined">;
def stack_protector : Separate<"-stack-protector">,
HelpText<"Enable stack protectors">;
def fvisibility : Separate<"-fvisibility">,
HelpText<"Default symbol visibility">;
-def fvisibility_inlines_hidden : Flag<"-fvisibility-inlines-hidden">,
- HelpText<"Give inline C++ member functions default visibility by default">;
def ftemplate_depth : Separate<"-ftemplate-depth">,
HelpText<"Maximum depth of recursive template instantiation">;
def fconstexpr_depth : Separate<"-fconstexpr-depth">,
HelpText<"Maximum depth of recursive constexpr function calls">;
-def Wlarge_by_value_copy : Separate<"-Wlarge-by-value-copy">,
- HelpText<"Warn if a function definition returns or accepts an object larger "
- "in bytes that a given value">;
-def Wlarge_by_value_copy_EQ : Joined<"-Wlarge-by-value-copy=">,
- Alias<Wlarge_by_value_copy>;
-def trigraphs : Flag<"-trigraphs">,
- HelpText<"Process trigraph sequences">;
-def fwritable_strings : Flag<"-fwritable-strings">,
- HelpText<"Store string literals as writable data">;
def fconst_strings : Flag<"-fconst-strings">,
HelpText<"Use a const qualified type for string literals in C and ObjC">;
def fno_const_strings : Flag<"-fno-const-strings">,
HelpText<"Don't use a const qualified type for string literals in C and ObjC">;
def fno_bitfield_type_align : Flag<"-fno-bitfield-type-align">,
HelpText<"Ignore bit-field types when aligning structures">;
-def traditional_cpp : Flag<"-traditional-cpp">,
- HelpText<"Enable some traditional CPP emulation">;
def ffake_address_space_map : Flag<"-ffake-address-space-map">,
HelpText<"Use a fake address space map; OpenCL testing purposes only">;
-def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">,
- HelpText<"Parse templated function definitions at the end of the "
- "translation unit ">;
def funknown_anytype : Flag<"-funknown-anytype">,
HelpText<"Enable parser support for the __unknown_anytype type; for testing purposes only">;
def fdebugger_support : Flag<"-fdebugger-support">,
@@ -692,13 +436,11 @@ def fdebugger_support : Flag<"-fdebugger-support">,
def fdebugger_cast_result_to_id : Flag<"-fdebugger-cast-result-to-id">,
HelpText<"Enable casting unknown expression results to id">;
def fdebugger_objc_literal : Flag<"-fdebugger-objc-literal">,
- HelpText<"Enable special debugger support for objective-C subscripting and literals">;
+ HelpText<"Enable special debugger support for Objective-C subscripting and literals">;
def fdeprecated_macro : Flag<"-fdeprecated-macro">,
HelpText<"Defines the __DEPRECATED macro">;
def fno_deprecated_macro : Flag<"-fno-deprecated-macro">,
HelpText<"Undefines the __DEPRECATED macro">;
-def fapple_pragma_pack : Flag<"-fapple-pragma-pack">,
- HelpText<"Enable Apple gcc-compatible #pragma pack handling">;
//===----------------------------------------------------------------------===//
// Header Search Options
@@ -706,48 +448,19 @@ def fapple_pragma_pack : Flag<"-fapple-pragma-pack">,
def nostdsysteminc : Flag<"-nostdsysteminc">,
HelpText<"Disable standard system #include directories">;
-def nostdincxx : Flag<"-nostdinc++">,
- HelpText<"Disable standard #include directories for the C++ standard library">;
-def nobuiltininc : Flag<"-nobuiltininc">,
- HelpText<"Disable builtin #include directories">;
-def fmodule_cache_path : Separate<"-fmodule-cache-path">,
- MetaVarName<"<directory>">,
- HelpText<"Specify the module cache path">;
def fmodule_name : Joined<"-fmodule-name=">,
MetaVarName<"<name>">,
HelpText<"Specify the name of the module to build">;
def fdisable_module_hash : Flag<"-fdisable-module-hash">,
HelpText<"Disable the module hash">;
-def fmodules : Flag<"-fmodules">,
- HelpText<"Enable the 'modules' language feature">;
-
-def F : JoinedOrSeparate<"-F">, MetaVarName<"<directory>">,
- HelpText<"Add directory to framework include search path">;
-def I : JoinedOrSeparate<"-I">, MetaVarName<"<directory>">,
- HelpText<"Add directory to include search path">;
-def idirafter : JoinedOrSeparate<"-idirafter">, MetaVarName<"<directory>">,
- HelpText<"Add directory to AFTER include search path">;
-def index_header_map : Flag<"-index-header-map">,
- HelpText<"Make the next included directory (-I or -F) an indexer header map">;
-def iquote : JoinedOrSeparate<"-iquote">, MetaVarName<"<directory>">,
- HelpText<"Add directory to QUOTE include search path">;
def c_isystem : JoinedOrSeparate<"-c-isystem">, MetaVarName<"<directory>">,
HelpText<"Add directory to the C SYSTEM include search path">;
-def cxx_isystem : JoinedOrSeparate<"-cxx-isystem">, MetaVarName<"<directory>">,
- HelpText<"Add directory to the C++ SYSTEM include search path">;
def objc_isystem : JoinedOrSeparate<"-objc-isystem">,
MetaVarName<"<directory>">,
HelpText<"Add directory to the ObjC SYSTEM include search path">;
def objcxx_isystem : JoinedOrSeparate<"-objcxx-isystem">,
MetaVarName<"<directory>">,
HelpText<"Add directory to the ObjC++ SYSTEM include search path">;
-def iframework : JoinedOrSeparate<"-iframework">, MetaVarName<"<directory>">,
- HelpText<"Add directory to SYSTEM framework search path">;
-def isystem : JoinedOrSeparate<"-isystem">, MetaVarName<"<directory>">,
- HelpText<"Add directory to SYSTEM include search path">;
-def iwithsysroot : JoinedOrSeparate<"-iwithsysroot">,MetaVarName<"<directory>">,
- HelpText<"Add directory to SYSTEM include search path, "
- "absolute paths are relative to -isysroot">;
def internal_isystem : JoinedOrSeparate<"-internal-isystem">,
MetaVarName<"<directory>">,
HelpText<"Add directory to the internal system include search path; these "
@@ -759,29 +472,19 @@ def internal_externc_isystem : JoinedOrSeparate<"-internal-externc-isystem">,
"implicit extern \"C\" semantics; these are assumed to not be "
"user-provided and are used to model system and standard headers' "
"paths.">;
-def iprefix : JoinedOrSeparate<"-iprefix">, MetaVarName<"<prefix>">,
- HelpText<"Set the -iwithprefix/-iwithprefixbefore prefix">;
-def iwithprefix : JoinedOrSeparate<"-iwithprefix">, MetaVarName<"<dir>">,
- HelpText<"Set directory to SYSTEM include search path with prefix">;
-def iwithprefixbefore : JoinedOrSeparate<"-iwithprefixbefore">,
- MetaVarName<"<dir>">,
- HelpText<"Set directory to include search path with prefix">;
-def isysroot : JoinedOrSeparate<"-isysroot">, MetaVarName<"<dir>">,
- HelpText<"Set the system root directory (usually /)">;
-def v : Flag<"-v">, HelpText<"Enable verbose output">;
+def isystem_prefix : JoinedOrSeparate<"-isystem-prefix">,
+ MetaVarName<"<prefix>">,
+ HelpText<"Treat all #include paths starting with <prefix> as including a "
+ "system header.">;
+def ino_system_prefix : JoinedOrSeparate<"-ino-system-prefix">,
+ MetaVarName<"<prefix>">,
+ HelpText<"Treat all #include paths starting with <prefix> as not including a "
+ "system header.">;
//===----------------------------------------------------------------------===//
// Preprocessor Options
//===----------------------------------------------------------------------===//
-def D : JoinedOrSeparate<"-D">, MetaVarName<"<macro>">,
- HelpText<"Predefine the specified macro">;
-def include_ : JoinedOrSeparate<"-include">, MetaVarName<"<file>">, EnumName<"include">,
- HelpText<"Include file before parsing">;
-def imacros : JoinedOrSeparate<"-imacros">, MetaVarName<"<file>">,
- HelpText<"Include macros from file before parsing">;
-def include_pch : Separate<"-include-pch">, MetaVarName<"<file>">,
- HelpText<"Include precompiled header file">;
def include_pth : Separate<"-include-pth">, MetaVarName<"<file>">,
HelpText<"Include file before parsing">;
def chain_include : Separate<"-chain-include">, MetaVarName<"<file>">,
@@ -791,29 +494,8 @@ def preamble_bytes_EQ : Joined<"-preamble-bytes=">,
"covering the first N bytes of the main file">;
def token_cache : Separate<"-token-cache">, MetaVarName<"<path>">,
HelpText<"Use specified token cache file">;
-def U : JoinedOrSeparate<"-U">, MetaVarName<"<macro>">,
- HelpText<"Undefine the specified macro">;
-def undef : Flag<"-undef">, MetaVarName<"<macro>">,
- HelpText<"undef all system defines">;
def detailed_preprocessing_record : Flag<"-detailed-preprocessing-record">,
HelpText<"include a detailed record of preprocessing actions">;
-def mqdsp6_compat : Flag<"-mqdsp6-compat">,
- HelpText<"Enable hexagon-qdsp6 backward compatibility">;
-
-//===----------------------------------------------------------------------===//
-// Preprocessed Output Options
-//===----------------------------------------------------------------------===//
-
-def P : Flag<"-P">,
- HelpText<"Disable linemarker output in -E mode">;
-def C : Flag<"-C">,
- HelpText<"Enable comment output in -E mode">;
-def CC : Flag<"-CC">,
- HelpText<"Enable comment output in -E mode, even from macro expansions">;
-def dM : Flag<"-dM">,
- HelpText<"Print macro definitions in -E mode instead of normal output">;
-def dD : Flag<"-dD">,
- HelpText<"Print macro definitions in -E mode in addition to normal output">;
//===----------------------------------------------------------------------===//
// OpenCL Options
@@ -840,3 +522,5 @@ def cl_std_EQ : Joined<"-cl-std=">,
def fcuda_is_device : Flag<"-fcuda-is-device">,
HelpText<"Generate code for CUDA device">;
+
+} // let Flags = [CC1Option]
diff --git a/include/clang/Driver/CMakeLists.txt b/include/clang/Driver/CMakeLists.txt
index abd4cfe..1277d51 100644
--- a/include/clang/Driver/CMakeLists.txt
+++ b/include/clang/Driver/CMakeLists.txt
@@ -2,10 +2,6 @@ clang_tablegen(Options.inc -gen-opt-parser-defs
SOURCE Options.td
TARGET ClangDriverOptions)
-clang_tablegen(CC1Options.inc -gen-opt-parser-defs
- SOURCE CC1Options.td
- TARGET ClangCC1Options)
-
clang_tablegen(CC1AsOptions.inc -gen-opt-parser-defs
SOURCE CC1AsOptions.td
TARGET ClangCC1AsOptions)
diff --git a/include/clang/Driver/Compilation.h b/include/clang/Driver/Compilation.h
index 6f1a221..7a10d56 100644
--- a/include/clang/Driver/Compilation.h
+++ b/include/clang/Driver/Compilation.h
@@ -76,6 +76,8 @@ public:
const DerivedArgList &getArgs() const { return *TranslatedArgs; }
+ DerivedArgList &getArgs() { return *TranslatedArgs; }
+
ActionList &getActions() { return Actions; }
const ActionList &getActions() const { return Actions; }
diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h
index 0538334..6095055 100644
--- a/include/clang/Driver/Driver.h
+++ b/include/clang/Driver/Driver.h
@@ -156,6 +156,9 @@ private:
/// used where an integrated CPP would).
unsigned CCCUseClangCPP : 1;
+ /// \brief Force use of clang frontend.
+ unsigned ForcedClangUse : 1;
+
public:
/// Use lazy precompiled headers for PCH support.
unsigned CCCUsePCH : 1;
@@ -174,7 +177,7 @@ private:
/// \brief Cache of all the ToolChains in use by the driver.
///
/// This maps from the string representation of a triple to a ToolChain
- /// created targetting that triple. The driver owns all the ToolChain objects
+ /// created targeting that triple. The driver owns all the ToolChain objects
/// stored in it, and will clean them up when torn down.
mutable llvm::StringMap<ToolChain *> ToolChains;
@@ -229,6 +232,9 @@ public:
InstalledDir = Value;
}
+ bool shouldForceClangUse() const { return ForcedClangUse; }
+ void setForcedClangUse(bool V = true) { ForcedClangUse = V; }
+
/// @}
/// @name Primary Functionality
/// @{
diff --git a/include/clang/Driver/Makefile b/include/clang/Driver/Makefile
index 45bc40f..7d066c7 100644
--- a/include/clang/Driver/Makefile
+++ b/include/clang/Driver/Makefile
@@ -1,18 +1,14 @@
CLANG_LEVEL := ../../..
-BUILT_SOURCES = Options.inc CC1Options.inc CC1AsOptions.inc
+BUILT_SOURCES = Options.inc CC1AsOptions.inc
TABLEGEN_INC_FILES_COMMON = 1
include $(CLANG_LEVEL)/Makefile
-$(ObjDir)/Options.inc.tmp : Options.td OptParser.td $(CLANG_TBLGEN) $(ObjDir)/.dir
+$(ObjDir)/Options.inc.tmp : Options.td CC1Options.td OptParser.td $(CLANG_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building Clang Driver Option tables with tblgen"
$(Verb) $(ClangTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
-$(ObjDir)/CC1Options.inc.tmp : CC1Options.td OptParser.td $(CLANG_TBLGEN) $(ObjDir)/.dir
- $(Echo) "Building Clang CC1 Option tables with tblgen"
- $(Verb) $(ClangTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
-
$(ObjDir)/CC1AsOptions.inc.tmp : CC1AsOptions.td OptParser.td $(CLANG_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building Clang CC1 Assembler Option tables with tblgen"
$(Verb) $(ClangTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
diff --git a/include/clang/Driver/ObjCRuntime.h b/include/clang/Driver/ObjCRuntime.h
deleted file mode 100644
index 094873a..0000000
--- a/include/clang/Driver/ObjCRuntime.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//===--- ObjCRuntime.h - Objective C runtime features -----------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_DRIVER_OBJCRUNTIME_H_
-#define CLANG_DRIVER_OBJCRUNTIME_H_
-
-namespace clang {
-namespace driver {
-
-class ObjCRuntime {
-public:
- enum Kind { GNU, NeXT };
-private:
- unsigned RuntimeKind : 1;
-public:
- void setKind(Kind k) { RuntimeKind = k; }
- Kind getKind() const { return static_cast<Kind>(RuntimeKind); }
-
- /// True if the runtime provides native ARC entrypoints. ARC may
- /// still be usable without this if the tool-chain provides a
- /// statically-linked runtime support library.
- unsigned HasARC : 1;
-
- /// True if the runtime supports ARC zeroing __weak.
- unsigned HasWeak : 1;
-
- /// \brief True if the runtime supports subscripting methods.
- unsigned HasSubscripting : 1;
-
- /// True if the runtime provides the following entrypoint:
- /// void objc_terminate(void);
- /// If available, this will be called instead of abort() when an
- /// exception is thrown out of an EH cleanup.
- unsigned HasTerminate : 1;
-
- ObjCRuntime() : RuntimeKind(NeXT), HasARC(false), HasWeak(false),
- HasSubscripting(false), HasTerminate(false) {}
-};
-
-}
-}
-
-#endif
diff --git a/include/clang/Driver/OptParser.td b/include/clang/Driver/OptParser.td
index 25ecbc3..9e6d5b9 100644
--- a/include/clang/Driver/OptParser.td
+++ b/include/clang/Driver/OptParser.td
@@ -85,6 +85,9 @@ def HelpHidden : OptionFlag;
// NoForward - The option should not be implicitly forwarded to other tools.
def NoForward : OptionFlag;
+// CC1Option - This option should be accepted by clang -cc1.
+def CC1Option : OptionFlag;
+
// Define the option group class.
class OptionGroup<string name> {
@@ -134,5 +137,5 @@ class MetaVarName<string name> { string MetaVarName = name; }
// FIXME: Have generator validate that these appear in correct position (and
// aren't duplicated).
-def INPUT : Option<"<input>", KIND_INPUT>, Flags<[DriverOption]>;
+def INPUT : Option<"<input>", KIND_INPUT>, Flags<[DriverOption,CC1Option]>;
def UNKNOWN : Option<"<unknown>", KIND_UNKNOWN>;
diff --git a/include/clang/Driver/OptTable.h b/include/clang/Driver/OptTable.h
index 3af6f8f..27bd119 100644
--- a/include/clang/Driver/OptTable.h
+++ b/include/clang/Driver/OptTable.h
@@ -25,7 +25,8 @@ namespace options {
RenderAsInput = (1 << 5),
RenderJoined = (1 << 6),
RenderSeparate = (1 << 7),
- Unsupported = (1 << 8)
+ Unsupported = (1 << 8),
+ CC1Option = (1 << 9)
};
}
@@ -34,7 +35,7 @@ namespace options {
class InputArgList;
class Option;
- /// OptTable - Provide access to the Option info table.
+ /// \brief Provide access to the Option info table.
///
/// The OptTable class provides a layer of indirection which allows Option
/// instance to be created lazily. In the common case, only a few options will
@@ -43,7 +44,7 @@ namespace options {
/// parts of the driver still use Option instances where convenient.
class OptTable {
public:
- /// Info - Entry for a single option instance in the option data table.
+ /// \brief Entry for a single option instance in the option data table.
struct Info {
const char *Name;
const char *HelpText;
@@ -56,17 +57,17 @@ namespace options {
};
private:
- /// The static option information table.
+ /// \brief The static option information table.
const Info *OptionInfos;
unsigned NumOptionInfos;
- /// The lazily constructed options table, indexed by option::ID - 1.
+ /// \brief The lazily constructed options table, indexed by option::ID - 1.
mutable Option **Options;
- /// Prebound input option instance.
+ /// \brief Prebound input option instance.
const Option *TheInputOption;
- /// Prebound unknown option instance.
+ /// \brief Prebound unknown option instance.
const Option *TheUnknownOption;
/// The index of the first option which can be parsed (i.e., is not a
@@ -87,10 +88,10 @@ namespace options {
public:
~OptTable();
- /// getNumOptions - Return the total number of option classes.
+ /// \brief Return the total number of option classes.
unsigned getNumOptions() const { return NumOptionInfos; }
- /// getOption - Get the given \arg id's Option instance, lazily creating it
+ /// \brief Get the given Opt's Option instance, lazily creating it
/// if necessary.
///
/// \return The option, or null for the INVALID option id.
@@ -106,72 +107,71 @@ namespace options {
return Entry;
}
- /// getOptionName - Lookup the name of the given option.
+ /// \brief Lookup the name of the given option.
const char *getOptionName(OptSpecifier id) const {
return getInfo(id).Name;
}
- /// getOptionKind - Get the kind of the given option.
+ /// \brief Get the kind of the given option.
unsigned getOptionKind(OptSpecifier id) const {
return getInfo(id).Kind;
}
- /// getOptionGroupID - Get the group id for the given option.
+ /// \brief Get the group id for the given option.
unsigned getOptionGroupID(OptSpecifier id) const {
return getInfo(id).GroupID;
}
- /// isOptionHelpHidden - Should the help for the given option be hidden by
- /// default.
+ /// \brief Should the help for the given option be hidden by default.
bool isOptionHelpHidden(OptSpecifier id) const {
return getInfo(id).Flags & options::HelpHidden;
}
- /// getOptionHelpText - Get the help text to use to describe this option.
+ /// \brief Get the help text to use to describe this option.
const char *getOptionHelpText(OptSpecifier id) const {
return getInfo(id).HelpText;
}
- /// getOptionMetaVar - Get the meta-variable name to use when describing
+ /// \brief Get the meta-variable name to use when describing
/// this options values in the help text.
const char *getOptionMetaVar(OptSpecifier id) const {
return getInfo(id).MetaVar;
}
- /// ParseOneArg - Parse a single argument; returning the new argument and
+ /// \brief Parse a single argument; returning the new argument and
/// updating Index.
///
- /// \param [in] [out] Index - The current parsing position in the argument
+ /// \param [in,out] Index - The current parsing position in the argument
/// string list; on return this will be the index of the next argument
/// string to parse.
///
- /// \return - The parsed argument, or 0 if the argument is missing values
+ /// \return The parsed argument, or 0 if the argument is missing values
/// (in which case Index still points at the conceptual next argument string
/// to parse).
Arg *ParseOneArg(const ArgList &Args, unsigned &Index) const;
- /// ParseArgs - Parse an list of arguments into an InputArgList.
+ /// \brief Parse an list of arguments into an InputArgList.
///
- /// The resulting InputArgList will reference the strings in [ArgBegin,
- /// ArgEnd), and their lifetime should extend past that of the returned
+ /// The resulting InputArgList will reference the strings in [\p ArgBegin,
+ /// \p ArgEnd), and their lifetime should extend past that of the returned
/// InputArgList.
///
/// The only error that can occur in this routine is if an argument is
- /// missing values; in this case \arg MissingArgCount will be non-zero.
+ /// missing values; in this case \p MissingArgCount will be non-zero.
///
/// \param ArgBegin - The beginning of the argument vector.
/// \param ArgEnd - The end of the argument vector.
/// \param MissingArgIndex - On error, the index of the option which could
/// not be parsed.
/// \param MissingArgCount - On error, the number of missing options.
- /// \return - An InputArgList; on error this will contain all the options
+ /// \return An InputArgList; on error this will contain all the options
/// which could be parsed.
InputArgList *ParseArgs(const char* const *ArgBegin,
const char* const *ArgEnd,
unsigned &MissingArgIndex,
unsigned &MissingArgCount) const;
- /// PrintHelp - Render the help text for an option table.
+ /// \brief Render the help text for an option table.
///
/// \param OS - The stream to write the help text to.
/// \param Name - The name to use in the usage line.
diff --git a/include/clang/Driver/Option.h b/include/clang/Driver/Option.h
index 8243f6d..e6c4e12 100644
--- a/include/clang/Driver/Option.h
+++ b/include/clang/Driver/Option.h
@@ -91,6 +91,9 @@ namespace driver {
/// This option should not be implicitly forwarded.
bool NoForward : 1;
+ /// CC1Option - This option should be accepted by clang -cc1.
+ bool CC1Option : 1;
+
protected:
Option(OptionClass Kind, OptSpecifier ID, const char *Name,
const OptionGroup *Group, const Option *Alias);
@@ -126,6 +129,9 @@ namespace driver {
bool hasNoForward() const { return NoForward; }
void setNoForward(bool Value) { NoForward = Value; }
+ bool isCC1Option() const { return CC1Option; }
+ void setIsCC1Option(bool Value) { CC1Option = Value; }
+
bool hasForwardToGCC() const {
return !NoForward && !DriverOption && !LinkerInput;
}
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index b796771..c7698a2 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -19,6 +19,7 @@ include "OptParser.td"
// Meta-group which defines
def CompileOnly_Group : OptionGroup<"<CompileOnly group>">;
+def Action_Group : OptionGroup<"<action group>">;
def I_Group : OptionGroup<"<I group>">, Group<CompileOnly_Group>;
def L_Group : OptionGroup<"<L group>">, Group<CompileOnly_Group>;
@@ -32,10 +33,13 @@ def d_Group : OptionGroup<"<d group>">;
def f_Group : OptionGroup<"<f group>">, Group<CompileOnly_Group>;
def f_clang_Group : OptionGroup<"<f (clang-only) group>">, Group<CompileOnly_Group>;
def g_Group : OptionGroup<"<g group>">;
+def g_flags_Group : OptionGroup<"<g flags group>">;
def i_Group : OptionGroup<"<i group>">, Group<CompileOnly_Group>;
def clang_i_Group : OptionGroup<"<clang i group>">, Group<i_Group>;
def m_Group : OptionGroup<"<m group>">, Group<CompileOnly_Group>;
def m_x86_Features_Group : OptionGroup<"<m x86 features group>">, Group<m_Group>;
+def m_hexagon_Features_Group : OptionGroup<"<m hexagon features group>">, Group<m_Group>;
+def opencl_Group : OptionGroup<"<opencl group>">;
def u_Group : OptionGroup<"<u group>">;
def pedantic_Group : OptionGroup<"<pedantic group>">,
@@ -120,18 +124,19 @@ def ccc_arrmt_modify : Flag<"-ccc-arrmt-modify">, Alias<ccc_arcmt_modify>;
def ccc_arcmt_migrate : Separate<"-ccc-arcmt-migrate">, CCCDriverOpt,
HelpText<"Apply modifications and produces temporary files that conform to ARC">;
def arcmt_migrate_report_output : Separate<"-arcmt-migrate-report-output">,
- HelpText<"Output path for the plist report">;
+ HelpText<"Output path for the plist report">, Flags<[CC1Option]>;
def arcmt_migrate_emit_arc_errors : Flag<"-arcmt-migrate-emit-errors">,
- HelpText<"Emit ARC errors even if the migrator can fix them">;
+ HelpText<"Emit ARC errors even if the migrator can fix them">,
+ Flags<[CC1Option]>;
def _migrate : Flag<"--migrate">, Flags<[DriverOption]>,
HelpText<"Run the migrator">;
def ccc_objcmt_migrate : Separate<"-ccc-objcmt-migrate">, CCCDriverOpt,
HelpText<"Apply modifications and produces temporary files to migrate to "
"modern ObjC syntax">;
-def objcmt_migrate_literals : Flag<"-objcmt-migrate-literals">,
+def objcmt_migrate_literals : Flag<"-objcmt-migrate-literals">, Flags<[CC1Option]>,
HelpText<"Enable migration to modern ObjC literals">;
-def objcmt_migrate_subscripting : Flag<"-objcmt-migrate-subscripting">,
+def objcmt_migrate_subscripting : Flag<"-objcmt-migrate-subscripting">, Flags<[CC1Option]>,
HelpText<"Enable migration to modern ObjC subscripting">;
// Make sure all other -ccc- options are rejected.
@@ -146,66 +151,76 @@ def _HASH_HASH_HASH : Flag<"-###">, Flags<[DriverOption]>,
def _DASH_DASH : Flag<"--">, Flags<[DriverOption]>;
def A : JoinedOrSeparate<"-A">;
def B : JoinedOrSeparate<"-B">;
-def CC : Flag<"-CC">;
-def C : Flag<"-C">;
-def D : JoinedOrSeparate<"-D">, Group<CompileOnly_Group>;
-def E : Flag<"-E">, Flags<[DriverOption]>,
+def CC : Flag<"-CC">, Flags<[CC1Option]>;
+def C : Flag<"-C">, Flags<[CC1Option]>;
+def D : JoinedOrSeparate<"-D">, Group<CompileOnly_Group>, Flags<[CC1Option]>;
+def E : Flag<"-E">, Flags<[DriverOption,CC1Option]>, Group<Action_Group>,
HelpText<"Only run the preprocessor">;
-def F : JoinedOrSeparate<"-F">, Flags<[RenderJoined]>;
+def F : JoinedOrSeparate<"-F">, Flags<[RenderJoined,CC1Option]>,
+ HelpText<"Add directory to framework include search path">;
def G : Separate<"-G">, Flags<[DriverOption]>;
-def H : Flag<"-H">;
+def H : Flag<"-H">, Flags<[CC1Option]>,
+ HelpText<"Show header includes and nesting depth">;
def I_ : Flag<"-I-">, Group<I_Group>;
-def I : JoinedOrSeparate<"-I">, Group<I_Group>;
+def I : JoinedOrSeparate<"-I">, Group<I_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to include search path">;
def L : JoinedOrSeparate<"-L">, Flags<[RenderJoined]>;
def MD : Flag<"-MD">, Group<M_Group>;
def MF : JoinedOrSeparate<"-MF">, Group<M_Group>;
-def MG : Flag<"-MG">, Group<M_Group>;
+def MG : Flag<"-MG">, Group<M_Group>, Flags<[CC1Option]>,
+ HelpText<"Add missing headers to dependency list">;
def MMD : Flag<"-MMD">, Group<M_Group>;
def MM : Flag<"-MM">, Group<M_Group>;
-def MP : Flag<"-MP">, Group<M_Group>;
-def MQ : JoinedOrSeparate<"-MQ">, Group<M_Group>;
-def MT : JoinedOrSeparate<"-MT">, Group<M_Group>;
+def MP : Flag<"-MP">, Group<M_Group>, Flags<[CC1Option]>,
+ HelpText<"Create phony target for each dependency (other than main file)">;
+def MQ : JoinedOrSeparate<"-MQ">, Group<M_Group>, Flags<[CC1Option]>,
+ HelpText<"Specify target to quote for dependency">;
+def MT : JoinedOrSeparate<"-MT">, Group<M_Group>, Flags<[CC1Option]>,
+ HelpText<"Specify target for dependency">;
def Mach : Flag<"-Mach">;
def M : Flag<"-M">, Group<M_Group>;
-def O0 : Joined<"-O0">, Group<O_Group>;
-def O4 : Joined<"-O4">, Group<O_Group>;
+def O0 : Joined<"-O0">, Group<O_Group>, Flags<[CC1Option]>;
+def O4 : Joined<"-O4">, Group<O_Group>, Flags<[CC1Option]>;
def ObjCXX : Flag<"-ObjC++">, Flags<[DriverOption]>,
HelpText<"Treat source input files as Objective-C++ inputs">;
def ObjC : Flag<"-ObjC">, Flags<[DriverOption]>,
HelpText<"Treat source input files as Objective-C inputs">;
-def O : Joined<"-O">, Group<O_Group>;
-def P : Flag<"-P">;
+def O : Joined<"-O">, Group<O_Group>, Flags<[CC1Option]>;
+def P : Flag<"-P">, Flags<[CC1Option]>,
+ HelpText<"Disable linemarker output in -E mode">;
def Qn : Flag<"-Qn">;
def Qunused_arguments : Flag<"-Qunused-arguments">, Flags<[DriverOption]>,
HelpText<"Don't emit warning for unused driver arguments">;
def Q : Flag<"-Q">;
def R : Flag<"-R">;
-def S : Flag<"-S">, Flags<[DriverOption]>,
+def S : Flag<"-S">, Flags<[DriverOption,CC1Option]>, Group<Action_Group>,
HelpText<"Only run preprocess and compilation steps">;
def Tbss : JoinedOrSeparate<"-Tbss">, Group<T_Group>;
def Tdata : JoinedOrSeparate<"-Tdata">, Group<T_Group>;
def Ttext : JoinedOrSeparate<"-Ttext">, Group<T_Group>;
def T : JoinedOrSeparate<"-T">, Group<T_Group>;
-def U : JoinedOrSeparate<"-U">, Group<CompileOnly_Group>;
+def U : JoinedOrSeparate<"-U">, Group<CompileOnly_Group>, Flags<[CC1Option]>;
def V : JoinedOrSeparate<"-V">, Flags<[DriverOption, Unsupported]>;
def Wa_COMMA : CommaJoined<"-Wa,">,
HelpText<"Pass the comma separated arguments in <arg> to the assembler">,
MetaVarName<"<arg>">;
-def Wall : Flag<"-Wall">, Group<W_Group>;
-def Wdeprecated : Flag<"-Wdeprecated">, Group<W_Group>;
-def Wno_deprecated : Flag<"-Wno-deprecated">, Group<W_Group>;
-def Wextra : Flag<"-Wextra">, Group<W_Group>;
+def Wall : Flag<"-Wall">, Group<W_Group>, Flags<[CC1Option]>;
+def Wdeprecated : Flag<"-Wdeprecated">, Group<W_Group>, Flags<[CC1Option]>;
+def Wno_deprecated : Flag<"-Wno-deprecated">, Group<W_Group>, Flags<[CC1Option]>;
+def Wextra : Flag<"-Wextra">, Group<W_Group>, Flags<[CC1Option]>;
def Wl_COMMA : CommaJoined<"-Wl,">, Flags<[LinkerInput, RenderAsInput]>,
HelpText<"Pass the comma separated arguments in <arg> to the linker">,
MetaVarName<"<arg>">;
-def Wno_nonportable_cfstrings : Joined<"-Wno-nonportable-cfstrings">, Group<W_Group>;
-def Wnonportable_cfstrings : Joined<"-Wnonportable-cfstrings">, Group<W_Group>;
+def Wno_nonportable_cfstrings : Joined<"-Wno-nonportable-cfstrings">, Group<W_Group>,
+ Flags<[CC1Option]>;
+def Wnonportable_cfstrings : Joined<"-Wnonportable-cfstrings">, Group<W_Group>,
+ Flags<[CC1Option]>;
def Wp_COMMA : CommaJoined<"-Wp,">,
HelpText<"Pass the comma separated arguments in <arg> to the preprocessor">,
MetaVarName<"<arg>">;
-def Wwrite_strings : Flag<"-Wwrite-strings">, Group<W_Group>;
-def Wno_write_strings : Flag<"-Wno-write-strings">, Group<W_Group>;
-def W_Joined : Joined<"-W">, Group<W_Group>;
+def Wwrite_strings : Flag<"-Wwrite-strings">, Group<W_Group>, Flags<[CC1Option]>;
+def Wno_write_strings : Flag<"-Wno-write-strings">, Group<W_Group>, Flags<[CC1Option]>;
+def W_Joined : Joined<"-W">, Group<W_Group>, Flags<[CC1Option]>;
def Xanalyzer : Separate<"-Xanalyzer">,
HelpText<"Pass <arg> to the static analyzer">, MetaVarName<"<arg>">;
def Xarch__ : JoinedAndSeparate<"-Xarch_">, Flags<[DriverOption]>;
@@ -233,20 +248,29 @@ def bind__at__load : Flag<"-bind_at_load">;
def bundle__loader : Separate<"-bundle_loader">;
def bundle : Flag<"-bundle">;
def b : JoinedOrSeparate<"-b">, Flags<[Unsupported]>;
+def cl_kernel_arg_info : Flag<"-cl-kernel-arg-info">, Flags<[CC1Option]>, Group<opencl_Group>,
+HelpText<"OpenCL only. This option allows the compiler to store information about the arguments of a kernel(s)"> ;
def client__name : JoinedOrSeparate<"-client_name">;
def combine : Flag<"-combine">, Flags<[DriverOption, Unsupported]>;
def compatibility__version : JoinedOrSeparate<"-compatibility_version">;
def coverage : Flag<"-coverage">;
def cpp_precomp : Flag<"-cpp-precomp">, Group<clang_ignored_f_Group>;
def current__version : JoinedOrSeparate<"-current_version">;
-def cxx_isystem : JoinedOrSeparate<"-cxx-isystem">, Group<clang_i_Group>;
+def cxx_isystem : JoinedOrSeparate<"-cxx-isystem">, Group<clang_i_Group>,
+ HelpText<"Add directory to the C++ SYSTEM include search path">, Flags<[CC1Option]>,
+ MetaVarName<"<directory>">;
def c : Flag<"-c">, Flags<[DriverOption]>,
HelpText<"Only run preprocess, compile, and assemble steps">;
def dA : Flag<"-dA">, Group<d_Group>;
-def dD : Flag<"-dD">, Group<d_Group>;
-def dM : Flag<"-dM">, Group<d_Group>;
+def dD : Flag<"-dD">, Group<d_Group>, Flags<[CC1Option]>,
+ HelpText<"Print macro definitions in -E mode in addition to normal output">;
+def dM : Flag<"-dM">, Group<d_Group>, Flags<[CC1Option]>,
+ HelpText<"Print macro definitions in -E mode instead of normal output">;
def dead__strip : Flag<"-dead_strip">;
-def dependency_file : Separate<"-dependency-file">;
+def dependency_file : Separate<"-dependency-file">, Flags<[CC1Option]>,
+ HelpText<"Filename (or -) to write dependency output to">;
+def dependency_dot : Separate<"-dependency-dot">, Flags<[CC1Option]>,
+ HelpText<"Filename to write DOT-formatted header dependencies to">;
def dumpmachine : Flag<"-dumpmachine">;
def dumpspecs : Flag<"-dumpspecs">, Flags<[Unsupported]>;
def dumpversion : Flag<"-dumpversion">;
@@ -259,7 +283,7 @@ def d_Flag : Flag<"-d">, Group<d_Group>;
def d_Joined : Joined<"-d">, Group<d_Group>;
def emit_ast : Flag<"-emit-ast">,
HelpText<"Emit Clang AST files for source inputs">;
-def emit_llvm : Flag<"-emit-llvm">,
+def emit_llvm : Flag<"-emit-llvm">, Flags<[CC1Option]>, Group<Action_Group>,
HelpText<"Use the LLVM representation for assembler and object files">;
def exported__symbols__list : Separate<"-exported_symbols_list">;
def e : JoinedOrSeparate<"-e">;
@@ -269,13 +293,18 @@ def fPIE : Flag<"-fPIE">, Group<f_Group>;
def fno_PIE : Flag<"-fno-PIE">, Group<f_Group>;
def faccess_control : Flag<"-faccess-control">, Group<f_Group>;
def fallow_unsupported : Flag<"-fallow-unsupported">, Group<f_Group>;
-def faltivec : Flag<"-faltivec">, Group<f_Group>;
-def fapple_kext : Flag<"-fapple-kext">, Group<f_Group>;
-def fapple_pragma_pack : Flag<"-fapple-pragma-pack">, Group<f_Group>;
-def faddress_sanitizer : Flag<"-faddress-sanitizer">, Group<f_Group>;
-def fno_address_sanitizer : Flag<"-fno-address-sanitizer">, Group<f_Group>;
-def fthread_sanitizer : Flag<"-fthread-sanitizer">, Group<f_Group>;
-def fno_thread_sanitizer : Flag<"-fno-thread-sanitizer">, Group<f_Group>;
+def faltivec : Flag<"-faltivec">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable AltiVec vector initializer syntax">;
+def fapple_kext : Flag<"-fapple-kext">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Use Apple's kernel extensions ABI">;
+def fapple_pragma_pack : Flag<"-fapple-pragma-pack">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable Apple gcc-compatible #pragma pack handling">;
+def faddress_sanitizer : Flag<"-faddress-sanitizer">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable AddressSanitizer instrumentation (memory error detection)">;
+def fno_address_sanitizer : Flag<"-fno-address-sanitizer">, Group<f_Group>, Flags<[CC1Option]>;
+def fthread_sanitizer : Flag<"-fthread-sanitizer">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable ThreadSanitizer instrumentation (race detection)">;
+def fno_thread_sanitizer : Flag<"-fno-thread-sanitizer">, Group<f_Group>, Flags<[CC1Option]>;
def fasm : Flag<"-fasm">, Group<f_Group>;
def fasm_blocks : Flag<"-fasm-blocks">, Group<f_Group>;
@@ -286,17 +315,24 @@ def fastcp : Flag<"-fastcp">, Group<f_Group>;
def fastf : Flag<"-fastf">, Group<f_Group>;
def fast : Flag<"-fast">, Group<f_Group>;
def fasynchronous_unwind_tables : Flag<"-fasynchronous-unwind-tables">, Group<f_Group>;
-def fblocks : Flag<"-fblocks">, Group<f_Group>;
+def fblocks : Flag<"-fblocks">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable the 'blocks' language feature">;
def fbootclasspath_EQ : Joined<"-fbootclasspath=">, Group<f_Group>;
-def fborland_extensions : Flag<"-fborland-extensions">, Group<f_Group>;
+def fborland_extensions : Flag<"-fborland-extensions">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Accept non-standard constructs supported by the Borland compiler">;
+def fbounds_checking : Flag<"-fbounds-checking">, Group<f_Group>,
+ HelpText<"Enable run-time bounds checks.">;
+def fbounds_checking_EQ : Joined<"-fbounds-checking=">, Flags<[CC1Option]>,
+ Group<f_Group>;
def fbuiltin_strcat : Flag<"-fbuiltin-strcat">, Group<f_Group>;
def fbuiltin_strcpy : Flag<"-fbuiltin-strcpy">, Group<f_Group>;
def fbuiltin : Flag<"-fbuiltin">, Group<f_Group>;
def fcaret_diagnostics : Flag<"-fcaret-diagnostics">, Group<f_Group>;
-def fcatch_undefined_behavior : Flag<"-fcatch-undefined-behavior">,
- Group<f_Group>, HelpText<"Generate runtime checks for undefined behavior.">;
+def fcatch_undefined_behavior : Flag<"-fcatch-undefined-behavior">, Flags<[CC1Option]>,
+ Group<f_Group>, HelpText<"Generate runtime checks for undefined behavior.">;
def fclasspath_EQ : Joined<"-fclasspath=">, Group<f_Group>;
-def fcolor_diagnostics : Flag<"-fcolor-diagnostics">, Group<f_Group>;
+def fcolor_diagnostics : Flag<"-fcolor-diagnostics">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Use colors in diagnostics">;
def fcommon : Flag<"-fcommon">, Group<f_Group>;
def fcompile_resource_EQ : Joined<"-fcompile-resource=">, Group<f_Group>;
def fconstant_cfstrings : Flag<"-fconstant-cfstrings">, Group<f_Group>;
@@ -306,32 +342,53 @@ def fconstexpr_backtrace_limit_EQ : Joined<"-fconstexpr-backtrace-limit=">,
Group<f_Group>;
def fno_crash_diagnostics : Flag<"-fno-crash-diagnostics">, Group<f_clang_Group>, Flags<[NoArgumentUnused]>;
def fcreate_profile : Flag<"-fcreate-profile">, Group<f_Group>;
-def fcxx_exceptions: Flag<"-fcxx-exceptions">, Group<f_Group>;
+def fcxx_exceptions: Flag<"-fcxx-exceptions">, Group<f_Group>,
+ HelpText<"Enable C++ exceptions">, Flags<[CC1Option]>;
def fcxx_modules : Flag <"-fcxx-modules">, Group<f_Group>, Flags<[NoForward]>;
def fdebug_pass_arguments : Flag<"-fdebug-pass-arguments">, Group<f_Group>;
def fdebug_pass_structure : Flag<"-fdebug-pass-structure">, Group<f_Group>;
def fdiagnostics_fixit_info : Flag<"-fdiagnostics-fixit-info">, Group<f_clang_Group>;
-def fdiagnostics_print_source_range_info : Flag<"-fdiagnostics-print-source-range-info">, Group<f_clang_Group>;
-def fdiagnostics_parseable_fixits : Flag<"-fdiagnostics-parseable-fixits">, Group<f_clang_Group>;
-def fdiagnostics_show_option : Flag<"-fdiagnostics-show-option">, Group<f_Group>;
-def fdiagnostics_show_note_include_stack : Flag<"-fdiagnostics-show-note-include-stack">, Group<f_Group>;
+def fdiagnostics_parseable_fixits : Flag<"-fdiagnostics-parseable-fixits">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Print fix-its in machine parseable form">;
+def fdiagnostics_print_source_range_info : Flag<"-fdiagnostics-print-source-range-info">,
+ Group<f_clang_Group>, Flags<[CC1Option]>,
+ HelpText<"Print source range spans in numeric form">;
+def fdiagnostics_show_option : Flag<"-fdiagnostics-show-option">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Print option name with mappable diagnostics">;
+def fdiagnostics_show_name : Flag<"-fdiagnostics-show-name">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Print diagnostic name">;
+def fdiagnostics_show_note_include_stack : Flag<"-fdiagnostics-show-note-include-stack">,
+ Group<f_Group>, Flags<[CC1Option]>, HelpText<"Display include stacks for diagnostic notes">;
def fdiagnostics_format_EQ : Joined<"-fdiagnostics-format=">, Group<f_clang_Group>;
def fdiagnostics_show_category_EQ : Joined<"-fdiagnostics-show-category=">, Group<f_clang_Group>;
-def fdollars_in_identifiers : Flag<"-fdollars-in-identifiers">, Group<f_Group>;
+def fdiagnostics_show_template_tree : Flag<"-fdiagnostics-show-template-tree">,
+ Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Print a template comparison tree for differing templates">;
+def fdollars_in_identifiers : Flag<"-fdollars-in-identifiers">, Group<f_Group>,
+ HelpText<"Allow '$' in identifiers">, Flags<[CC1Option]>;
def fdwarf2_cfi_asm : Flag<"-fdwarf2-cfi-asm">, Group<f_Group>;
-def fno_dwarf2_cfi_asm : Flag<"-fno-dwarf2-cfi-asm">, Group<f_Group>;
+def fno_dwarf2_cfi_asm : Flag<"-fno-dwarf2-cfi-asm">, Group<f_Group>, Flags<[CC1Option]>;
def fdwarf_directory_asm : Flag<"-fdwarf-directory-asm">, Group<f_Group>;
-def fno_dwarf_directory_asm : Flag<"-fno-dwarf-directory-asm">, Group<f_Group>;
+def fno_dwarf_directory_asm : Flag<"-fno-dwarf-directory-asm">, Group<f_Group>, Flags<[CC1Option]>;
def felide_constructors : Flag<"-felide-constructors">, Group<f_Group>;
+def fno_elide_type : Flag<"-fno-elide-type">, Group<f_Group>,
+ Flags<[CC1Option]>,
+ HelpText<"Do not elide types when printing diagnostics">;
def feliminate_unused_debug_symbols : Flag<"-feliminate-unused-debug-symbols">, Group<f_Group>;
-def femit_all_decls : Flag<"-femit-all-decls">, Group<f_Group>;
+def femit_all_decls : Flag<"-femit-all-decls">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Emit all declarations, even if unused">;
def fencoding_EQ : Joined<"-fencoding=">, Group<f_Group>;
def ferror_limit_EQ : Joined<"-ferror-limit=">, Group<f_Group>;
-def fexceptions : Flag<"-fexceptions">, Group<f_Group>;
+def fexceptions : Flag<"-fexceptions">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable support for exception handling">;
def fextdirs_EQ : Joined<"-fextdirs=">, Group<f_Group>;
def fhosted : Flag<"-fhosted">, Group<f_Group>;
-def ffast_math : Flag<"-ffast-math">, Group<f_Group>;
-def fmath_errno : Flag<"-fmath-errno">, Group<f_Group>;
+def ffast_math : Flag<"-ffast-math">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable the *frontend*'s 'fast-math' mode. This has no effect on "
+ "optimizations, but provides a preprocessor macro __FAST_MATH__ the "
+ "same as GCC's -ffast-math flag.">;
+def fmath_errno : Flag<"-fmath-errno">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Require math functions to indicate errors by setting errno">;
def fno_math_errno : Flag<"-fno-math-errno">, Group<f_Group>;
def fsignaling_math : Flag<"-fsignaling-math">, Group<f_Group>;
def fno_signaling_math : Flag<"-fno-signaling-math">, Group<f_Group>;
@@ -343,7 +400,7 @@ def fassociative_math : Flag<"-fassociative-math">, Group<f_Group>;
def fno_associative_math : Flag<"-fno-associative-math">, Group<f_Group>;
def freciprocal_math : Flag<"-freciprocal-math">, Group<f_Group>;
def fno_reciprocal_math : Flag<"-fno-reciprocal-math">, Group<f_Group>;
-def ffinite_math_only : Flag<"-ffinite-math-only">, Group<f_Group>;
+def ffinite_math_only : Flag<"-ffinite-math-only">, Group<f_Group>, Flags<[CC1Option]>;
def fno_finite_math_only : Flag<"-fno-finite-math-only">, Group<f_Group>;
def fsigned_zeros : Flag<"-fsigned-zeros">, Group<f_Group>;
def fno_signed_zeros : Flag<"-fno-signed-zeros">, Group<f_Group>;
@@ -352,31 +409,41 @@ def fno_honor_nans : Flag<"-fno-honor-nans">, Group<f_Group>;
def fhonor_infinities : Flag<"-fhonor-infinities">, Group<f_Group>;
def fno_honor_infinities : Flag<"-fno-honor-infinities">, Group<f_Group>;
// Sic. This option was misspelled originally.
-def fhonor_infinites : Flag<"-fhonor-infinites">, Group<f_Group>,
- Alias<fhonor_infinities>;
-def fno_honor_infinites : Flag<"-fno-honor-infinites">, Group<f_Group>,
- Alias<fno_honor_infinities>;
+def fhonor_infinites : Flag<"-fhonor-infinites">, Alias<fhonor_infinities>;
+def fno_honor_infinites : Flag<"-fno-honor-infinites">, Alias<fno_honor_infinities>;
def ftrapping_math : Flag<"-ftrapping-math">, Group<f_Group>;
def fno_trapping_math : Flag<"-fno-trapping-math">, Group<f_Group>;
+def ffp_contract : Joined<"-ffp-contract=">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Form fused FP ops (e.g. FMAs): fast (everywhere)"
+ " | on (according to FP_CONTRACT pragma, default) | off (never fuse)">;
def ffor_scope : Flag<"-ffor-scope">, Group<f_Group>;
def fno_for_scope : Flag<"-fno-for-scope">, Group<f_Group>;
-def ffreestanding : Flag<"-ffreestanding">, Group<f_Group>;
-def fgnu_keywords : Flag<"-fgnu-keywords">, Group<f_Group>;
-def fgnu89_inline : Flag<"-fgnu89-inline">, Group<f_Group>;
+def frewrite_includes : Flag<"-frewrite-includes">, Group<f_Group>,
+ Flags<[CC1Option]>;
+def fno_rewrite_includes : Flag<"-fno-rewrite-includes">, Group<f_Group>;
+
+def ffreestanding : Flag<"-ffreestanding">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Assert that the compilation takes place in a freestanding environment">;
+def fgnu_keywords : Flag<"-fgnu-keywords">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Allow GNU-extension keywords regardless of language standard">;
+def fgnu89_inline : Flag<"-fgnu89-inline">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Use the gnu89 inline semantics">;
def fno_gnu89_inline : Flag<"-fno-gnu89-inline">, Group<f_Group>;
-def fgnu_runtime : Flag<"-fgnu-runtime">, Group<f_Group>;
-def fheinous_gnu_extensions : Flag<"-fheinous-gnu-extensions">;
+def fgnu_runtime : Flag<"-fgnu-runtime">, Group<f_Group>,
+ HelpText<"Generate output compatible with the standard GNU Objective-C runtime">;
+def fheinous_gnu_extensions : Flag<"-fheinous-gnu-extensions">, Flags<[CC1Option]>;
def filelist : Separate<"-filelist">, Flags<[LinkerInput]>;
def findirect_virtual_calls : Flag<"-findirect-virtual-calls">, Alias<fapple_kext>;
def finline_functions : Flag<"-finline-functions">, Group<clang_ignored_f_Group>;
def finline : Flag<"-finline">, Group<clang_ignored_f_Group>;
-def finstrument_functions : Flag<"-finstrument-functions">, Group<f_Group>;
+def finstrument_functions : Flag<"-finstrument-functions">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Generate calls to instrument function entry and exit">;
def fkeep_inline_functions : Flag<"-fkeep-inline-functions">, Group<clang_ignored_f_Group>;
def flat__namespace : Flag<"-flat_namespace">;
def flax_vector_conversions : Flag<"-flax-vector-conversions">, Group<f_Group>;
-def flimit_debug_info : Flag<"-flimit-debug-info">, Group<f_Group>,
+def flimit_debug_info : Flag<"-flimit-debug-info">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Limit debug information produced to reduce size of debug binary">;
def flimited_precision_EQ : Joined<"-flimited-precision=">, Group<f_Group>;
def flto : Flag<"-flto">, Group<f_Group>;
@@ -385,49 +452,73 @@ def fmacro_backtrace_limit_EQ : Joined<"-fmacro-backtrace-limit=">,
Group<f_Group>;
def fmerge_all_constants : Flag<"-fmerge-all-constants">, Group<f_Group>;
def fmessage_length_EQ : Joined<"-fmessage-length=">, Group<f_Group>;
-def fms_extensions : Flag<"-fms-extensions">, Group<f_Group>;
-def fms_compatibility : Flag<"-fms-compatibility">, Group<f_Group>;
-def fmsc_version : Joined<"-fmsc-version=">, Group<f_Group>;
-def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">, Group<f_Group>;
+def fms_extensions : Flag<"-fms-extensions">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Accept some non-standard constructs supported by the Microsoft compiler">;
+def fenable_experimental_ms_inline_asm : Flag<"-fenable-experimental-ms-inline-asm">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable support for Microsoft style inine assembly">;
+def fms_compatibility : Flag<"-fms-compatibility">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable Microsoft compatibility mode">;
+def fmsc_version : Joined<"-fmsc-version=">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Version of the Microsoft C/C++ compiler to report in _MSC_VER (0 = don't define it (default))">;
+def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">, Group<f_Group>,
+ HelpText<"Parse templated function definitions at the end of the "
+ "translation unit ">, Flags<[CC1Option]>;
def fmodule_cache_path : Separate<"-fmodule-cache-path">, Group<i_Group>,
- Flags<[NoForward]>;
-def fmodules : Flag <"-fmodules">, Group<f_Group>, Flags<[NoForward]>;
+ Flags<[NoForward,CC1Option]>, MetaVarName<"<directory>">,
+ HelpText<"Specify the module cache path">;
+def fmodules : Flag <"-fmodules">, Group<f_Group>, Flags<[NoForward,CC1Option]>,
+ HelpText<"Enable the 'modules' language feature">;
def fmudflapth : Flag<"-fmudflapth">, Group<f_Group>;
def fmudflap : Flag<"-fmudflap">, Group<f_Group>;
def fnested_functions : Flag<"-fnested-functions">, Group<f_Group>;
def fnext_runtime : Flag<"-fnext-runtime">, Group<f_Group>;
-def fno_access_control : Flag<"-fno-access-control">, Group<f_Group>;
+def fno_access_control : Flag<"-fno-access-control">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Disable C++ access control">;
def fno_apple_pragma_pack : Flag<"-fno-apple-pragma-pack">, Group<f_Group>;
def fno_asm : Flag<"-fno-asm">, Group<f_Group>;
def fno_asynchronous_unwind_tables : Flag<"-fno-asynchronous-unwind-tables">, Group<f_Group>;
-def fno_assume_sane_operator_new : Flag<"-fno-assume-sane-operator-new">, Group<f_Group>;
+def fno_assume_sane_operator_new : Flag<"-fno-assume-sane-operator-new">, Group<f_Group>,
+ HelpText<"Don't assume that C++'s global operator new can't alias any pointer">,
+ Flags<[CC1Option]>;
def fno_blocks : Flag<"-fno-blocks">, Group<f_Group>;
def fno_borland_extensions : Flag<"-fno-borland-extensions">, Group<f_Group>;
def fno_builtin_strcat : Flag<"-fno-builtin-strcat">, Group<f_Group>;
def fno_builtin_strcpy : Flag<"-fno-builtin-strcpy">, Group<f_Group>;
-def fno_builtin : Flag<"-fno-builtin">, Group<f_Group>;
-def fno_caret_diagnostics : Flag<"-fno-caret-diagnostics">, Group<f_Group>;
+def fno_builtin : Flag<"-fno-builtin">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Disable implicit builtin knowledge of functions">;
+def fno_caret_diagnostics : Flag<"-fno-caret-diagnostics">, Group<f_Group>,
+ Flags<[CC1Option]>;
def fno_color_diagnostics : Flag<"-fno-color-diagnostics">, Group<f_Group>;
-def fno_common : Flag<"-fno-common">, Group<f_Group>;
-def fno_constant_cfstrings : Flag<"-fno-constant-cfstrings">, Group<f_Group>;
+def fno_common : Flag<"-fno-common">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Compile common globals like normal definitions">;
+def fno_constant_cfstrings : Flag<"-fno-constant-cfstrings">, Group<f_Group>,
+ Flags<[CC1Option]>,
+ HelpText<"Disable creation of CodeFoundation-type constant strings">;
def fno_cxx_exceptions: Flag<"-fno-cxx-exceptions">, Group<f_Group>;
def fno_cxx_modules : Flag <"-fno-cxx-modules">, Group<f_Group>, Flags<[NoForward]>;
-def fno_diagnostics_fixit_info : Flag<"-fno-diagnostics-fixit-info">, Group<f_Group>;
+def fno_diagnostics_fixit_info : Flag<"-fno-diagnostics-fixit-info">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Do not include fixit information in diagnostics">;
+def fno_diagnostics_show_name : Flag<"-fno-diagnostics-show-name">, Group<f_Group>;
def fno_diagnostics_show_option : Flag<"-fno-diagnostics-show-option">, Group<f_Group>;
-def fno_diagnostics_show_note_include_stack : Flag<"-fno-diagnostics-show-note-include-stack">, Group<f_Group>;
-def fno_dollars_in_identifiers : Flag<"-fno-dollars-in-identifiers">, Group<f_Group>;
-def fno_elide_constructors : Flag<"-fno-elide-constructors">, Group<f_Group>;
+def fno_diagnostics_show_note_include_stack : Flag<"-fno-diagnostics-show-note-include-stack">,
+ Flags<[CC1Option]>, Group<f_Group>, HelpText<"Display include stacks for diagnostic notes">;
+def fno_dollars_in_identifiers : Flag<"-fno-dollars-in-identifiers">, Group<f_Group>,
+ HelpText<"Disallow '$' in identifiers">, Flags<[CC1Option]>;
+def fno_elide_constructors : Flag<"-fno-elide-constructors">, Group<f_Group>,
+ HelpText<"Disable C++ copy constructor elision">, Flags<[CC1Option]>;
def fno_eliminate_unused_debug_symbols : Flag<"-fno-eliminate-unused-debug-symbols">, Group<f_Group>;
def fno_exceptions : Flag<"-fno-exceptions">, Group<f_Group>;
-def fno_gnu_keywords : Flag<"-fno-gnu-keywords">, Group<f_Group>;
-def fno_inline_functions : Flag<"-fno-inline-functions">, Group<f_Group>;
-def fno_inline : Flag<"-fno-inline">, Group<f_Group>;
+def fno_gnu_keywords : Flag<"-fno-gnu-keywords">, Group<f_Group>, Flags<[CC1Option]>;
+def fno_inline_functions : Flag<"-fno-inline-functions">, Group<f_clang_Group>, Flags<[CC1Option]>;
+def fno_inline : Flag<"-fno-inline">, Group<f_clang_Group>, Flags<[CC1Option]>;
def fno_keep_inline_functions : Flag<"-fno-keep-inline-functions">, Group<clang_ignored_f_Group>;
-def fno_lax_vector_conversions : Flag<"-fno-lax-vector-conversions">, Group<f_Group>;
-def fno_limit_debug_info : Flag<"-fno-limit-debug-info">, Group<f_Group>,
+def fno_lax_vector_conversions : Flag<"-fno-lax-vector-conversions">, Group<f_Group>,
+ HelpText<"Disallow implicit conversions between vectors with a different number of elements or different element types">, Flags<[CC1Option]>;
+def fno_limit_debug_info : Flag<"-fno-limit-debug-info">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Do not limit debug information produced to reduce size of debug binary">;
-def fno_merge_all_constants : Flag<"-fno-merge-all-constants">, Group<f_Group>;
+def fno_merge_all_constants : Flag<"-fno-merge-all-constants">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Disallow merging of constants.">;
def fno_modules : Flag <"-fno-modules">, Group<f_Group>, Flags<[NoForward]>;
def fno_ms_extensions : Flag<"-fno-ms-extensions">, Group<f_Group>;
def fno_ms_compatibility : Flag<"-fno-ms-compatibility">, Group<f_Group>;
@@ -435,44 +526,62 @@ def fno_delayed_template_parsing : Flag<"-fno-delayed-template-parsing">, Group<
def fno_objc_exceptions: Flag<"-fno-objc-exceptions">, Group<f_Group>;
def fno_objc_legacy_dispatch : Flag<"-fno-objc-legacy-dispatch">, Group<f_Group>;
def fno_omit_frame_pointer : Flag<"-fno-omit-frame-pointer">, Group<f_Group>;
-def fno_operator_names : Flag<"-fno-operator-names">, Group<f_Group>;
+def fno_operator_names : Flag<"-fno-operator-names">, Group<f_Group>,
+ HelpText<"Do not treat C++ operator name keywords as synonyms for operators">,
+ Flags<[CC1Option]>;
def fno_pascal_strings : Flag<"-fno-pascal-strings">, Group<f_Group>;
-def fno_rtti : Flag<"-fno-rtti">, Group<f_Group>;
+def fno_rtti : Flag<"-fno-rtti">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Disable generation of rtti information">;
def fno_short_enums : Flag<"-fno-short-enums">, Group<f_Group>;
-def fno_show_column : Flag<"-fno-show-column">, Group<f_Group>;
-def fno_show_source_location : Flag<"-fno-show-source-location">, Group<f_Group>;
-def fno_spell_checking : Flag<"-fno-spell-checking">, Group<f_Group>;
+def fno_show_column : Flag<"-fno-show-column">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Do not include column number on diagnostics">;
+def fno_show_source_location : Flag<"-fno-show-source-location">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Do not include source location information with diagnostics">;
+def fno_spell_checking : Flag<"-fno-spell-checking">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Disable spell-checking">;
def fno_stack_protector : Flag<"-fno-stack-protector">, Group<f_Group>;
def fno_strict_aliasing : Flag<"-fno-strict-aliasing">, Group<f_Group>;
def fno_strict_enums : Flag<"-fno-strict-enums">, Group<f_Group>;
def fno_strict_overflow : Flag<"-fno-strict-overflow">, Group<f_Group>;
-def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">, Group<f_Group>;
-def fno_use_cxa_atexit : Flag<"-fno-use-cxa-atexit">, Group<f_Group>;
+def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Do not emit code to make initialization of local statics thread safe">;
+def fno_use_cxa_atexit : Flag<"-fno-use-cxa-atexit">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Don't use __cxa_atexit for calling destructors">;
def fno_unit_at_a_time : Flag<"-fno-unit-at-a-time">, Group<f_Group>;
def fno_unwind_tables : Flag<"-fno-unwind-tables">, Group<f_Group>;
def fno_verbose_asm : Flag<"-fno-verbose-asm">, Group<f_Group>;
def fno_working_directory : Flag<"-fno-working-directory">, Group<f_Group>;
def fno_wrapv : Flag<"-fno-wrapv">, Group<f_Group>;
def fno_zero_initialized_in_bss : Flag<"-fno-zero-initialized-in-bss">, Group<f_Group>;
-def fobjc_arc : Flag<"-fobjc-arc">, Group<f_Group>;
+def fobjc_arc : Flag<"-fobjc-arc">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Synthesize retain and release calls for Objective-C pointers">;
def fno_objc_arc : Flag<"-fno-objc-arc">, Group<f_Group>;
-def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">, Group<f_Group>;
+def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Use EH-safe code when synthesizing retains and releases in -fobjc-arc">;
def fno_objc_arc_exceptions : Flag<"-fno-objc-arc-exceptions">, Group<f_Group>;
def fobjc_atdefs : Flag<"-fobjc-atdefs">, Group<clang_ignored_f_Group>;
def fobjc_call_cxx_cdtors : Flag<"-fobjc-call-cxx-cdtors">, Group<clang_ignored_f_Group>;
-def fobjc_exceptions: Flag<"-fobjc-exceptions">, Group<f_Group>;
+def fobjc_exceptions: Flag<"-fobjc-exceptions">, Group<f_Group>,
+ HelpText<"Enable Objective-C exceptions">, Flags<[CC1Option]>;
-def fobjc_gc_only : Flag<"-fobjc-gc-only">, Group<f_Group>;
-def fobjc_gc : Flag<"-fobjc-gc">, Group<f_Group>;
+def fobjc_gc_only : Flag<"-fobjc-gc-only">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Use GC exclusively for Objective-C related memory management">;
+def fobjc_gc : Flag<"-fobjc-gc">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable Objective-C garbage collection">;
def fobjc_legacy_dispatch : Flag<"-fobjc-legacy-dispatch">, Group<f_Group>;
def fobjc_new_property : Flag<"-fobjc-new-property">, Group<clang_ignored_f_Group>;
def fobjc_infer_related_result_type : Flag<"-fobjc-infer-related-result-type">,
Group<f_Group>;
def fno_objc_infer_related_result_type : Flag<
- "-fno-objc-infer-related-result-type">, Group<f_Group>;
+ "-fno-objc-infer-related-result-type">, Group<f_Group>,
+ HelpText<
+ "do not infer Objective-C related result type based on method family">,
+ Flags<[CC1Option]>;
def fobjc_link_runtime: Flag<"-fobjc-link-runtime">, Group<f_Group>;
// Objective-C ABI options.
+def fobjc_runtime_EQ : Joined<"-fobjc-runtime=">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Specify the target Objective-C runtime kind and version">;
def fobjc_abi_version_EQ : Joined<"-fobjc-abi-version=">, Group<f_Group>;
def fobjc_nonfragile_abi_version_EQ : Joined<"-fobjc-nonfragile-abi-version=">, Group<f_Group>;
def fobjc_nonfragile_abi : Flag<"-fobjc-nonfragile-abi">, Group<f_Group>;
@@ -490,8 +599,10 @@ def force__load : Separate<"-force_load">;
def foutput_class_dir_EQ : Joined<"-foutput-class-dir=">, Group<f_Group>;
def fpack_struct : Flag<"-fpack-struct">, Group<f_Group>;
def fno_pack_struct : Flag<"-fno-pack-struct">, Group<f_Group>;
-def fpack_struct_EQ : Joined<"-fpack-struct=">, Group<f_Group>;
-def fpascal_strings : Flag<"-fpascal-strings">, Group<f_Group>;
+def fpack_struct_EQ : Joined<"-fpack-struct=">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Specify the default maximum struct packing alignment">;
+def fpascal_strings : Flag<"-fpascal-strings">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Recognize and construct Pascal-style string literals">;
def fpch_preprocess : Flag<"-fpch-preprocess">, Group<f_Group>;
def fpic : Flag<"-fpic">, Group<f_Group>;
def fno_pic : Flag<"-fno-pic">, Group<f_Group>;
@@ -503,11 +614,15 @@ def framework : Separate<"-framework">, Flags<[LinkerInput]>;
def frandom_seed_EQ : Joined<"-frandom-seed=">, Group<clang_ignored_f_Group>;
def frtti : Flag<"-frtti">, Group<f_Group>;
def fsched_interblock : Flag<"-fsched-interblock">, Group<clang_ignored_f_Group>;
-def fshort_enums : Flag<"-fshort-enums">, Group<f_Group>;
+def fshort_enums : Flag<"-fshort-enums">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Allocate to an enum type only as many bytes as it needs for the declared range of possible values">;
def freorder_blocks : Flag<"-freorder-blocks">, Group<clang_ignored_f_Group>;
-def fshort_wchar : Flag<"-fshort-wchar">, Group<f_Group>;
-def fshow_overloads_EQ : Joined<"-fshow-overloads=">, Group<f_Group>;
-def fshow_column : Flag<"-fshow-column">, Group<f_Group>;
+def fshort_wchar : Flag<"-fshort-wchar">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Force wchar_t to be a short unsigned int">;
+def fshow_overloads_EQ : Joined<"-fshow-overloads=">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Which overload candidates to show when overload resolution fails: "
+ "best|all; defaults to all">;
+def fshow_column : Flag<"-fshow-column">, Group<f_Group>, Flags<[CC1Option]>;
def fshow_source_location : Flag<"-fshow-source-location">, Group<f_Group>;
def fspell_checking : Flag<"-fspell-checking">, Group<f_Group>;
def fsigned_bitfields : Flag<"-fsigned-bitfields">, Group<f_Group>;
@@ -515,17 +630,21 @@ def fsigned_char : Flag<"-fsigned-char">, Group<f_Group>;
def fstack_protector_all : Flag<"-fstack-protector-all">, Group<f_Group>;
def fstack_protector : Flag<"-fstack-protector">, Group<f_Group>;
def fstrict_aliasing : Flag<"-fstrict-aliasing">, Group<f_Group>;
-def fstrict_enums : Flag<"-fstrict-enums">, Group<f_Group>;
+def fstrict_enums : Flag<"-fstrict-enums">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Enable optimizations based on the strict definition of an enum's "
+ "value range.">;
def fstrict_overflow : Flag<"-fstrict-overflow">, Group<f_Group>;
-def fsyntax_only : Flag<"-fsyntax-only">, Flags<[DriverOption]>;
+def fsyntax_only : Flag<"-fsyntax-only">, Flags<[DriverOption,CC1Option]>, Group<Action_Group>;
def ftabstop_EQ : Joined<"-ftabstop=">, Group<f_Group>;
def ftemplate_depth_EQ : Joined<"-ftemplate-depth=">, Group<f_Group>;
def ftemplate_depth_ : Joined<"-ftemplate-depth-">, Group<f_Group>;
def ftemplate_backtrace_limit_EQ : Joined<"-ftemplate-backtrace-limit=">,
Group<f_Group>;
def ftest_coverage : Flag<"-ftest-coverage">, Group<f_Group>;
-def Wlarge_by_value_copy_def : Flag<"-Wlarge-by-value-copy">;
-def Wlarge_by_value_copy_EQ : Joined<"-Wlarge-by-value-copy=">;
+def Wlarge_by_value_copy_def : Flag<"-Wlarge-by-value-copy">,
+ HelpText<"Warn if a function definition returns or accepts an object larger "
+ "in bytes that a given value">;
+def Wlarge_by_value_copy_EQ : Joined<"-Wlarge-by-value-copy=">, Flags<[CC1Option]>;
// Just silence warnings about -Wlarger-than, -Wframe-larger-than for now.
def Wlarger_than : Separate<"-Wlarger-than">, Group<clang_ignored_f_Group>;
@@ -536,68 +655,116 @@ def Wframe_larger_than_EQ : Joined<"-Wframe-larger-than=">, Alias<Wframe_larger_
def fterminated_vtables : Flag<"-fterminated-vtables">, Alias<fapple_kext>;
def fthreadsafe_statics : Flag<"-fthreadsafe-statics">, Group<f_Group>;
-def ftime_report : Flag<"-ftime-report">, Group<f_Group>;
-def ftrapv : Flag<"-ftrapv">, Group<f_Group>;
-def ftrapv_handler_EQ : Joined<"-ftrapv-handler=">, Group<f_Group>;
-def ftrap_function_EQ : Joined<"-ftrap-function=">, Group<f_Group>,
+def ftime_report : Flag<"-ftime-report">, Group<f_Group>, Flags<[CC1Option]>;
+def ftlsmodel_EQ : Joined<"-ftls-model=">, Group<f_Group>, Flags<[CC1Option]>;
+def ftrapv : Flag<"-ftrapv">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Trap on integer overflow">;
+def ftrapv_handler_EQ : Joined<"-ftrapv-handler=">, Group<f_Group>,
+ MetaVarName<"<function name>">,
+ HelpText<"Specify the function to be called on overflow.">;
+def ftrapv_handler : Separate<"-ftrapv-handler">, Group<f_Group>, Flags<[CC1Option]>;
+def ftrap_function_EQ : Joined<"-ftrap-function=">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Issue call to specified function rather than a trap instruction">;
def funit_at_a_time : Flag<"-funit-at-a-time">, Group<f_Group>;
-def funroll_loops : Flag<"-funroll-loops">, Group<f_Group>;
+def funroll_loops : Flag<"-funroll-loops">, Group<f_Group>,
+ HelpText<"Turn on loop unroller">, Flags<[CC1Option]>;
def funsigned_bitfields : Flag<"-funsigned-bitfields">, Group<f_Group>;
def funsigned_char : Flag<"-funsigned-char">, Group<f_Group>;
def funwind_tables : Flag<"-funwind-tables">, Group<f_Group>;
def fuse_cxa_atexit : Flag<"-fuse-cxa-atexit">, Group<f_Group>;
def fverbose_asm : Flag<"-fverbose-asm">, Group<f_Group>;
def fvisibility_EQ : Joined<"-fvisibility=">, Group<f_Group>;
-def fvisibility_inlines_hidden : Flag<"-fvisibility-inlines-hidden">, Group<f_Group>;
-def fwrapv : Flag<"-fwrapv">, Group<f_Group>;
-def fwritable_strings : Flag<"-fwritable-strings">, Group<f_Group>;
+def fvisibility_inlines_hidden : Flag<"-fvisibility-inlines-hidden">, Group<f_Group>,
+ HelpText<"Give inline C++ member functions default visibility by default">,
+ Flags<[CC1Option]>;
+def fwrapv : Flag<"-fwrapv">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Treat signed integer overflow as two's complement">;
+def fwritable_strings : Flag<"-fwritable-strings">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Store string literals as writable data">;
def fzero_initialized_in_bss : Flag<"-fzero-initialized-in-bss">, Group<f_Group>;
-def ffunction_sections: Flag <"-ffunction-sections">, Group<f_Group>;
-def fdata_sections : Flag <"-fdata-sections">, Group<f_Group>;
+def ffunction_sections: Flag <"-ffunction-sections">, Group<f_Group>,
+ Flags<[CC1Option]>, HelpText<"Place each function in its own section (ELF Only)">;
+def fdata_sections : Flag <"-fdata-sections">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Place each data in its own section (ELF Only)">;
def f : Joined<"-f">, Group<f_Group>;
+def g_Flag : Flag<"-g">, Group<g_Group>,
+ HelpText<"Generate source level debug information">, Flags<[CC1Option]>;
+def gline_tables_only : Flag<"-gline-tables-only">, Group<g_Group>,
+ HelpText<"Emit debug line number tables only">, Flags<[CC1Option]>;
def g0 : Flag<"-g0">, Group<g_Group>;
+def g1 : Flag<"-g1">, Group<g_Group>;
def g2 : Flag<"-g2">, Group<g_Group>;
def g3 : Flag<"-g3">, Group<g_Group>;
-def gdwarf2 : Flag<"-gdwarf-2">, Group<g_Group>;
-def gfull : Flag<"-gfull">, Group<g_Group>;
def ggdb : Flag<"-ggdb">, Group<g_Group>;
-def gstabs : Flag<"-gstabs">, Group<g_Group>;
-def gstabsplus : Flag<"-gstabs+">, Group<g_Group>;
-def gstabs1 : Flag<"-gstabs1">, Group<g_Group>;
-def gstabs2 : Flag<"-gstabs2">, Group<g_Group>;
+def ggdb0 : Flag<"-ggdb0">, Group<g_Group>;
+def ggdb1 : Flag<"-ggdb1">, Group<g_Group>;
+def ggdb2 : Flag<"-ggdb2">, Group<g_Group>;
+def ggdb3 : Flag<"-ggdb3">, Group<g_Group>;
+def gdwarf_2 : Flag<"-gdwarf-2">, Group<g_Group>;
+def gdwarf_3 : Flag<"-gdwarf-3">, Group<g_Group>;
+def gdwarf_4 : Flag<"-gdwarf-4">, Group<g_Group>;
+def gfull : Flag<"-gfull">, Group<g_Group>;
def gused : Flag<"-gused">, Group<g_Group>;
-def g_Flag : Flag<"-g">, Group<g_Group>;
+def gstabs : Joined<"-gstabs">, Group<g_Group>, Flags<[Unsupported]>;
+def gcoff : Joined<"-gcoff">, Group<g_Group>, Flags<[Unsupported]>;
+def gxcoff : Joined<"-gxcoff">, Group<g_Group>, Flags<[Unsupported]>;
+def gvms : Joined<"-gvms">, Group<g_Group>, Flags<[Unsupported]>;
+def gtoggle : Flag<"-gtoggle">, Group<g_flags_Group>, Flags<[Unsupported]>;
+def grecord_gcc_switches : Flag<"-grecord-gcc-switches">, Group<g_flags_Group>;
+def gno_record_gcc_switches : Flag<"-gno-record-gcc-switches">,
+ Group<g_flags_Group>;
+def gstrict_dwarf : Flag<"-gstrict-dwarf">, Group<g_flags_Group>;
+def gno_strict_dwarf : Flag<"-gno-strict-dwarf">, Group<g_flags_Group>;
def headerpad__max__install__names : Joined<"-headerpad_max_install_names">;
-def index_header_map : Flag<"-index-header-map">;
-def idirafter : JoinedOrSeparate<"-idirafter">, Group<clang_i_Group>;
-def iframework : Joined<"-iframework">, Group<clang_i_Group>;
-def imacros : JoinedOrSeparate<"-imacros">, Group<clang_i_Group>;
+def help : Flag<"-help">, Flags<[CC1Option]>,
+ HelpText<"Display available options">;
+def index_header_map : Flag<"-index-header-map">, Flags<[CC1Option]>,
+ HelpText<"Make the next included directory (-I or -F) an indexer header map">;
+def idirafter : JoinedOrSeparate<"-idirafter">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to AFTER include search path">;
+def iframework : Joined<"-iframework">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to SYSTEM framework search path">;
+def imacros : JoinedOrSeparate<"-imacros">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Include macros from file before parsing">, MetaVarName<"<file>">;
def image__base : Separate<"-image_base">;
-def include_ : JoinedOrSeparate<"-include">, Group<clang_i_Group>, EnumName<"include">;
-def include_pch : Separate<"-include-pch">, Group<clang_i_Group>;
+def include_ : JoinedOrSeparate<"-include">, Group<clang_i_Group>, EnumName<"include">,
+ MetaVarName<"<file>">, HelpText<"Include file before parsing">, Flags<[CC1Option]>;
+def include_pch : Separate<"-include-pch">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Include precompiled header file">, MetaVarName<"<file>">;
def init : Separate<"-init">;
def install__name : Separate<"-install_name">;
def integrated_as : Flag<"-integrated-as">, Flags<[DriverOption]>;
-def iprefix : JoinedOrSeparate<"-iprefix">, Group<clang_i_Group>;
-def iquote : JoinedOrSeparate<"-iquote">, Group<clang_i_Group>;
-def isysroot : JoinedOrSeparate<"-isysroot">, Group<clang_i_Group>;
-def isystem : JoinedOrSeparate<"-isystem">, Group<clang_i_Group>;
-def iwithprefixbefore : JoinedOrSeparate<"-iwithprefixbefore">, Group<clang_i_Group>;
-def iwithprefix : JoinedOrSeparate<"-iwithprefix">, Group<clang_i_Group>;
-def iwithsysroot : JoinedOrSeparate<"-iwithsysroot">, Group<clang_i_Group>;
+def iprefix : JoinedOrSeparate<"-iprefix">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Set the -iwithprefix/-iwithprefixbefore prefix">, MetaVarName<"<dir>">;
+def iquote : JoinedOrSeparate<"-iquote">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to QUOTE include search path">, MetaVarName<"<directory>">;
+def isysroot : JoinedOrSeparate<"-isysroot">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Set the system root directory (usually /)">, MetaVarName<"<dir>">;
+def isystem : JoinedOrSeparate<"-isystem">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to SYSTEM include search path">, MetaVarName<"<directory>">;
+def iwithprefixbefore : JoinedOrSeparate<"-iwithprefixbefore">, Group<clang_i_Group>,
+ HelpText<"Set directory to include search path with prefix">, MetaVarName<"<dir>">,
+ Flags<[CC1Option]>;
+def iwithprefix : JoinedOrSeparate<"-iwithprefix">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Set directory to SYSTEM include search path with prefix">, MetaVarName<"<dir>">;
+def iwithsysroot : JoinedOrSeparate<"-iwithsysroot">, Group<clang_i_Group>,
+ HelpText<"Add directory to SYSTEM include search path, "
+ "absolute paths are relative to -isysroot">, MetaVarName<"<directory>">,
+ Flags<[CC1Option]>;
def i : Joined<"-i">, Group<i_Group>;
def keep__private__externs : Flag<"-keep_private_externs">;
def l : JoinedOrSeparate<"-l">, Flags<[LinkerInput, RenderJoined]>;
def lazy__framework : Separate<"-lazy_framework">, Flags<[LinkerInput]>;
def lazy__library : Separate<"-lazy_library">, Flags<[LinkerInput]>;
def m32 : Flag<"-m32">, Group<m_Group>, Flags<[DriverOption]>;
-def mqdsp6_compat : Flag<"-mqdsp6-compat">, Group<m_Group>, Flags<[DriverOption]>;
+def mqdsp6_compat : Flag<"-mqdsp6-compat">, Group<m_Group>, Flags<[DriverOption,CC1Option]>,
+ HelpText<"Enable hexagon-qdsp6 backward compatibility">;
def m3dnowa : Flag<"-m3dnowa">, Group<m_x86_Features_Group>;
def m3dnow : Flag<"-m3dnow">, Group<m_x86_Features_Group>;
def m64 : Flag<"-m64">, Group<m_Group>, Flags<[DriverOption]>;
def mabi_EQ : Joined<"-mabi=">, Group<m_Group>;
def march_EQ : Joined<"-march=">, Group<m_Group>;
+def maltivec : Flag<"-maltivec">, Alias<faltivec>;
def mcmodel_EQ : Joined<"-mcmodel=">, Group<m_Group>;
def mconstant_cfstrings : Flag<"-mconstant-cfstrings">, Group<clang_ignored_m_Group>;
def mcpu_EQ : Joined<"-mcpu=">, Group<m_Group>;
@@ -613,16 +780,21 @@ def mios_version_min_EQ : Joined<"-mios-version-min=">, Alias<miphoneos_version_
def mios_simulator_version_min_EQ : Joined<"-mios-simulator-version-min=">, Group<m_Group>;
def mkernel : Flag<"-mkernel">, Group<m_Group>;
def mlinker_version_EQ : Joined<"-mlinker-version=">, Flags<[NoForward]>;
-def mllvm : Separate<"-mllvm">;
+def mllvm : Separate<"-mllvm">, Flags<[CC1Option]>,
+ HelpText<"Additional arguments to forward to LLVM's option processing">;
def mmacosx_version_min_EQ : Joined<"-mmacosx-version-min=">, Group<m_Group>;
-def mms_bitfields : Flag<"-mms-bitfields">, Group<m_Group>;
-def mstackrealign : Flag<"-mstackrealign">, Group<m_Group>;
-def mstack_alignment : Joined<"-mstack-alignment=">, Group<m_Group>;
+def mms_bitfields : Flag<"-mms-bitfields">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard.">;
+def mstackrealign : Flag<"-mstackrealign">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"Force realign the stack at entry to every function.">;
+def mstack_alignment : Joined<"-mstack-alignment=">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"Set the stack alignment">;
def mmmx : Flag<"-mmmx">, Group<m_x86_Features_Group>;
def mno_3dnowa : Flag<"-mno-3dnowa">, Group<m_x86_Features_Group>;
def mno_3dnow : Flag<"-mno-3dnow">, Group<m_x86_Features_Group>;
def mno_constant_cfstrings : Flag<"-mno-constant-cfstrings">, Group<m_Group>;
-def mno_global_merge : Flag<"-mno-global-merge">, Group<m_Group>;
+def mno_global_merge : Flag<"-mno-global-merge">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"Disable merging of globals">;
def mno_mmx : Flag<"-mno-mmx">, Group<m_x86_Features_Group>;
def mno_pascal_strings : Flag<"-mno-pascal-strings">, Group<m_Group>;
def mno_red_zone : Flag<"-mno-red-zone">, Group<m_Group>;
@@ -641,25 +813,35 @@ def mno_ssse3 : Flag<"-mno-ssse3">, Group<m_x86_Features_Group>;
def mno_aes : Flag<"-mno-aes">, Group<m_x86_Features_Group>;
def mno_avx : Flag<"-mno-avx">, Group<m_x86_Features_Group>;
def mno_avx2 : Flag<"-mno-avx2">, Group<m_x86_Features_Group>;
+def mno_pclmul : Flag<"-mno-pclmul">, Group<m_x86_Features_Group>;
def mno_lzcnt : Flag<"-mno-lzcnt">, Group<m_x86_Features_Group>;
+def mno_rdrnd : Flag<"-mno-rdrnd">, Group<m_x86_Features_Group>;
def mno_bmi : Flag<"-mno-bmi">, Group<m_x86_Features_Group>;
def mno_bmi2 : Flag<"-mno-bmi2">, Group<m_x86_Features_Group>;
def mno_popcnt : Flag<"-mno-popcnt">, Group<m_x86_Features_Group>;
def mno_fma4 : Flag<"-mno-fma4">, Group<m_x86_Features_Group>;
+def mno_fma : Flag<"-mno-fma">, Group<m_x86_Features_Group>;
+def mno_xop : Flag<"-mno-xop">, Group<m_x86_Features_Group>;
def mno_thumb : Flag<"-mno-thumb">, Group<m_Group>;
def marm : Flag<"-marm">, Alias<mno_thumb>;
def mno_warn_nonportable_cfstrings : Flag<"-mno-warn-nonportable-cfstrings">, Group<m_Group>;
def mno_omit_leaf_frame_pointer : Flag<"-mno-omit-leaf-frame-pointer">, Group<f_Group>;
-def momit_leaf_frame_pointer : Flag<"-momit-leaf-frame-pointer">, Group<f_Group>;
+def momit_leaf_frame_pointer : Flag<"-momit-leaf-frame-pointer">, Group<f_Group>,
+ HelpText<"Omit frame pointer setup for leaf functions.">, Flags<[CC1Option]>;
def mpascal_strings : Flag<"-mpascal-strings">, Group<m_Group>;
def mred_zone : Flag<"-mred-zone">, Group<m_Group>;
def mregparm_EQ : Joined<"-mregparm=">, Group<m_Group>;
-def mrelax_all : Flag<"-mrelax-all">, Group<m_Group>;
-def mrtd: Flag<"-mrtd">, Group<m_Group>;
+def mrelax_all : Flag<"-mrelax-all">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"(integrated-as) Relax all machine instructions">;
+def mrtd : Flag<"-mrtd">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"Make StdCall calling convention the default">;
def msmall_data_threshold_EQ : Joined <"-msmall-data-threshold=">, Group<m_Group>;
-def msoft_float : Flag<"-msoft-float">, Group<m_Group>;
+def msoft_float : Flag<"-msoft-float">, Group<m_Group>, Flags<[CC1Option]>,
+ HelpText<"Use software floating point">;
+def mno_implicit_float : Flag<"-mno-implicit-float">, Group<m_Group>,
+ HelpText<"Don't generate implicit floating point instructions">;
def msse2 : Flag<"-msse2">, Group<m_x86_Features_Group>;
def msse3 : Flag<"-msse3">, Group<m_x86_Features_Group>;
def msse4a : Flag<"-msse4a">, Group<m_x86_Features_Group>;
@@ -671,11 +853,21 @@ def mssse3 : Flag<"-mssse3">, Group<m_x86_Features_Group>;
def maes : Flag<"-maes">, Group<m_x86_Features_Group>;
def mavx : Flag<"-mavx">, Group<m_x86_Features_Group>;
def mavx2 : Flag<"-mavx2">, Group<m_x86_Features_Group>;
+def mpclmul : Flag<"-mpclmul">, Group<m_x86_Features_Group>;
def mlzcnt : Flag<"-mlzcnt">, Group<m_x86_Features_Group>;
+def mrdrnd : Flag<"-mrdrnd">, Group<m_x86_Features_Group>;
def mbmi : Flag<"-mbmi">, Group<m_x86_Features_Group>;
def mbmi2 : Flag<"-mbmi2">, Group<m_x86_Features_Group>;
def mpopcnt : Flag<"-mpopcnt">, Group<m_x86_Features_Group>;
def mfma4 : Flag<"-mfma4">, Group<m_x86_Features_Group>;
+def mfma : Flag<"-mfma">, Group<m_x86_Features_Group>;
+def mxop : Flag<"-mxop">, Group<m_x86_Features_Group>;
+def mips16 : Flag<"-mips16">, Group<m_Group>;
+def mno_mips16 : Flag<"-mno-mips16">, Group<m_Group>;
+def mdsp : Flag<"-mdsp">, Group<m_Group>;
+def mno_dsp : Flag<"-mno-dsp">, Group<m_Group>;
+def mdspr2 : Flag<"-mdspr2">, Group<m_Group>;
+def mno_dspr2 : Flag<"-mno-dspr2">, Group<m_Group>;
def mthumb : Flag<"-mthumb">, Group<m_Group>;
def mtune_EQ : Joined<"-mtune=">, Group<m_Group>;
def multi__module : Flag<"-multi_module">;
@@ -689,8 +881,10 @@ def no_canonical_prefixes : Flag<"-no-canonical-prefixes">, Flags<[HelpHidden]>,
def no_cpp_precomp : Flag<"-no-cpp-precomp">, Group<clang_ignored_f_Group>;
def no_integrated_as : Flag<"-no-integrated-as">, Flags<[DriverOption]>;
def no_integrated_cpp : Flag<"-no-integrated-cpp">, Flags<[DriverOption]>;
+def no_pedantic : Flag<"-no-pedantic">, Group<pedantic_Group>;
def no__dead__strip__inits__and__terms : Flag<"-no_dead_strip_inits_and_terms">;
-def nobuiltininc : Flag<"-nobuiltininc">;
+def nobuiltininc : Flag<"-nobuiltininc">, Flags<[CC1Option]>,
+ HelpText<"Disable builtin #include directories">;
def nodefaultlibs : Flag<"-nodefaultlibs">;
def nofixprebinding : Flag<"-nofixprebinding">;
def nolibc : Flag<"-nolibc">;
@@ -700,16 +894,17 @@ def noseglinkedit : Flag<"-noseglinkedit">;
def nostartfiles : Flag<"-nostartfiles">;
def nostdinc : Flag<"-nostdinc">;
def nostdlibinc : Flag<"-nostdlibinc">;
-def nostdincxx : Flag<"-nostdinc++">;
+def nostdincxx : Flag<"-nostdinc++">, Flags<[CC1Option]>,
+ HelpText<"Disable standard #include directories for the C++ standard library">;
def nostdlib : Flag<"-nostdlib">;
def object : Flag<"-object">;
-def o : JoinedOrSeparate<"-o">, Flags<[DriverOption, RenderAsInput]>,
+def o : JoinedOrSeparate<"-o">, Flags<[DriverOption, RenderAsInput, CC1Option]>,
HelpText<"Write output to <file>">, MetaVarName<"<file>">;
def pagezero__size : JoinedOrSeparate<"-pagezero_size">;
def pass_exit_codes : Flag<"-pass-exit-codes">, Flags<[Unsupported]>;
-def pedantic_errors : Flag<"-pedantic-errors">, Group<pedantic_Group>;
-def pedantic : Flag<"-pedantic">, Group<pedantic_Group>;
-def pg : Flag<"-pg">;
+def pedantic_errors : Flag<"-pedantic-errors">, Group<pedantic_Group>, Flags<[CC1Option]>;
+def pedantic : Flag<"-pedantic">, Group<pedantic_Group>, Flags<[CC1Option]>;
+def pg : Flag<"-pg">, HelpText<"Enable mcount instrumentation">, Flags<[CC1Option]>;
def pipe : Flag<"-pipe">,
HelpText<"Use pipes between commands, when possible">;
def prebind__all__twolevel__modules : Flag<"-prebind_all_twolevel_modules">;
@@ -717,7 +912,8 @@ def prebind : Flag<"-prebind">;
def preload : Flag<"-preload">;
def print_file_name_EQ : Joined<"-print-file-name=">,
HelpText<"Print the full library path of <file>">, MetaVarName<"<file>">;
-def print_ivar_layout : Flag<"-print-ivar-layout">;
+def print_ivar_layout : Flag<"-print-ivar-layout">, Flags<[CC1Option]>,
+ HelpText<"Enable Objective-C Ivar layout bitmap print trace">;
def print_libgcc_file_name : Flag<"-print-libgcc-file-name">,
HelpText<"Print the library path for \"libgcc.a\"">;
def print_multi_directory : Flag<"-print-multi-directory">;
@@ -729,13 +925,14 @@ def print_search_dirs : Flag<"-print-search-dirs">,
HelpText<"Print the paths used for finding libraries and programs">;
def private__bundle : Flag<"-private_bundle">;
def pthreads : Flag<"-pthreads">;
-def pthread : Flag<"-pthread">;
+def pthread : Flag<"-pthread">, Flags<[CC1Option]>,
+ HelpText<"Support POSIX threads in generated code">;
def p : Flag<"-p">;
def pie : Flag<"-pie">;
def read__only__relocs : Separate<"-read_only_relocs">;
def remap : Flag<"-remap">;
-def rewrite_objc : Flag<"-rewrite-objc">, Flags<[DriverOption]>,
- HelpText<"Rewrite Objective-C source to C++">;
+def rewrite_objc : Flag<"-rewrite-objc">, Flags<[DriverOption,CC1Option]>,
+ HelpText<"Rewrite Objective-C source to C++">, Group<Action_Group>;
def rewrite_legacy_objc : Flag<"-rewrite-legacy-objc">, Flags<[DriverOption]>,
HelpText<"Rewrite Legacy Objective-C source to C++">;
def rdynamic : Flag<"-rdynamic">;
@@ -767,8 +964,10 @@ def static_libgcc : Flag<"-static-libgcc">;
def static_libstdcxx : Flag<"-static-libstdc++">;
def static : Flag<"-static">, Flags<[NoArgumentUnused]>;
def std_default_EQ : Joined<"-std-default=">;
-def std_EQ : Joined<"-std=">, Group<L_Group>;
-def stdlib_EQ : Joined<"-stdlib=">;
+def std_EQ : Joined<"-std=">, Flags<[CC1Option]>, Group<L_Group>,
+ HelpText<"Language standard to compile for">;
+def stdlib_EQ : Joined<"-stdlib=">, Flags<[CC1Option]>,
+ HelpText<"C++ standard library to use">;
def sub__library : JoinedOrSeparate<"-sub_library">;
def sub__umbrella : JoinedOrSeparate<"-sub_umbrella">;
def s : Flag<"-s">;
@@ -780,21 +979,24 @@ def gcc_toolchain : Separate<"-gcc-toolchain">, Flags<[DriverOption]>,
def ccc_host_triple : Separate<"-ccc-host-triple">, Alias<target>;
def time : Flag<"-time">,
HelpText<"Time individual commands">;
-def traditional_cpp : Flag<"-traditional-cpp">;
+def traditional_cpp : Flag<"-traditional-cpp">, Flags<[CC1Option]>,
+ HelpText<"Enable some traditional CPP emulation">;
def traditional : Flag<"-traditional">;
-def trigraphs : Flag<"-trigraphs">;
+def trigraphs : Flag<"-trigraphs">, Flags<[CC1Option]>,
+ HelpText<"Process trigraph sequences">;
def twolevel__namespace__hints : Flag<"-twolevel_namespace_hints">;
def twolevel__namespace : Flag<"-twolevel_namespace">;
def t : Flag<"-t">;
def umbrella : Separate<"-umbrella">;
def undefined : JoinedOrSeparate<"-undefined">, Group<u_Group>;
-def undef : Flag<"-undef">, Group<u_Group>;
+def undef : Flag<"-undef">, Group<u_Group>, Flags<[CC1Option]>,
+ HelpText<"undef all system defines">;
def unexported__symbols__list : Separate<"-unexported_symbols_list">;
def u : JoinedOrSeparate<"-u">, Group<u_Group>;
def use_gold_plugin : Flag<"-use-gold-plugin">;
-def v : Flag<"-v">,
+def v : Flag<"-v">, Flags<[CC1Option]>,
HelpText<"Show commands to run and use verbose output">;
-def verify : Flag<"-verify">, Flags<[DriverOption]>,
+def verify : Flag<"-verify">, Flags<[DriverOption,CC1Option]>,
HelpText<"Verify output using a verifier.">;
def weak_l : Joined<"-weak-l">, Flags<[LinkerInput]>;
def weak__framework : Separate<"-weak_framework">, Flags<[LinkerInput]>;
@@ -802,15 +1004,15 @@ def weak__library : Separate<"-weak_library">, Flags<[LinkerInput]>;
def weak__reference__mismatches : Separate<"-weak_reference_mismatches">;
def whatsloaded : Flag<"-whatsloaded">;
def whyload : Flag<"-whyload">;
-def w : Flag<"-w">;
-def x : JoinedOrSeparate<"-x">, Flags<[DriverOption]>,
+def w : Flag<"-w">, HelpText<"Suppress all warnings.">, Flags<[CC1Option]>;
+def x : JoinedOrSeparate<"-x">, Flags<[DriverOption,CC1Option]>,
HelpText<"Treat subsequent input files as having type <language>">,
MetaVarName<"<language>">;
def y : Joined<"-y">;
-def working_directory : Separate<"-working-directory">,
+def working_directory : JoinedOrSeparate<"-working-directory">, Flags<[CC1Option]>,
HelpText<"Resolve file paths relative to the specified directory">;
-def working_directory_EQ : Joined<"-working-directory=">,
+def working_directory_EQ : Joined<"-working-directory=">, Flags<[CC1Option]>,
Alias<working_directory>;
// Double dash options, which are usually an alias for one of the previous
@@ -854,8 +1056,7 @@ def _for_linker : Separate<"--for-linker">, Alias<Xlinker>;
def _force_link_EQ : Joined<"--force-link=">, Alias<u>;
def _force_link : Separate<"--force-link">, Alias<u>;
def _help_hidden : Flag<"--help-hidden">;
-def _help : Flag<"--help">,
- HelpText<"Display available options">;
+def _help : Flag<"--help">, Alias<help>;
def _imacros_EQ : Joined<"--imacros=">, Alias<imacros>;
def _imacros : Separate<"--imacros">, Alias<imacros>;
def _include_barrier : Flag<"--include-barrier">, Alias<I_>;
@@ -883,6 +1084,7 @@ def _machine_EQ : Joined<"--machine=">, Alias<m_Joined>;
def _machine : Separate<"--machine">, Alias<m_Joined>;
def _no_integrated_cpp : Flag<"--no-integrated-cpp">, Alias<no_integrated_cpp>;
def _no_line_commands : Flag<"--no-line-commands">, Alias<P>;
+def _no_pedantic : Flag<"--no-pedantic">, Alias<no_pedantic>;
def _no_standard_includes : Flag<"--no-standard-includes">, Alias<nostdinc>;
def _no_standard_libraries : Flag<"--no-standard-libraries">, Alias<nostdlib>;
def _no_undefined : Flag<"--no-undefined">, Flags<[LinkerInput]>;
@@ -945,12 +1147,14 @@ def _undefine_macro : Separate<"--undefine-macro">, Alias<U>;
def _unsigned_char : Flag<"--unsigned-char">, Alias<funsigned_char>;
def _user_dependencies : Flag<"--user-dependencies">, Alias<MM>;
def _verbose : Flag<"--verbose">, Alias<v>;
-def _version : Flag<"--version">;
+def _version : Flag<"--version">, Flags<[CC1Option]>;
def _warn__EQ : Joined<"--warn-=">, Alias<W_Joined>;
def _warn_ : Joined<"--warn-">, Alias<W_Joined>;
def _write_dependencies : Flag<"--write-dependencies">, Alias<MD>;
def _write_user_dependencies : Flag<"--write-user-dependencies">, Alias<MMD>;
def _ : Joined<"--">, Flags<[Unsupported]>;
+def mieee_rnd_near : Flag<"-mieee-rnd-near">, Group<m_hexagon_Features_Group>;
+def serialize_diags : Separate<"-serialize-diagnostics">, Alias<_serialize_diags>;
// Special internal option to handle -Xlinker --no-demangle.
def Z_Xlinker__no_demangle : Flag<"-Z-Xlinker-no-demangle">,
@@ -965,3 +1169,5 @@ def Z_reserved_lib_stdcxx : Flag<"-Z-reserved-lib-stdc++">,
Flags<[LinkerInput, NoArgumentUnused, Unsupported]>, Group<reserved_lib_Group>;
def Z_reserved_lib_cckext : Flag<"-Z-reserved-lib-cckext">,
Flags<[LinkerInput, NoArgumentUnused, Unsupported]>, Group<reserved_lib_Group>;
+
+include "CC1Options.td"
diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h
index c35cf67..ab417bb 100644
--- a/include/clang/Driver/ToolChain.h
+++ b/include/clang/Driver/ToolChain.h
@@ -18,6 +18,8 @@
#include <string>
namespace clang {
+ class ObjCRuntime;
+
namespace driver {
class ArgList;
class Compilation;
@@ -25,7 +27,6 @@ namespace driver {
class Driver;
class InputArgList;
class JobAction;
- class ObjCRuntime;
class Tool;
/// ToolChain - Access to tools for a single platform.
@@ -137,6 +138,9 @@ public:
/// default.
virtual bool IsStrictAliasingDefault() const { return true; }
+ /// IsMathErrnoDefault - Does this tool chain use -fmath-errno by default.
+ virtual bool IsMathErrnoDefault() const { return true; }
+
/// IsObjCDefaultSynthPropertiesDefault - Does this tool chain enable
/// -fobjc-default-synthesize-properties by default.
virtual bool IsObjCDefaultSynthPropertiesDefault() const { return false; }
@@ -145,11 +149,6 @@ public:
/// -fobjc-nonfragile-abi by default.
virtual bool IsObjCNonFragileABIDefault() const { return false; }
- /// IsObjCLegacyDispatchDefault - Does this tool chain set
- /// -fobjc-legacy-dispatch by default (this is only used with the non-fragile
- /// ABI).
- virtual bool IsObjCLegacyDispatchDefault() const { return true; }
-
/// UseObjCMixedDispatchDefault - When using non-legacy dispatch, should the
/// mixed dispatch method be used?
virtual bool UseObjCMixedDispatch() const { return false; }
@@ -207,11 +206,11 @@ public:
virtual std::string ComputeEffectiveClangTriple(const ArgList &Args,
types::ID InputType = types::TY_INVALID) const;
- /// configureObjCRuntime - Configure the known properties of the
- /// Objective-C runtime for this platform.
+ /// getDefaultObjCRuntime - Return the default Objective-C runtime
+ /// for this platform.
///
/// FIXME: this really belongs on some sort of DeploymentTarget abstraction
- virtual void configureObjCRuntime(ObjCRuntime &runtime) const;
+ virtual ObjCRuntime getDefaultObjCRuntime(bool isNonFragile) const;
/// hasBlocksRuntime - Given that the user is compiling with
/// -fblocks, does this tool chain guarantee the existence of a
@@ -227,6 +226,10 @@ public:
virtual void AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const;
+ // addClangTargetOptions - Add options that need to be passed to cc1 for
+ // this target.
+ virtual void addClangTargetOptions(ArgStringList &CC1Args) const;
+
// GetRuntimeLibType - Determine the runtime library type to use with the
// given compilation arguments.
virtual RuntimeLibType GetRuntimeLibType(const ArgList &Args) const;
diff --git a/include/clang/Driver/Types.def b/include/clang/Driver/Types.def
index b107dfb..318c55a 100644
--- a/include/clang/Driver/Types.def
+++ b/include/clang/Driver/Types.def
@@ -40,17 +40,17 @@
// C family source language (with and without preprocessing).
TYPE("cpp-output", PP_C, INVALID, "i", "u")
-TYPE("c", C, PP_C, 0, "u")
-TYPE("cl", CL, PP_C, 0, "u")
-TYPE("cuda", CUDA, PP_CXX, 0, "u")
+TYPE("c", C, PP_C, "c", "u")
+TYPE("cl", CL, PP_C, "cl", "u")
+TYPE("cuda", CUDA, PP_CXX, "cpp", "u")
TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", "u")
TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", "u")
-TYPE("objective-c", ObjC, PP_ObjC, 0, "u")
+TYPE("objective-c", ObjC, PP_ObjC, "m", "u")
TYPE("c++-cpp-output", PP_CXX, INVALID, "ii", "u")
-TYPE("c++", CXX, PP_CXX, 0, "u")
+TYPE("c++", CXX, PP_CXX, "cpp", "u")
TYPE("objective-c++-cpp-output", PP_ObjCXX, INVALID, "mii", "u")
TYPE("objc++-cpp-output", PP_ObjCXX_Alias, INVALID, "mii", "u")
-TYPE("objective-c++", ObjCXX, PP_ObjCXX, 0, "u")
+TYPE("objective-c++", ObjCXX, PP_ObjCXX, "mm", "u")
// C family input files to precompile.
TYPE("c-header-cpp-output", PP_CHeader, INVALID, "i", "p")
diff --git a/include/clang/Driver/Types.h b/include/clang/Driver/Types.h
index 9187529..3dea471 100644
--- a/include/clang/Driver/Types.h
+++ b/include/clang/Driver/Types.h
@@ -23,7 +23,7 @@ namespace types {
TY_LAST
};
- /// getTypeName - Return the name of the type for \arg Id.
+ /// getTypeName - Return the name of the type for \p Id.
const char *getTypeName(ID Id);
/// getPreprocessedType - Get the ID of the type for this input when
@@ -70,7 +70,7 @@ namespace types {
bool isObjC(ID Id);
/// lookupTypeForExtension - Lookup the type to use for the file
- /// extension \arg Ext.
+ /// extension \p Ext.
ID lookupTypeForExtension(const char *Ext);
/// lookupTypeForTypSpecifier - Lookup the type to use for a user
@@ -81,7 +81,7 @@ namespace types {
/// to be done for this type.
unsigned getNumCompilationPhases(ID Id);
- /// getCompilationPhase - Return the \args N th compilation phase to
+ /// getCompilationPhase - Return the \p N th compilation phase to
/// be done for this type.
phases::ID getCompilationPhase(ID Id, unsigned N);
diff --git a/include/clang/Frontend/ASTConsumers.h b/include/clang/Frontend/ASTConsumers.h
index cef9509..3731478 100644
--- a/include/clang/Frontend/ASTConsumers.h
+++ b/include/clang/Frontend/ASTConsumers.h
@@ -33,11 +33,15 @@ class TargetOptions;
// original C code. The output is intended to be in a format such that
// clang could re-parse the output back into the same AST, but the
// implementation is still incomplete.
-ASTConsumer *CreateASTPrinter(raw_ostream *OS);
+ASTConsumer *CreateASTPrinter(raw_ostream *OS, StringRef FilterString);
// AST dumper: dumps the raw AST in human-readable form to stderr; this is
// intended for debugging.
-ASTConsumer *CreateASTDumper();
+ASTConsumer *CreateASTDumper(StringRef FilterString);
+
+// AST Decl node lister: prints qualified names of all filterable AST Decl
+// nodes.
+ASTConsumer *CreateASTDeclNodeLister();
// AST XML-dumper: dumps out the AST to stderr in a very detailed XML
// format; this is intended for particularly intense debugging.
diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h
index 041eabb..144b796 100644
--- a/include/clang/Frontend/ASTUnit.h
+++ b/include/clang/Frontend/ASTUnit.h
@@ -19,6 +19,7 @@
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/PreprocessingRecord.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
@@ -248,7 +249,15 @@ private:
std::vector<serialization::DeclID> TopLevelDeclsInPreamble;
/// \brief Whether we should be caching code-completion results.
- bool ShouldCacheCodeCompletionResults;
+ bool ShouldCacheCodeCompletionResults : 1;
+
+ /// \brief Whether to include brief documentation within the set of code
+ /// completions cached.
+ bool IncludeBriefCommentsInCodeCompletion : 1;
+
+ /// \brief True if non-system source files should be treated as volatile
+ /// (likely to change while trying to use them).
+ bool UserFilesAreVolatile : 1;
/// \brief The language options used when we load an AST file.
LangOptions ASTFileLangOpts;
@@ -275,12 +284,11 @@ public:
/// \brief A bitmask that indicates which code-completion contexts should
/// contain this completion result.
///
- /// The bits in the bitmask correspond to the values of
- /// CodeCompleteContext::Kind. To map from a completion context kind to a
- /// bit, subtract one from the completion context kind and shift 1 by that
- /// number of bits. Many completions can occur in several different
- /// contexts.
- unsigned ShowInContexts;
+ /// The bits in the bitmask correspond to the values of
+ /// CodeCompleteContext::Kind. To map from a completion context kind to a
+ /// bit, shift 1 by that number of bits. Many completions can occur in
+ /// several different contexts.
+ uint64_t ShowInContexts;
/// \brief The priority given to this code-completion result.
unsigned Priority;
@@ -396,7 +404,9 @@ private:
/// just about any usage.
/// Becomes a noop in release mode; only useful for debug mode checking.
class ConcurrencyState {
+#ifndef NDEBUG
void *Mutex; // a llvm::sys::MutexImpl in debug;
+#endif
public:
ConcurrencyState();
@@ -612,7 +622,8 @@ public:
/// \brief Create a ASTUnit. Gets ownership of the passed CompilerInvocation.
static ASTUnit *create(CompilerInvocation *CI,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
- bool CaptureDiagnostics = false);
+ bool CaptureDiagnostics,
+ bool UserFilesAreVolatile);
/// \brief Create a ASTUnit from an AST file.
///
@@ -629,7 +640,8 @@ public:
RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0,
bool CaptureDiagnostics = false,
- bool AllowPCHWithCompilerErrors = false);
+ bool AllowPCHWithCompilerErrors = false,
+ bool UserFilesAreVolatile = false);
private:
/// \brief Helper function for \c LoadFromCompilerInvocation() and
@@ -679,6 +691,8 @@ public:
bool CaptureDiagnostics = false,
bool PrecompilePreamble = false,
bool CacheCodeCompletionResults = false,
+ bool IncludeBriefCommentsInCodeCompletion = false,
+ bool UserFilesAreVolatile = false,
OwningPtr<ASTUnit> *ErrAST = 0);
/// LoadFromCompilerInvocation - Create an ASTUnit from a source file, via a
@@ -698,7 +712,9 @@ public:
bool CaptureDiagnostics = false,
bool PrecompilePreamble = false,
TranslationUnitKind TUKind = TU_Complete,
- bool CacheCodeCompletionResults = false);
+ bool CacheCodeCompletionResults = false,
+ bool IncludeBriefCommentsInCodeCompletion = false,
+ bool UserFilesAreVolatile = false);
/// LoadFromCommandLine - Create an ASTUnit from a vector of command line
/// arguments, which must specify exactly one source file.
@@ -730,8 +746,10 @@ public:
bool PrecompilePreamble = false,
TranslationUnitKind TUKind = TU_Complete,
bool CacheCodeCompletionResults = false,
+ bool IncludeBriefCommentsInCodeCompletion = false,
bool AllowPCHWithCompilerErrors = false,
bool SkipFunctionBodies = false,
+ bool UserFilesAreVolatile = false,
OwningPtr<ASTUnit> *ErrAST = 0);
/// \brief Reparse the source files using the same command-line options that
@@ -757,11 +775,15 @@ public:
/// \param IncludeCodePatterns Whether to include code patterns (such as a
/// for loop) in the code-completion results.
///
+ /// \param IncludeBriefComments Whether to include brief documentation within
+ /// the set of code completions returned.
+ ///
/// FIXME: The Diag, LangOpts, SourceMgr, FileMgr, StoredDiagnostics, and
/// OwnedBuffers parameters are all disgusting hacks. They will go away.
void CodeComplete(StringRef File, unsigned Line, unsigned Column,
RemappedFile *RemappedFiles, unsigned NumRemappedFiles,
bool IncludeMacros, bool IncludeCodePatterns,
+ bool IncludeBriefComments,
CodeCompleteConsumer &Consumer,
DiagnosticsEngine &Diag, LangOptions &LangOpts,
SourceManager &SourceMgr, FileManager &FileMgr,
diff --git a/include/clang/Frontend/Analyses.def b/include/clang/Frontend/Analyses.def
index b5b9394..29ddc9e 100644
--- a/include/clang/Frontend/Analyses.def
+++ b/include/clang/Frontend/Analyses.def
@@ -47,7 +47,9 @@ ANALYSIS_PURGE(PurgeNone, "none", "Do not purge symbols, bindings, or constrain
#endif
ANALYSIS_IPA(None, "none", "Perform only intra-procedural analysis")
-ANALYSIS_IPA(Inlining, "inlining", "Experimental: Inline callees when their definitions are available")
+ANALYSIS_IPA(Inlining, "inlining", "Inline callees when their definitions are available")
+ANALYSIS_IPA(DynamicDispatch, "dynamic", "Experimental: Enable inlining of dynamically dispatched methods")
+ANALYSIS_IPA(DynamicDispatchBifurcate, "dynamic-bifurcate", "Experimental: Enable inlining of dynamically dispatched methods, bifurcate paths when exact type info is unavailable")
#ifndef ANALYSIS_INLINING_MODE
#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC)
diff --git a/include/clang/Frontend/AnalyzerOptions.h b/include/clang/Frontend/AnalyzerOptions.h
index 847bfbd..4e489fe 100644
--- a/include/clang/Frontend/AnalyzerOptions.h
+++ b/include/clang/Frontend/AnalyzerOptions.h
@@ -96,7 +96,6 @@ public:
unsigned VisualizeEGUbi : 1;
unsigned UnoptimizedCFG : 1;
unsigned CFGAddImplicitDtors : 1;
- unsigned CFGAddInitializers : 1;
unsigned EagerlyTrimEGraph : 1;
unsigned PrintStats : 1;
unsigned NoRetryExhausted : 1;
@@ -121,7 +120,6 @@ public:
VisualizeEGUbi = 0;
UnoptimizedCFG = 0;
CFGAddImplicitDtors = 0;
- CFGAddInitializers = 0;
EagerlyTrimEGraph = 0;
PrintStats = 0;
NoRetryExhausted = 0;
diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h
index e844f88..3e34093 100644
--- a/include/clang/Frontend/CodeGenOptions.h
+++ b/include/clang/Frontend/CodeGenOptions.h
@@ -35,84 +35,101 @@ public:
Mixed = 2
};
- unsigned AsmVerbose : 1; /// -dA, -fverbose-asm.
- unsigned ObjCAutoRefCountExceptions : 1; /// Whether ARC should be EH-safe.
- unsigned CUDAIsDevice : 1; /// Set when compiling for CUDA device.
- unsigned CXAAtExit : 1; /// Use __cxa_atexit for calling destructors.
- unsigned CXXCtorDtorAliases: 1; /// Emit complete ctors/dtors as linker
- /// aliases to base ctors when possible.
- unsigned DataSections : 1; /// Set when -fdata-sections is enabled
- unsigned DebugInfo : 1; /// Should generate debug info (-g).
- unsigned LimitDebugInfo : 1; /// Limit generated debug info to reduce size.
- unsigned DisableFPElim : 1; /// Set when -fomit-frame-pointer is enabled.
- unsigned DisableLLVMOpts : 1; /// Don't run any optimizations, for use in
- /// getting .bc files that correspond to the
- /// internal state before optimizations are
- /// done.
- unsigned DisableRedZone : 1; /// Set when -mno-red-zone is enabled.
- unsigned DisableTailCalls : 1; /// Do not emit tail calls.
- unsigned EmitDeclMetadata : 1; /// Emit special metadata indicating what
- /// Decl* various IR entities came from. Only
- /// useful when running CodeGen as a
- /// subroutine.
- unsigned EmitGcovArcs : 1; /// Emit coverage data files, aka. GCDA.
- unsigned EmitGcovNotes : 1; /// Emit coverage "notes" files, aka GCNO.
- unsigned ForbidGuardVariables : 1; /// Issue errors if C++ guard variables
- /// are required
- unsigned FunctionSections : 1; /// Set when -ffunction-sections is enabled
- unsigned HiddenWeakTemplateVTables : 1; /// Emit weak vtables and RTTI for
- /// template classes with hidden visibility
- unsigned HiddenWeakVTables : 1; /// Emit weak vtables, RTTI, and thunks with
- /// hidden visibility.
- unsigned InstrumentFunctions : 1; /// Set when -finstrument-functions is
- /// enabled.
- unsigned InstrumentForProfiling : 1; /// Set when -pg is enabled
- unsigned LessPreciseFPMAD : 1; /// Enable less precise MAD instructions to be
- /// generated.
- unsigned MergeAllConstants : 1; /// Merge identical constants.
- unsigned NoCommon : 1; /// Set when -fno-common or C++ is enabled.
- unsigned NoDwarf2CFIAsm : 1; /// Set when -fno-dwarf2-cfi-asm is enabled.
- unsigned NoDwarfDirectoryAsm : 1; /// Set when -fno-dwarf-directory-asm is
- /// enabled.
- unsigned NoExecStack : 1; /// Set when -Wa,--noexecstack is enabled.
- unsigned NoGlobalMerge : 1; /// Set when -mno-global-merge is enabled.
- unsigned NoImplicitFloat : 1; /// Set when -mno-implicit-float is enabled.
- unsigned NoInfsFPMath : 1; /// Assume FP arguments, results not +-Inf.
- unsigned NoInline : 1; /// Set when -fno-inline is enabled. Disables
- /// use of the inline keyword.
- unsigned NoNaNsFPMath : 1; /// Assume FP arguments, results not NaN.
- unsigned NoZeroInitializedInBSS : 1; /// -fno-zero-initialized-in-bss
- unsigned ObjCDispatchMethod : 2; /// Method of Objective-C dispatch to use.
- unsigned ObjCRuntimeHasARC : 1; /// The target runtime supports ARC natively
- unsigned ObjCRuntimeHasTerminate : 1; /// The ObjC runtime has objc_terminate
- unsigned OmitLeafFramePointer : 1; /// Set when -momit-leaf-frame-pointer is
- /// enabled.
- unsigned OptimizationLevel : 3; /// The -O[0-4] option specified.
- unsigned OptimizeSize : 2; /// If -Os (==1) or -Oz (==2) is specified.
- unsigned RelaxAll : 1; /// Relax all machine code instructions.
- unsigned RelaxedAliasing : 1; /// Set when -fno-strict-aliasing is enabled.
- unsigned SaveTempLabels : 1; /// Save temporary labels.
- unsigned SimplifyLibCalls : 1; /// Set when -fbuiltin is enabled.
- unsigned SoftFloat : 1; /// -soft-float.
- unsigned StrictEnums : 1; /// Optimize based on strict enum definition.
- unsigned TimePasses : 1; /// Set when -ftime-report is enabled.
- unsigned UnitAtATime : 1; /// Unused. For mirroring GCC optimization
- /// selection.
- unsigned UnrollLoops : 1; /// Control whether loops are unrolled.
- unsigned UnsafeFPMath : 1; /// Allow unsafe floating point optzns.
- unsigned UnwindTables : 1; /// Emit unwind tables.
+ enum DebugInfoKind {
+ NoDebugInfo, // Don't generate debug info.
+ DebugLineTablesOnly, // Emit only debug info necessary for generating
+ // line number tables (-gline-tables-only).
+ LimitedDebugInfo, // Limit generated debug info to reduce size
+ // (-flimit-debug-info).
+ FullDebugInfo // Generate complete debug info.
+ };
+
+ enum TLSModel {
+ GeneralDynamicTLSModel,
+ LocalDynamicTLSModel,
+ InitialExecTLSModel,
+ LocalExecTLSModel
+ };
+
+ unsigned AsmVerbose : 1; ///< -dA, -fverbose-asm.
+ unsigned ObjCAutoRefCountExceptions : 1; ///< Whether ARC should be EH-safe.
+ unsigned CUDAIsDevice : 1; ///< Set when compiling for CUDA device.
+ unsigned CXAAtExit : 1; ///< Use __cxa_atexit for calling destructors.
+ unsigned CXXCtorDtorAliases: 1; ///< Emit complete ctors/dtors as linker
+ ///< aliases to base ctors when possible.
+ unsigned DataSections : 1; ///< Set when -fdata-sections is enabled.
+ unsigned DisableFPElim : 1; ///< Set when -fomit-frame-pointer is enabled.
+ unsigned DisableLLVMOpts : 1; ///< Don't run any optimizations, for use in
+ ///< getting .bc files that correspond to the
+ ///< internal state before optimizations are
+ ///< done.
+ unsigned DisableRedZone : 1; ///< Set when -mno-red-zone is enabled.
+ unsigned DisableTailCalls : 1; ///< Do not emit tail calls.
+ unsigned EmitDeclMetadata : 1; ///< Emit special metadata indicating what
+ ///< Decl* various IR entities came from. Only
+ ///< useful when running CodeGen as a
+ ///< subroutine.
+ unsigned EmitGcovArcs : 1; ///< Emit coverage data files, aka. GCDA.
+ unsigned EmitGcovNotes : 1; ///< Emit coverage "notes" files, aka GCNO.
+ unsigned EmitOpenCLArgMetadata : 1; ///< Emit OpenCL kernel arg metadata.
+ unsigned EmitMicrosoftInlineAsm : 1; ///< Enable emission of MS-style inline
+ ///< assembly.
+ unsigned ForbidGuardVariables : 1; ///< Issue errors if C++ guard variables
+ ///< are required.
+ unsigned FunctionSections : 1; ///< Set when -ffunction-sections is enabled.
+ unsigned HiddenWeakTemplateVTables : 1; ///< Emit weak vtables and RTTI for
+ ///< template classes with hidden visibility
+ unsigned HiddenWeakVTables : 1; ///< Emit weak vtables, RTTI, and thunks with
+ ///< hidden visibility.
+ unsigned InstrumentFunctions : 1; ///< Set when -finstrument-functions is
+ ///< enabled.
+ unsigned InstrumentForProfiling : 1; ///< Set when -pg is enabled.
+ unsigned LessPreciseFPMAD : 1; ///< Enable less precise MAD instructions to
+ ///< be generated.
+ unsigned MergeAllConstants : 1; ///< Merge identical constants.
+ unsigned NoCommon : 1; ///< Set when -fno-common or C++ is enabled.
+ unsigned NoDwarf2CFIAsm : 1; ///< Set when -fno-dwarf2-cfi-asm is enabled.
+ unsigned NoDwarfDirectoryAsm : 1; ///< Set when -fno-dwarf-directory-asm is
+ ///< enabled.
+ unsigned NoExecStack : 1; ///< Set when -Wa,--noexecstack is enabled.
+ unsigned NoGlobalMerge : 1; ///< Set when -mno-global-merge is enabled.
+ unsigned NoImplicitFloat : 1; ///< Set when -mno-implicit-float is enabled.
+ unsigned NoInfsFPMath : 1; ///< Assume FP arguments, results not +-Inf.
+ unsigned NoInline : 1; ///< Set when -fno-inline is enabled. Disables
+ ///< use of the inline keyword.
+ unsigned NoNaNsFPMath : 1; ///< Assume FP arguments, results not NaN.
+ unsigned NoZeroInitializedInBSS : 1; ///< -fno-zero-initialized-in-bss.
+ unsigned ObjCDispatchMethod : 2; ///< Method of Objective-C dispatch to use.
+ unsigned OmitLeafFramePointer : 1; ///< Set when -momit-leaf-frame-pointer is
+ ///< enabled.
+ unsigned OptimizationLevel : 3; ///< The -O[0-4] option specified.
+ unsigned OptimizeSize : 2; ///< If -Os (==1) or -Oz (==2) is specified.
+ unsigned RelaxAll : 1; ///< Relax all machine code instructions.
+ unsigned RelaxedAliasing : 1; ///< Set when -fno-strict-aliasing is enabled.
+ unsigned SaveTempLabels : 1; ///< Save temporary labels.
+ unsigned SimplifyLibCalls : 1; ///< Set when -fbuiltin is enabled.
+ unsigned SoftFloat : 1; ///< -soft-float.
+ unsigned StrictEnums : 1; ///< Optimize based on strict enum definition.
+ unsigned TimePasses : 1; ///< Set when -ftime-report is enabled.
+ unsigned UnitAtATime : 1; ///< Unused. For mirroring GCC optimization
+ ///< selection.
+ unsigned UnrollLoops : 1; ///< Control whether loops are unrolled.
+ unsigned UnsafeFPMath : 1; ///< Allow unsafe floating point optzns.
+ unsigned UnwindTables : 1; ///< Emit unwind tables.
/// Attempt to use register sized accesses to bit-fields in structures, when
/// possible.
unsigned UseRegisterSizedBitfieldAccess : 1;
- unsigned VerifyModule : 1; /// Control whether the module should be run
- /// through the LLVM Verifier.
+ unsigned VerifyModule : 1; ///< Control whether the module should be run
+ ///< through the LLVM Verifier.
- unsigned StackRealignment : 1; /// Control whether to permit stack
- /// realignment.
- unsigned StackAlignment; /// Overrides default stack alignment,
- /// if not 0.
+ unsigned StackRealignment : 1; ///< Control whether to permit stack
+ ///< realignment.
+ unsigned UseInitArray : 1; ///< Control whether to use .init_array or
+ ///< .ctors.
+ unsigned StackAlignment; ///< Overrides default stack alignment,
+ ///< if not 0.
/// The code model to use (-mcmodel).
std::string CodeModel;
@@ -127,6 +144,9 @@ public:
/// The string to embed in debug information as the current working directory.
std::string DebugCompilationDir;
+ /// The kind of generated debug info.
+ DebugInfoKind DebugInfo;
+
/// The string to embed in the debug information for the compile unit, if
/// non-empty.
std::string DwarfDebugFlags;
@@ -162,6 +182,12 @@ public:
/// or 0 if unspecified.
unsigned NumRegisterParameters;
+ /// The run-time penalty for bounds checking, or 0 to disable.
+ unsigned char BoundsChecking;
+
+ /// The default TLS model to use.
+ TLSModel DefaultTLSModel;
+
public:
CodeGenOptions() {
AsmVerbose = 0;
@@ -169,8 +195,6 @@ public:
CXAAtExit = 1;
CXXCtorDtorAliases = 0;
DataSections = 0;
- DebugInfo = 0;
- LimitDebugInfo = 0;
DisableFPElim = 0;
DisableLLVMOpts = 0;
DisableRedZone = 0;
@@ -178,6 +202,8 @@ public:
EmitDeclMetadata = 0;
EmitGcovArcs = 0;
EmitGcovNotes = 0;
+ EmitOpenCLArgMetadata = 0;
+ EmitMicrosoftInlineAsm = 0;
ForbidGuardVariables = 0;
FunctionSections = 0;
HiddenWeakTemplateVTables = 0;
@@ -196,8 +222,6 @@ public:
NumRegisterParameters = 0;
ObjCAutoRefCountExceptions = 0;
ObjCDispatchMethod = Legacy;
- ObjCRuntimeHasARC = 0;
- ObjCRuntimeHasTerminate = 0;
OmitLeafFramePointer = 0;
OptimizationLevel = 0;
OptimizeSize = 0;
@@ -216,9 +240,13 @@ public:
VerifyModule = 1;
StackRealignment = 0;
StackAlignment = 0;
+ BoundsChecking = 0;
+ UseInitArray = 0;
+ DebugInfo = NoDebugInfo;
Inlining = NoInlining;
RelocationModel = "pic";
+ DefaultTLSModel = GeneralDynamicTLSModel;
}
ObjCDispatchMethodKind getObjCDispatchMethod() const {
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
index 1bb7695..b28e103 100644
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -560,8 +560,7 @@ public:
static CodeCompleteConsumer *
createCodeCompletionConsumer(Preprocessor &PP, const std::string &Filename,
unsigned Line, unsigned Column,
- bool ShowMacros,
- bool ShowCodePatterns, bool ShowGlobals,
+ const CodeCompleteOptions &Opts,
raw_ostream &OS);
/// \brief Create the Sema object to be used for parsing.
diff --git a/include/clang/Frontend/CompilerInvocation.h b/include/clang/Frontend/CompilerInvocation.h
index 0d2260a..d6fe003 100644
--- a/include/clang/Frontend/CompilerInvocation.h
+++ b/include/clang/Frontend/CompilerInvocation.h
@@ -38,10 +38,13 @@ namespace driver {
class ArgList;
}
-/// CompilerInvocation - Fill out Opts based on the options given in Args.
+/// \brief Fill out Opts based on the options given in Args.
+///
/// Args must have been created from the OptTable returned by
-/// createCC1OptTable(). When errors are encountered, return false and,
-/// if Diags is non-null, report the error(s).
+/// createCC1OptTable().
+///
+/// When errors are encountered, return false and, if Diags is non-null,
+/// report the error(s).
bool ParseDiagnosticArgs(DiagnosticOptions &Opts, driver::ArgList &Args,
DiagnosticsEngine *Diags = 0);
@@ -58,8 +61,7 @@ public:
const LangOptions *getLangOpts() const { return LangOpts.getPtr(); }
};
-/// CompilerInvocation - Helper class for holding the data necessary to invoke
-/// the compiler.
+/// \brief Helper class for holding the data necessary to invoke the compiler.
///
/// This class is designed to represent an abstract "invocation" of the
/// compiler, including data such as the include paths, the code generation
@@ -85,10 +87,10 @@ class CompilerInvocation : public CompilerInvocationBase {
/// Options controlling the frontend itself.
FrontendOptions FrontendOpts;
- /// Options controlling the #include directive.
+ /// Options controlling the \#include directive.
HeaderSearchOptions HeaderSearchOpts;
- /// Options controlling the preprocessor (aside from #include handling).
+ /// Options controlling the preprocessor (aside from \#include handling).
PreprocessorOptions PreprocessorOpts;
/// Options controlling preprocessed output.
@@ -103,10 +105,10 @@ public:
/// @name Utility Methods
/// @{
- /// CreateFromArgs - Create a compiler invocation from a list of input
- /// options. Returns true on success.
+ /// \brief Create a compiler invocation from a list of input options.
+ /// \returns true on success.
///
- /// \param Res [out] - The resulting invocation.
+ /// \param [out] Res - The resulting invocation.
/// \param ArgBegin - The first element in the argument vector.
/// \param ArgEnd - The last element in the argument vector.
/// \param Diags - The diagnostic engine to use for errors.
@@ -115,7 +117,7 @@ public:
const char* const *ArgEnd,
DiagnosticsEngine &Diags);
- /// GetBuiltinIncludePath - Get the directory where the compiler headers
+ /// \brief Get the directory where the compiler headers
/// reside, relative to the compiler binary (found by the passed in
/// arguments).
///
@@ -125,24 +127,14 @@ public:
/// executable), for finding the builtin compiler path.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr);
- /// toArgs - Convert the CompilerInvocation to a list of strings suitable for
+ /// \brief Convert the CompilerInvocation to a list of strings suitable for
/// passing to CreateFromArgs.
- void toArgs(std::vector<std::string> &Res);
-
- /// setLangDefaults - Set language defaults for the given input language and
- /// language standard in this CompilerInvocation.
- ///
- /// \param IK - The input language.
- /// \param LangStd - The input language standard.
- void setLangDefaults(InputKind IK,
- LangStandard::Kind LangStd = LangStandard::lang_unspecified) {
- setLangDefaults(*getLangOpts(), IK, LangStd);
- }
+ void toArgs(std::vector<std::string> &Res) const;
- /// setLangDefaults - Set language defaults for the given input language and
+ /// \brief Set language defaults for the given input language and
/// language standard in the given LangOptions object.
///
- /// \param LangOpts - The LangOptions object to set up.
+ /// \param Opts - The LangOptions object to set up.
/// \param IK - The input language.
/// \param LangStd - The input language standard.
static void setLangDefaults(LangOptions &Opts, InputKind IK,
diff --git a/include/clang/Frontend/DiagnosticOptions.h b/include/clang/Frontend/DiagnosticOptions.h
index 1c6ba6a..8dec37c 100644
--- a/include/clang/Frontend/DiagnosticOptions.h
+++ b/include/clang/Frontend/DiagnosticOptions.h
@@ -47,6 +47,9 @@ public:
/// diagnostics, indicated by markers in the
/// input source file.
+ unsigned ElideType: 1; /// Elide identical types in template diffing
+ unsigned ShowTemplateTree: 1; /// Print a template tree when diffing
+
unsigned ErrorLimit; /// Limit # errors emitted.
unsigned MacroBacktraceLimit; /// Limit depth of macro expansion backtrace.
unsigned TemplateBacktraceLimit; /// Limit depth of instantiation backtrace.
diff --git a/include/clang/Frontend/DiagnosticRenderer.h b/include/clang/Frontend/DiagnosticRenderer.h
index 5ad88a8..09d7ecb 100644
--- a/include/clang/Frontend/DiagnosticRenderer.h
+++ b/include/clang/Frontend/DiagnosticRenderer.h
@@ -43,7 +43,6 @@ typedef llvm::PointerUnion<const Diagnostic *,
/// class.
class DiagnosticRenderer {
protected:
- const SourceManager &SM;
const LangOptions &LangOpts;
const DiagnosticOptions &DiagOpts;
@@ -66,8 +65,7 @@ protected:
/// which change the amount of information displayed.
DiagnosticsEngine::Level LastLevel;
- DiagnosticRenderer(const SourceManager &SM,
- const LangOptions &LangOpts,
+ DiagnosticRenderer(const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts);
virtual ~DiagnosticRenderer();
@@ -76,20 +74,24 @@ protected:
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag Info) = 0;
virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges) = 0;
+ ArrayRef<CharSourceRange> Ranges,
+ const SourceManager &SM) = 0;
virtual void emitBasicNote(StringRef Message) = 0;
virtual void emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints) = 0;
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) = 0;
- virtual void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc) = 0;
+ virtual void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,
+ const SourceManager &SM) = 0;
virtual void beginDiagnostic(DiagOrStoredDiag D,
DiagnosticsEngine::Level Level) {}
@@ -98,12 +100,14 @@ protected:
private:
- void emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level);
- void emitIncludeStackRecursively(SourceLocation Loc);
+ void emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level,
+ const SourceManager &SM);
+ void emitIncludeStackRecursively(SourceLocation Loc, const SourceManager &SM);
void emitMacroExpansionsAndCarets(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> Hints,
+ const SourceManager &SM,
unsigned &MacroDepth,
unsigned OnMacroInst = 0);
public:
@@ -119,9 +123,12 @@ public:
/// \param Message The diagnostic message to emit.
/// \param Ranges The underlined ranges for this code snippet.
/// \param FixItHints The FixIt hints active for this diagnostic.
+ /// \param SM The SourceManager; will be null if the diagnostic came from the
+ /// frontend, thus \param Loc will be invalid.
void emitDiagnostic(SourceLocation Loc, DiagnosticsEngine::Level Level,
StringRef Message, ArrayRef<CharSourceRange> Ranges,
ArrayRef<FixItHint> FixItHints,
+ const SourceManager *SM,
DiagOrStoredDiag D = (Diagnostic *)0);
void emitStoredDiagnostic(StoredDiagnostic &Diag);
@@ -131,19 +138,20 @@ public:
/// notes. It is up to subclasses to further define the behavior.
class DiagnosticNoteRenderer : public DiagnosticRenderer {
public:
- DiagnosticNoteRenderer(const SourceManager &SM,
- const LangOptions &LangOpts,
+ DiagnosticNoteRenderer(const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts)
- : DiagnosticRenderer(SM, LangOpts, DiagOpts) {}
+ : DiagnosticRenderer(LangOpts, DiagOpts) {}
virtual ~DiagnosticNoteRenderer();
virtual void emitBasicNote(StringRef Message);
virtual void emitIncludeLocation(SourceLocation Loc,
- PresumedLoc PLoc);
+ PresumedLoc PLoc,
+ const SourceManager &SM);
- virtual void emitNote(SourceLocation Loc, StringRef Message) = 0;
+ virtual void emitNote(SourceLocation Loc, StringRef Message,
+ const SourceManager *SM) = 0;
};
} // end clang namespace
#endif
diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h
index 6839028..c0056de 100644
--- a/include/clang/Frontend/FrontendAction.h
+++ b/include/clang/Frontend/FrontendAction.h
@@ -188,7 +188,7 @@ public:
bool BeginSourceFile(CompilerInstance &CI, const FrontendInputFile &Input);
/// Execute - Set the source managers main input file, and run the action.
- void Execute();
+ bool Execute();
/// EndSourceFile - Perform any per-file post processing, deallocate per-file
/// objects, and run statistics and output file cleanup code.
diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h
index 8f7fe87..477ac45 100644
--- a/include/clang/Frontend/FrontendActions.h
+++ b/include/clang/Frontend/FrontendActions.h
@@ -50,6 +50,12 @@ protected:
StringRef InFile);
};
+class ASTDeclListAction : public ASTFrontendAction {
+protected:
+ virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile);
+};
+
class ASTDumpXMLAction : public ASTFrontendAction {
protected:
virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
index 888388c..ce1cd9b 100644
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -11,6 +11,7 @@
#define LLVM_CLANG_FRONTEND_FRONTENDOPTIONS_H
#include "clang/Frontend/CommandLineSourceLoc.h"
+#include "clang/Sema/CodeCompleteOptions.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <vector>
@@ -19,6 +20,7 @@ namespace clang {
namespace frontend {
enum ActionKind {
+ ASTDeclList, ///< Parse ASTs and list Decl nodes.
ASTDump, ///< Parse ASTs and dump them.
ASTDumpXML, ///< Parse ASTs and dump them in XML.
ASTPrint, ///< Parse ASTs and print them.
@@ -42,7 +44,7 @@ namespace frontend {
PrintDeclContext, ///< Print DeclContext and their Decls.
PrintPreamble, ///< Print the "preamble" of the input file
PrintPreprocessedInput, ///< -E mode.
- RewriteMacros, ///< Expand macros but not #includes.
+ RewriteMacros, ///< Expand macros but not \#includes.
RewriteObjC, ///< ObjC->C Rewriter.
RewriteTest, ///< Rewriter playground
RunAnalysis, ///< Run one or more source code analyses.
@@ -84,7 +86,7 @@ struct FrontendInputFile {
FrontendInputFile(StringRef File, InputKind Kind, bool IsSystem = false)
: File(File.str()), Kind(Kind), IsSystem(IsSystem) { }
};
-
+
/// FrontendOptions - Options for controlling the behavior of the frontend.
class FrontendOptions {
public:
@@ -93,12 +95,6 @@ public:
/// instruct the AST writer to create
/// relocatable PCH files.
unsigned ShowHelp : 1; ///< Show the -help text.
- unsigned ShowMacrosInCodeCompletion : 1; ///< Show macros in code completion
- /// results.
- unsigned ShowCodePatternsInCodeCompletion : 1; ///< Show code patterns in code
- /// completion results.
- unsigned ShowGlobalSymbolsInCodeCompletion : 1; ///< Show top-level decls in
- /// code completion results.
unsigned ShowStats : 1; ///< Show frontend performance
/// metrics and statistics.
unsigned ShowTimers : 1; ///< Show timers for individual
@@ -116,6 +112,8 @@ public:
/// not need them (e.g. with code
/// completion).
+ CodeCompleteOptions CodeCompleteOpts;
+
enum {
ARCMT_None,
ARCMT_Check,
@@ -144,6 +142,9 @@ public:
/// If given, the new suffix for fix-it rewritten files.
std::string FixItSuffix;
+ /// If given, filter dumped AST Decl nodes by this substring.
+ std::string ASTDumpFilter;
+
/// If given, enable code completion at the provided location.
ParsedSourceLocation CodeCompletionAt;
@@ -183,9 +184,6 @@ public:
ActionName = "";
RelocatablePCH = 0;
ShowHelp = 0;
- ShowMacrosInCodeCompletion = 0;
- ShowCodePatternsInCodeCompletion = 0;
- ShowGlobalSymbolsInCodeCompletion = 1;
ShowStats = 0;
ShowTimers = 0;
ShowVersion = 0;
diff --git a/include/clang/Frontend/HeaderSearchOptions.h b/include/clang/Frontend/HeaderSearchOptions.h
index 687f439..ebc8f26 100644
--- a/include/clang/Frontend/HeaderSearchOptions.h
+++ b/include/clang/Frontend/HeaderSearchOptions.h
@@ -17,12 +17,12 @@ namespace clang {
namespace frontend {
/// IncludeDirGroup - Identifiers the group a include entry belongs to, which
- /// represents its relative positive in the search list. A #include of a ""
+ /// represents its relative positive in the search list. A \#include of a ""
/// path starts at the -iquote group, then searches the Angled group, then
/// searches the system group, etc.
enum IncludeDirGroup {
- Quoted = 0, ///< '#include ""' paths, added by'gcc -iquote'.
- Angled, ///< Paths for '#include <>' added by '-I'.
+ Quoted = 0, ///< '\#include ""' paths, added by 'gcc -iquote'.
+ Angled, ///< Paths for '\#include <>' added by '-I'.
IndexHeaderMap, ///< Like Angled, but marks header maps used when
/// building frameworks.
System, ///< Like Angled, but marks system directories.
@@ -69,6 +69,18 @@ public:
IsInternal(isInternal), ImplicitExternC(implicitExternC) {}
};
+ struct SystemHeaderPrefix {
+ /// A prefix to be matched against paths in \#include directives.
+ std::string Prefix;
+
+ /// True if paths beginning with this prefix should be treated as system
+ /// headers.
+ bool IsSystemHeader;
+
+ SystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader)
+ : Prefix(Prefix), IsSystemHeader(IsSystemHeader) {}
+ };
+
/// If non-empty, the directory to use as a "virtual system root" for include
/// paths.
std::string Sysroot;
@@ -76,6 +88,9 @@ public:
/// User specified include entries.
std::vector<Entry> UserEntries;
+ /// User-specified system header prefixes.
+ std::vector<SystemHeaderPrefix> SystemHeaderPrefixes;
+
/// The directory which holds the compiler resource files (builtin includes,
/// etc.).
std::string ResourceDir;
@@ -117,6 +132,13 @@ public:
UserEntries.push_back(Entry(Path, Group, IsUserSupplied, IsFramework,
IgnoreSysRoot, IsInternal, ImplicitExternC));
}
+
+ /// AddSystemHeaderPrefix - Override whether \#include directives naming a
+ /// path starting with \arg Prefix should be considered as naming a system
+ /// header.
+ void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) {
+ SystemHeaderPrefixes.push_back(SystemHeaderPrefix(Prefix, IsSystemHeader));
+ }
};
} // end namespace clang
diff --git a/include/clang/Frontend/LangStandards.def b/include/clang/Frontend/LangStandards.def
index 4bcff4a..a604d4b 100644
--- a/include/clang/Frontend/LangStandards.def
+++ b/include/clang/Frontend/LangStandards.def
@@ -111,6 +111,12 @@ LANGSTANDARD(gnucxx11, "gnu++11",
LANGSTANDARD(opencl, "cl",
"OpenCL 1.0",
BCPLComment | C99 | Digraphs | HexFloat)
+LANGSTANDARD(opencl11, "CL1.1",
+ "OpenCL 1.1",
+ BCPLComment | C99 | Digraphs | HexFloat)
+LANGSTANDARD(opencl12, "CL1.2",
+ "OpenCL 1.2",
+ BCPLComment | C99 | Digraphs | HexFloat)
// CUDA
LANGSTANDARD(cuda, "cuda",
diff --git a/include/clang/Frontend/PreprocessorOutputOptions.h b/include/clang/Frontend/PreprocessorOutputOptions.h
index 1eda0d4..9793aa6 100644
--- a/include/clang/Frontend/PreprocessorOutputOptions.h
+++ b/include/clang/Frontend/PreprocessorOutputOptions.h
@@ -18,9 +18,10 @@ class PreprocessorOutputOptions {
public:
unsigned ShowCPP : 1; ///< Print normal preprocessed output.
unsigned ShowComments : 1; ///< Show comments.
- unsigned ShowLineMarkers : 1; ///< Show #line markers.
+ unsigned ShowLineMarkers : 1; ///< Show \#line markers.
unsigned ShowMacroComments : 1; ///< Show comments, even in macros.
unsigned ShowMacros : 1; ///< Print macro definitions.
+ unsigned RewriteIncludes : 1; ///< Preprocess include directives only.
public:
PreprocessorOutputOptions() {
@@ -29,6 +30,7 @@ public:
ShowLineMarkers = 1;
ShowMacroComments = 0;
ShowMacros = 0;
+ RewriteIncludes = 0;
}
};
diff --git a/include/clang/Frontend/TextDiagnostic.h b/include/clang/Frontend/TextDiagnostic.h
index 314003b..c869c08 100644
--- a/include/clang/Frontend/TextDiagnostic.h
+++ b/include/clang/Frontend/TextDiagnostic.h
@@ -39,7 +39,6 @@ class TextDiagnostic : public DiagnosticRenderer {
public:
TextDiagnostic(raw_ostream &OS,
- const SourceManager &SM,
const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts);
@@ -60,7 +59,7 @@ public:
///
/// This is a static helper to handle the line wrapping, colorizing, and
/// rendering of a diagnostic message to a particular ostream. It is
- /// publically visible so that clients which do not have sufficient state to
+ /// publicly visible so that clients which do not have sufficient state to
/// build a complete TextDiagnostic object can still get consistent
/// formatting of their diagnostic messages.
///
@@ -83,39 +82,46 @@ protected:
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag D);
virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges);
+ ArrayRef<CharSourceRange> Ranges,
+ const SourceManager &SM);
virtual void emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints) {
- emitSnippetAndCaret(Loc, Level, Ranges, Hints);
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
+ emitSnippetAndCaret(Loc, Level, Ranges, Hints, SM);
}
virtual void emitBasicNote(StringRef Message);
- virtual void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc);
+ virtual void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,
+ const SourceManager &SM);
private:
void emitSnippetAndCaret(SourceLocation Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints);
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM);
void emitSnippet(StringRef SourceLine);
void highlightRange(const CharSourceRange &R,
unsigned LineNo, FileID FID,
const SourceColumnMap &map,
- std::string &CaretLine);
+ std::string &CaretLine,
+ const SourceManager &SM);
std::string buildFixItInsertionLine(unsigned LineNo,
const SourceColumnMap &map,
- ArrayRef<FixItHint> Hints);
- void emitParseableFixits(ArrayRef<FixItHint> Hints);
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM);
+ void emitParseableFixits(ArrayRef<FixItHint> Hints, const SourceManager &SM);
};
} // end namespace clang
diff --git a/include/clang/Frontend/TextDiagnosticPrinter.h b/include/clang/Frontend/TextDiagnosticPrinter.h
index 9b6ac24..23cf521 100644
--- a/include/clang/Frontend/TextDiagnosticPrinter.h
+++ b/include/clang/Frontend/TextDiagnosticPrinter.h
@@ -26,9 +26,7 @@ class TextDiagnostic;
class TextDiagnosticPrinter : public DiagnosticConsumer {
raw_ostream &OS;
- const LangOptions *LangOpts;
const DiagnosticOptions *DiagOpts;
- const SourceManager *SM;
/// \brief Handle to the currently active text diagnostic emitter.
OwningPtr<TextDiagnostic> TextDiag;
diff --git a/include/clang/Frontend/VerifyDiagnosticConsumer.h b/include/clang/Frontend/VerifyDiagnosticConsumer.h
index 2fc6ccc..a74589e 100644
--- a/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -11,12 +11,18 @@
#define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICSCLIENT_H
#include "clang/Basic/Diagnostic.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include <climits>
namespace clang {
class DiagnosticsEngine;
class TextDiagnosticBuffer;
+class FileEntry;
/// VerifyDiagnosticConsumer - Create a diagnostic client which will use
/// markers in the input source to check that all the emitted diagnostics match
@@ -35,18 +41,58 @@ class TextDiagnosticBuffer;
///
/// Here's an example:
///
+/// \code
/// int A = B; // expected-error {{use of undeclared identifier 'B'}}
+/// \endcode
///
/// You can place as many diagnostics on one line as you wish. To make the code
/// more readable, you can use slash-newline to separate out the diagnostics.
///
+/// Alternatively, it is possible to specify the line on which the diagnostic
+/// should appear by appending "@<line>" to "expected-<type>", for example:
+///
+/// \code
+/// #warning some text
+/// // expected-warning@10 {{some text}}
+/// \endcode
+///
+/// The line number may be absolute (as above), or relative to the current
+/// line by prefixing the number with either '+' or '-'.
+///
/// The simple syntax above allows each specification to match exactly one
/// error. You can use the extended syntax to customize this. The extended
-/// syntax is "expected-<type> <n> {{diag text}}", where <type> is one of
-/// "error", "warning" or "note", and <n> is a positive integer. This allows the
-/// diagnostic to appear as many times as specified. Example:
+/// syntax is "expected-<type> <n> {{diag text}}", where \<type> is one of
+/// "error", "warning" or "note", and \<n> is a positive integer. This allows
+/// the diagnostic to appear as many times as specified. Example:
///
+/// \code
/// void f(); // expected-note 2 {{previous declaration is here}}
+/// \endcode
+///
+/// Where the diagnostic is expected to occur a minimum number of times, this
+/// can be specified by appending a '+' to the number. Example:
+///
+/// \code
+/// void f(); // expected-note 0+ {{previous declaration is here}}
+/// void g(); // expected-note 1+ {{previous declaration is here}}
+/// \endcode
+///
+/// In the first example, the diagnostic becomes optional, i.e. it will be
+/// swallowed if it occurs, but will not generate an error if it does not
+/// occur. In the second example, the diagnostic must occur at least once.
+/// As a short-hand, "one or more" can be specified simply by '+'. Example:
+///
+/// \code
+/// void g(); // expected-note + {{previous declaration is here}}
+/// \endcode
+///
+/// A range can also be specified by "<n>-<m>". Example:
+///
+/// \code
+/// void f(); // expected-note 0-1 {{previous declaration is here}}
+/// \endcode
+///
+/// In this example, the diagnostic may appear only once, if at all.
///
/// Regex matching mode may be selected by appending '-re' to type. Example:
///
@@ -62,20 +108,85 @@ class TextDiagnosticBuffer;
/// // expected-error-re {{variable has has type 'struct (.*)'}}
/// // expected-error-re {{variable has has type 'struct[[:space:]](.*)'}}
///
-class VerifyDiagnosticConsumer: public DiagnosticConsumer {
+class VerifyDiagnosticConsumer: public DiagnosticConsumer,
+ public CommentHandler {
public:
+ /// Directive - Abstract class representing a parsed verify directive.
+ ///
+ class Directive {
+ public:
+ static Directive *create(bool RegexKind, SourceLocation DirectiveLoc,
+ SourceLocation DiagnosticLoc,
+ StringRef Text, unsigned Min, unsigned Max);
+ public:
+ /// Constant representing n or more matches.
+ static const unsigned MaxCount = UINT_MAX;
+
+ SourceLocation DirectiveLoc;
+ SourceLocation DiagnosticLoc;
+ const std::string Text;
+ unsigned Min, Max;
+
+ virtual ~Directive() { }
+
+ // Returns true if directive text is valid.
+ // Otherwise returns false and populates E.
+ virtual bool isValid(std::string &Error) = 0;
+
+ // Returns true on match.
+ virtual bool match(StringRef S) = 0;
+
+ protected:
+ Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
+ StringRef Text, unsigned Min, unsigned Max)
+ : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
+ Text(Text), Min(Min), Max(Max) {
+ assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
+ assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!");
+ }
+
+ private:
+ Directive(const Directive&); // DO NOT IMPLEMENT
+ void operator=(const Directive&); // DO NOT IMPLEMENT
+ };
+
+ typedef std::vector<Directive*> DirectiveList;
+
+ /// ExpectedData - owns directive objects and deletes on destructor.
+ ///
+ struct ExpectedData {
+ DirectiveList Errors;
+ DirectiveList Warnings;
+ DirectiveList Notes;
+
+ ~ExpectedData() {
+ llvm::DeleteContainerPointers(Errors);
+ llvm::DeleteContainerPointers(Warnings);
+ llvm::DeleteContainerPointers(Notes);
+ }
+ };
+
+#ifndef NDEBUG
+ typedef llvm::DenseSet<FileID> FilesWithDiagnosticsSet;
+ typedef llvm::SmallPtrSet<const FileEntry *, 4> FilesParsedForDirectivesSet;
+#endif
+
+private:
DiagnosticsEngine &Diags;
DiagnosticConsumer *PrimaryClient;
bool OwnsPrimaryClient;
OwningPtr<TextDiagnosticBuffer> Buffer;
- Preprocessor *CurrentPreprocessor;
-
-private:
- FileID FirstErrorFID; // FileID of first diagnostic
+ const Preprocessor *CurrentPreprocessor;
+ unsigned ActiveSourceFiles;
+#ifndef NDEBUG
+ FilesWithDiagnosticsSet FilesWithDiagnostics;
+ FilesParsedForDirectivesSet FilesParsedForDirectives;
+#endif
+ ExpectedData ED;
void CheckDiagnostics();
public:
- /// Create a new verifying diagnostic client, which will issue errors to \arg
+ /// Create a new verifying diagnostic client, which will issue errors to
/// the currently-attached diagnostic client when a diagnostic does not match
/// what is expected (as indicated in the source file).
VerifyDiagnosticConsumer(DiagnosticsEngine &Diags);
@@ -86,6 +197,15 @@ public:
virtual void EndSourceFile();
+ /// \brief Manually register a file as parsed.
+ inline void appendParsedFile(const FileEntry *File) {
+#ifndef NDEBUG
+ FilesParsedForDirectives.insert(File);
+#endif
+ }
+
+ virtual bool HandleComment(Preprocessor &PP, SourceRange Comment);
+
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info);
diff --git a/include/clang/Lex/CodeCompletionHandler.h b/include/clang/Lex/CodeCompletionHandler.h
index d876776..91c3b78 100644
--- a/include/clang/Lex/CodeCompletionHandler.h
+++ b/include/clang/Lex/CodeCompletionHandler.h
@@ -43,11 +43,11 @@ public:
/// where the name of a macro is expected.
///
/// \param IsDefinition Whether this is the definition of a macro, e.g.,
- /// in a #define.
+ /// in a \#define.
virtual void CodeCompleteMacroName(bool IsDefinition) { }
/// \brief Callback invoked when performing code completion in a preprocessor
- /// expression, such as the condition of an #if or #elif directive.
+ /// expression, such as the condition of an \#if or \#elif directive.
virtual void CodeCompletePreprocessorExpression() { }
/// \brief Callback invoked when performing code completion inside a
@@ -62,7 +62,7 @@ public:
/// \brief Callback invoked when performing code completion in a part of the
/// file where we expect natural language, e.g., a comment, string, or
- /// #error directive.
+ /// \#error directive.
virtual void CodeCompleteNaturalLanguage() { }
};
diff --git a/include/clang/Lex/DirectoryLookup.h b/include/clang/Lex/DirectoryLookup.h
index 95f0d27..d773fc6 100644
--- a/include/clang/Lex/DirectoryLookup.h
+++ b/include/clang/Lex/DirectoryLookup.h
@@ -25,7 +25,7 @@ class HeaderSearch;
class Module;
/// DirectoryLookup - This class represents one entry in the search list that
-/// specifies the search order for directories in #include directives. It
+/// specifies the search order for directories in \#include directives. It
/// represents either a directory, a framework, or a headermap.
///
class DirectoryLookup {
@@ -146,14 +146,14 @@ public:
/// part of a known module, this will be set to the module that should
/// be imported instead of preprocessing/parsing the file found.
///
- /// \param InUserSpecifiedSystemHeader [out] If the file is found, set to true
- /// if the file is located in a framework that has been user-specified to be
- /// treated as a system framework.
+ /// \param [out] InUserSpecifiedSystemFramework If the file is found,
+ /// set to true if the file is located in a framework that has been
+ /// user-specified to be treated as a system framework.
const FileEntry *LookupFile(StringRef Filename, HeaderSearch &HS,
SmallVectorImpl<char> *SearchPath,
SmallVectorImpl<char> *RelativePath,
Module **SuggestedModule,
- bool &InUserSpecifiedSystemHeader) const;
+ bool &InUserSpecifiedSystemFramework) const;
private:
const FileEntry *DoFrameworkLookup(
diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h
index 08bc5b6..107408d 100644
--- a/include/clang/Lex/HeaderMap.h
+++ b/include/clang/Lex/HeaderMap.h
@@ -26,7 +26,7 @@ namespace clang {
struct HMapHeader;
/// This class represents an Apple concept known as a 'header map'. To the
-/// #include file resolution process, it basically acts like a directory of
+/// \#include file resolution process, it basically acts like a directory of
/// symlinks to files. Its advantages are that it is dense and more efficient
/// to create and process than a directory of symlinks.
class HeaderMap {
diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h
index 5128ce6..8e9491f 100644
--- a/include/clang/Lex/HeaderSearch.h
+++ b/include/clang/Lex/HeaderSearch.h
@@ -16,6 +16,7 @@
#include "clang/Lex/DirectoryLookup.h"
#include "clang/Lex/ModuleMap.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Allocator.h"
@@ -30,18 +31,18 @@ class FileEntry;
class FileManager;
class IdentifierInfo;
-/// HeaderFileInfo - The preprocessor keeps track of this information for each
-/// file that is #included.
+/// \brief The preprocessor keeps track of this information for each
+/// file that is \#included.
struct HeaderFileInfo {
- /// isImport - True if this is a #import'd or #pragma once file.
+ /// \brief True if this is a \#import'd or \#pragma once file.
unsigned isImport : 1;
- /// isPragmaOnce - True if this is #pragma once file.
+ /// \brief True if this is a \#pragma once file.
unsigned isPragmaOnce : 1;
/// DirInfo - Keep track of whether this is a system header, and if so,
/// whether it is C++ clean or not. This can be set by the include paths or
- /// by #pragma gcc system_header. This is an instance of
+ /// by \#pragma gcc system_header. This is an instance of
/// SrcMgr::CharacteristicKind.
unsigned DirInfo : 2;
@@ -61,8 +62,7 @@ struct HeaderFileInfo {
/// those framework headers.
unsigned IndexHeaderMapHeader : 1;
- /// NumIncludes - This is the number of times the file has been included
- /// already.
+ /// \brief The number of times the file has been included already.
unsigned short NumIncludes;
/// \brief The ID number of the controlling macro.
@@ -72,8 +72,8 @@ struct HeaderFileInfo {
/// external storage.
unsigned ControllingMacroID;
- /// ControllingMacro - If this file has a #ifndef XXX (or equivalent) guard
- /// that protects the entire contents of the file, this is the identifier
+ /// If this file has a \#ifndef XXX (or equivalent) guard that
+ /// protects the entire contents of the file, this is the identifier
/// for the macro that controls whether or not it has any effect.
///
/// Note: Most clients should use getControllingMacro() to access
@@ -117,8 +117,8 @@ public:
virtual HeaderFileInfo GetHeaderFileInfo(const FileEntry *FE) = 0;
};
-/// HeaderSearch - This class encapsulates the information needed to find the
-/// file referenced by a #include or #include_next, (sub-)framework lookup, etc.
+/// \brief Encapsulates the information needed to find the file referenced
+/// by a \#include or \#include_next, (sub-)framework lookup, etc.
class HeaderSearch {
/// This structure is used to record entries in our framework cache.
struct FrameworkCacheEntry {
@@ -133,8 +133,8 @@ class HeaderSearch {
FileManager &FileMgr;
DiagnosticsEngine &Diags;
- /// #include search path information. Requests for #include "x" search the
- /// directory of the #including file first, then each directory in SearchDirs
+ /// \#include search path information. Requests for \#include "x" search the
+ /// directory of the \#including file first, then each directory in SearchDirs
/// consecutively. Requests for <x> search the current dir first, then each
/// directory in SearchDirs, starting at AngledDirIdx, consecutively. If
/// NoCurDirSearch is true, then the check for the file in the current
@@ -144,24 +144,32 @@ class HeaderSearch {
unsigned SystemDirIdx;
bool NoCurDirSearch;
+ /// \brief \#include prefixes for which the 'system header' property is
+ /// overridden.
+ ///
+ /// For a \#include "x" or \#include \<x> directive, the last string in this
+ /// list which is a prefix of 'x' determines whether the file is treated as
+ /// a system header.
+ std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes;
+
/// \brief The path to the module cache.
std::string ModuleCachePath;
- /// FileInfo - This contains all of the preprocessor-specific data about files
- /// that are included. The vector is indexed by the FileEntry's UID.
- ///
+ /// \brief All of the preprocessor-specific data about files that are
+ /// included, indexed by the FileEntry's UID.
std::vector<HeaderFileInfo> FileInfo;
- /// LookupFileCache - This is keeps track of each lookup performed by
- /// LookupFile. The first part of the value is the starting index in
- /// SearchDirs that the cached search was performed from. If there is a hit
- /// and this value doesn't match the current query, the cache has to be
- /// ignored. The second value is the entry in SearchDirs that satisfied the
- /// query.
+ /// \brief Keeps track of each lookup performed by LookupFile.
+ ///
+ /// The first part of the value is the starting index in SearchDirs
+ /// that the cached search was performed from. If there is a hit and
+ /// this value doesn't match the current query, the cache has to be
+ /// ignored. The second value is the entry in SearchDirs that satisfied
+ /// the query.
llvm::StringMap<std::pair<unsigned, unsigned>, llvm::BumpPtrAllocator>
LookupFileCache;
- /// FrameworkMap - This is a collection mapping a framework or subframework
+ /// \brief Collection mapping a framework or subframework
/// name like "Carbon" to the Carbon.framework directory.
llvm::StringMap<FrameworkCacheEntry, llvm::BumpPtrAllocator> FrameworkMap;
@@ -212,8 +220,7 @@ public:
FileManager &getFileMgr() const { return FileMgr; }
- /// SetSearchPaths - Interface for setting the file search paths.
- ///
+ /// \brief Interface for setting the file search paths.
void SetSearchPaths(const std::vector<DirectoryLookup> &dirs,
unsigned angledDirIdx, unsigned systemDirIdx,
bool noCurDirSearch) {
@@ -226,7 +233,7 @@ public:
//LookupFileCache.clear();
}
- /// AddSearchPath - Add an additional search path.
+ /// \brief Add an additional search path.
void AddSearchPath(const DirectoryLookup &dir, bool isAngled) {
unsigned idx = isAngled ? SystemDirIdx : AngledDirIdx;
SearchDirs.insert(SearchDirs.begin() + idx, dir);
@@ -235,12 +242,18 @@ public:
SystemDirIdx++;
}
- /// HasIncludeAliasMap - Checks whether the map exists or not
+ /// \brief Set the list of system header prefixes.
+ void SetSystemHeaderPrefixes(ArrayRef<std::pair<std::string, bool> > P) {
+ SystemHeaderPrefixes.assign(P.begin(), P.end());
+ }
+
+ /// \brief Checks whether the map exists or not.
bool HasIncludeAliasMap() const {
return IncludeAliases;
}
- /// AddIncludeAlias - Map the source include name to the dest include name.
+ /// \brief Map the source include name to the dest include name.
+ ///
/// The Source should include the angle brackets or quotes, the dest
/// should not. This allows for distinction between <> and "" headers.
void AddIncludeAlias(StringRef Source, StringRef Dest) {
@@ -271,7 +284,7 @@ public:
/// \brief Retrieve the path to the module cache.
StringRef getModuleCachePath() const { return ModuleCachePath; }
- /// ClearFileInfo - Forget everything we know about headers so far.
+ /// \brief Forget everything we know about headers so far.
void ClearFileInfo() {
FileInfo.clear();
}
@@ -293,7 +306,7 @@ public:
/// already known.
void setTarget(const TargetInfo &Target);
- /// LookupFile - Given a "foo" or <foo> reference, look up the indicated file,
+ /// \brief Given a "foo" or \<foo> reference, look up the indicated file,
/// return null on failure.
///
/// \returns If successful, this returns 'UsedDir', the DirectoryLookup member
@@ -302,9 +315,9 @@ public:
/// \param isAngled indicates whether the file reference is a <> reference.
///
/// \param CurDir If non-null, the file was found in the specified directory
- /// search location. This is used to implement #include_next.
+ /// search location. This is used to implement \#include_next.
///
- /// \param CurFileEnt If non-null, indicates where the #including file is, in
+ /// \param CurFileEnt If non-null, indicates where the \#including file is, in
/// case a relative search is needed.
///
/// \param SearchPath If non-null, will be set to the search path relative
@@ -327,73 +340,76 @@ public:
Module **SuggestedModule,
bool SkipCache = false);
- /// LookupSubframeworkHeader - Look up a subframework for the specified
- /// #include file. For example, if #include'ing <HIToolbox/HIToolbox.h> from
- /// within ".../Carbon.framework/Headers/Carbon.h", check to see if HIToolbox
- /// is a subframework within Carbon.framework. If so, return the FileEntry
- /// for the designated file, otherwise return null.
+ /// \brief Look up a subframework for the specified \#include file.
+ ///
+ /// For example, if \#include'ing <HIToolbox/HIToolbox.h> from
+ /// within ".../Carbon.framework/Headers/Carbon.h", check to see if
+ /// HIToolbox is a subframework within Carbon.framework. If so, return
+ /// the FileEntry for the designated file, otherwise return null.
const FileEntry *LookupSubframeworkHeader(
StringRef Filename,
const FileEntry *RelativeFileEnt,
SmallVectorImpl<char> *SearchPath,
SmallVectorImpl<char> *RelativePath);
- /// LookupFrameworkCache - Look up the specified framework name in our
- /// framework cache, returning the DirectoryEntry it is in if we know,
- /// otherwise, return null.
+ /// \brief Look up the specified framework name in our framework cache.
+ /// \returns The DirectoryEntry it is in if we know, null otherwise.
FrameworkCacheEntry &LookupFrameworkCache(StringRef FWName) {
return FrameworkMap.GetOrCreateValue(FWName).getValue();
}
- /// ShouldEnterIncludeFile - Mark the specified file as a target of of a
- /// #include, #include_next, or #import directive. Return false if #including
- /// the file will have no effect or true if we should include it.
+ /// \brief Mark the specified file as a target of of a \#include,
+ /// \#include_next, or \#import directive.
+ ///
+ /// \return false if \#including the file will have no effect or true
+ /// if we should include it.
bool ShouldEnterIncludeFile(const FileEntry *File, bool isImport);
- /// getFileDirFlavor - Return whether the specified file is a normal header,
+ /// \brief Return whether the specified file is a normal header,
/// a system header, or a C++ friendly system header.
SrcMgr::CharacteristicKind getFileDirFlavor(const FileEntry *File) {
return (SrcMgr::CharacteristicKind)getFileInfo(File).DirInfo;
}
- /// MarkFileIncludeOnce - Mark the specified file as a "once only" file, e.g.
- /// due to #pragma once.
+ /// \brief Mark the specified file as a "once only" file, e.g. due to
+ /// \#pragma once.
void MarkFileIncludeOnce(const FileEntry *File) {
HeaderFileInfo &FI = getFileInfo(File);
FI.isImport = true;
FI.isPragmaOnce = true;
}
- /// MarkFileSystemHeader - Mark the specified file as a system header, e.g.
- /// due to #pragma GCC system_header.
+ /// \brief Mark the specified file as a system header, e.g. due to
+ /// \#pragma GCC system_header.
void MarkFileSystemHeader(const FileEntry *File) {
getFileInfo(File).DirInfo = SrcMgr::C_System;
}
- /// IncrementIncludeCount - Increment the count for the number of times the
- /// specified FileEntry has been entered.
+ /// \brief Increment the count for the number of times the specified
+ /// FileEntry has been entered.
void IncrementIncludeCount(const FileEntry *File) {
++getFileInfo(File).NumIncludes;
}
- /// SetFileControllingMacro - Mark the specified file as having a controlling
- /// macro. This is used by the multiple-include optimization to eliminate
- /// no-op #includes.
+ /// \brief Mark the specified file as having a controlling macro.
+ ///
+ /// This is used by the multiple-include optimization to eliminate
+ /// no-op \#includes.
void SetFileControllingMacro(const FileEntry *File,
const IdentifierInfo *ControllingMacro) {
getFileInfo(File).ControllingMacro = ControllingMacro;
}
/// \brief Determine whether this file is intended to be safe from
- /// multiple inclusions, e.g., it has #pragma once or a controlling
+ /// multiple inclusions, e.g., it has \#pragma once or a controlling
/// macro.
///
- /// This routine does not consider the effect of #import
+ /// This routine does not consider the effect of \#import
bool isFileMultipleIncludeGuarded(const FileEntry *File);
/// CreateHeaderMap - This method returns a HeaderMap for the specified
- /// FileEntry, uniquing them through the the 'HeaderMaps' datastructure.
+ /// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
const HeaderMap *CreateHeaderMap(const FileEntry *FE);
/// \brief Retrieve the name of the module file that should be used to
@@ -408,7 +424,7 @@ public:
/// \brief Retrieve the name of the module file that should be used to
/// load a module with the given name.
///
- /// \param Module The module whose module file name will be returned.
+ /// \param ModuleName The module whose module file name will be returned.
///
/// \returns The name of the module file that corresponds to this module,
/// or an empty string if this module does not correspond to any module file.
@@ -445,8 +461,6 @@ public:
///
/// \param File The module map file.
///
- /// \param OnlyModule If non-NULL, this will receive the
- ///
/// \returns true if an error occurred, false otherwise.
bool loadModuleMapFile(const FileEntry *File);
@@ -480,8 +494,7 @@ public:
// Used by ASTReader.
void setHeaderFileInfoForUID(HeaderFileInfo HFI, unsigned UID);
- /// getFileInfo - Return the HeaderFileInfo structure for the specified
- /// FileEntry.
+ /// \brief Return the HeaderFileInfo structure for the specified FileEntry.
const HeaderFileInfo &getFileInfo(const FileEntry *FE) const {
return const_cast<HeaderSearch*>(this)->getFileInfo(FE);
}
@@ -552,8 +565,7 @@ private:
/// named directory.
LoadModuleMapResult loadModuleMapFile(const DirectoryEntry *Dir);
- /// getFileInfo - Return the HeaderFileInfo structure for the specified
- /// FileEntry.
+ /// \brief Return the HeaderFileInfo structure for the specified FileEntry.
HeaderFileInfo &getFileInfo(const FileEntry *FE);
};
diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h
index 04bcead..ca233de 100644
--- a/include/clang/Lex/Lexer.h
+++ b/include/clang/Lex/Lexer.h
@@ -97,14 +97,14 @@ public:
Lexer(FileID FID, const llvm::MemoryBuffer *InputBuffer, Preprocessor &PP);
/// Lexer constructor - Create a new raw lexer object. This object is only
- /// suitable for calls to 'LexRawToken'. This lexer assumes that the text
- /// range will outlive it, so it doesn't take ownership of it.
+ /// suitable for calls to 'LexFromRawLexer'. This lexer assumes that the
+ /// text range will outlive it, so it doesn't take ownership of it.
Lexer(SourceLocation FileLoc, const LangOptions &LangOpts,
const char *BufStart, const char *BufPtr, const char *BufEnd);
/// Lexer constructor - Create a new raw lexer object. This object is only
- /// suitable for calls to 'LexRawToken'. This lexer assumes that the text
- /// range will outlive it, so it doesn't take ownership of it.
+ /// suitable for calls to 'LexFromRawLexer'. This lexer assumes that the
+ /// text range will outlive it, so it doesn't take ownership of it.
Lexer(FileID FID, const llvm::MemoryBuffer *InputBuffer,
const SourceManager &SM, const LangOptions &LangOpts);
@@ -200,7 +200,7 @@ public:
/// ReadToEndOfLine - Read the rest of the current preprocessor line as an
/// uninterpreted string. This switches the lexer out of directive mode.
- std::string ReadToEndOfLine();
+ void ReadToEndOfLine(SmallVectorImpl<char> *Result = 0);
/// Diag - Forwarding function for diagnostics. This translate a source
@@ -335,6 +335,28 @@ public:
///
/// Returns a null range if a part of the range resides inside a macro
/// expansion or the range does not reside on the same FileID.
+ ///
+ /// This function is trying to deal with macros and return a range based on
+ /// file locations. The cases where it can successfully handle macros are:
+ ///
+ /// -begin or end range lies at the start or end of a macro expansion, in
+ /// which case the location will be set to the expansion point, e.g:
+ /// \#define M 1 2
+ /// a M
+ /// If you have a range [a, 2] (where 2 came from the macro), the function
+ /// will return a range for "a M"
+ /// if you have range [a, 1], the function will fail because the range
+ /// overlaps with only a part of the macro
+ ///
+ /// -The macro is a function macro and the range can be mapped to the macro
+ /// arguments, e.g:
+ /// \#define M 1 2
+ /// \#define FM(x) x
+ /// FM(a b M)
+ /// if you have range [b, 2], the function will return the file range "b M"
+ /// inside the macro arguments.
+ /// if you have range [a, 2], the function will return the file range
+ /// "FM(a b M)" since the range includes all of the macro expansion.
static CharSourceRange makeFileCharRange(CharSourceRange Range,
const SourceManager &SM,
const LangOptions &LangOpts);
@@ -519,6 +541,9 @@ public:
const LangOptions &LangOpts,
bool SkipTrailingWhitespaceAndNewLine);
+ /// \brief Returns true if the given character could appear in an identifier.
+ static bool isIdentifierBodyChar(char c, const LangOptions &LangOpts);
+
private:
/// getCharAndSizeSlowNoWarn - Same as getCharAndSizeSlow, but never emits a
diff --git a/include/clang/Lex/LiteralSupport.h b/include/clang/Lex/LiteralSupport.h
index 7e7f82f..bbce62d 100644
--- a/include/clang/Lex/LiteralSupport.h
+++ b/include/clang/Lex/LiteralSupport.h
@@ -232,6 +232,7 @@ private:
void init(const Token *StringToks, unsigned NumStringToks);
bool CopyStringFragment(StringRef Fragment);
bool DiagnoseBadString(const Token& Tok);
+ void DiagnoseLexingError(SourceLocation Loc);
};
} // end namespace clang
diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h
index 8775d39..cbd201f 100644
--- a/include/clang/Lex/MacroInfo.h
+++ b/include/clang/Lex/MacroInfo.h
@@ -22,7 +22,7 @@
namespace clang {
class Preprocessor;
-/// MacroInfo - Each identifier that is #define'd has an instance of this class
+/// MacroInfo - Each identifier that is \#define'd has an instance of this class
/// associated with it, used to implement macro expansion.
class MacroInfo {
//===--------------------------------------------------------------------===//
@@ -35,7 +35,7 @@ class MacroInfo {
/// Arguments - The list of arguments for a function-like macro. This can be
/// empty, for, e.g. "#define X()". In a C99-style variadic macro, this
- /// includes the __VA_ARGS__ identifier on the list.
+ /// includes the \c __VA_ARGS__ identifier on the list.
IdentifierInfo **ArgumentList;
unsigned NumArguments;
@@ -45,15 +45,14 @@ class MacroInfo {
/// If invalid, this macro has not been explicitly given any visibility.
SourceLocation VisibilityLocation;
- /// ReplacementTokens - This is the list of tokens that the macro is defined
- /// to.
+ /// \brief This is the list of tokens that the macro is defined to.
SmallVector<Token, 8> ReplacementTokens;
/// \brief Length in characters of the macro definition.
mutable unsigned DefinitionLength;
mutable bool IsDefinitionLengthCached : 1;
- /// IsFunctionLike - True if this macro is a function-like macro, false if it
+ /// \brief True if this macro is a function-like macro, false if it
/// is an object-like macro.
bool IsFunctionLike : 1;
@@ -71,7 +70,7 @@ class MacroInfo {
/// it has not yet been redefined or undefined.
bool IsBuiltinMacro : 1;
- /// IsFromAST - True if this macro was loaded from an AST file.
+ /// \brief True if this macro was loaded from an AST file.
bool IsFromAST : 1;
/// \brief Whether this macro changed after it was loaded from an AST file.
@@ -82,8 +81,8 @@ private:
// State that changes as the macro is used.
/// IsDisabled - True if we have started an expansion of this macro already.
- /// This disbles recursive expansion, which would be quite bad for things like
- /// #define A A.
+ /// This disables recursive expansion, which would be quite bad for things
+ /// like \#define A A.
bool IsDisabled : 1;
/// IsUsed - True if this macro is either defined in the main file and has
diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h
index 4ebb1d4..fe5abdf 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -126,7 +126,7 @@ public:
/// \brief Retrieve a module with the given name.
///
- /// \param The name of the module to look up.
+ /// \param Name The name of the module to look up.
///
/// \returns The named module, if known; otherwise, returns null.
Module *findModule(StringRef Name);
@@ -134,7 +134,7 @@ public:
/// \brief Retrieve a module with the given name using lexical name lookup,
/// starting at the given context.
///
- /// \param The name of the module to look up.
+ /// \param Name The name of the module to look up.
///
/// \param Context The module context, from which we will perform lexical
/// name lookup.
@@ -145,7 +145,7 @@ public:
/// \brief Retrieve a module with the given name within the given context,
/// using direct (qualified) name lookup.
///
- /// \param The name of the module to look up.
+ /// \param Name The name of the module to look up.
///
/// \param Context The module for which we will look for a submodule. If
/// null, we will look for a top-level module.
diff --git a/include/clang/Lex/MultipleIncludeOpt.h b/include/clang/Lex/MultipleIncludeOpt.h
index 95b00df..a2a5a77 100644
--- a/include/clang/Lex/MultipleIncludeOpt.h
+++ b/include/clang/Lex/MultipleIncludeOpt.h
@@ -6,8 +6,9 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the MultipleIncludeOpt interface.
+///
+/// \file
+/// \brief Defines the MultipleIncludeOpt interface.
//
//===----------------------------------------------------------------------===//
@@ -17,17 +18,18 @@
namespace clang {
class IdentifierInfo;
-/// MultipleIncludeOpt - This class implements the simple state machine that the
-/// Lexer class uses to detect files subject to the 'multiple-include'
-/// optimization. The public methods in this class are triggered by various
+/// \brief Implements the simple state machine that the Lexer class uses to
+/// detect files subject to the 'multiple-include' optimization.
+///
+/// The public methods in this class are triggered by various
/// events that occur when a file is lexed, and after the entire file is lexed,
/// information about which macro (if any) controls the header is returned.
class MultipleIncludeOpt {
/// ReadAnyTokens - This is set to false when a file is first opened and true
/// any time a token is returned to the client or a (non-multiple-include)
- /// directive is parsed. When the final #endif is parsed this is reset back
- /// to false, that way any tokens before the first #ifdef or after the last
- /// #endif can be easily detected.
+ /// directive is parsed. When the final \#endif is parsed this is reset back
+ /// to false, that way any tokens before the first \#ifdef or after the last
+ /// \#endif can be easily detected.
bool ReadAnyTokens;
/// ReadAnyTokens - This is set to false when a file is first opened and true
@@ -56,7 +58,7 @@ public:
TheMacro = 0;
}
- /// getHasReadAnyTokensVal - This is used for the #ifndef hande-shake at the
+ /// getHasReadAnyTokensVal - This is used for the \#ifndef hande-shake at the
/// top of the file when reading preprocessor directives. Otherwise, reading
/// the "ifndef x" would count as reading tokens.
bool getHasReadAnyTokensVal() const { return ReadAnyTokens; }
@@ -68,14 +70,13 @@ public:
/// buffer, this method is called to disable the MIOpt if needed.
void ExpandedMacro() { DidMacroExpansion = true; }
- /// EnterTopLevelIFNDEF - When entering a top-level #ifndef directive (or the
- /// "#if !defined" equivalent) without any preceding tokens, this method is
- /// called.
+ /// \brief Called when entering a top-level \#ifndef directive (or the
+ /// "\#if !defined" equivalent) without any preceding tokens.
///
/// Note, we don't care about the input value of 'ReadAnyTokens'. The caller
/// ensures that this is only called if there are no tokens read before the
- /// #ifndef. The caller is required to do this, because reading the #if line
- /// obviously reads in in tokens.
+ /// \#ifndef. The caller is required to do this, because reading the \#if
+ /// line obviously reads in in tokens.
void EnterTopLevelIFNDEF(const IdentifierInfo *M) {
// If the macro is already set, this is after the top-level #endif.
if (TheMacro)
@@ -93,16 +94,14 @@ public:
TheMacro = M;
}
- /// EnterTopLevelConditional - This is invoked when a top level conditional
- /// (except #ifndef) is found.
+ /// \brief Invoked when a top level conditional (except \#ifndef) is found.
void EnterTopLevelConditional() {
- /// If a conditional directive (except #ifndef) is found at the top level,
- /// there is a chunk of the file not guarded by the controlling macro.
+ // If a conditional directive (except #ifndef) is found at the top level,
+ // there is a chunk of the file not guarded by the controlling macro.
Invalidate();
}
- /// ExitTopLevelConditional - This method is called when the lexer exits the
- /// top-level conditional.
+ /// \brief Called when the lexer exits the top-level conditional.
void ExitTopLevelConditional() {
// If we have a macro, that means the top of the file was ok. Set our state
// back to "not having read any tokens" so we can detect anything after the
@@ -114,8 +113,8 @@ public:
ReadAnyTokens = false;
}
- /// GetControllingMacroAtEndOfFile - Once the entire file has been lexed, if
- /// there is a controlling macro, return it.
+ /// \brief Once the entire file has been lexed, if there is a controlling
+ /// macro, return it.
const IdentifierInfo *GetControllingMacroAtEndOfFile() const {
// If we haven't read any tokens after the #endif, return the controlling
// macro if it's valid (if it isn't, it will be null).
diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h
index 33558c8..962b4df 100644
--- a/include/clang/Lex/PPCallbacks.h
+++ b/include/clang/Lex/PPCallbacks.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the PPCallbacks interface.
-//
+///
+/// \file
+/// \brief Defines the PPCallbacks interface.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LEX_PPCALLBACKS_H
@@ -26,9 +27,10 @@ namespace clang {
class IdentifierInfo;
class MacroInfo;
-/// PPCallbacks - This interface provides a way to observe the actions of the
-/// preprocessor as it does its thing. Clients can define their hooks here to
-/// implement preprocessor level tools.
+/// \brief This interface provides a way to observe the actions of the
+/// preprocessor as it does its thing.
+///
+/// Clients can define their hooks here to implement preprocessor level tools.
class PPCallbacks {
public:
virtual ~PPCallbacks();
@@ -37,29 +39,29 @@ public:
EnterFile, ExitFile, SystemHeaderPragma, RenameFile
};
- /// FileChanged - This callback is invoked whenever a source file is
- /// entered or exited. The SourceLocation indicates the new location, and
- /// EnteringFile indicates whether this is because we are entering a new
- /// #include'd file (when true) or whether we're exiting one because we ran
- /// off the end (when false).
+ /// \brief Callback invoked whenever a source file is entered or exited.
///
- /// \param PrevFID the file that was exited if \arg Reason is ExitFile.
+ /// \param Loc Indicates the new location.
+ /// \param PrevFID the file that was exited if \p Reason is ExitFile.
virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID = FileID()) {
}
- /// FileSkipped - This callback is invoked whenever a source file is
- /// skipped as the result of header guard optimization. ParentFile
- /// is the file that #includes the skipped file. FilenameTok is the
- /// token in ParentFile that indicates the skipped file.
+ /// \brief Callback invoked whenever a source file is skipped as the result
+ /// of header guard optimization.
+ ///
+ /// \param ParentFile The file that \#included the skipped file.
+ ///
+ /// \param FilenameTok The token in ParentFile that indicates the
+ /// skipped file.
virtual void FileSkipped(const FileEntry &ParentFile,
const Token &FilenameTok,
SrcMgr::CharacteristicKind FileType) {
}
- /// FileNotFound - This callback is invoked whenever an inclusion directive
- /// results in a file-not-found error.
+ /// \brief Callback invoked whenever an inclusion directive results in a
+ /// file-not-found error.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
@@ -75,8 +77,8 @@ public:
return false;
}
- /// \brief This callback is invoked whenever an inclusion directive of
- /// any kind (\c #include, \c #import, etc.) has been processed, regardless
+ /// \brief Callback invoked whenever an inclusion directive of
+ /// any kind (\c \#include, \c \#import, etc.) has been processed, regardless
/// of whether the inclusion will actually result in an inclusion.
///
/// \param HashLoc The location of the '#' that starts the inclusion
@@ -118,119 +120,116 @@ public:
StringRef RelativePath) {
}
- /// EndOfMainFile - This callback is invoked when the end of the main file is
- /// reach, no subsequent callbacks will be made.
+ /// \brief Callback invoked when the end of the main file is reached.
+ ///
+ /// No subsequent callbacks will be made.
virtual void EndOfMainFile() {
}
- /// Ident - This callback is invoked when a #ident or #sccs directive is read.
+ /// \brief Callback invoked when a \#ident or \#sccs directive is read.
/// \param Loc The location of the directive.
/// \param str The text of the directive.
///
virtual void Ident(SourceLocation Loc, const std::string &str) {
}
- /// PragmaComment - This callback is invoked when a #pragma comment directive
- /// is read.
- ///
+ /// \brief Callback invoked when a \#pragma comment directive is read.
virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind,
const std::string &Str) {
}
- /// PragmaMessage - This callback is invoked when a #pragma message directive
- /// is read.
+ /// \brief Callback invoked when a \#pragma message directive is read.
/// \param Loc The location of the message directive.
- /// \param str The text of the message directive.
- ///
+ /// \param Str The text of the message directive.
virtual void PragmaMessage(SourceLocation Loc, StringRef Str) {
}
- /// PragmaDiagnosticPush - This callback is invoked when a
- /// #pragma gcc dianostic push directive is read.
+ /// \brief Callback invoked when a \#pragma gcc dianostic push directive
+ /// is read.
virtual void PragmaDiagnosticPush(SourceLocation Loc,
StringRef Namespace) {
}
- /// PragmaDiagnosticPop - This callback is invoked when a
- /// #pragma gcc dianostic pop directive is read.
+ /// \brief Callback invoked when a \#pragma gcc dianostic pop directive
+ /// is read.
virtual void PragmaDiagnosticPop(SourceLocation Loc,
StringRef Namespace) {
}
- /// PragmaDiagnostic - This callback is invoked when a
- /// #pragma gcc dianostic directive is read.
+ /// \brief Callback invoked when a \#pragma gcc dianostic directive is read.
virtual void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace,
diag::Mapping mapping, StringRef Str) {
}
- /// MacroExpands - This is called by
- /// Preprocessor::HandleMacroExpandedIdentifier when a macro invocation is
- /// found.
+ /// \brief Called by Preprocessor::HandleMacroExpandedIdentifier when a
+ /// macro invocation is found.
virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo* MI,
SourceRange Range) {
}
- /// MacroDefined - This hook is called whenever a macro definition is seen.
+ /// \brief Hook called whenever a macro definition is seen.
virtual void MacroDefined(const Token &MacroNameTok, const MacroInfo *MI) {
}
- /// MacroUndefined - This hook is called whenever a macro #undef is seen.
+ /// \brief Hook called whenever a macro \#undef is seen.
+ ///
/// MI is released immediately following this callback.
virtual void MacroUndefined(const Token &MacroNameTok, const MacroInfo *MI) {
}
- /// Defined - This hook is called whenever the 'defined' operator is seen.
+ /// \brief Hook called whenever the 'defined' operator is seen.
virtual void Defined(const Token &MacroNameTok) {
}
- /// SourceRangeSkipped - This hook is called when a source range is skipped.
+ /// \brief Hook called when a source range is skipped.
/// \param Range The SourceRange that was skipped. The range begins at the
- /// #if/#else directive and ends after the #endif/#else directive.
+ /// \#if/\#else directive and ends after the \#endif/\#else directive.
virtual void SourceRangeSkipped(SourceRange Range) {
}
- /// If -- This hook is called whenever an #if is seen.
+ /// \brief Hook called whenever an \#if is seen.
/// \param Loc the source location of the directive.
/// \param ConditionRange The SourceRange of the expression being tested.
+ ///
// FIXME: better to pass in a list (or tree!) of Tokens.
virtual void If(SourceLocation Loc, SourceRange ConditionRange) {
}
- /// Elif -- This hook is called whenever an #elif is seen.
+ /// \brief Hook called whenever an \#elif is seen.
/// \param Loc the source location of the directive.
/// \param ConditionRange The SourceRange of the expression being tested.
- /// \param IfLoc the source location of the #if/#ifdef/#ifndef directive.
+ /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
// FIXME: better to pass in a list (or tree!) of Tokens.
virtual void Elif(SourceLocation Loc, SourceRange ConditionRange,
SourceLocation IfLoc) {
}
- /// Ifdef -- This hook is called whenever an #ifdef is seen.
+ /// \brief Hook called whenever an \#ifdef is seen.
/// \param Loc the source location of the directive.
- /// \param II Information on the token being tested.
+ /// \param MacroNameTok Information on the token being tested.
virtual void Ifdef(SourceLocation Loc, const Token &MacroNameTok) {
}
- /// Ifndef -- This hook is called whenever an #ifndef is seen.
+ /// \brief Hook called whenever an \#ifndef is seen.
/// \param Loc the source location of the directive.
- /// \param II Information on the token being tested.
+ /// \param MacroNameTok Information on the token being tested.
virtual void Ifndef(SourceLocation Loc, const Token &MacroNameTok) {
}
- /// Else -- This hook is called whenever an #else is seen.
+ /// \brief Hook called whenever an \#else is seen.
/// \param Loc the source location of the directive.
- /// \param IfLoc the source location of the #if/#ifdef/#ifndef directive.
+ /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
virtual void Else(SourceLocation Loc, SourceLocation IfLoc) {
}
- /// Endif -- This hook is called whenever an #endif is seen.
+ /// \brief Hook called whenever an \#endif is seen.
/// \param Loc the source location of the directive.
- /// \param IfLoc the source location of the #if/#ifdef/#ifndef directive.
+ /// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
virtual void Endif(SourceLocation Loc, SourceLocation IfLoc) {
}
};
-/// PPChainedCallbacks - Simple wrapper class for chaining callbacks.
+/// \brief Simple wrapper class for chaining callbacks.
class PPChainedCallbacks : public PPCallbacks {
virtual void anchor();
PPCallbacks *First, *Second;
@@ -342,38 +341,38 @@ public:
Second->SourceRangeSkipped(Range);
}
- /// If -- This hook is called whenever an #if is seen.
+ /// \brief Hook called whenever an \#if is seen.
virtual void If(SourceLocation Loc, SourceRange ConditionRange) {
First->If(Loc, ConditionRange);
Second->If(Loc, ConditionRange);
}
- /// Elif -- This hook is called whenever an #if is seen.
+ /// \brief Hook called whenever an \#if is seen.
virtual void Elif(SourceLocation Loc, SourceRange ConditionRange,
SourceLocation IfLoc) {
First->Elif(Loc, ConditionRange, IfLoc);
Second->Elif(Loc, ConditionRange, IfLoc);
}
- /// Ifdef -- This hook is called whenever an #ifdef is seen.
+ /// \brief Hook called whenever an \#ifdef is seen.
virtual void Ifdef(SourceLocation Loc, const Token &MacroNameTok) {
First->Ifdef(Loc, MacroNameTok);
Second->Ifdef(Loc, MacroNameTok);
}
- /// Ifndef -- This hook is called whenever an #ifndef is seen.
+ /// \brief Hook called whenever an \#ifndef is seen.
virtual void Ifndef(SourceLocation Loc, const Token &MacroNameTok) {
First->Ifndef(Loc, MacroNameTok);
Second->Ifndef(Loc, MacroNameTok);
}
- /// Else -- This hook is called whenever an #else is seen.
+ /// \brief Hook called whenever an \#else is seen.
virtual void Else(SourceLocation Loc, SourceLocation IfLoc) {
First->Else(Loc, IfLoc);
Second->Else(Loc, IfLoc);
}
- /// Endif -- This hook is called whenever an #endif is seen.
+ /// \brief Hook called whenever an \#endif is seen.
virtual void Endif(SourceLocation Loc, SourceLocation IfLoc) {
First->Endif(Loc, IfLoc);
Second->Endif(Loc, IfLoc);
diff --git a/include/clang/Lex/Pragma.h b/include/clang/Lex/Pragma.h
index 4868811..087448f 100644
--- a/include/clang/Lex/Pragma.h
+++ b/include/clang/Lex/Pragma.h
@@ -26,12 +26,12 @@ namespace clang {
class PragmaNamespace;
/**
- * \brief Describes how the pragma was introduced, e.g., with #pragma,
+ * \brief Describes how the pragma was introduced, e.g., with \#pragma,
* _Pragma, or __pragma.
*/
enum PragmaIntroducerKind {
/**
- * \brief The pragma was introduced via #pragma.
+ * \brief The pragma was introduced via \#pragma.
*/
PIK_HashPragma,
@@ -54,7 +54,7 @@ namespace clang {
/// pragmas the handler with a null identifier is invoked, if it exists.
///
/// Note that the PragmaNamespace class can be used to subdivide pragmas, e.g.
-/// we treat "#pragma STDC" and "#pragma GCC" as namespaces that contain other
+/// we treat "\#pragma STDC" and "\#pragma GCC" as namespaces that contain other
/// pragmas.
class PragmaHandler {
std::string Name;
@@ -84,8 +84,8 @@ public:
/// PragmaNamespace - This PragmaHandler subdivides the namespace of pragmas,
/// allowing hierarchical pragmas to be defined. Common examples of namespaces
-/// are "#pragma GCC", "#pragma STDC", and "#pragma omp", but any namespaces may
-/// be (potentially recursively) defined.
+/// are "\#pragma GCC", "\#pragma STDC", and "\#pragma omp", but any namespaces
+/// may be (potentially recursively) defined.
class PragmaNamespace : public PragmaHandler {
/// Handlers - This is a map of the handlers in this namespace with their name
/// as key.
diff --git a/include/clang/Lex/PreprocessingRecord.h b/include/clang/Lex/PreprocessingRecord.h
index 45e3a5d..fb3e081 100644
--- a/include/clang/Lex/PreprocessingRecord.h
+++ b/include/clang/Lex/PreprocessingRecord.h
@@ -59,8 +59,8 @@ namespace clang {
/// \brief A macro definition.
MacroDefinitionKind,
- /// \brief An inclusion directive, such as \c #include, \c
- /// #import, or \c #include_next.
+ /// \brief An inclusion directive, such as \c \#include, \c
+ /// \#import, or \c \#include_next.
InclusionDirectiveKind,
/// @}
@@ -197,19 +197,19 @@ namespace clang {
};
/// \brief Record the location of an inclusion directive, such as an
- /// \c #include or \c #import statement.
+ /// \c \#include or \c \#import statement.
class InclusionDirective : public PreprocessingDirective {
public:
/// \brief The kind of inclusion directives known to the
/// preprocessor.
enum InclusionKind {
- /// \brief An \c #include directive.
+ /// \brief An \c \#include directive.
Include,
- /// \brief An Objective-C \c #import directive.
+ /// \brief An Objective-C \c \#import directive.
Import,
- /// \brief A GNU \c #include_next directive.
+ /// \brief A GNU \c \#include_next directive.
IncludeNext,
- /// \brief A Clang \c #__include_macros directive.
+ /// \brief A Clang \c \#__include_macros directive.
IncludeMacros
};
@@ -551,7 +551,7 @@ namespace clang {
///
/// Can be used to avoid implicit deserializations of preallocated
/// preprocessed entities if we only care about entities of a specific file
- /// and not from files #included in the range given at
+ /// and not from files \#included in the range given at
/// \see getPreprocessedEntitiesInRange.
bool isEntityInFileID(iterator PPEI, FileID FID);
@@ -565,7 +565,7 @@ namespace clang {
}
/// \brief Returns true if the given range intersects with a conditional
- /// directive. if a #if/#endif block is fully contained within the range,
+ /// directive. if a \#if/\#endif block is fully contained within the range,
/// this function will return false.
bool rangeIntersectsConditionalDirective(SourceRange Range) const;
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index 055008f..02e3f1e 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -58,7 +58,7 @@ class ModuleLoader;
/// Preprocessor - This object engages in a tight little dance with the lexer to
/// efficiently preprocess tokens. Lexers know only about tokens within a
/// single source file, and don't know anything about preprocessor-level issues
-/// like the #include stack, token expansion, etc.
+/// like the \#include stack, token expansion, etc.
///
class Preprocessor : public RefCountedBase<Preprocessor> {
DiagnosticsEngine *Diags;
@@ -103,7 +103,7 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
unsigned CounterValue; // Next __COUNTER__ value.
enum {
- /// MaxIncludeStackDepth - Maximum depth of #includes.
+ /// MaxIncludeStackDepth - Maximum depth of \#includes.
MaxAllowedIncludeStackDepth = 200
};
@@ -121,9 +121,19 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
/// DisableMacroExpansion - True if macro expansion is disabled.
bool DisableMacroExpansion : 1;
+ /// MacroExpansionInDirectivesOverride - Temporarily disables
+ /// DisableMacroExpansion (i.e. enables expansion) when parsing preprocessor
+ /// directives.
+ bool MacroExpansionInDirectivesOverride : 1;
+
+ class ResetMacroExpansionHelper;
+
/// \brief Whether we have already loaded macros from the external source.
mutable bool ReadMacrosFromExternalSource : 1;
+ /// \brief True if pragmas are enabled.
+ bool PragmasEnabled : 1;
+
/// \brief True if we are pre-expanding macro arguments.
bool InMacroArgPreExpansion;
@@ -165,11 +175,12 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
unsigned CodeCompletionOffset;
/// \brief The location for the code-completion point. This gets instantiated
- /// when the CodeCompletionFile gets #include'ed for preprocessing.
+ /// when the CodeCompletionFile gets \#include'ed for preprocessing.
SourceLocation CodeCompletionLoc;
/// \brief The start location for the file of the code-completion point.
- /// This gets instantiated when the CodeCompletionFile gets #include'ed
+ ///
+ /// This gets instantiated when the CodeCompletionFile gets \#include'ed
/// for preprocessing.
SourceLocation CodeCompletionFileLoc;
@@ -215,7 +226,7 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
/// CurLookup - The DirectoryLookup structure used to find the current
/// FileEntry, if CurLexer is non-null and if applicable. This allows us to
- /// implement #include_next and find directory-specific properties.
+ /// implement \#include_next and find directory-specific properties.
const DirectoryLookup *CurDirLookup;
/// CurTokenLexer - This is the current macro we are expanding, if we are
@@ -232,7 +243,7 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
} CurLexerKind;
/// IncludeMacroStack - This keeps track of the stack of files currently
- /// #included, and macros currently being expanded from, not counting
+ /// \#included, and macros currently being expanded from, not counting
/// CurLexer/CurTokenLexer.
struct IncludeStackInfo {
enum CurLexerKind CurLexerKind;
@@ -251,9 +262,18 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
std::vector<IncludeStackInfo> IncludeMacroStack;
/// Callbacks - These are actions invoked when some preprocessor activity is
- /// encountered (e.g. a file is #included, etc).
+ /// encountered (e.g. a file is \#included, etc).
PPCallbacks *Callbacks;
+ struct MacroExpandsInfo {
+ Token Tok;
+ MacroInfo *MI;
+ SourceRange Range;
+ MacroExpandsInfo(Token Tok, MacroInfo *MI, SourceRange Range)
+ : Tok(Tok), MI(MI), Range(Range) { }
+ };
+ SmallVector<MacroExpandsInfo, 2> DelayedMacroExpandsCallbacks;
+
/// Macros - For each IdentifierInfo with 'HasMacro' set, we keep a mapping
/// to the actual definition of the macro.
llvm::DenseMap<IdentifierInfo*, MacroInfo*> Macros;
@@ -400,6 +420,9 @@ public:
bool getCommentRetentionState() const { return KeepComments; }
+ void setPragmasEnabled(bool Enabled) { PragmasEnabled = Enabled; }
+ bool getPragmasEnabled() const { return PragmasEnabled; }
+
void SetSuppressIncludeNotFoundError(bool Suppress) {
SuppressIncludeNotFoundError = Suppress;
}
@@ -434,8 +457,8 @@ public:
Callbacks = C;
}
- /// getMacroInfo - Given an identifier, return the MacroInfo it is #defined to
- /// or null if it isn't #define'd.
+ /// \brief Given an identifier, return the MacroInfo it is \#defined to
+ /// or null if it isn't \#define'd.
MacroInfo *getMacroInfo(IdentifierInfo *II) const {
if (!II->hasMacroDefinition())
return 0;
@@ -443,8 +466,7 @@ public:
return getInfoForMacro(II);
}
- /// setMacroInfo - Specify a macro for this identifier.
- ///
+ /// \brief Specify a macro for this identifier.
void setMacroInfo(IdentifierInfo *II, MacroInfo *MI,
bool LoadedFromAST = false);
@@ -488,12 +510,12 @@ public:
}
/// \brief Add the specified comment handler to the preprocessor.
- void AddCommentHandler(CommentHandler *Handler);
+ void addCommentHandler(CommentHandler *Handler);
/// \brief Remove the specified comment handler.
///
/// It is an error to remove a handler that has not been registered.
- void RemoveCommentHandler(CommentHandler *Handler);
+ void removeCommentHandler(CommentHandler *Handler);
/// \brief Set the code completion handler to the given object.
void setCodeCompletionHandler(CodeCompletionHandler &Handler) {
@@ -634,6 +656,12 @@ public:
while (Result.getKind() == tok::comment);
}
+ /// Disables macro expansion everywhere except for preprocessor directives.
+ void SetMacroExpansionOnlyInDirectives() {
+ DisableMacroExpansion = true;
+ MacroExpansionInDirectivesOverride = true;
+ }
+
/// LookAhead - This peeks ahead N tokens and returns that token without
/// consuming any tokens. LookAhead(0) returns the next token that would be
/// returned by Lex(), LookAhead(1) returns the token after it, etc. This
@@ -752,25 +780,24 @@ public:
getDiagnostics().setSuppressAllDiagnostics(true);
}
- /// \brief The location of the currently-active #pragma clang
+ /// \brief The location of the currently-active \#pragma clang
/// arc_cf_code_audited begin. Returns an invalid location if there
/// is no such pragma active.
SourceLocation getPragmaARCCFCodeAuditedLoc() const {
return PragmaARCCFCodeAuditedLoc;
}
- /// \brief Set the location of the currently-active #pragma clang
+ /// \brief Set the location of the currently-active \#pragma clang
/// arc_cf_code_audited begin. An invalid location ends the pragma.
void setPragmaARCCFCodeAuditedLoc(SourceLocation Loc) {
PragmaARCCFCodeAuditedLoc = Loc;
}
- /// \brief Instruct the preprocessor to skip part of the main
- /// the main source file.
+ /// \brief Instruct the preprocessor to skip part of the main source file.
///
- /// \brief Bytes The number of bytes in the preamble to skip.
+ /// \param Bytes The number of bytes in the preamble to skip.
///
- /// \brief StartOfLine Whether skipping these bytes puts the lexer at the
+ /// \param StartOfLine Whether skipping these bytes puts the lexer at the
/// start of a line.
void setSkipMainFilePreamble(unsigned Bytes, bool StartOfLine) {
SkipMainFilePreamble.first = Bytes;
@@ -1035,24 +1062,27 @@ public:
/// \brief Retrieves the module that we're currently building, if any.
Module *getCurrentModule();
- /// AllocateMacroInfo - Allocate a new MacroInfo object with the provide
- /// SourceLocation.
+ /// \brief Allocate a new MacroInfo object with the provided SourceLocation.
MacroInfo *AllocateMacroInfo(SourceLocation L);
- /// CloneMacroInfo - Allocate a new MacroInfo object which is clone of MI.
+ /// \brief Allocate a new MacroInfo object which is clone of \p MI.
MacroInfo *CloneMacroInfo(const MacroInfo &MI);
- /// GetIncludeFilenameSpelling - Turn the specified lexer token into a fully
- /// checked and spelled filename, e.g. as an operand of #include. This returns
- /// true if the input filename was in <>'s or false if it were in ""'s. The
- /// caller is expected to provide a buffer that is large enough to hold the
- /// spelling of the filename, but is also expected to handle the case when
- /// this method decides to use a different buffer.
+ /// \brief Turn the specified lexer token into a fully checked and spelled
+ /// filename, e.g. as an operand of \#include.
+ ///
+ /// The caller is expected to provide a buffer that is large enough to hold
+ /// the spelling of the filename, but is also expected to handle the case
+ /// when this method decides to use a different buffer.
+ ///
+ /// \returns true if the input filename was in <>'s or false if it was
+ /// in ""'s.
bool GetIncludeFilenameSpelling(SourceLocation Loc,StringRef &Filename);
- /// LookupFile - Given a "foo" or <foo> reference, look up the indicated file,
- /// return null on failure. isAngled indicates whether the file reference is
- /// for system #include's or not (i.e. using <> instead of "").
+ /// \brief Given a "foo" or \<foo> reference, look up the indicated file.
+ ///
+ /// Returns null on failure. \p isAngled indicates whether the file
+ /// reference is for system \#include's or not (i.e. using <> instead of "").
const FileEntry *LookupFile(StringRef Filename,
bool isAngled, const DirectoryLookup *FromDir,
const DirectoryLookup *&CurDir,
@@ -1063,18 +1093,19 @@ public:
/// GetCurLookup - The DirectoryLookup structure used to find the current
/// FileEntry, if CurLexer is non-null and if applicable. This allows us to
- /// implement #include_next and find directory-specific properties.
+ /// implement \#include_next and find directory-specific properties.
const DirectoryLookup *GetCurDirLookup() { return CurDirLookup; }
- /// isInPrimaryFile - Return true if we're in the top-level file, not in a
- /// #include.
+ /// \brief Return true if we're in the top-level file, not in a \#include.
bool isInPrimaryFile() const;
- /// ConcatenateIncludeName - Handle cases where the #include name is expanded
+ /// ConcatenateIncludeName - Handle cases where the \#include name is expanded
/// from a macro as multiple tokens, which need to be glued together. This
/// occurs for code like:
- /// #define FOO <a/b.h>
- /// #include FOO
+ /// \code
+ /// \#define FOO <a/b.h>
+ /// \#include FOO
+ /// \endcode
/// because in this case, "<a/b.h>" is returned as 7 tokens, not one.
///
/// This code concatenates and consumes tokens up to the '>' token. It
@@ -1109,15 +1140,16 @@ private:
IncludeMacroStack.pop_back();
}
- /// AllocateMacroInfo - Allocate a new MacroInfo object.
+ /// \brief Allocate a new MacroInfo object.
MacroInfo *AllocateMacroInfo();
- /// ReleaseMacroInfo - Release the specified MacroInfo. This memory will
- /// be reused for allocating new MacroInfo objects.
+ /// \brief Release the specified MacroInfo for re-use.
+ ///
+ /// This memory will be reused for allocating new MacroInfo objects.
void ReleaseMacroInfo(MacroInfo* MI);
/// ReadMacroName - Lex and validate a macro name, which occurs after a
- /// #define or #undef. This emits a diagnostic, sets the token kind to eod,
+ /// \#define or \#undef. This emits a diagnostic, sets the token kind to eod,
/// and discards the rest of the macro line if the macro name is invalid.
void ReadMacroName(Token &MacroNameTok, char isDefineUndef = 0);
@@ -1128,20 +1160,19 @@ private:
/// Return true if an error occurs parsing the arg list.
bool ReadMacroDefinitionArgList(MacroInfo *MI, Token& LastTok);
- /// SkipExcludedConditionalBlock - We just read a #if or related directive and
- /// decided that the subsequent tokens are in the #if'd out portion of the
- /// file. Lex the rest of the file, until we see an #endif. If
+ /// We just read a \#if or related directive and decided that the
+ /// subsequent tokens are in the \#if'd out portion of the
+ /// file. Lex the rest of the file, until we see an \#endif. If \p
/// FoundNonSkipPortion is true, then we have already emitted code for part of
- /// this #if directive, so #else/#elif blocks should never be entered. If
- /// FoundElse is false, then #else directives are ok, if not, then we have
- /// already seen one so a #else directive is a duplicate. When this returns,
+ /// this \#if directive, so \#else/\#elif blocks should never be entered. If
+ /// \p FoundElse is false, then \#else directives are ok, if not, then we have
+ /// already seen one so a \#else directive is a duplicate. When this returns,
/// the caller can lex the first valid token.
void SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
bool FoundNonSkipPortion, bool FoundElse,
SourceLocation ElseLoc = SourceLocation());
- /// PTHSkipExcludedConditionalBlock - A fast PTH version of
- /// SkipExcludedConditionalBlock.
+ /// \brief A fast PTH version of SkipExcludedConditionalBlock.
void PTHSkipExcludedConditionalBlock();
/// EvaluateDirectiveExpression - Evaluate an integer constant expression that
@@ -1150,11 +1181,10 @@ private:
bool EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro);
/// RegisterBuiltinPragmas - Install the standard preprocessor pragmas:
- /// #pragma GCC poison/system_header/dependency and #pragma once.
+ /// \#pragma GCC poison/system_header/dependency and \#pragma once.
void RegisterBuiltinPragmas();
- /// RegisterBuiltinMacros - Register builtin macros, such as __LINE__ with the
- /// identifier table.
+ /// \brief Register builtin macros such as __LINE__ with the identifier table.
void RegisterBuiltinMacros();
/// HandleMacroExpandedIdentifier - If an identifier token is read that is to
diff --git a/include/clang/Lex/PreprocessorLexer.h b/include/clang/Lex/PreprocessorLexer.h
index b551cd4..8a0b3cf 100644
--- a/include/clang/Lex/PreprocessorLexer.h
+++ b/include/clang/Lex/PreprocessorLexer.h
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the PreprocessorLexer interface.
-//
+///
+/// \file
+/// \brief Defines the PreprocessorLexer interface.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_PreprocessorLexer_H
@@ -38,16 +39,17 @@ protected:
// Context-specific lexing flags set by the preprocessor.
//===--------------------------------------------------------------------===//
- /// ParsingPreprocessorDirective - This is true when parsing #XXX. This turns
- /// '\n' into a tok::eod token.
+ /// \brief True when parsing \#XXX; turns '\\n' into a tok::eod token.
bool ParsingPreprocessorDirective;
- /// ParsingFilename - True after #include: this turns <xx> into a
- /// tok::angle_string_literal token.
+ /// \brief True after \#include; turns \<xx> into a tok::angle_string_literal
+ /// token.
bool ParsingFilename;
- /// LexingRawMode - True if in raw mode: This flag disables interpretation of
- /// tokens and is a far faster mode to lex in than non-raw-mode. This flag:
+ /// \brief True if in raw mode.
+ ///
+ /// Raw mode disables interpretation of tokens and is a far faster mode to
+ /// lex in than non-raw-mode. This flag:
/// 1. If EOF of the current lexer is found, the include stack isn't popped.
/// 2. Identifier information is not looked up for identifier tokens. As an
/// effect of this, implicit macro expansion is naturally disabled.
@@ -59,11 +61,11 @@ protected:
/// Note that in raw mode that the PP pointer may be null.
bool LexingRawMode;
- /// MIOpt - This is a state machine that detects the #ifndef-wrapping a file
+ /// \brief A state machine that detects the \#ifndef-wrapping a file
/// idiom for the multiple-include optimization.
MultipleIncludeOpt MIOpt;
- /// ConditionalStack - Information about the set of #if/#ifdef/#ifndef blocks
+ /// \brief Information about the set of \#if/\#ifdef/\#ifndef blocks
/// we are currently in.
SmallVector<PPConditionalInfo, 4> ConditionalStack;
@@ -83,16 +85,15 @@ protected:
virtual void IndirectLex(Token& Result) = 0;
- /// getSourceLocation - Return the source location for the next observable
- /// location.
+ /// \brief Return the source location for the next observable location.
virtual SourceLocation getSourceLocation() = 0;
//===--------------------------------------------------------------------===//
// #if directive handling.
- /// pushConditionalLevel - When we enter a #if directive, this keeps track of
- /// what we are currently in for diagnostic emission (e.g. #if with missing
- /// #endif).
+ /// pushConditionalLevel - When we enter a \#if directive, this keeps track of
+ /// what we are currently in for diagnostic emission (e.g. \#if with missing
+ /// \#endif).
void pushConditionalLevel(SourceLocation DirectiveStart, bool WasSkipping,
bool FoundNonSkip, bool FoundElse) {
PPConditionalInfo CI;
@@ -116,8 +117,8 @@ protected:
return false;
}
- /// peekConditionalLevel - Return the top of the conditional stack. This
- /// requires that there be a conditional active.
+ /// \brief Return the top of the conditional stack.
+ /// \pre This requires that there be a conditional active.
PPConditionalInfo &peekConditionalLevel() {
assert(!ConditionalStack.empty() && "No conditionals active!");
return ConditionalStack.back();
@@ -130,21 +131,23 @@ public:
//===--------------------------------------------------------------------===//
// Misc. lexing methods.
- /// LexIncludeFilename - After the preprocessor has parsed a #include, lex and
- /// (potentially) macro expand the filename. If the sequence parsed is not
- /// lexically legal, emit a diagnostic and return a result EOD token.
+ /// \brief After the preprocessor has parsed a \#include, lex and
+ /// (potentially) macro expand the filename.
+ ///
+ /// If the sequence parsed is not lexically legal, emit a diagnostic and
+ /// return a result EOD token.
void LexIncludeFilename(Token &Result);
- /// setParsingPreprocessorDirective - Inform the lexer whether or not
- /// we are currently lexing a preprocessor directive.
+ /// \brief Inform the lexer whether or not we are currently lexing a
+ /// preprocessor directive.
void setParsingPreprocessorDirective(bool f) {
ParsingPreprocessorDirective = f;
}
- /// isLexingRawMode - Return true if this lexer is in raw mode or not.
+ /// \brief Return true if this lexer is in raw mode or not.
bool isLexingRawMode() const { return LexingRawMode; }
- /// getPP - Return the preprocessor object for this lexer.
+ /// \brief Return the preprocessor object for this lexer.
Preprocessor *getPP() const { return PP; }
FileID getFileID() const {
@@ -163,7 +166,7 @@ public:
const FileEntry *getFileEntry() const;
/// \brief Iterator that traverses the current stack of preprocessor
- /// conditional directives (#if/#ifdef/#ifndef).
+ /// conditional directives (\#if/\#ifdef/\#ifndef).
typedef SmallVectorImpl<PPConditionalInfo>::const_iterator
conditional_iterator;
diff --git a/include/clang/Lex/Token.h b/include/clang/Lex/Token.h
index a88f607..9c5a023 100644
--- a/include/clang/Lex/Token.h
+++ b/include/clang/Lex/Token.h
@@ -87,7 +87,7 @@ public:
bool is(tok::TokenKind K) const { return Kind == (unsigned) K; }
bool isNot(tok::TokenKind K) const { return Kind != (unsigned) K; }
- /// isAnyIdentifier - Return true if this is a raw identifier (when lexing
+ /// \brief Return true if this is a raw identifier (when lexing
/// in raw mode) or a non-keyword identifier (when lexing in non-raw mode).
bool isAnyIdentifier() const {
return is(tok::identifier) || is(tok::raw_identifier);
@@ -112,7 +112,7 @@ public:
return false;
}
- /// getLocation - Return a source location identifier for the specified
+ /// \brief Return a source location identifier for the specified
/// offset in the current file.
SourceLocation getLocation() const { return Loc; }
unsigned getLength() const {
@@ -139,8 +139,8 @@ public:
return isAnnotation() ? getAnnotationEndLoc() : getLocation();
}
- /// getAnnotationRange - SourceRange of the group of tokens that this
- /// annotation token represents.
+ /// \brief SourceRange of the group of tokens that this annotation token
+ /// represents.
SourceRange getAnnotationRange() const {
return SourceRange(getLocation(), getAnnotationEndLoc());
}
@@ -153,8 +153,7 @@ public:
return tok::getTokenName( (tok::TokenKind) Kind);
}
- /// startToken - Reset all flags to cleared.
- ///
+ /// \brief Reset all flags to cleared.
void startToken() {
Kind = tok::unknown;
Flags = 0;
@@ -208,24 +207,25 @@ public:
PtrData = val;
}
- /// setFlag - Set the specified flag.
+ /// \brief Set the specified flag.
void setFlag(TokenFlags Flag) {
Flags |= Flag;
}
- /// clearFlag - Unset the specified flag.
+ /// \brief Unset the specified flag.
void clearFlag(TokenFlags Flag) {
Flags &= ~Flag;
}
- /// getFlags - Return the internal represtation of the flags.
- /// Only intended for low-level operations such as writing tokens to
- // disk.
+ /// \brief Return the internal represtation of the flags.
+ ///
+ /// This is only intended for low-level operations such as writing tokens to
+ /// disk.
unsigned getFlags() const {
return Flags;
}
- /// setFlagValue - Set a flag to either true or false.
+ /// \brief Set a flag to either true or false.
void setFlagValue(TokenFlags Flag, bool Val) {
if (Val)
setFlag(Flag);
@@ -237,25 +237,23 @@ public:
///
bool isAtStartOfLine() const { return (Flags & StartOfLine) ? true : false; }
- /// hasLeadingSpace - Return true if this token has whitespace before it.
+ /// \brief Return true if this token has whitespace before it.
///
bool hasLeadingSpace() const { return (Flags & LeadingSpace) ? true : false; }
- /// isExpandDisabled - Return true if this identifier token should never
+ /// \brief Return true if this identifier token should never
/// be expanded in the future, due to C99 6.10.3.4p2.
bool isExpandDisabled() const {
return (Flags & DisableExpand) ? true : false;
}
- /// isObjCAtKeyword - Return true if we have an ObjC keyword identifier.
+ /// \brief Return true if we have an ObjC keyword identifier.
bool isObjCAtKeyword(tok::ObjCKeywordKind objcKey) const;
- /// getObjCKeywordID - Return the ObjC keyword kind.
+ /// \brief Return the ObjC keyword kind.
tok::ObjCKeywordKind getObjCKeywordID() const;
- /// needsCleaning - Return true if this token has trigraphs or escaped
- /// newlines in it.
- ///
+ /// \brief Return true if this token has trigraphs or escaped newlines in it.
bool needsCleaning() const { return (Flags & NeedsCleaning) ? true : false; }
/// \brief Return true if this token has an empty macro before it.
@@ -269,23 +267,22 @@ public:
bool hasUDSuffix() const { return (Flags & HasUDSuffix) ? true : false; }
};
-/// PPConditionalInfo - Information about the conditional stack (#if directives)
+/// \brief Information about the conditional stack (\#if directives)
/// currently active.
struct PPConditionalInfo {
- /// IfLoc - Location where the conditional started.
- ///
+ /// \brief Location where the conditional started.
SourceLocation IfLoc;
- /// WasSkipping - True if this was contained in a skipping directive, e.g.
- /// in a "#if 0" block.
+ /// \brief True if this was contained in a skipping directive, e.g.,
+ /// in a "\#if 0" block.
bool WasSkipping;
- /// FoundNonSkip - True if we have emitted tokens already, and now we're in
- /// an #else block or something. Only useful in Skipping blocks.
+ /// \brief True if we have emitted tokens already, and now we're in
+ /// an \#else block or something. Only useful in Skipping blocks.
bool FoundNonSkip;
- /// FoundElse - True if we've seen a #else in this block. If so,
- /// #elif/#else directives are not allowed.
+ /// \brief True if we've seen a \#else in this block. If so,
+ /// \#elif/\#else directives are not allowed.
bool FoundElse;
};
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 0ae5dc8..353b59e 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -23,14 +23,20 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/SaveAndRestore.h"
#include <stack>
namespace clang {
class PragmaHandler;
class Scope;
+ class BalancedDelimiterTracker;
class DeclGroupRef;
class DiagnosticBuilder;
class Parser;
+ class ParsingDeclRAIIObject;
+ class ParsingDeclSpec;
+ class ParsingDeclarator;
+ class ParsingFieldDeclarator;
class PragmaUnusedHandler;
class ColonProtectionRAIIObject;
class InMessageExpressionRAIIObject;
@@ -79,7 +85,9 @@ class Parser : public CodeCompletionHandler {
friend class ColonProtectionRAIIObject;
friend class InMessageExpressionRAIIObject;
friend class PoisonSEHIdentifiersRAIIObject;
+ friend class ObjCDeclContextSwitch;
friend class ParenBraceBracketBalancer;
+ friend class BalancedDelimiterTracker;
Preprocessor &PP;
@@ -94,7 +102,7 @@ class Parser : public CodeCompletionHandler {
SourceLocation PrevTokLocation;
unsigned short ParenCount, BracketCount, BraceCount;
-
+
/// Actions - These are the callbacks we invoke as we parse various constructs
/// in the file.
Sema &Actions;
@@ -165,6 +173,7 @@ class Parser : public CodeCompletionHandler {
OwningPtr<PragmaHandler> RedefineExtnameHandler;
OwningPtr<PragmaHandler> FPContractHandler;
OwningPtr<PragmaHandler> OpenCLExtensionHandler;
+ OwningPtr<CommentHandler> CommentSemaHandler;
/// Whether the '>' token acts as an operator or not. This will be
/// true except when we are parsing an expression within a C++
@@ -197,6 +206,13 @@ class Parser : public CodeCompletionHandler {
IdentifierInfo *getSEHExceptKeyword();
+ /// True if we are within an Objective-C container while parsing C-like decls.
+ ///
+ /// This is necessary because Sema thinks we have left the container
+ /// to parse the C-like decls, meaning Actions.getObjCDeclContext() will
+ /// be NULL.
+ bool ParsingInObjCContainer;
+
bool SkipFunctionBodies;
public:
@@ -207,6 +223,7 @@ public:
const TargetInfo &getTargetInfo() const { return PP.getTargetInfo(); }
Preprocessor &getPreprocessor() const { return PP; }
Sema &getActions() const { return Actions; }
+ AttributeFactory &getAttrFactory() { return AttrFactory; }
const Token &getCurToken() const { return Tok; }
Scope *getCurScope() const { return Actions.getCurScope(); }
@@ -434,78 +451,6 @@ private:
return PP.LookAhead(0);
}
- /// \brief RAII class that helps handle the parsing of an open/close delimiter
- /// pair, such as braces { ... } or parentheses ( ... ).
- class BalancedDelimiterTracker {
- Parser& P;
- tok::TokenKind Kind, Close;
- SourceLocation (Parser::*Consumer)();
- SourceLocation LOpen, LClose;
-
- unsigned short &getDepth() {
- switch (Kind) {
- case tok::l_brace: return P.BraceCount;
- case tok::l_square: return P.BracketCount;
- case tok::l_paren: return P.ParenCount;
- default: llvm_unreachable("Wrong token kind");
- }
- }
-
- enum { MaxDepth = 256 };
-
- bool diagnoseOverflow();
- bool diagnoseMissingClose();
-
- public:
- BalancedDelimiterTracker(Parser& p, tok::TokenKind k) : P(p), Kind(k) {
- switch (Kind) {
- default: llvm_unreachable("Unexpected balanced token");
- case tok::l_brace:
- Close = tok::r_brace;
- Consumer = &Parser::ConsumeBrace;
- break;
- case tok::l_paren:
- Close = tok::r_paren;
- Consumer = &Parser::ConsumeParen;
- break;
-
- case tok::l_square:
- Close = tok::r_square;
- Consumer = &Parser::ConsumeBracket;
- break;
- }
- }
-
- SourceLocation getOpenLocation() const { return LOpen; }
- SourceLocation getCloseLocation() const { return LClose; }
- SourceRange getRange() const { return SourceRange(LOpen, LClose); }
-
- bool consumeOpen() {
- if (!P.Tok.is(Kind))
- return true;
-
- if (getDepth() < MaxDepth) {
- LOpen = (P.*Consumer)();
- return false;
- }
-
- return diagnoseOverflow();
- }
-
- bool expectAndConsume(unsigned DiagID,
- const char *Msg = "",
- tok::TokenKind SkipToTok = tok::unknown);
- bool consumeClose() {
- if (P.Tok.is(Close)) {
- LClose = (P.*Consumer)();
- return false;
- }
-
- return diagnoseMissingClose();
- }
- void skipToEnd();
- };
-
/// getTypeAnnotation - Read a parsed type out of an annotation token.
static ParsedType getTypeAnnotation(Token &Tok) {
return ParsedType::getFromOpaquePtr(Tok.getAnnotationValue());
@@ -621,9 +566,11 @@ private:
class ObjCDeclContextSwitch {
Parser &P;
Decl *DC;
+ SaveAndRestore<bool> WithinObjCContainer;
public:
- explicit ObjCDeclContextSwitch(Parser &p) : P(p),
- DC(p.getObjCDeclContext()) {
+ explicit ObjCDeclContextSwitch(Parser &p)
+ : P(p), DC(p.getObjCDeclContext()),
+ WithinObjCContainer(P.ParsingInObjCContainer, DC != 0) {
if (DC)
P.Actions.ActOnObjCTemporaryExitContainerContext(cast<DeclContext>(DC));
}
@@ -650,6 +597,17 @@ private:
/// to the semicolon, consumes that extra token.
bool ExpectAndConsumeSemi(unsigned DiagID);
+ /// \brief The kind of extra semi diagnostic to emit.
+ enum ExtraSemiKind {
+ OutsideFunction = 0,
+ InsideStruct = 1,
+ InstanceVariableList = 2,
+ AfterMemberFunctionDefinition = 3
+ };
+
+ /// \brief Consume any extra semi-colons until the end of the line.
+ void ConsumeExtraSemi(ExtraSemiKind Kind, unsigned TST = TST_unspecified);
+
//===--------------------------------------------------------------------===//
// Scope manipulation
@@ -715,6 +673,9 @@ private:
public:
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID);
DiagnosticBuilder Diag(const Token &Tok, unsigned DiagID);
+ DiagnosticBuilder Diag(unsigned DiagID) {
+ return Diag(Tok, DiagID);
+ }
private:
void SuggestParentheses(SourceLocation Loc, unsigned DK,
@@ -951,120 +912,7 @@ private:
return *ClassStack.top();
}
- /// \brief RAII object used to inform the actions that we're
- /// currently parsing a declaration. This is active when parsing a
- /// variable's initializer, but not when parsing the body of a
- /// class or function definition.
- class ParsingDeclRAIIObject {
- Sema &Actions;
- Sema::ParsingDeclState State;
- bool Popped;
-
- public:
- ParsingDeclRAIIObject(Parser &P) : Actions(P.Actions) {
- push();
- }
-
- ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *Other)
- : Actions(P.Actions) {
- if (Other) steal(*Other);
- else push();
- }
-
- /// Creates a RAII object which steals the state from a different
- /// object instead of pushing.
- ParsingDeclRAIIObject(ParsingDeclRAIIObject &Other)
- : Actions(Other.Actions) {
- steal(Other);
- }
-
- ~ParsingDeclRAIIObject() {
- abort();
- }
-
- /// Resets the RAII object for a new declaration.
- void reset() {
- abort();
- push();
- }
-
- /// Signals that the context was completed without an appropriate
- /// declaration being parsed.
- void abort() {
- pop(0);
- }
-
- void complete(Decl *D) {
- assert(!Popped && "ParsingDeclaration has already been popped!");
- pop(D);
- }
-
- private:
- void steal(ParsingDeclRAIIObject &Other) {
- State = Other.State;
- Popped = Other.Popped;
- Other.Popped = true;
- }
-
- void push() {
- State = Actions.PushParsingDeclaration();
- Popped = false;
- }
-
- void pop(Decl *D) {
- if (!Popped) {
- Actions.PopParsingDeclaration(State, D);
- Popped = true;
- }
- }
- };
-
- /// A class for parsing a DeclSpec.
- class ParsingDeclSpec : public DeclSpec {
- ParsingDeclRAIIObject ParsingRAII;
-
- public:
- ParsingDeclSpec(Parser &P) : DeclSpec(P.AttrFactory), ParsingRAII(P) {}
- ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII)
- : DeclSpec(P.AttrFactory), ParsingRAII(P, RAII) {}
-
- void complete(Decl *D) {
- ParsingRAII.complete(D);
- }
-
- void abort() {
- ParsingRAII.abort();
- }
- };
-
- /// A class for parsing a declarator.
- class ParsingDeclarator : public Declarator {
- ParsingDeclRAIIObject ParsingRAII;
-
- public:
- ParsingDeclarator(Parser &P, const ParsingDeclSpec &DS, TheContext C)
- : Declarator(DS, C), ParsingRAII(P) {
- }
-
- const ParsingDeclSpec &getDeclSpec() const {
- return static_cast<const ParsingDeclSpec&>(Declarator::getDeclSpec());
- }
-
- ParsingDeclSpec &getMutableDeclSpec() const {
- return const_cast<ParsingDeclSpec&>(getDeclSpec());
- }
-
- void clear() {
- Declarator::clear();
- ParsingRAII.reset();
- }
-
- void complete(Decl *D) {
- ParsingRAII.complete(D);
- }
- };
-
- /// \brief RAII object used to
+ /// \brief RAII object used to manage the parsing of a class definition.
class ParsingClassDefinition {
Parser &P;
bool Popped;
@@ -1183,7 +1031,7 @@ private:
void ParseLexedMethodDef(LexedMethod &LM);
void ParseLexedMemberInitializers(ParsingClass &Class);
void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI);
- Decl *ParseLexedObjCMethodDefs(LexedMethod &LM);
+ void ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod);
bool ConsumeAndStoreFunctionPrologue(CachedTokens &Toks);
bool ConsumeAndStoreUntil(tok::TokenKind T1,
CachedTokens &Toks,
@@ -1209,10 +1057,13 @@ private:
ParsingDeclSpec *DS = 0);
bool isDeclarationAfterDeclarator();
bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
- DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs,
- AccessSpecifier AS = AS_none);
- DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS,
+ DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
+ ParsedAttributesWithRange &attrs,
+ ParsingDeclSpec *DS = 0,
AccessSpecifier AS = AS_none);
+ DeclGroupPtrTy ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs,
+ ParsingDeclSpec &DS,
+ AccessSpecifier AS);
Decl *ParseFunctionDefinition(ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
@@ -1245,11 +1096,12 @@ private:
struct ObjCImplParsingDataRAII {
Parser &P;
Decl *Dcl;
+ bool HasCFunction;
typedef SmallVector<LexedMethod*, 8> LateParsedObjCMethodContainer;
LateParsedObjCMethodContainer LateParsedObjCMethods;
ObjCImplParsingDataRAII(Parser &parser, Decl *D)
- : P(parser), Dcl(D) {
+ : P(parser), Dcl(D), HasCFunction(false) {
P.CurParsedObjCImpl = this;
Finished = false;
}
@@ -1262,6 +1114,7 @@ private:
bool Finished;
};
ObjCImplParsingDataRAII *CurParsedObjCImpl;
+ void StashAwayMethodOrFunctionBodyTokens(Decl *MDecl);
DeclGroupPtrTy ParseObjCAtImplementationDeclaration(SourceLocation AtLoc);
DeclGroupPtrTy ParseObjCAtEndDeclaration(SourceRange atEnd);
@@ -1380,6 +1233,8 @@ private:
// C++ Expressions
ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
+ bool areTokensAdjacent(const Token &A, const Token &B);
+
void CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectTypePtr,
bool EnteringContext, IdentifierInfo &II,
CXXScopeSpec &SS);
@@ -1452,8 +1307,6 @@ private:
// C++ 5.2.3: Explicit type conversion (functional notation)
ExprResult ParseCXXTypeConstructExpression(const DeclSpec &DS);
- bool isCXXSimpleTypeSpecifier() const;
-
/// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers.
/// This should only be called when the current token is known to be part of
/// simple-type-specifier.
@@ -1508,6 +1361,7 @@ private:
ExprResult ParseObjCBooleanLiteral(SourceLocation AtLoc, bool ArgValue);
ExprResult ParseObjCArrayLiteral(SourceLocation AtLoc);
ExprResult ParseObjCDictionaryLiteral(SourceLocation AtLoc);
+ ExprResult ParseObjCBoxedExpr(SourceLocation AtLoc);
ExprResult ParseObjCEncodeExpression(SourceLocation AtLoc);
ExprResult ParseObjCSelectorExpression(SourceLocation AtLoc);
ExprResult ParseObjCProtocolExpression(SourceLocation AtLoc);
@@ -1639,7 +1493,7 @@ private:
enum DeclSpecContext {
DSC_normal, // normal context
DSC_class, // class context, enables 'friend'
- DSC_type_specifier, // C++ type-specifier-seq
+ DSC_type_specifier, // C++ type-specifier-seq or C specifier-qualifier-list
DSC_trailing, // C++11 trailing-type-specifier in a trailing return type
DSC_top_level // top-level/namespace declaration context
};
@@ -1659,7 +1513,7 @@ private:
DeclGroupPtrTy ParseSimpleDeclaration(StmtVector &Stmts,
unsigned Context,
SourceLocation &DeclEnd,
- ParsedAttributes &attrs,
+ ParsedAttributesWithRange &attrs,
bool RequireSemi,
ForRangeInit *FRI = 0);
bool MightBeDeclarator(unsigned Context);
@@ -1705,7 +1559,7 @@ private:
Decl *TagDecl);
struct FieldCallback {
- virtual Decl *invoke(FieldDeclarator &Field) = 0;
+ virtual void invoke(ParsingFieldDeclarator &Field) = 0;
virtual ~FieldCallback() {}
private:
@@ -1713,7 +1567,7 @@ private:
};
struct ObjCPropertyCallback;
- void ParseStructDeclaration(DeclSpec &DS, FieldCallback &Callback);
+ void ParseStructDeclaration(ParsingDeclSpec &DS, FieldCallback &Callback);
bool isDeclarationSpecifier(bool DisambiguatingWithExpression = false);
bool isTypeSpecifierQualifier();
@@ -1789,11 +1643,11 @@ private:
/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
/// a constructor-style initializer, when parsing declaration statements.
/// Returns true for function declarator and false for constructor-style
- /// initializer. If 'warnIfAmbiguous' is true a warning will be emitted to
- /// indicate that the parens were disambiguated as function declarator.
+ /// initializer. Sets 'IsAmbiguous' to true to indicate that this declaration
+ /// might be a constructor-style initializer.
/// If during the disambiguation process a parsing error is encountered,
/// the function returns true to let the declaration parsing code handle it.
- bool isCXXFunctionDeclarator(bool warnIfAmbiguous);
+ bool isCXXFunctionDeclarator(bool *IsAmbiguous = 0);
/// isCXXConditionDeclaration - Disambiguates between a declaration or an
/// expression for a condition of a if/switch/while/for statement.
@@ -1850,7 +1704,8 @@ private:
/// BracedCastResult.
/// Doesn't consume tokens.
TPResult
- isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False());
+ isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(),
+ bool *HasMissingTypename = 0);
// "Tentative parsing" functions, used for disambiguation. If a parsing error
// is encountered they will return TPResult::Error().
@@ -1859,13 +1714,13 @@ private:
// that more tentative parsing is necessary for disambiguation.
// They all consume tokens, so backtracking should be used after calling them.
- TPResult TryParseDeclarationSpecifier();
+ TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0);
TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl);
TPResult TryParseTypeofSpecifier();
TPResult TryParseProtocolQualifiers();
TPResult TryParseInitDeclaratorList();
TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true);
- TPResult TryParseParameterDeclarationClause();
+ TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0);
TPResult TryParseFunctionDeclarator();
TPResult TryParseBracketDeclarator();
@@ -1874,7 +1729,7 @@ private:
= Declarator::TypeNameContext,
AccessSpecifier AS = AS_none,
Decl **OwnedType = 0);
- void ParseBlockId();
+ void ParseBlockId(SourceLocation CaretLoc);
// Check for the start of a C++11 attribute-specifier-seq in a context where
// an attribute is not allowed.
@@ -1953,8 +1808,16 @@ private:
}
void ParseMicrosoftAttributes(ParsedAttributes &attrs,
SourceLocation *endLoc = 0);
- void ParseMicrosoftDeclSpec(ParsedAttributes &attrs);
+ void ParseMicrosoftDeclSpec(ParsedAttributes &Attrs);
+ bool IsSimpleMicrosoftDeclSpec(IdentifierInfo *Ident);
+ void ParseComplexMicrosoftDeclSpec(IdentifierInfo *Ident,
+ SourceLocation Loc,
+ ParsedAttributes &Attrs);
+ void ParseMicrosoftDeclSpecWithSingleArg(IdentifierInfo *AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs);
void ParseMicrosoftTypeAttributes(ParsedAttributes &attrs);
+ void ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs);
void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
void ParseOpenCLAttributes(ParsedAttributes &attrs);
void ParseOpenCLQualifiers(DeclSpec &DS);
@@ -2040,6 +1903,7 @@ private:
void ParseFunctionDeclarator(Declarator &D,
ParsedAttributes &attrs,
BalancedDelimiterTracker &Tracker,
+ bool IsAmbiguous,
bool RequiresArg = false);
bool isFunctionDeclaratorIdentifierList();
void ParseFunctionDeclaratorIdentifierList(
@@ -2100,6 +1964,7 @@ private:
//===--------------------------------------------------------------------===//
// C++ 9: classes [class] and C structs/unions.
+ bool isValidAfterTypeSpecifier(bool CouldBeBitfield);
void ParseClassSpecifier(tok::TokenKind TagTokKind, SourceLocation TagLoc,
DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo,
AccessSpecifier AS, bool EnteringContext,
diff --git a/include/clang/Rewrite/FrontendActions.h b/include/clang/Rewrite/FrontendActions.h
index 6e9ecac..ea876d9 100644
--- a/include/clang/Rewrite/FrontendActions.h
+++ b/include/clang/Rewrite/FrontendActions.h
@@ -73,6 +73,11 @@ protected:
void ExecuteAction();
};
+class RewriteIncludesAction : public PreprocessorFrontendAction {
+protected:
+ void ExecuteAction();
+};
+
} // end namespace clang
#endif
diff --git a/include/clang/Rewrite/Rewriter.h b/include/clang/Rewrite/Rewriter.h
index f1358a0..5ffd88b 100644
--- a/include/clang/Rewrite/Rewriter.h
+++ b/include/clang/Rewrite/Rewriter.h
@@ -279,6 +279,13 @@ public:
buffer_iterator buffer_begin() { return RewriteBuffers.begin(); }
buffer_iterator buffer_end() { return RewriteBuffers.end(); }
+ /// SaveFiles - Save all changed files to disk.
+ ///
+ /// Returns whether not all changes were saved successfully.
+ /// Outputs diagnostics via the source manager's diagnostic engine
+ /// in case of an error.
+ bool overwriteChangedFiles();
+
private:
unsigned getLocationOffsetAndFileID(SourceLocation Loc, FileID &FID) const;
};
diff --git a/include/clang/Rewrite/Rewriters.h b/include/clang/Rewrite/Rewriters.h
index 203b9bc..f5ade5a 100644
--- a/include/clang/Rewrite/Rewriters.h
+++ b/include/clang/Rewrite/Rewriters.h
@@ -18,6 +18,7 @@
namespace clang {
class Preprocessor;
+class PreprocessorOutputOptions;
/// RewriteMacrosInInput - Implement -rewrite-macros mode.
void RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS);
@@ -25,6 +26,10 @@ void RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS);
/// DoRewriteTest - A simple test for the TokenRewriter class.
void DoRewriteTest(Preprocessor &PP, raw_ostream *OS);
+/// RewriteIncludesInInput - Implement -frewrite-includes mode.
+void RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
+ const PreprocessorOutputOptions &Opts);
+
} // end namespace clang
#endif
diff --git a/include/clang/Rewrite/TokenRewriter.h b/include/clang/Rewrite/TokenRewriter.h
index 9ebd33a..894db09 100644
--- a/include/clang/Rewrite/TokenRewriter.h
+++ b/include/clang/Rewrite/TokenRewriter.h
@@ -16,12 +16,12 @@
#define LLVM_CLANG_TOKENREWRITER_H
#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Token.h"
#include "llvm/ADT/OwningPtr.h"
#include <list>
#include <map>
namespace clang {
- class Token;
class LangOptions;
class ScratchBuffer;
diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h
index 142f144..5239044 100644
--- a/include/clang/Sema/AttributeList.h
+++ b/include/clang/Sema/AttributeList.h
@@ -52,6 +52,16 @@ struct AvailabilityChange {
/// 4: __attribute__(( aligned(16) )). ParmName is unused, Args/Num used.
///
class AttributeList { // TODO: This should really be called ParsedAttribute
+public:
+ /// The style used to specify an attribute.
+ enum Syntax {
+ AS_GNU,
+ AS_CXX11,
+ AS_Declspec,
+ // eg) __w64, __ptr32, etc. It is implied that an MSTypespec is also
+ // a declspec.
+ AS_MSTypespec
+ };
private:
IdentifierInfo *AttrName;
IdentifierInfo *ScopeName;
@@ -64,11 +74,8 @@ private:
/// The expressions themselves are stored after the object.
unsigned NumArgs : 16;
- /// True if Microsoft style: declspec(foo).
- unsigned DeclspecAttribute : 1;
-
- /// True if C++0x-style: [[foo]].
- unsigned CXX0XAttribute : 1;
+ /// Corresponds to the Syntax enum.
+ unsigned SyntaxUsed : 2;
/// True if already diagnosed as invalid.
mutable unsigned Invalid : 1;
@@ -123,15 +130,14 @@ private:
IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc,
Expr **args, unsigned numArgs,
- bool declspec, bool cxx0x)
+ Syntax syntaxUsed)
: AttrName(attrName), ScopeName(scopeName), ParmName(parmName),
AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
- NumArgs(numArgs),
- DeclspecAttribute(declspec), CXX0XAttribute(cxx0x), Invalid(false),
+ NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false),
UsedAsTypeAttr(false), IsAvailability(false),
NextInPosition(0), NextInPool(0) {
if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*));
- AttrKind = getKind(getName());
+ AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
@@ -142,17 +148,17 @@ private:
const AvailabilityChange &obsoleted,
SourceLocation unavailable,
const Expr *messageExpr,
- bool declspec, bool cxx0x)
+ Syntax syntaxUsed)
: AttrName(attrName), ScopeName(scopeName), ParmName(parmName),
AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
- NumArgs(0), DeclspecAttribute(declspec), CXX0XAttribute(cxx0x),
+ NumArgs(0), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
UnavailableLoc(unavailable), MessageExpr(messageExpr),
NextInPosition(0), NextInPool(0) {
new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated);
new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted);
- AttrKind = getKind(getName());
+ AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
friend class AttributePool;
@@ -162,17 +168,6 @@ public:
enum Kind {
#define PARSED_ATTR(NAME) AT_##NAME,
#include "clang/Sema/AttrParsedAttrList.inc"
- PARSED_ATTR(address_space)
- PARSED_ATTR(base_check)
- PARSED_ATTR(cf_returns_autoreleased)
- PARSED_ATTR(ext_vector_type)
- PARSED_ATTR(mode)
- PARSED_ATTR(neon_polyvector_type)
- PARSED_ATTR(neon_vector_type)
- PARSED_ATTR(objc_gc)
- PARSED_ATTR(objc_ownership)
- PARSED_ATTR(opencl_image_access)
- PARSED_ATTR(vector_size)
#undef PARSED_ATTR
IgnoredAttribute,
UnknownAttribute
@@ -189,8 +184,12 @@ public:
IdentifierInfo *getParameterName() const { return ParmName; }
SourceLocation getParameterLoc() const { return ParmLoc; }
- bool isDeclspecAttribute() const { return DeclspecAttribute; }
- bool isCXX0XAttribute() const { return CXX0XAttribute; }
+ /// Returns true if the attribute is a pure __declspec or a synthesized
+ /// declspec representing a type specification (like __w64 or __ptr32).
+ bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec ||
+ SyntaxUsed == AS_MSTypespec; }
+ bool isCXX0XAttribute() const { return SyntaxUsed == AS_CXX11; }
+ bool isMSTypespecAttribute() const { return SyntaxUsed == AS_MSTypespec; }
bool isInvalid() const { return Invalid; }
void setInvalid(bool b = true) const { Invalid = b; }
@@ -199,7 +198,8 @@ public:
void setUsedAsTypeAttr() { UsedAsTypeAttr = true; }
Kind getKind() const { return Kind(AttrKind); }
- static Kind getKind(const IdentifierInfo *Name);
+ static Kind getKind(const IdentifierInfo *Name, const IdentifierInfo *Scope,
+ Syntax SyntaxUsed);
AttributeList *getNext() const { return NextInPosition; }
void setNext(AttributeList *N) { NextInPosition = N; }
@@ -256,27 +256,27 @@ public:
}
const AvailabilityChange &getAvailabilityIntroduced() const {
- assert(getKind() == AT_availability && "Not an availability attribute");
+ assert(getKind() == AT_Availability && "Not an availability attribute");
return getAvailabilitySlot(IntroducedSlot);
}
const AvailabilityChange &getAvailabilityDeprecated() const {
- assert(getKind() == AT_availability && "Not an availability attribute");
+ assert(getKind() == AT_Availability && "Not an availability attribute");
return getAvailabilitySlot(DeprecatedSlot);
}
const AvailabilityChange &getAvailabilityObsoleted() const {
- assert(getKind() == AT_availability && "Not an availability attribute");
+ assert(getKind() == AT_Availability && "Not an availability attribute");
return getAvailabilitySlot(ObsoletedSlot);
}
SourceLocation getUnavailableLoc() const {
- assert(getKind() == AT_availability && "Not an availability attribute");
+ assert(getKind() == AT_Availability && "Not an availability attribute");
return UnavailableLoc;
}
const Expr * getMessageExpr() const {
- assert(getKind() == AT_availability && "Not an availability attribute");
+ assert(getKind() == AT_Availability && "Not an availability attribute");
return MessageExpr;
}
};
@@ -383,14 +383,13 @@ public:
IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc,
Expr **args, unsigned numArgs,
- bool declspec = false, bool cxx0x = false) {
+ AttributeList::Syntax syntax) {
void *memory = allocate(sizeof(AttributeList)
+ numArgs * sizeof(Expr*));
return add(new (memory) AttributeList(attrName, attrRange,
scopeName, scopeLoc,
parmName, parmLoc,
- args, numArgs,
- declspec, cxx0x));
+ args, numArgs, syntax));
}
AttributeList *create(IdentifierInfo *attrName, SourceRange attrRange,
@@ -401,14 +400,13 @@ public:
const AvailabilityChange &obsoleted,
SourceLocation unavailable,
const Expr *MessageExpr,
- bool declspec = false, bool cxx0x = false) {
+ AttributeList::Syntax syntax) {
void *memory = allocate(AttributeFactory::AvailabilityAllocSize);
return add(new (memory) AttributeList(attrName, attrRange,
scopeName, scopeLoc,
parmName, parmLoc,
introduced, deprecated, obsoleted,
- unavailable, MessageExpr,
- declspec, cxx0x));
+ unavailable, MessageExpr, syntax));
}
AttributeList *createIntegerAttribute(ASTContext &C, IdentifierInfo *Name,
@@ -510,10 +508,10 @@ public:
IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc,
Expr **args, unsigned numArgs,
- bool declspec = false, bool cxx0x = false) {
+ AttributeList::Syntax syntax) {
AttributeList *attr =
pool.create(attrName, attrRange, scopeName, scopeLoc, parmName, parmLoc,
- args, numArgs, declspec, cxx0x);
+ args, numArgs, syntax);
add(attr);
return attr;
}
@@ -526,12 +524,11 @@ public:
const AvailabilityChange &obsoleted,
SourceLocation unavailable,
const Expr *MessageExpr,
- bool declspec = false, bool cxx0x = false) {
+ AttributeList::Syntax syntax) {
AttributeList *attr =
pool.create(attrName, attrRange, scopeName, scopeLoc, parmName, parmLoc,
introduced, deprecated, obsoleted, unavailable,
- MessageExpr,
- declspec, cxx0x);
+ MessageExpr, syntax);
add(attr);
return attr;
}
diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h
index fe9bed5..d43aaaf 100644
--- a/include/clang/Sema/CodeCompleteConsumer.h
+++ b/include/clang/Sema/CodeCompleteConsumer.h
@@ -15,6 +15,7 @@
#include "clang/AST/Type.h"
#include "clang/AST/CanonicalType.h"
+#include "clang/Sema/CodeCompleteOptions.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
@@ -237,7 +238,7 @@ public:
/// This context usually implies that no completions should be added,
/// unless they come from an appropriate natural-language dictionary.
CCC_NaturalLanguage,
- /// \brief Code completion for a selector, as in an @selector expression.
+ /// \brief Code completion for a selector, as in an \@selector expression.
CCC_SelectorName,
/// \brief Code completion within a type-qualifier list.
CCC_TypeQualifiers,
@@ -379,7 +380,7 @@ public:
CK_Equal,
/// \brief Horizontal whitespace (' ').
CK_HorizontalSpace,
- /// \brief Verticle whitespace ('\n' or '\r\n', depending on the
+ /// \brief Vertical whitespace ('\\n' or '\\r\\n', depending on the
/// platform).
CK_VerticalSpace
};
@@ -444,6 +445,10 @@ private:
/// \brief The name of the parent context.
StringRef ParentName;
+
+ /// \brief A brief documentation comment attached to the declaration of
+ /// entity being completed by this result.
+ const char *BriefComment;
CodeCompletionString(const CodeCompletionString &); // DO NOT IMPLEMENT
CodeCompletionString &operator=(const CodeCompletionString &); // DITTO
@@ -451,7 +456,8 @@ private:
CodeCompletionString(const Chunk *Chunks, unsigned NumChunks,
unsigned Priority, CXAvailabilityKind Availability,
const char **Annotations, unsigned NumAnnotations,
- CXCursorKind ParentKind, StringRef ParentName);
+ CXCursorKind ParentKind, StringRef ParentName,
+ const char *BriefComment);
~CodeCompletionString() { }
friend class CodeCompletionBuilder;
@@ -493,6 +499,10 @@ public:
StringRef getParentContextName() const {
return ParentName;
}
+
+ const char *getBriefComment() const {
+ return BriefComment;
+ }
/// \brief Retrieve a string representation of the code completion string,
/// which is mainly useful for debugging.
@@ -569,6 +579,7 @@ private:
CXAvailabilityKind Availability;
CXCursorKind ParentKind;
StringRef ParentName;
+ const char *BriefComment;
/// \brief The chunks stored in this string.
SmallVector<Chunk, 4> Chunks;
@@ -580,14 +591,14 @@ public:
CodeCompletionTUInfo &CCTUInfo)
: Allocator(Allocator), CCTUInfo(CCTUInfo),
Priority(0), Availability(CXAvailability_Available),
- ParentKind(CXCursor_NotImplemented) { }
+ ParentKind(CXCursor_NotImplemented), BriefComment(NULL) { }
CodeCompletionBuilder(CodeCompletionAllocator &Allocator,
CodeCompletionTUInfo &CCTUInfo,
unsigned Priority, CXAvailabilityKind Availability)
: Allocator(Allocator), CCTUInfo(CCTUInfo),
Priority(Priority), Availability(Availability),
- ParentKind(CXCursor_NotImplemented) { }
+ ParentKind(CXCursor_NotImplemented), BriefComment(NULL) { }
/// \brief Retrieve the allocator into which the code completion
/// strings should be allocated.
@@ -628,6 +639,8 @@ public:
/// \brief Add the parent context information to this code completion.
void addParentContext(DeclContext *DC);
+
+ void addBriefComment(StringRef Comment);
CXCursorKind getParentKind() const { return ParentKind; }
StringRef getParentName() const { return ParentName; }
@@ -638,15 +651,12 @@ class CodeCompletionResult {
public:
/// \brief Describes the kind of result generated.
enum ResultKind {
- RK_Declaration = 0, //< Refers to a declaration
- RK_Keyword, //< Refers to a keyword or symbol.
- RK_Macro, //< Refers to a macro
- RK_Pattern //< Refers to a precomputed pattern.
+ RK_Declaration = 0, ///< Refers to a declaration
+ RK_Keyword, ///< Refers to a keyword or symbol.
+ RK_Macro, ///< Refers to a macro
+ RK_Pattern ///< Refers to a precomputed pattern.
};
- /// \brief The kind of result stored here.
- ResultKind Kind;
-
/// \brief When Kind == RK_Declaration or RK_Pattern, the declaration we are
/// referring to. In the latter case, the declaration might be NULL.
NamedDecl *Declaration;
@@ -667,16 +677,19 @@ public:
/// \brief The priority of this particular code-completion result.
unsigned Priority;
+ /// \brief Specifies which parameter (of a function, Objective-C method,
+ /// macro, etc.) we should start with when formatting the result.
+ unsigned StartParameter;
+
+ /// \brief The kind of result stored here.
+ ResultKind Kind;
+
/// \brief The cursor kind that describes this result.
CXCursorKind CursorKind;
/// \brief The availability of this result.
CXAvailabilityKind Availability;
- /// \brief Specifies which parameter (of a function, Objective-C method,
- /// macro, etc.) we should start with when formatting the result.
- unsigned StartParameter;
-
/// \brief Whether this result is hidden by another name.
bool Hidden : 1;
@@ -705,10 +718,10 @@ public:
NestedNameSpecifier *Qualifier = 0,
bool QualifierIsInformative = false,
bool Accessible = true)
- : Kind(RK_Declaration), Declaration(Declaration),
- Priority(getPriorityFromDecl(Declaration)),
- Availability(CXAvailability_Available), StartParameter(0),
- Hidden(false), QualifierIsInformative(QualifierIsInformative),
+ : Declaration(Declaration), Priority(getPriorityFromDecl(Declaration)),
+ StartParameter(0), Kind(RK_Declaration),
+ Availability(CXAvailability_Available), Hidden(false),
+ QualifierIsInformative(QualifierIsInformative),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
DeclaringEntity(false), Qualifier(Qualifier) {
computeCursorKindAndAvailability(Accessible);
@@ -716,22 +729,22 @@ public:
/// \brief Build a result that refers to a keyword or symbol.
CodeCompletionResult(const char *Keyword, unsigned Priority = CCP_Keyword)
- : Kind(RK_Keyword), Declaration(0), Keyword(Keyword), Priority(Priority),
- Availability(CXAvailability_Available),
- StartParameter(0), Hidden(false), QualifierIsInformative(0),
- StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- DeclaringEntity(false), Qualifier(0) {
- computeCursorKindAndAvailability();
+ : Declaration(0), Keyword(Keyword), Priority(Priority), StartParameter(0),
+ Kind(RK_Keyword), CursorKind(CXCursor_NotImplemented),
+ Availability(CXAvailability_Available), Hidden(false),
+ QualifierIsInformative(0), StartsNestedNameSpecifier(false),
+ AllParametersAreInformative(false), DeclaringEntity(false), Qualifier(0)
+ {
}
/// \brief Build a result that refers to a macro.
CodeCompletionResult(IdentifierInfo *Macro, unsigned Priority = CCP_Macro)
- : Kind(RK_Macro), Declaration(0), Macro(Macro), Priority(Priority),
- Availability(CXAvailability_Available), StartParameter(0),
- Hidden(false), QualifierIsInformative(0),
- StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- DeclaringEntity(false), Qualifier(0) {
- computeCursorKindAndAvailability();
+ : Declaration(0), Macro(Macro), Priority(Priority), StartParameter(0),
+ Kind(RK_Macro), CursorKind(CXCursor_MacroDefinition),
+ Availability(CXAvailability_Available), Hidden(false),
+ QualifierIsInformative(0), StartsNestedNameSpecifier(false),
+ AllParametersAreInformative(false), DeclaringEntity(false), Qualifier(0)
+ {
}
/// \brief Build a result that refers to a pattern.
@@ -740,8 +753,8 @@ public:
CXCursorKind CursorKind = CXCursor_NotImplemented,
CXAvailabilityKind Availability = CXAvailability_Available,
NamedDecl *D = 0)
- : Kind(RK_Pattern), Declaration(D), Pattern(Pattern), Priority(Priority),
- CursorKind(CursorKind), Availability(Availability), StartParameter(0),
+ : Declaration(D), Pattern(Pattern), Priority(Priority), StartParameter(0),
+ Kind(RK_Pattern), CursorKind(CursorKind), Availability(Availability),
Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
DeclaringEntity(false), Qualifier(0)
@@ -752,11 +765,10 @@ public:
/// declaration.
CodeCompletionResult(CodeCompletionString *Pattern, NamedDecl *D,
unsigned Priority)
- : Kind(RK_Pattern), Declaration(D), Pattern(Pattern), Priority(Priority),
- Availability(CXAvailability_Available), StartParameter(0),
- Hidden(false), QualifierIsInformative(false),
- StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- DeclaringEntity(false), Qualifier(0) {
+ : Declaration(D), Pattern(Pattern), Priority(Priority), StartParameter(0),
+ Kind(RK_Pattern), Availability(CXAvailability_Available), Hidden(false),
+ QualifierIsInformative(false), StartsNestedNameSpecifier(false),
+ AllParametersAreInformative(false), DeclaringEntity(false), Qualifier(0) {
computeCursorKindAndAvailability();
}
@@ -781,11 +793,13 @@ public:
/// string itself.
CodeCompletionString *CreateCodeCompletionString(Sema &S,
CodeCompletionAllocator &Allocator,
- CodeCompletionTUInfo &CCTUInfo);
+ CodeCompletionTUInfo &CCTUInfo,
+ bool IncludeBriefComments);
CodeCompletionString *CreateCodeCompletionString(ASTContext &Ctx,
Preprocessor &PP,
CodeCompletionAllocator &Allocator,
- CodeCompletionTUInfo &CCTUInfo);
+ CodeCompletionTUInfo &CCTUInfo,
+ bool IncludeBriefComments);
/// \brief Determine a base priority for the given declaration.
static unsigned getPriorityFromDecl(NamedDecl *ND);
@@ -819,16 +833,7 @@ raw_ostream &operator<<(raw_ostream &OS,
/// information.
class CodeCompleteConsumer {
protected:
- /// \brief Whether to include macros in the code-completion results.
- bool IncludeMacros;
-
- /// \brief Whether to include code patterns (such as for loops) within
- /// the completion results.
- bool IncludeCodePatterns;
-
- /// \brief Whether to include global (top-level) declarations and names in
- /// the completion results.
- bool IncludeGlobals;
+ const CodeCompleteOptions CodeCompleteOpts;
/// \brief Whether the output format for the code-completion consumer is
/// binary.
@@ -901,22 +906,31 @@ public:
CodeCompletionTUInfo &CCTUInfo) const;
};
- CodeCompleteConsumer() : IncludeMacros(false), IncludeCodePatterns(false),
- IncludeGlobals(true), OutputIsBinary(false) { }
-
- CodeCompleteConsumer(bool IncludeMacros, bool IncludeCodePatterns,
- bool IncludeGlobals, bool OutputIsBinary)
- : IncludeMacros(IncludeMacros), IncludeCodePatterns(IncludeCodePatterns),
- IncludeGlobals(IncludeGlobals), OutputIsBinary(OutputIsBinary) { }
+ CodeCompleteConsumer(const CodeCompleteOptions &CodeCompleteOpts,
+ bool OutputIsBinary)
+ : CodeCompleteOpts(CodeCompleteOpts), OutputIsBinary(OutputIsBinary)
+ { }
/// \brief Whether the code-completion consumer wants to see macros.
- bool includeMacros() const { return IncludeMacros; }
+ bool includeMacros() const {
+ return CodeCompleteOpts.IncludeMacros;
+ }
/// \brief Whether the code-completion consumer wants to see code patterns.
- bool includeCodePatterns() const { return IncludeCodePatterns; }
+ bool includeCodePatterns() const {
+ return CodeCompleteOpts.IncludeCodePatterns;
+ }
/// \brief Whether to include global (top-level) declaration results.
- bool includeGlobals() const { return IncludeGlobals; }
+ bool includeGlobals() const {
+ return CodeCompleteOpts.IncludeGlobals;
+ }
+
+ /// \brief Whether to include brief documentation comments within the set of
+ /// code completions returned.
+ bool includeBriefComments() const {
+ return CodeCompleteOpts.IncludeBriefComments;
+ }
/// \brief Determine whether the output of this consumer is binary.
bool isOutputBinary() const { return OutputIsBinary; }
@@ -963,11 +977,9 @@ class PrintingCodeCompleteConsumer : public CodeCompleteConsumer {
public:
/// \brief Create a new printing code-completion consumer that prints its
/// results to the given raw output stream.
- PrintingCodeCompleteConsumer(bool IncludeMacros, bool IncludeCodePatterns,
- bool IncludeGlobals,
+ PrintingCodeCompleteConsumer(const CodeCompleteOptions &CodeCompleteOpts,
raw_ostream &OS)
- : CodeCompleteConsumer(IncludeMacros, IncludeCodePatterns, IncludeGlobals,
- false), OS(OS),
+ : CodeCompleteConsumer(CodeCompleteOpts, false), OS(OS),
CCTUInfo(new GlobalCodeCompletionAllocator) {}
/// \brief Prints the finalized code-completion results.
diff --git a/include/clang/Sema/CodeCompleteOptions.h b/include/clang/Sema/CodeCompleteOptions.h
new file mode 100644
index 0000000..30712db
--- /dev/null
+++ b/include/clang/Sema/CodeCompleteOptions.h
@@ -0,0 +1,37 @@
+//===---- CodeCompleteOptions.h - Code Completion Options -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SEMA_CODECOMPLETEOPTIONS_H
+#define LLVM_CLANG_SEMA_CODECOMPLETEOPTIONS_H
+
+/// Options controlling the behavior of code completion.
+class CodeCompleteOptions {
+public:
+ ///< Show macros in code completion results.
+ unsigned IncludeMacros : 1;
+
+ ///< Show code patterns in code completion results.
+ unsigned IncludeCodePatterns : 1;
+
+ ///< Show top-level decls in code completion results.
+ unsigned IncludeGlobals : 1;
+
+ ///< Show brief documentation comments in code completion results.
+ unsigned IncludeBriefComments : 1;
+
+ CodeCompleteOptions() :
+ IncludeMacros(0),
+ IncludeCodePatterns(0),
+ IncludeGlobals(1),
+ IncludeBriefComments(0)
+ { }
+};
+
+#endif
+
diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h
index 67fd393..792b0c6 100644
--- a/include/clang/Sema/DeclSpec.h
+++ b/include/clang/Sema/DeclSpec.h
@@ -6,15 +6,18 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines the classes used to store parsed information about
-// declaration-specifiers and declarators.
-//
-// static const int volatile x, *y, *(*(*z)[10])(const void *x);
-// ------------------------- - -- ---------------------------
-// declaration-specifiers \ | /
-// declarators
-//
+///
+/// \file
+/// \brief This file defines the classes used to store parsed information about
+/// declaration-specifiers and declarators.
+///
+/// \verbatim
+/// static const int volatile x, *y, *(*(*z)[10])(const void *x);
+/// ------------------------- - -- ---------------------------
+/// declaration-specifiers \ | /
+/// declarators
+/// \endverbatim
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SEMA_DECLSPEC_H
@@ -48,8 +51,9 @@ namespace clang {
class Declarator;
struct TemplateIdAnnotation;
-/// CXXScopeSpec - Represents a C++ nested-name-specifier or a global scope
-/// specifier. These can be in 3 states:
+/// \brief Represents a C++ nested-name-specifier or a global scope specifier.
+///
+/// These can be in 3 states:
/// 1) Not present, identified by isEmpty()
/// 2) Present, identified by isNotEmpty()
/// 2.a) Valid, idenified by isValid()
@@ -158,9 +162,14 @@ public:
NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const;
/// \brief Retrieve the location of the name in the last qualifier
- /// in this nested name specifier. For example:
- /// ::foo::bar<0>::
- /// ^~~
+ /// in this nested name specifier.
+ ///
+ /// For example, the location of \c bar
+ /// in
+ /// \verbatim
+ /// \::foo::bar<0>::
+ /// ^~~
+ /// \endverbatim
SourceLocation getLastQualifierNameLoc() const;
/// No scope specifier.
@@ -199,13 +208,14 @@ public:
unsigned location_size() const { return Builder.getBuffer().second; }
};
-/// DeclSpec - This class captures information about "declaration specifiers",
-/// which encompasses storage-class-specifiers, type-specifiers,
-/// type-qualifiers, and function-specifiers.
+/// \brief Captures information about "declaration specifiers".
+///
+/// "Declaration specifiers" encompasses storage-class-specifiers,
+/// type-specifiers, type-qualifiers, and function-specifiers.
class DeclSpec {
public:
- // storage-class-specifier
- // Note: The order of these enumerators is important for diagnostics.
+ /// \brief storage-class-specifier
+ /// \note The order of these enumerators is important for diagnostics.
enum SCS {
SCS_unspecified = 0,
SCS_typedef,
@@ -466,8 +476,7 @@ public:
SourceRange getTypeofParensRange() const { return TypeofParensRange; }
void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; }
- /// getSpecifierName - Turn a type-specifier-type into a string like "_Bool"
- /// or "union".
+ /// \brief Turn a type-specifier-type into a string like "_Bool" or "union".
static const char *getSpecifierName(DeclSpec::TST T);
static const char *getSpecifierName(DeclSpec::TQ Q);
static const char *getSpecifierName(DeclSpec::TSS S);
@@ -510,7 +519,7 @@ public:
FS_explicitLoc = SourceLocation();
}
- /// hasTypeSpecifier - Return true if any type-specifier has been found.
+ /// \brief Return true if any type-specifier has been found.
bool hasTypeSpecifier() const {
return getTypeSpecType() != DeclSpec::TST_unspecified ||
getTypeSpecWidth() != DeclSpec::TSW_unspecified ||
@@ -518,9 +527,8 @@ public:
getTypeSpecSign() != DeclSpec::TSS_unspecified;
}
- /// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this
+ /// \brief Return a bitmask of which flavors of specifiers this
/// DeclSpec includes.
- ///
unsigned getParsedSpecifiers() const;
SCS getStorageClassSpecAsWritten() const {
@@ -590,7 +598,8 @@ public:
}
bool SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec,
- unsigned &DiagID, const LangOptions &Lang);
+ unsigned &DiagID, const LangOptions &Lang,
+ bool IsTypeSpec);
bool SetFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
@@ -624,17 +633,22 @@ public:
return Attrs.getPool();
}
- /// AddAttributes - contatenates two attribute lists.
+ /// \brief Concatenates two attribute lists.
+ ///
/// The GCC attribute syntax allows for the following:
///
+ /// \code
/// short __attribute__(( unused, deprecated ))
/// int __attribute__(( may_alias, aligned(16) )) var;
+ /// \endcode
///
/// This declares 4 attributes using 2 lists. The following syntax is
/// also allowed and equivalent to the previous declaration.
///
+ /// \code
/// short __attribute__((unused)) __attribute__((deprecated))
/// int __attribute__((may_alias)) __attribute__((aligned(16))) var;
+ /// \endcode
///
void addAttributes(AttributeList *AL) {
Attrs.addAll(AL);
@@ -648,7 +662,7 @@ public:
ParsedAttributes &getAttributes() { return Attrs; }
const ParsedAttributes &getAttributes() const { return Attrs; }
- /// TakeAttributes - Return the current attribute list and remove them from
+ /// \brief Return the current attribute list and remove them from
/// the DeclSpec so that it doesn't own them.
ParsedAttributes takeAttributes() {
// The non-const "copy" constructor clears the operand automatically.
@@ -684,13 +698,14 @@ public:
ObjCDeclSpec *getObjCQualifiers() const { return ObjCQualifiers; }
void setObjCQualifiers(ObjCDeclSpec *quals) { ObjCQualifiers = quals; }
- /// isMissingDeclaratorOk - This checks if this DeclSpec can stand alone,
- /// without a Declarator. Only tag declspecs can stand alone.
+ /// \brief Checks if this DeclSpec can stand alone, without a Declarator.
+ ///
+ /// Only tag declspecs can stand alone.
bool isMissingDeclaratorOk();
};
-/// ObjCDeclSpec - This class captures information about
-/// "declaration specifiers" specific to objective-c
+/// \brief Captures information about "declaration specifiers" specific to
+/// Objective-C.
class ObjCDeclSpec {
public:
/// ObjCDeclQualifier - Qualifier used on types in method
@@ -853,12 +868,14 @@ public:
assert(Other.Kind == IK_Identifier && "Cannot copy non-identifiers");
}
- /// \brief Destroy this unqualified-id.
- ~UnqualifiedId() { clear(); }
-
/// \brief Clear out this unqualified-id, setting it to default (invalid)
/// state.
- void clear();
+ void clear() {
+ Kind = IK_Identifier;
+ Identifier = 0;
+ StartLocation = SourceLocation();
+ EndLocation = SourceLocation();
+ }
/// \brief Determine whether this unqualified-id refers to a valid name.
bool isValid() const { return StartLocation.isValid(); }
@@ -979,12 +996,11 @@ public:
SourceLocation getLocStart() const LLVM_READONLY { return StartLocation; }
SourceLocation getLocEnd() const LLVM_READONLY { return EndLocation; }
};
-
-/// CachedTokens - A set of tokens that has been cached for later
-/// parsing.
+
+/// \brief A set of tokens that has been cached for later parsing.
typedef SmallVector<Token, 4> CachedTokens;
-/// DeclaratorChunk - One instance of this struct is used for each type in a
+/// \brief One instance of this struct is used for each type in a
/// declarator that is parsed.
///
/// This is intended to be a small value object.
@@ -1088,6 +1104,9 @@ struct DeclaratorChunk {
/// contains the location of the ellipsis.
unsigned isVariadic : 1;
+ /// Can this declaration be a constructor-style initializer?
+ unsigned isAmbiguous : 1;
+
/// \brief Whether the ref-qualifier (if any) is an lvalue reference.
/// Otherwise, it's an rvalue reference.
unsigned RefQualifierIsLValueRef : 1;
@@ -1102,6 +1121,10 @@ struct DeclaratorChunk {
/// DeleteArgInfo - If this is true, we need to delete[] ArgInfo.
unsigned DeleteArgInfo : 1;
+ /// HasTrailingReturnType - If this is true, a trailing return type was
+ /// specified.
+ unsigned HasTrailingReturnType : 1;
+
/// When isVariadic is true, the location of the ellipsis in the source.
unsigned EllipsisLoc;
@@ -1132,8 +1155,7 @@ struct DeclaratorChunk {
/// any.
unsigned MutableLoc;
- /// \brief When ExceptionSpecType isn't EST_None or EST_Delayed, the
- /// location of the keyword introducing the spec.
+ /// \brief The location of the keyword introducing the spec, if any.
unsigned ExceptionSpecLoc;
/// ArgInfo - This is a pointer to a new[]'d array of ParamInfo objects that
@@ -1152,13 +1174,13 @@ struct DeclaratorChunk {
Expr *NoexceptExpr;
};
- /// TrailingReturnType - If this isn't null, it's the trailing return type
- /// specified. This is actually a ParsedType, but stored as void* to
- /// allow union storage.
- void *TrailingReturnType;
+ /// \brief If HasTrailingReturnType is true, this is the trailing return
+ /// type specified.
+ UnionParsedType TrailingReturnType;
- /// freeArgs - reset the argument list to having zero arguments. This is
- /// used in various places for error recovery.
+ /// \brief Reset the argument list to having zero arguments.
+ ///
+ /// This is used in various places for error recovery.
void freeArgs() {
if (DeleteArgInfo) {
delete[] ArgInfo;
@@ -1220,6 +1242,13 @@ struct DeclaratorChunk {
ExceptionSpecificationType getExceptionSpecType() const {
return static_cast<ExceptionSpecificationType>(ExceptionSpecType);
}
+
+ /// \brief Determine whether this function declarator had a
+ /// trailing-return-type.
+ bool hasTrailingReturnType() const { return HasTrailingReturnType; }
+
+ /// \brief Get the trailing-return-type for this function declarator.
+ ParsedType getTrailingReturnType() const { return TrailingReturnType; }
};
struct BlockPointerTypeInfo : TypeInfoCommon {
@@ -1273,7 +1302,7 @@ struct DeclaratorChunk {
}
}
- /// getAttrs - If there are attributes applied to this declaratorchunk, return
+ /// \brief If there are attributes applied to this declaratorchunk, return
/// them.
const AttributeList *getAttrs() const {
return Common.AttrList;
@@ -1283,8 +1312,7 @@ struct DeclaratorChunk {
return Common.AttrList;
}
- /// getPointer - Return a DeclaratorChunk for a pointer.
- ///
+ /// \brief Return a DeclaratorChunk for a pointer.
static DeclaratorChunk getPointer(unsigned TypeQuals, SourceLocation Loc,
SourceLocation ConstQualLoc,
SourceLocation VolatileQualLoc,
@@ -1300,8 +1328,7 @@ struct DeclaratorChunk {
return I;
}
- /// getReference - Return a DeclaratorChunk for a reference.
- ///
+ /// \brief Return a DeclaratorChunk for a reference.
static DeclaratorChunk getReference(unsigned TypeQuals, SourceLocation Loc,
bool lvalue) {
DeclaratorChunk I;
@@ -1313,8 +1340,7 @@ struct DeclaratorChunk {
return I;
}
- /// getArray - Return a DeclaratorChunk for an array.
- ///
+ /// \brief Return a DeclaratorChunk for an array.
static DeclaratorChunk getArray(unsigned TypeQuals,
bool isStatic, bool isStar, Expr *NumElts,
SourceLocation LBLoc, SourceLocation RBLoc) {
@@ -1333,6 +1359,7 @@ struct DeclaratorChunk {
/// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function.
/// "TheDeclarator" is the declarator that this will be added to.
static DeclaratorChunk getFunction(bool hasProto, bool isVariadic,
+ bool isAmbiguous,
SourceLocation EllipsisLoc,
ParamInfo *ArgInfo, unsigned NumArgs,
unsigned TypeQuals,
@@ -1350,11 +1377,10 @@ struct DeclaratorChunk {
SourceLocation LocalRangeBegin,
SourceLocation LocalRangeEnd,
Declarator &TheDeclarator,
- ParsedType TrailingReturnType =
- ParsedType());
+ TypeResult TrailingReturnType =
+ TypeResult());
- /// getBlockPointer - Return a DeclaratorChunk for a block.
- ///
+ /// \brief Return a DeclaratorChunk for a block.
static DeclaratorChunk getBlockPointer(unsigned TypeQuals,
SourceLocation Loc) {
DeclaratorChunk I;
@@ -1377,8 +1403,7 @@ struct DeclaratorChunk {
return I;
}
- /// getParen - Return a DeclaratorChunk for a paren.
- ///
+ /// \brief Return a DeclaratorChunk for a paren.
static DeclaratorChunk getParen(SourceLocation LParenLoc,
SourceLocation RParenLoc) {
DeclaratorChunk I;
@@ -1399,10 +1424,12 @@ enum FunctionDefinitionKind {
FDK_Defaulted,
FDK_Deleted
};
-
-/// Declarator - Information about one declarator, including the parsed type
-/// information and the identifier. When the declarator is fully formed, this
-/// is turned into the appropriate Decl object.
+
+/// \brief Information about one declarator, including the parsed type
+/// information and the identifier.
+///
+/// When the declarator is fully formed, this is turned into the appropriate
+/// Decl object.
///
/// Declarators come in two types: normal declarators and abstract declarators.
/// Abstract declarators are used when parsing types, and don't have an
@@ -1441,8 +1468,7 @@ private:
UnqualifiedId Name;
SourceRange Range;
- /// Context - Where we are parsing this declarator.
- ///
+ /// \brief Where we are parsing this declarator.
TheContext Context;
/// DeclTypeInfo - This holds each type that the declarator includes as it is
@@ -1463,13 +1489,13 @@ private:
/// Actually a FunctionDefinitionKind.
unsigned FunctionDefinition : 2;
- // Redeclaration - Is this Declarator is a redeclaration.
+ /// \brief Is this Declarator a redeclaration?
bool Redeclaration : 1;
/// Attrs - Attributes.
ParsedAttributes Attrs;
- /// AsmLabel - The asm label, if specified.
+ /// \brief The asm label, if specified.
Expr *AsmLabel;
/// InlineParams - This is a local array used for the first function decl
@@ -1478,7 +1504,7 @@ private:
DeclaratorChunk::ParamInfo InlineParams[16];
bool InlineParamsUsed;
- /// Extension - true if the declaration is preceded by __extension__.
+ /// \brief true if the declaration is preceded by \c __extension__.
bool Extension : 1;
/// \brief If this is the second or subsequent declarator in this declaration,
@@ -1536,7 +1562,7 @@ public:
Context == ObjCResultContext);
}
- /// getSourceRange - Get the source range that spans this declarator.
+ /// \brief Get the source range that spans this declarator.
const SourceRange &getSourceRange() const LLVM_READONLY { return Range; }
SourceLocation getLocStart() const LLVM_READONLY { return Range.getBegin(); }
SourceLocation getLocEnd() const LLVM_READONLY { return Range.getEnd(); }
@@ -1564,7 +1590,7 @@ public:
Range.setEnd(SR.getEnd());
}
- /// clear - Reset the contents of this Declarator.
+ /// \brief Reset the contents of this Declarator.
void clear() {
SS.clear();
Name.clear();
@@ -1730,13 +1756,12 @@ public:
SetRangeEnd(EndLoc);
}
- /// AddInnermostTypeInfo - Add a new innermost chunk to this declarator.
+ /// \brief Add a new innermost chunk to this declarator.
void AddInnermostTypeInfo(const DeclaratorChunk &TI) {
DeclTypeInfo.insert(DeclTypeInfo.begin(), TI);
}
- /// getNumTypeObjects() - Return the number of types applied to this
- /// declarator.
+ /// \brief Return the number of types applied to this declarator.
unsigned getNumTypeObjects() const { return DeclTypeInfo.size(); }
/// Return the specified TypeInfo from this declarator. TypeInfo #0 is
@@ -1902,17 +1927,16 @@ public:
bool isRedeclaration() const { return Redeclaration; }
};
-/// FieldDeclarator - This little struct is used to capture information about
+/// \brief This little struct is used to capture information about
/// structure field declarators, which is basically just a bitfield size.
struct FieldDeclarator {
Declarator D;
Expr *BitfieldSize;
- explicit FieldDeclarator(DeclSpec &DS) : D(DS, Declarator::MemberContext) {
- BitfieldSize = 0;
- }
+ explicit FieldDeclarator(const DeclSpec &DS)
+ : D(DS, Declarator::MemberContext), BitfieldSize(0) { }
};
-/// VirtSpecifiers - Represents a C++0x virt-specifier-seq.
+/// \brief Represents a C++11 virt-specifier-seq.
class VirtSpecifiers {
public:
enum Specifier {
@@ -1945,7 +1969,7 @@ private:
SourceLocation LastLocation;
};
-/// LambdaCapture - An individual capture in a lambda introducer.
+/// \brief An individual capture in a lambda introducer.
struct LambdaCapture {
LambdaCaptureKind Kind;
SourceLocation Loc;
@@ -1959,7 +1983,7 @@ struct LambdaCapture {
{}
};
-/// LambdaIntroducer - Represents a complete lambda introducer.
+/// \brief Represents a complete lambda introducer.
struct LambdaIntroducer {
SourceRange Range;
SourceLocation DefaultLoc;
@@ -1969,7 +1993,7 @@ struct LambdaIntroducer {
LambdaIntroducer()
: Default(LCD_None) {}
- /// addCapture - Append a capture in a lambda introducer.
+ /// \brief Append a capture in a lambda introducer.
void addCapture(LambdaCaptureKind Kind,
SourceLocation Loc,
IdentifierInfo* Id = 0,
diff --git a/include/clang/Sema/DelayedDiagnostic.h b/include/clang/Sema/DelayedDiagnostic.h
index 3320cd8..c241266 100644
--- a/include/clang/Sema/DelayedDiagnostic.h
+++ b/include/clang/Sema/DelayedDiagnostic.h
@@ -21,7 +21,7 @@
#ifndef LLVM_CLANG_SEMA_DELAYED_DIAGNOSTIC_H
#define LLVM_CLANG_SEMA_DELAYED_DIAGNOSTIC_H
-#include "clang/AST/DeclCXX.h"
+#include "clang/Sema/Sema.h"
namespace clang {
namespace sema {
@@ -40,17 +40,17 @@ public:
bool isMemberAccess() const { return IsMember; }
- AccessedEntity(ASTContext &Context,
+ AccessedEntity(PartialDiagnostic::StorageAllocator &Allocator,
MemberNonce _,
CXXRecordDecl *NamingClass,
DeclAccessPair FoundDecl,
QualType BaseObjectType)
: Access(FoundDecl.getAccess()), IsMember(true),
Target(FoundDecl.getDecl()), NamingClass(NamingClass),
- BaseObjectType(BaseObjectType), Diag(0, Context.getDiagAllocator()) {
+ BaseObjectType(BaseObjectType), Diag(0, Allocator) {
}
- AccessedEntity(ASTContext &Context,
+ AccessedEntity(PartialDiagnostic::StorageAllocator &Allocator,
BaseNonce _,
CXXRecordDecl *BaseClass,
CXXRecordDecl *DerivedClass,
@@ -58,7 +58,7 @@ public:
: Access(Access), IsMember(false),
Target(BaseClass),
NamingClass(DerivedClass),
- Diag(0, Context.getDiagAllocator()) {
+ Diag(0, Allocator) {
}
bool isQuiet() const { return Diag.getDiagID() == 0; }
@@ -214,7 +214,63 @@ private:
};
};
+/// DelayedDiagnosticPool - A collection of diagnostics which were
+/// delayed.
+class DelayedDiagnosticPool {
+ const DelayedDiagnosticPool *Parent;
+ llvm::SmallVector<DelayedDiagnostic, 4> Diagnostics;
+
+ // Do not implement.
+ DelayedDiagnosticPool(const DelayedDiagnosticPool &other);
+ DelayedDiagnosticPool &operator=(const DelayedDiagnosticPool &other);
+public:
+ DelayedDiagnosticPool(const DelayedDiagnosticPool *parent) : Parent(parent) {}
+ ~DelayedDiagnosticPool() {
+ for (llvm::SmallVectorImpl<DelayedDiagnostic>::iterator
+ i = Diagnostics.begin(), e = Diagnostics.end(); i != e; ++i)
+ i->Destroy();
+ }
+
+ const DelayedDiagnosticPool *getParent() const { return Parent; }
+
+ /// Does this pool, or any of its ancestors, contain any diagnostics?
+ bool empty() const {
+ return (Diagnostics.empty() && (Parent == NULL || Parent->empty()));
+ }
+
+ /// Add a diagnostic to this pool.
+ void add(const DelayedDiagnostic &diag) {
+ Diagnostics.push_back(diag);
+ }
+
+ /// Steal the diagnostics from the given pool.
+ void steal(DelayedDiagnosticPool &pool) {
+ if (pool.Diagnostics.empty()) return;
+
+ if (Diagnostics.empty()) {
+ Diagnostics = llvm_move(pool.Diagnostics);
+ } else {
+ Diagnostics.append(pool.pool_begin(), pool.pool_end());
+ }
+ pool.Diagnostics.clear();
+ }
+
+ typedef llvm::SmallVectorImpl<DelayedDiagnostic>::const_iterator
+ pool_iterator;
+ pool_iterator pool_begin() const { return Diagnostics.begin(); }
+ pool_iterator pool_end() const { return Diagnostics.end(); }
+ bool pool_empty() const { return Diagnostics.empty(); }
+};
+
}
+
+/// Add a diagnostic to the current delay pool.
+inline void Sema::DelayedDiagnostics::add(const sema::DelayedDiagnostic &diag) {
+ assert(shouldDelayDiagnostics() && "trying to delay without pool");
+ CurPool->add(diag);
+}
+
+
}
#endif
diff --git a/include/clang/Sema/Designator.h b/include/clang/Sema/Designator.h
index fe01f4d..55603fe 100644
--- a/include/clang/Sema/Designator.h
+++ b/include/clang/Sema/Designator.h
@@ -179,18 +179,10 @@ public:
/// Designation - Represent a full designation, which is a sequence of
/// designators. This class is mostly a helper for InitListDesignations.
class Designation {
- /// InitIndex - The index of the initializer expression this is for. For
- /// example, if the initializer were "{ A, .foo=B, C }" a Designation would
- /// exist with InitIndex=1, because element #1 has a designation.
- unsigned InitIndex;
-
/// Designators - The actual designators for this initializer.
SmallVector<Designator, 2> Designators;
- Designation(unsigned Idx) : InitIndex(Idx) {}
public:
- Designation() : InitIndex(4000) {}
-
/// AddDesignator - Add a designator to the end of this list.
void AddDesignator(Designator D) {
Designators.push_back(D);
diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h
index 0dd6887..77659be 100644
--- a/include/clang/Sema/Initialization.h
+++ b/include/clang/Sema/Initialization.h
@@ -15,6 +15,7 @@
#include "clang/Sema/Ownership.h"
#include "clang/Sema/Overload.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/Basic/SourceLocation.h"
@@ -855,8 +856,8 @@ public:
///
/// \param BaseType the base type to which we will be casting.
///
- /// \param IsLValue true if the result of this cast will be treated as
- /// an lvalue.
+ /// \param Category Indicates whether the result will be treated as an
+ /// rvalue, an xvalue, or an lvalue.
void AddDerivedToBaseCastStep(QualType BaseType,
ExprValueKind Category);
@@ -865,9 +866,6 @@ public:
/// \param BindingTemporary True if we are binding a reference to a temporary
/// object (thereby extending its lifetime); false if we are binding to an
/// lvalue or an lvalue treated as an rvalue.
- ///
- /// \param UnnecessaryCopy True if we should check for a copy
- /// constructor for a completely unnecessary but
void AddReferenceBindingStep(QualType T, bool BindingTemporary);
/// \brief Add a new step that makes an extraneous copy of the input
diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h
index d334447..d2fc285 100644
--- a/include/clang/Sema/Overload.h
+++ b/include/clang/Sema/Overload.h
@@ -659,12 +659,25 @@ namespace clang {
/// A structure used to record information about a failed
/// template argument deduction.
struct DeductionFailureInfo {
- // A Sema::TemplateDeductionResult.
- unsigned Result;
+ /// A Sema::TemplateDeductionResult.
+ unsigned Result : 8;
+
+ /// \brief Indicates whether a diagnostic is stored in Diagnostic.
+ unsigned HasDiagnostic : 1;
/// \brief Opaque pointer containing additional data about
/// this deduction failure.
void *Data;
+
+ /// \brief A diagnostic indicating why deduction failed.
+ union {
+ void *Align;
+ char Diagnostic[sizeof(PartialDiagnosticAt)];
+ };
+
+ /// \brief Retrieve the diagnostic which caused this deduction failure,
+ /// if any.
+ PartialDiagnosticAt *getSFINAEDiagnostic();
/// \brief Retrieve the template parameter this deduction failure
/// refers to, if any.
@@ -740,11 +753,7 @@ namespace clang {
public:
OverloadCandidateSet(SourceLocation Loc) : Loc(Loc), NumInlineSequences(0){}
- ~OverloadCandidateSet() {
- for (iterator i = begin(), e = end(); i != e; ++i)
- for (unsigned ii = 0, ie = i->NumConversions; ii != ie; ++ii)
- i->Conversions[ii].~ImplicitConversionSequence();
- }
+ ~OverloadCandidateSet() { clear(); }
SourceLocation getLocation() const { return Loc; }
diff --git a/include/clang/Sema/ParsedTemplate.h b/include/clang/Sema/ParsedTemplate.h
index c1b4710..69080ad 100644
--- a/include/clang/Sema/ParsedTemplate.h
+++ b/include/clang/Sema/ParsedTemplate.h
@@ -58,7 +58,7 @@ namespace clang {
SourceLocation TemplateLoc)
: Kind(ParsedTemplateArgument::Template),
Arg(Template.getAsOpaquePtr()),
- Loc(TemplateLoc), SS(SS), EllipsisLoc() { }
+ SS(SS), Loc(TemplateLoc), EllipsisLoc() { }
/// \brief Determine whether the given template argument is invalid.
bool isInvalid() const { return Arg == 0; }
@@ -118,13 +118,13 @@ namespace clang {
/// expression), or an ActionBase::TemplateTy (for a template).
void *Arg;
- /// \brief the location of the template argument.
- SourceLocation Loc;
-
/// \brief The nested-name-specifier that can accompany a template template
/// argument.
CXXScopeSpec SS;
-
+
+ /// \brief the location of the template argument.
+ SourceLocation Loc;
+
/// \brief The ellipsis location that can accompany a template template
/// argument (turning it into a template template argument expansion).
SourceLocation EllipsisLoc;
diff --git a/include/clang/Sema/Scope.h b/include/clang/Sema/Scope.h
index 48f5417..b78556e 100644
--- a/include/clang/Sema/Scope.h
+++ b/include/clang/Sema/Scope.h
@@ -71,7 +71,7 @@ public:
FunctionPrototypeScope = 0x100,
/// AtCatchScope - This is a scope that corresponds to the Objective-C
- /// @catch statement.
+ /// \@catch statement.
AtCatchScope = 0x200,
/// ObjCMethodScope - This scope corresponds to an Objective-C method body.
@@ -270,7 +270,7 @@ public:
return getFlags() & Scope::FunctionPrototypeScope;
}
- /// isAtCatchScope - Return true if this scope is @catch.
+ /// isAtCatchScope - Return true if this scope is \@catch.
bool isAtCatchScope() const {
return getFlags() & Scope::AtCatchScope;
}
diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h
index ceaf586..b4752f5 100644
--- a/include/clang/Sema/ScopeInfo.h
+++ b/include/clang/Sema/ScopeInfo.h
@@ -74,7 +74,7 @@ public:
///
ScopeKind Kind;
- /// \brief Whether this function contains a VLA, @try, try, C++
+ /// \brief Whether this function contains a VLA, \@try, try, C++
/// initializer, or anything else that can't be jumped past.
bool HasBranchProtectedScope;
@@ -84,6 +84,14 @@ public:
/// \brief Whether this function contains any indirect gotos.
bool HasIndirectGoto;
+ /// A flag that is set when parsing a -dealloc method and no [super dealloc]
+ /// call was found yet.
+ bool ObjCShouldCallSuperDealloc;
+
+ /// A flag that is set when parsing a -finalize method and no [super finalize]
+ /// call was found yet.
+ bool ObjCShouldCallSuperFinalize;
+
/// \brief Used to determine if errors occurred in this function or block.
DiagnosticErrorTrap ErrorTrap;
@@ -93,7 +101,7 @@ public:
/// \brief The list of return statements that occur within the function or
/// block, if there is any chance of applying the named return value
- /// optimization.
+ /// optimization, or if we need to infer a return type.
SmallVector<ReturnStmt*, 4> Returns;
/// \brief The stack of currently active compound stamement scopes in the
@@ -127,6 +135,8 @@ public:
HasBranchProtectedScope(false),
HasBranchIntoScope(false),
HasIndirectGoto(false),
+ ObjCShouldCallSuperDealloc(false),
+ ObjCShouldCallSuperFinalize(false),
ErrorTrap(Diag) { }
virtual ~FunctionScopeInfo();
@@ -344,6 +354,9 @@ public:
/// \brief Whether any of the capture expressions requires cleanups.
bool ExprNeedsCleanups;
+ /// \brief Whether the lambda contains an unexpanded parameter pack.
+ bool ContainsUnexpandedParameterPack;
+
/// \brief Variables used to index into by-copy array captures.
llvm::SmallVector<VarDecl *, 4> ArrayIndexVars;
@@ -355,7 +368,7 @@ public:
CXXMethodDecl *CallOperator)
: CapturingScopeInfo(Diag, ImpCap_None), Lambda(Lambda),
CallOperator(CallOperator), NumExplicitCaptures(0), Mutable(false),
- ExprNeedsCleanups(false)
+ ExprNeedsCleanups(false), ContainsUnexpandedParameterPack(false)
{
Kind = SK_Lambda;
}
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index c8767b6..058e0d9 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -28,6 +28,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/LambdaMangleContext.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/NSAPI.h"
#include "clang/Lex/ModuleLoader.h"
@@ -36,7 +37,9 @@
#include "clang/Basic/TypeTraits.h"
#include "clang/Basic/ExpressionTraits.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include <deque>
@@ -167,8 +170,10 @@ namespace clang {
namespace sema {
class AccessedEntity;
class BlockScopeInfo;
+ class CapturingScopeInfo;
class CompoundScopeInfo;
class DelayedDiagnostic;
+ class DelayedDiagnosticPool;
class FunctionScopeInfo;
class LambdaScopeInfo;
class PossiblyUnreachableDiag;
@@ -220,13 +225,13 @@ public:
/// This is used as part of a hack to omit that class from ADL results.
DeclarationName VAListTagName;
- /// PackContext - Manages the stack for #pragma pack. An alignment
+ /// PackContext - Manages the stack for \#pragma pack. An alignment
/// of 0 indicates default alignment.
void *PackContext; // Really a "PragmaPackStack*"
- bool MSStructPragmaOn; // True when #pragma ms_struct on
+ bool MSStructPragmaOn; // True when \#pragma ms_struct on
- /// VisContext - Manages the stack for #pragma GCC visibility.
+ /// VisContext - Manages the stack for \#pragma GCC visibility.
void *VisContext; // Really a "PragmaVisStack*"
/// ExprNeedsCleanups - True if the current evaluation context
@@ -262,10 +267,15 @@ public:
///
/// This set is used to suppress redundant diagnostics.
llvm::SmallPtrSet<NamedDecl *, 4> HiddenDefinitions;
-
+
/// FieldCollector - Collects CXXFieldDecls during parsing of C++ classes.
OwningPtr<CXXFieldCollector> FieldCollector;
+ typedef llvm::SmallSetVector<const NamedDecl*, 16> NamedDeclSetType;
+
+ /// \brief Set containing all declared private fields that are not used.
+ NamedDeclSetType UnusedPrivateFields;
+
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;
/// PureVirtualClassDiagSet - a set of class declarations which we have
@@ -355,93 +365,63 @@ public:
class DelayedDiagnostics;
- class ParsingDeclState {
- unsigned SavedStackSize;
- friend class Sema::DelayedDiagnostics;
- };
-
- class ProcessingContextState {
- unsigned SavedParsingDepth;
- unsigned SavedActiveStackBase;
+ class DelayedDiagnosticsState {
+ sema::DelayedDiagnosticPool *SavedPool;
friend class Sema::DelayedDiagnostics;
};
+ typedef DelayedDiagnosticsState ParsingDeclState;
+ typedef DelayedDiagnosticsState ProcessingContextState;
/// A class which encapsulates the logic for delaying diagnostics
/// during parsing and other processing.
class DelayedDiagnostics {
- /// \brief The stack of diagnostics that were delayed due to being
- /// produced during the parsing of a declaration.
- sema::DelayedDiagnostic *Stack;
-
- /// \brief The number of objects on the delayed-diagnostics stack.
- unsigned StackSize;
-
- /// \brief The current capacity of the delayed-diagnostics stack.
- unsigned StackCapacity;
-
- /// \brief The index of the first "active" delayed diagnostic in
- /// the stack. When parsing class definitions, we ignore active
- /// delayed diagnostics from the surrounding context.
- unsigned ActiveStackBase;
-
- /// \brief The depth of the declarations we're currently parsing.
- /// This gets saved and reset whenever we enter a class definition.
- unsigned ParsingDepth;
+ /// \brief The current pool of diagnostics into which delayed
+ /// diagnostics should go.
+ sema::DelayedDiagnosticPool *CurPool;
public:
- DelayedDiagnostics() : Stack(0), StackSize(0), StackCapacity(0),
- ActiveStackBase(0), ParsingDepth(0) {}
-
- ~DelayedDiagnostics() {
- delete[] reinterpret_cast<char*>(Stack);
- }
+ DelayedDiagnostics() : CurPool(0) {}
/// Adds a delayed diagnostic.
- void add(const sema::DelayedDiagnostic &diag);
+ void add(const sema::DelayedDiagnostic &diag); // in DelayedDiagnostic.h
/// Determines whether diagnostics should be delayed.
- bool shouldDelayDiagnostics() { return ParsingDepth > 0; }
-
- /// Observe that we've started parsing a declaration. Access and
- /// deprecation diagnostics will be delayed; when the declaration
- /// is completed, all active delayed diagnostics will be evaluated
- /// in its context, and then active diagnostics stack will be
- /// popped down to the saved depth.
- ParsingDeclState pushParsingDecl() {
- ParsingDepth++;
-
- ParsingDeclState state;
- state.SavedStackSize = StackSize;
- return state;
- }
-
- /// Observe that we're completed parsing a declaration.
- static void popParsingDecl(Sema &S, ParsingDeclState state, Decl *decl);
+ bool shouldDelayDiagnostics() { return CurPool != 0; }
- /// Observe that we've started processing a different context, the
- /// contents of which are semantically separate from the
- /// declarations it may lexically appear in. This sets aside the
- /// current stack of active diagnostics and starts afresh.
- ProcessingContextState pushContext() {
- assert(StackSize >= ActiveStackBase);
+ /// Returns the current delayed-diagnostics pool.
+ sema::DelayedDiagnosticPool *getCurrentPool() const {
+ return CurPool;
+ }
- ProcessingContextState state;
- state.SavedParsingDepth = ParsingDepth;
- state.SavedActiveStackBase = ActiveStackBase;
+ /// Enter a new scope. Access and deprecation diagnostics will be
+ /// collected in this pool.
+ DelayedDiagnosticsState push(sema::DelayedDiagnosticPool &pool) {
+ DelayedDiagnosticsState state;
+ state.SavedPool = CurPool;
+ CurPool = &pool;
+ return state;
+ }
- ActiveStackBase = StackSize;
- ParsingDepth = 0;
+ /// Leave a delayed-diagnostic state that was previously pushed.
+ /// Do not emit any of the diagnostics. This is performed as part
+ /// of the bookkeeping of popping a pool "properly".
+ void popWithoutEmitting(DelayedDiagnosticsState state) {
+ CurPool = state.SavedPool;
+ }
+ /// Enter a new scope where access and deprecation diagnostics are
+ /// not delayed.
+ DelayedDiagnosticsState pushUndelayed() {
+ DelayedDiagnosticsState state;
+ state.SavedPool = CurPool;
+ CurPool = 0;
return state;
}
- /// Observe that we've stopped processing a context. This
- /// restores the previous stack of active diagnostics.
- void popContext(ProcessingContextState state) {
- assert(ActiveStackBase == StackSize);
- assert(ParsingDepth == 0);
- ActiveStackBase = state.SavedActiveStackBase;
- ParsingDepth = state.SavedParsingDepth;
+ /// Undo a previous pushUndelayed().
+ void popUndelayed(DelayedDiagnosticsState state) {
+ assert(CurPool == NULL);
+ CurPool = state.SavedPool;
}
} DelayedDiagnostics;
@@ -452,11 +432,11 @@ public:
DeclContext *SavedContext;
ProcessingContextState SavedContextState;
QualType SavedCXXThisTypeOverride;
-
+
public:
ContextRAII(Sema &S, DeclContext *ContextToPush)
: S(S), SavedContext(S.CurContext),
- SavedContextState(S.DelayedDiagnostics.pushContext()),
+ SavedContextState(S.DelayedDiagnostics.pushUndelayed()),
SavedCXXThisTypeOverride(S.CXXThisTypeOverride)
{
assert(ContextToPush && "pushing null context");
@@ -466,7 +446,7 @@ public:
void pop() {
if (!SavedContext) return;
S.CurContext = SavedContext;
- S.DelayedDiagnostics.popContext(SavedContextState);
+ S.DelayedDiagnostics.popUndelayed(SavedContextState);
S.CXXThisTypeOverride = SavedCXXThisTypeOverride;
SavedContext = 0;
}
@@ -477,12 +457,12 @@ public:
};
/// WeakUndeclaredIdentifiers - Identifiers contained in
- /// #pragma weak before declared. rare. may alias another
+ /// \#pragma weak before declared. rare. may alias another
/// identifier, declared or undeclared
llvm::DenseMap<IdentifierInfo*,WeakInfo> WeakUndeclaredIdentifiers;
/// ExtnameUndeclaredIdentifiers - Identifiers contained in
- /// #pragma redefine_extname before declared. Used in Solaris system headers
+ /// \#pragma redefine_extname before declared. Used in Solaris system headers
/// to define functions that occur in multiple standards to call the version
/// in the currently selected standard.
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*> ExtnameUndeclaredIdentifiers;
@@ -492,7 +472,7 @@ public:
void LoadExternalWeakUndeclaredIdentifiers();
/// WeakTopLevelDecl - Translation-unit scoped declarations generated by
- /// #pragma weak during processing of other Decls.
+ /// \#pragma weak during processing of other Decls.
/// I couldn't figure out a clean way to generate these in-line, so
/// we store them here and handle separately -- which is a hack.
/// It would be best to refactor this.
@@ -513,10 +493,10 @@ public:
LazyDeclPtr StdBadAlloc;
/// \brief The C++ "std::initializer_list" template, which is defined in
- /// <initializer_list>.
+ /// \<initializer_list>.
ClassTemplateDecl *StdInitializerList;
- /// \brief The C++ "type_info" declaration, which is defined in <typeinfo>.
+ /// \brief The C++ "type_info" declaration, which is defined in \<typeinfo>.
RecordDecl *CXXTypeInfoDecl;
/// \brief The MSVC "_GUID" struct, which is defined in MSVC header files.
@@ -527,16 +507,28 @@ public:
/// \brief The declaration of the Objective-C NSNumber class.
ObjCInterfaceDecl *NSNumberDecl;
-
+
+ /// \brief Pointer to NSNumber type (NSNumber *).
+ QualType NSNumberPointer;
+
/// \brief The Objective-C NSNumber methods used to create NSNumber literals.
ObjCMethodDecl *NSNumberLiteralMethods[NSAPI::NumNSNumberLiteralMethods];
-
+
+ /// \brief The declaration of the Objective-C NSString class.
+ ObjCInterfaceDecl *NSStringDecl;
+
+ /// \brief Pointer to NSString type (NSString *).
+ QualType NSStringPointer;
+
+ /// \brief The declaration of the stringWithUTF8String: method.
+ ObjCMethodDecl *StringWithUTF8StringMethod;
+
/// \brief The declaration of the Objective-C NSArray class.
ObjCInterfaceDecl *NSArrayDecl;
/// \brief The declaration of the arrayWithObjects:count: method.
ObjCMethodDecl *ArrayWithObjectsMethod;
-
+
/// \brief The declaration of the Objective-C NSDictionary class.
ObjCInterfaceDecl *NSDictionaryDecl;
@@ -550,13 +542,6 @@ public:
/// have been declared.
bool GlobalNewDeleteDeclared;
- /// A flag that is set when parsing a -dealloc method and no [super dealloc]
- /// call was found yet.
- bool ObjCShouldCallSuperDealloc;
- /// A flag that is set when parsing a -finalize method and no [super finalize]
- /// call was found yet.
- bool ObjCShouldCallSuperFinalize;
-
/// \brief Describes how the expressions currently being parsed are
/// evaluated at run-time, if at all.
enum ExpressionEvaluationContext {
@@ -611,10 +596,10 @@ public:
llvm::SmallVector<LambdaExpr *, 2> Lambdas;
/// \brief The declaration that provides context for the lambda expression
- /// if the normal declaration context does not suffice, e.g., in a
+ /// if the normal declaration context does not suffice, e.g., in a
/// default function argument.
Decl *LambdaContextDecl;
-
+
/// \brief The context information used to mangle lambda expressions
/// within this context.
///
@@ -629,7 +614,7 @@ public:
/// \brief If we are processing a decltype type, a set of temporary binding
/// expressions for which we have deferred checking the destructor.
llvm::SmallVector<CXXBindTemporaryExpr*, 8> DelayedDecltypeBinds;
-
+
ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
unsigned NumCleanupObjects,
bool ParentNeedsCleanups,
@@ -638,11 +623,11 @@ public:
: Context(Context), ParentNeedsCleanups(ParentNeedsCleanups),
IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
LambdaContextDecl(LambdaContextDecl), LambdaMangle() { }
-
+
~ExpressionEvaluationContextRecord() {
delete LambdaMangle;
}
-
+
/// \brief Retrieve the mangling context for lambdas.
LambdaMangleContext &getLambdaMangleContext() {
assert(LambdaContextDecl && "Need to have a lambda context declaration");
@@ -654,17 +639,12 @@ public:
/// A stack of expression evaluation contexts.
SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
-
+
/// SpecialMemberOverloadResult - The overloading result for a special member
/// function.
///
/// This is basically a wrapper around PointerIntPair. The lowest bits of the
- /// integer are used to determine whether overload resolution succeeded, and
- /// whether, when looking up a copy constructor or assignment operator, we
- /// found a potential copy constructor/assignment operator whose first
- /// parameter is const-qualified. This is used for determining parameter types
- /// of other objects and is utterly meaningless on other types of special
- /// members.
+ /// integer are used to determine whether overload resolution succeeded.
class SpecialMemberOverloadResult : public llvm::FastFoldingSetNode {
public:
enum Kind {
@@ -735,7 +715,7 @@ public:
/// of selectors are "overloaded").
GlobalMethodPool MethodPool;
- /// Method selectors used in a @selector expression. Used for implementation
+ /// Method selectors used in a \@selector expression. Used for implementation
/// of -Wselector.
llvm::DenseMap<Selector, SourceLocation> ReferencedSelectors;
@@ -826,7 +806,8 @@ public:
bool findMacroSpelling(SourceLocation &loc, StringRef name);
/// \brief Get a string to suggest for zero-initialization of a type.
- const char *getFixItZeroInitializerForType(QualType T) const;
+ std::string getFixItZeroInitializerForType(QualType T) const;
+ std::string getFixItZeroLiteralForType(QualType T) const;
ExprResult Owned(Expr* E) { return E; }
ExprResult Owned(ExprResult R) { return R; }
@@ -861,9 +842,11 @@ public:
/// \brief Retrieve the current lambda expression, if any.
sema::LambdaScopeInfo *getCurLambda();
- /// WeakTopLevelDeclDecls - access to #pragma weak-generated Decls
+ /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls
SmallVector<Decl*,2> &WeakTopLevelDecls() { return WeakTopLevelDecl; }
+ void ActOnComment(SourceRange Comment);
+
//===--------------------------------------------------------------------===//
// Type Analysis / Processing: SemaType.cpp.
//
@@ -899,7 +882,7 @@ public:
TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy);
TypeSourceInfo *GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
TypeSourceInfo *ReturnTypeInfo);
-
+
/// \brief Package the given type and TSI into a ParsedType.
ParsedType CreateParsedType(QualType T, TypeSourceInfo *TInfo);
DeclarationNameInfo GetNameForDeclarator(Declarator &D);
@@ -936,19 +919,168 @@ public:
/// in an Objective-C message declaration. Return the appropriate type.
ParsedType ActOnObjCInstanceType(SourceLocation Loc);
+ /// \brief Abstract class used to diagnose incomplete types.
+ struct TypeDiagnoser {
+ bool Suppressed;
+
+ TypeDiagnoser(bool Suppressed = false) : Suppressed(Suppressed) { }
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) = 0;
+ virtual ~TypeDiagnoser() {}
+ };
+
+ static int getPrintable(int I) { return I; }
+ static unsigned getPrintable(unsigned I) { return I; }
+ static bool getPrintable(bool B) { return B; }
+ static const char * getPrintable(const char *S) { return S; }
+ static StringRef getPrintable(StringRef S) { return S; }
+ static const std::string &getPrintable(const std::string &S) { return S; }
+ static const IdentifierInfo *getPrintable(const IdentifierInfo *II) {
+ return II;
+ }
+ static DeclarationName getPrintable(DeclarationName N) { return N; }
+ static QualType getPrintable(QualType T) { return T; }
+ static SourceRange getPrintable(SourceRange R) { return R; }
+ static SourceRange getPrintable(SourceLocation L) { return L; }
+ static SourceRange getPrintable(Expr *E) { return E->getSourceRange(); }
+ static SourceRange getPrintable(TypeLoc TL) { return TL.getSourceRange();}
+
+ template<typename T1>
+ class BoundTypeDiagnoser1 : public TypeDiagnoser {
+ unsigned DiagID;
+ const T1 &Arg1;
+
+ public:
+ BoundTypeDiagnoser1(unsigned DiagID, const T1 &Arg1)
+ : TypeDiagnoser(DiagID == 0), DiagID(DiagID), Arg1(Arg1) { }
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ if (Suppressed) return;
+ S.Diag(Loc, DiagID) << getPrintable(Arg1) << T;
+ }
+
+ virtual ~BoundTypeDiagnoser1() { }
+ };
+
+ template<typename T1, typename T2>
+ class BoundTypeDiagnoser2 : public TypeDiagnoser {
+ unsigned DiagID;
+ const T1 &Arg1;
+ const T2 &Arg2;
+
+ public:
+ BoundTypeDiagnoser2(unsigned DiagID, const T1 &Arg1,
+ const T2 &Arg2)
+ : TypeDiagnoser(DiagID == 0), DiagID(DiagID), Arg1(Arg1),
+ Arg2(Arg2) { }
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ if (Suppressed) return;
+ S.Diag(Loc, DiagID) << getPrintable(Arg1) << getPrintable(Arg2) << T;
+ }
+
+ virtual ~BoundTypeDiagnoser2() { }
+ };
+
+ template<typename T1, typename T2, typename T3>
+ class BoundTypeDiagnoser3 : public TypeDiagnoser {
+ unsigned DiagID;
+ const T1 &Arg1;
+ const T2 &Arg2;
+ const T3 &Arg3;
+
+ public:
+ BoundTypeDiagnoser3(unsigned DiagID, const T1 &Arg1,
+ const T2 &Arg2, const T3 &Arg3)
+ : TypeDiagnoser(DiagID == 0), DiagID(DiagID), Arg1(Arg1),
+ Arg2(Arg2), Arg3(Arg3) { }
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ if (Suppressed) return;
+ S.Diag(Loc, DiagID)
+ << getPrintable(Arg1) << getPrintable(Arg2) << getPrintable(Arg3) << T;
+ }
+
+ virtual ~BoundTypeDiagnoser3() { }
+ };
+
bool RequireCompleteType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD,
- std::pair<SourceLocation, PartialDiagnostic> Note);
- bool RequireCompleteType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD);
+ TypeDiagnoser &Diagnoser);
bool RequireCompleteType(SourceLocation Loc, QualType T,
unsigned DiagID);
- bool RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
- std::pair<SourceLocation,
- PartialDiagnostic> Note);
+
+ template<typename T1>
+ bool RequireCompleteType(SourceLocation Loc, QualType T,
+ unsigned DiagID, const T1 &Arg1) {
+ BoundTypeDiagnoser1<T1> Diagnoser(DiagID, Arg1);
+ return RequireCompleteType(Loc, T, Diagnoser);
+ }
+
+ template<typename T1, typename T2>
+ bool RequireCompleteType(SourceLocation Loc, QualType T,
+ unsigned DiagID, const T1 &Arg1, const T2 &Arg2) {
+ BoundTypeDiagnoser2<T1, T2> Diagnoser(DiagID, Arg1, Arg2);
+ return RequireCompleteType(Loc, T, Diagnoser);
+ }
+
+ template<typename T1, typename T2, typename T3>
+ bool RequireCompleteType(SourceLocation Loc, QualType T,
+ unsigned DiagID, const T1 &Arg1, const T2 &Arg2,
+ const T3 &Arg3) {
+ BoundTypeDiagnoser3<T1, T2, T3> Diagnoser(DiagID, Arg1, Arg2,
+ Arg3);
+ return RequireCompleteType(Loc, T, Diagnoser);
+ }
+
+ bool RequireCompleteExprType(Expr *E, TypeDiagnoser &Diagnoser);
+ bool RequireCompleteExprType(Expr *E, unsigned DiagID);
+
+ template<typename T1>
+ bool RequireCompleteExprType(Expr *E, unsigned DiagID, const T1 &Arg1) {
+ BoundTypeDiagnoser1<T1> Diagnoser(DiagID, Arg1);
+ return RequireCompleteExprType(E, Diagnoser);
+ }
+
+ template<typename T1, typename T2>
+ bool RequireCompleteExprType(Expr *E, unsigned DiagID, const T1 &Arg1,
+ const T2 &Arg2) {
+ BoundTypeDiagnoser2<T1, T2> Diagnoser(DiagID, Arg1, Arg2);
+ return RequireCompleteExprType(E, Diagnoser);
+ }
+
+ template<typename T1, typename T2, typename T3>
+ bool RequireCompleteExprType(Expr *E, unsigned DiagID, const T1 &Arg1,
+ const T2 &Arg2, const T3 &Arg3) {
+ BoundTypeDiagnoser3<T1, T2, T3> Diagnoser(DiagID, Arg1, Arg2,
+ Arg3);
+ return RequireCompleteExprType(E, Diagnoser);
+ }
bool RequireLiteralType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD);
+ TypeDiagnoser &Diagnoser);
+ bool RequireLiteralType(SourceLocation Loc, QualType T, unsigned DiagID);
+
+ template<typename T1>
+ bool RequireLiteralType(SourceLocation Loc, QualType T,
+ unsigned DiagID, const T1 &Arg1) {
+ BoundTypeDiagnoser1<T1> Diagnoser(DiagID, Arg1);
+ return RequireLiteralType(Loc, T, Diagnoser);
+ }
+
+ template<typename T1, typename T2>
+ bool RequireLiteralType(SourceLocation Loc, QualType T,
+ unsigned DiagID, const T1 &Arg1, const T2 &Arg2) {
+ BoundTypeDiagnoser2<T1, T2> Diagnoser(DiagID, Arg1, Arg2);
+ return RequireLiteralType(Loc, T, Diagnoser);
+ }
+
+ template<typename T1, typename T2, typename T3>
+ bool RequireLiteralType(SourceLocation Loc, QualType T,
+ unsigned DiagID, const T1 &Arg1, const T2 &Arg2,
+ const T3 &Arg3) {
+ BoundTypeDiagnoser3<T1, T2, T3> Diagnoser(DiagID, Arg1, Arg2,
+ Arg3);
+ return RequireLiteralType(Loc, T, Diagnoser);
+ }
QualType getElaboratedType(ElaboratedTypeKeyword Keyword,
const CXXScopeSpec &SS, QualType T);
@@ -978,6 +1110,8 @@ public:
void DiagnoseUseOfUnimplementedSelectors();
+ bool isSimpleTypeSpecifier(tok::TokenKind Kind) const;
+
ParsedType getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
Scope *S, CXXScopeSpec *SS = 0,
bool isClassName = false,
@@ -988,7 +1122,7 @@ public:
IdentifierInfo **CorrectedII = 0);
TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S);
bool isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S);
- bool DiagnoseUnknownTypeName(const IdentifierInfo &II,
+ bool DiagnoseUnknownTypeName(IdentifierInfo *&II,
SourceLocation IILoc,
Scope *S,
CXXScopeSpec *SS,
@@ -1173,12 +1307,21 @@ public:
unsigned NumDecls);
DeclGroupPtrTy BuildDeclaratorGroup(Decl **Group, unsigned NumDecls,
bool TypeMayContainAuto = true);
+
+ /// Should be called on all declarations that might have attached
+ /// documentation comments.
+ void ActOnDocumentableDecl(Decl *D);
+ void ActOnDocumentableDecls(Decl **Group, unsigned NumDecls);
+
void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D,
SourceLocation LocAfterDecls);
void CheckForFunctionRedefinition(FunctionDecl *FD);
Decl *ActOnStartOfFunctionDef(Scope *S, Declarator &D);
Decl *ActOnStartOfFunctionDef(Scope *S, Decl *D);
void ActOnStartOfObjCMethodDef(Scope *S, Decl *D);
+ bool isObjCMethodDecl(Decl *D) {
+ return D && isa<ObjCMethodDecl>(D);
+ }
void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope);
Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body);
@@ -1213,7 +1356,7 @@ public:
/// \param ImportLoc The location of the 'import' keyword.
///
/// \param Path The module access path.
- DeclResult ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc,
+ DeclResult ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc,
ModuleIdPath Path);
/// \brief Retrieve a suitable printing policy.
@@ -1286,13 +1429,15 @@ public:
Declarator &D, Expr *BitfieldWidth);
FieldDecl *HandleField(Scope *S, RecordDecl *TagD, SourceLocation DeclStart,
- Declarator &D, Expr *BitfieldWidth, bool HasInit,
+ Declarator &D, Expr *BitfieldWidth,
+ InClassInitStyle InitStyle,
AccessSpecifier AS);
FieldDecl *CheckFieldDecl(DeclarationName Name, QualType T,
TypeSourceInfo *TInfo,
RecordDecl *Record, SourceLocation Loc,
- bool Mutable, Expr *BitfieldWidth, bool HasInit,
+ bool Mutable, Expr *BitfieldWidth,
+ InClassInitStyle InitStyle,
SourceLocation TSSL,
AccessSpecifier AS, NamedDecl *PrevDecl,
Declarator *D = 0);
@@ -1432,6 +1577,24 @@ public:
TypedefDecl *ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
TypeSourceInfo *TInfo);
bool isIncompatibleTypedef(TypeDecl *Old, TypedefNameDecl *New);
+
+ /// Attribute merging methods. Return true if a new attribute was added.
+ AvailabilityAttr *mergeAvailabilityAttr(Decl *D, SourceRange Range,
+ IdentifierInfo *Platform,
+ VersionTuple Introduced,
+ VersionTuple Deprecated,
+ VersionTuple Obsoleted,
+ bool IsUnavailable,
+ StringRef Message);
+ VisibilityAttr *mergeVisibilityAttr(Decl *D, SourceRange Range,
+ VisibilityAttr::VisibilityType Vis);
+ DLLImportAttr *mergeDLLImportAttr(Decl *D, SourceRange Range);
+ DLLExportAttr *mergeDLLExportAttr(Decl *D, SourceRange Range);
+ FormatAttr *mergeFormatAttr(Decl *D, SourceRange Range, StringRef Format,
+ int FormatIdx, int FirstArg);
+ SectionAttr *mergeSectionAttr(Decl *D, SourceRange Range, StringRef Name);
+ bool mergeDeclAttribute(Decl *New, InheritableAttr *Attr);
+
void mergeDeclAttributes(Decl *New, Decl *Old, bool MergeDeprecation = true);
void MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls);
bool MergeFunctionDecl(FunctionDecl *New, Decl *Old, Scope *S);
@@ -1558,16 +1721,59 @@ public:
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
llvm::APSInt &Value, CCEKind CCE);
+ /// \brief Abstract base class used to diagnose problems that occur while
+ /// trying to convert an expression to integral or enumeration type.
+ class ICEConvertDiagnoser {
+ public:
+ bool Suppress;
+ bool SuppressConversion;
+
+ ICEConvertDiagnoser(bool Suppress = false,
+ bool SuppressConversion = false)
+ : Suppress(Suppress), SuppressConversion(SuppressConversion) { }
+
+ /// \brief Emits a diagnostic complaining that the expression does not have
+ /// integral or enumeration type.
+ virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
+ QualType T) = 0;
+
+ /// \brief Emits a diagnostic when the expression has incomplete class type.
+ virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
+ QualType T) = 0;
+
+ /// \brief Emits a diagnostic when the only matching conversion function
+ /// is explicit.
+ virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) = 0;
+
+ /// \brief Emits a note for the explicit conversion function.
+ virtual DiagnosticBuilder
+ noteExplicitConv(Sema &S, CXXConversionDecl *Conv, QualType ConvTy) = 0;
+
+ /// \brief Emits a diagnostic when there are multiple possible conversion
+ /// functions.
+ virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) = 0;
+
+ /// \brief Emits a note for one of the candidate conversions.
+ virtual DiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) = 0;
+
+ /// \brief Emits a diagnostic when we picked a conversion function
+ /// (for cases when we are not allowed to pick a conversion function).
+ virtual DiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) = 0;
+
+ virtual ~ICEConvertDiagnoser() {}
+ };
+
ExprResult
ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *FromE,
- const PartialDiagnostic &NotIntDiag,
- const PartialDiagnostic &IncompleteDiag,
- const PartialDiagnostic &ExplicitConvDiag,
- const PartialDiagnostic &ExplicitConvNote,
- const PartialDiagnostic &AmbigDiag,
- const PartialDiagnostic &AmbigNote,
- const PartialDiagnostic &ConvDiag,
+ ICEConvertDiagnoser &Diagnoser,
bool AllowScopedEnumerations);
+
enum ObjCSubscriptKind {
OS_Array,
OS_Dictionary,
@@ -1910,14 +2116,16 @@ public:
unsigned Quals);
CXXMethodDecl *LookupCopyingAssignment(CXXRecordDecl *Class, unsigned Quals,
bool RValueThis, unsigned ThisQuals);
- CXXConstructorDecl *LookupMovingConstructor(CXXRecordDecl *Class);
- CXXMethodDecl *LookupMovingAssignment(CXXRecordDecl *Class, bool RValueThis,
- unsigned ThisQuals);
+ CXXConstructorDecl *LookupMovingConstructor(CXXRecordDecl *Class,
+ unsigned Quals);
+ CXXMethodDecl *LookupMovingAssignment(CXXRecordDecl *Class, unsigned Quals,
+ bool RValueThis, unsigned ThisQuals);
CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class);
LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R,
ArrayRef<QualType> ArgTys,
bool AllowRawAndTemplate);
+ bool isKnownName(StringRef name);
void ArgumentDependentLookup(DeclarationName Name, bool Operator,
SourceLocation Loc,
@@ -2000,13 +2208,11 @@ public:
bool isPropertyReadonly(ObjCPropertyDecl *PropertyDecl,
ObjCInterfaceDecl *IDecl);
- typedef llvm::DenseSet<Selector, llvm::DenseMapInfo<Selector> > SelectorSet;
+ typedef llvm::SmallPtrSet<Selector, 8> SelectorSet;
typedef llvm::DenseMap<Selector, ObjCMethodDecl*> ProtocolsMethodsMap;
/// CheckProtocolMethodDefs - This routine checks unimplemented
/// methods declared in protocol, and those referenced by it.
- /// \param IDecl - Used for checking for methods which may have been
- /// inherited.
void CheckProtocolMethodDefs(SourceLocation ImpLoc,
ObjCProtocolDecl *PDecl,
bool& IncompleteImpl,
@@ -2021,7 +2227,7 @@ public:
SourceLocation Loc);
/// ImplMethodsVsClassMethods - This is main routine to warn if any method
- /// remains unimplemented in the class or category @implementation.
+ /// remains unimplemented in the class or category \@implementation.
void ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
ObjCContainerDecl* IDecl,
bool IncompleteImpl = false);
@@ -2033,7 +2239,7 @@ public:
const SelectorSet &InsMap);
/// DefaultSynthesizeProperties - This routine default synthesizes all
- /// properties which must be synthesized in class's @implementation.
+ /// properties which must be synthesized in the class's \@implementation.
void DefaultSynthesizeProperties (Scope *S, ObjCImplDecl* IMPDecl,
ObjCInterfaceDecl *IDecl);
void DefaultSynthesizeProperties(Scope *S, Decl *D);
@@ -2050,8 +2256,8 @@ public:
ObjCPropertyDecl *LookupPropertyDecl(const ObjCContainerDecl *CDecl,
IdentifierInfo *II);
- /// Called by ActOnProperty to handle @property declarations in
- //// class extensions.
+ /// Called by ActOnProperty to handle \@property declarations in
+ /// class extensions.
Decl *HandlePropertyInClassExtension(Scope *S,
SourceLocation AtLoc,
SourceLocation LParenLoc,
@@ -2067,7 +2273,7 @@ public:
tok::ObjCKeywordKind MethodImplKind);
/// Called by ActOnProperty and HandlePropertyInClassExtension to
- /// handle creating the ObjcPropertyDecl for a category or @interface.
+ /// handle creating the ObjcPropertyDecl for a category or \@interface.
ObjCPropertyDecl *CreatePropertyDecl(Scope *S,
ObjCContainerDecl *CDecl,
SourceLocation AtLoc,
@@ -2123,7 +2329,7 @@ public:
/// \brief Add the given method to the list of globally-known methods.
void addMethodToGlobalList(ObjCMethodList *List, ObjCMethodDecl *Method);
-
+
private:
/// AddMethodToGlobalPool - Add an instance or factory method to the global
/// pool. See descriptoin of AddInstanceMethodToGlobalPool.
@@ -2213,7 +2419,10 @@ public:
};
FullExprArg MakeFullExpr(Expr *Arg) {
- return FullExprArg(ActOnFinishFullExpr(Arg).release());
+ return MakeFullExpr(Arg, Arg ? Arg->getExprLoc() : SourceLocation());
+ }
+ FullExprArg MakeFullExpr(Expr *Arg, SourceLocation CC) {
+ return FullExprArg(ActOnFinishFullExpr(Arg, CC).release());
}
StmtResult ActOnExprStmt(FullExprArg Expr);
@@ -2258,7 +2467,8 @@ public:
StmtResult ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl,
SourceLocation ColonLoc, Stmt *SubStmt);
- StmtResult ActOnAttributedStmt(SourceLocation AttrLoc, const AttrVec &Attrs,
+ StmtResult ActOnAttributedStmt(SourceLocation AttrLoc,
+ ArrayRef<const Attr*> Attrs,
Stmt *SubStmt);
StmtResult ActOnIfStmt(SourceLocation IfLoc,
@@ -2285,12 +2495,14 @@ public:
FullExprArg Third,
SourceLocation RParenLoc,
Stmt *Body);
- ExprResult ActOnObjCForCollectionOperand(SourceLocation forLoc,
+ ExprResult CheckObjCForCollectionOperand(SourceLocation forLoc,
Expr *collection);
StmtResult ActOnObjCForCollectionStmt(SourceLocation ForColLoc,
SourceLocation LParenLoc,
- Stmt *First, Expr *Second,
- SourceLocation RParenLoc, Stmt *Body);
+ Stmt *First, Expr *collection,
+ SourceLocation RParenLoc);
+ StmtResult FinishObjCForCollectionStmt(Stmt *ForCollection, Stmt *Body);
+
StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc,
SourceLocation LParenLoc, Stmt *LoopVar,
SourceLocation ColonLoc, Expr *Collection,
@@ -2329,6 +2541,10 @@ public:
SourceLocation RParenLoc,
bool MSAsm = false);
+ StmtResult ActOnMSAsmStmt(SourceLocation AsmLoc,
+ ArrayRef<Token> AsmToks,
+ ArrayRef<unsigned> LineEnds,
+ SourceLocation EndLoc);
VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType,
SourceLocation StartLoc,
@@ -2408,21 +2624,21 @@ public:
void DiagnoseEmptyLoopBody(const Stmt *S,
const Stmt *PossibleBody);
- ParsingDeclState PushParsingDeclaration() {
- return DelayedDiagnostics.pushParsingDecl();
- }
- void PopParsingDeclaration(ParsingDeclState state, Decl *decl) {
- DelayedDiagnostics::popParsingDecl(*this, state, decl);
+ ParsingDeclState PushParsingDeclaration(sema::DelayedDiagnosticPool &pool) {
+ return DelayedDiagnostics.push(pool);
}
+ void PopParsingDeclaration(ParsingDeclState state, Decl *decl);
typedef ProcessingContextState ParsingClassState;
ParsingClassState PushParsingClass() {
- return DelayedDiagnostics.pushContext();
+ return DelayedDiagnostics.pushUndelayed();
}
void PopParsingClass(ParsingClassState state) {
- DelayedDiagnostics.popContext(state);
+ DelayedDiagnostics.popUndelayed(state);
}
+ void redelayDiagnostics(sema::DelayedDiagnosticPool &pool);
+
void EmitDeprecationWarning(NamedDecl *D, StringRef Message,
SourceLocation Loc,
const ObjCInterfaceDecl *UnknownObjCClass=0);
@@ -2484,13 +2700,13 @@ public:
///
/// \param Loc The location at which the capture occurs.
///
- /// \param Kind The kind of capture, which may be implicit (for either a
+ /// \param Kind The kind of capture, which may be implicit (for either a
/// block or a lambda), or explicit by-value or by-reference (for a lambda).
///
/// \param EllipsisLoc The location of the ellipsis, if one is provided in
/// an explicit lambda capture.
///
- /// \param BuildAndDiagnose Whether we are actually supposed to add the
+ /// \param BuildAndDiagnose Whether we are actually supposed to add the
/// captures or diagnose errors. If false, this routine merely check whether
/// the capture can occur without performing the capture itself or complaining
/// if the variable cannot be captured.
@@ -2499,14 +2715,14 @@ public:
/// this variable in the innermost block or lambda. Only valid when the
/// variable can be captured.
///
- /// \param DeclRefType Will be set to the type of a refernce to the capture
- /// from within the current scope. Only valid when the variable can be
+ /// \param DeclRefType Will be set to the type of a reference to the capture
+ /// from within the current scope. Only valid when the variable can be
/// captured.
///
/// \returns true if an error occurred (i.e., the variable cannot be
/// captured) and false if the capture succeeded.
bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc, TryCaptureKind Kind,
- SourceLocation EllipsisLoc, bool BuildAndDiagnose,
+ SourceLocation EllipsisLoc, bool BuildAndDiagnose,
QualType &CaptureType,
QualType &DeclRefType);
@@ -2514,13 +2730,13 @@ public:
bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
TryCaptureKind Kind = TryCapture_Implicit,
SourceLocation EllipsisLoc = SourceLocation());
-
+
/// \brief Given a variable, determine the type that a reference to that
/// variable will have in the given scope.
QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
-
+
void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
- void MarkDeclarationsReferencedInExpr(Expr *E,
+ void MarkDeclarationsReferencedInExpr(Expr *E,
bool SkipLocalVariables = false);
/// \brief Try to recover by turning the given expression into a
@@ -2537,10 +2753,10 @@ public:
/// \brief Conditionally issue a diagnostic based on the current
/// evaluation context.
///
- /// \param stmt - If stmt is non-null, delay reporting the diagnostic until
- /// the function body is parsed, and then do a basic reachability analysis to
- /// determine if the statement is reachable. If it is unreachable, the
- /// diagnostic will not be emitted.
+ /// \param Statement If Statement is non-null, delay reporting the
+ /// diagnostic until the function body is parsed, and then do a basic
+ /// reachability analysis to determine if the statement is reachable.
+ /// If it is unreachable, the diagnostic will not be emitted.
bool DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement,
const PartialDiagnostic &PD);
@@ -2696,6 +2912,18 @@ public:
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs);
+ // This struct is for use by ActOnMemberAccess to allow
+ // BuildMemberReferenceExpr to be able to reinvoke ActOnMemberAccess after
+ // changing the access operator from a '.' to a '->' (to see if that is the
+ // change needed to fix an error about an unknown member, e.g. when the class
+ // defines a custom operator->).
+ struct ActOnMemberAccessExtraArgs {
+ Scope *S;
+ UnqualifiedId &Id;
+ Decl *ObjCImpDecl;
+ bool HasTrailingLParen;
+ };
+
ExprResult BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
SourceLocation OpLoc, bool IsArrow,
const CXXScopeSpec &SS,
@@ -2703,7 +2931,8 @@ public:
NamedDecl *FirstQualifierInScope,
LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs,
- bool SuppressQualifierCheck = false);
+ bool SuppressQualifierCheck = false,
+ ActOnMemberAccessExtraArgs *ExtraArgs = 0);
ExprResult PerformMemberExprBaseConversion(Expr *Base, bool IsArrow);
ExprResult LookupMemberExpr(LookupResult &R, ExprResult &Base,
@@ -2901,7 +3130,8 @@ public:
/// ActOnBlockArguments - This callback allows processing of block arguments.
/// If there are no arguments, this is still invoked.
- void ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope);
+ void ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo,
+ Scope *CurScope);
/// ActOnBlockError - If there is an error parsing a block, this callback
/// is invoked to pop the information about the block from the action impl.
@@ -3010,7 +3240,7 @@ public:
TypeResult Type);
/// InitializeVarWithConstructor - Creates an CXXConstructExpr
- /// and sets it as the initializer for the the passed in VarDecl.
+ /// and sets it as the initializer for the passed in VarDecl.
bool InitializeVarWithConstructor(VarDecl *VD,
CXXConstructorDecl *Constructor,
MultiExprArg Exprs,
@@ -3075,7 +3305,7 @@ public:
public:
explicit ImplicitExceptionSpecification(Sema &Self)
: Self(&Self), ComputedEST(EST_BasicNoexcept) {
- if (!Self.Context.getLangOpts().CPlusPlus0x)
+ if (!Self.getLangOpts().CPlusPlus0x)
ComputedEST = EST_DynamicNone;
}
@@ -3098,17 +3328,16 @@ public:
/// \brief Integrate an invoked expression into the collected data.
void CalledExpr(Expr *E);
- /// \brief Specify that the exception specification can't be detemined yet.
- void SetDelayed() {
- ClearExceptions();
- ComputedEST = EST_Delayed;
- }
-
- FunctionProtoType::ExtProtoInfo getEPI() const {
- FunctionProtoType::ExtProtoInfo EPI;
+ /// \brief Overwrite an EPI's exception specification with this
+ /// computed exception specification.
+ void getEPI(FunctionProtoType::ExtProtoInfo &EPI) const {
EPI.ExceptionSpecType = getExceptionSpecType();
EPI.NumExceptions = size();
EPI.Exceptions = data();
+ }
+ FunctionProtoType::ExtProtoInfo getEPI() const {
+ FunctionProtoType::ExtProtoInfo EPI;
+ getEPI(EPI);
return EPI;
}
};
@@ -3116,34 +3345,39 @@ public:
/// \brief Determine what sort of exception specification a defaulted
/// copy constructor of a class will have.
ImplicitExceptionSpecification
- ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl);
+ ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc,
+ CXXMethodDecl *MD);
/// \brief Determine what sort of exception specification a defaulted
/// default constructor of a class will have, and whether the parameter
/// will be const.
- std::pair<ImplicitExceptionSpecification, bool>
- ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl);
+ ImplicitExceptionSpecification
+ ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD);
/// \brief Determine what sort of exception specification a defautled
/// copy assignment operator of a class will have, and whether the
/// parameter will be const.
- std::pair<ImplicitExceptionSpecification, bool>
- ComputeDefaultedCopyAssignmentExceptionSpecAndConst(CXXRecordDecl *ClassDecl);
+ ImplicitExceptionSpecification
+ ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD);
/// \brief Determine what sort of exception specification a defaulted move
/// constructor of a class will have.
ImplicitExceptionSpecification
- ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl);
+ ComputeDefaultedMoveCtorExceptionSpec(CXXMethodDecl *MD);
/// \brief Determine what sort of exception specification a defaulted move
/// assignment operator of a class will have.
ImplicitExceptionSpecification
- ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl);
+ ComputeDefaultedMoveAssignmentExceptionSpec(CXXMethodDecl *MD);
/// \brief Determine what sort of exception specification a defaulted
/// destructor of a class will have.
ImplicitExceptionSpecification
- ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl);
+ ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD);
+
+ /// \brief Evaluate the implicit exception specification for a defaulted
+ /// special member function.
+ void EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD);
/// \brief Check the given exception-specification and update the
/// extended prototype information with the results.
@@ -3191,8 +3425,7 @@ public:
/// C++11 says that user-defined destructors with no exception spec get one
/// that looks as if the destructor was implicitly declared.
void AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl,
- CXXDestructorDecl *Destructor,
- bool WasDelayed = false);
+ CXXDestructorDecl *Destructor);
/// \brief Declare all inherited constructors for the given class.
///
@@ -3259,7 +3492,7 @@ public:
/// \brief Determine whether the given function is an implicitly-deleted
/// special member function.
bool isImplicitlyDeleted(FunctionDecl *FD);
-
+
/// \brief Check whether 'this' shows up in the type of a static member
/// function after the (naturally empty) cv-qualifier-seq would be.
///
@@ -3275,7 +3508,7 @@ public:
///
/// \returns true if an error occurred.
bool checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method);
-
+
/// MaybeBindToTemporary - If the passed in expression has a record type with
/// a non-trivial destructor, this will return CXXBindTemporaryExpr. Otherwise
/// it simply returns the passed in expression.
@@ -3352,34 +3585,32 @@ public:
/// \brief Try to retrieve the type of the 'this' pointer.
///
- /// \param Capture If true, capture 'this' in this context.
- ///
/// \returns The type of 'this', if possible. Otherwise, returns a NULL type.
QualType getCurrentThisType();
- /// \brief When non-NULL, the C++ 'this' expression is allowed despite the
+ /// \brief When non-NULL, the C++ 'this' expression is allowed despite the
/// current context not being a non-static member function. In such cases,
/// this provides the type used for 'this'.
QualType CXXThisTypeOverride;
-
+
/// \brief RAII object used to temporarily allow the C++ 'this' expression
/// to be used, with the given qualifiers on the current class type.
class CXXThisScopeRAII {
Sema &S;
QualType OldCXXThisTypeOverride;
bool Enabled;
-
+
public:
/// \brief Introduce a new scope where 'this' may be allowed (when enabled),
- /// using the given declaration (which is either a class template or a
+ /// using the given declaration (which is either a class template or a
/// class) along with the given qualifiers.
/// along with the qualifiers placed on '*this'.
- CXXThisScopeRAII(Sema &S, Decl *ContextDecl, unsigned CXXThisTypeQuals,
+ CXXThisScopeRAII(Sema &S, Decl *ContextDecl, unsigned CXXThisTypeQuals,
bool Enabled = true);
-
+
~CXXThisScopeRAII();
};
-
+
/// \brief Make sure the value of 'this' is actually available in the current
/// context, if it is a potentially evaluated context.
///
@@ -3390,14 +3621,14 @@ public:
void CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false);
/// \brief Determine whether the given type is the type of *this that is used
- /// outside of the body of a member function for a type that is currently
+ /// outside of the body of a member function for a type that is currently
/// being defined.
bool isThisOutsideMemberFunctionBody(QualType BaseType);
-
+
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
-
-
+
+
/// ActOnObjCBoolLiteral - Parse {__objc_yes,__objc_no} literals.
ExprResult ActOnObjCBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
@@ -3513,7 +3744,7 @@ public:
ExprResult BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc);
-
+
/// ActOnArrayTypeTrait - Parsed one of the bianry type trait support
/// pseudo-functions.
ExprResult ActOnArrayTypeTrait(ArrayTypeTrait ATT,
@@ -3572,7 +3803,7 @@ public:
ExprResult ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
- SourceLocation TildeLoc,
+ SourceLocation TildeLoc,
const DeclSpec& DS,
bool HasTrailingLParen);
@@ -3583,7 +3814,11 @@ public:
Stmt *MaybeCreateStmtWithCleanups(Stmt *SubStmt);
ExprResult MaybeCreateExprWithCleanups(ExprResult SubExpr);
- ExprResult ActOnFinishFullExpr(Expr *Expr);
+ ExprResult ActOnFinishFullExpr(Expr *Expr) {
+ return ActOnFinishFullExpr(Expr, Expr ? Expr->getExprLoc()
+ : SourceLocation());
+ }
+ ExprResult ActOnFinishFullExpr(Expr *Expr, SourceLocation CC);
StmtResult ActOnFinishFullStmt(Stmt *Stmt);
// Marks SS invalid if it represents an incomplete type.
@@ -3660,7 +3895,7 @@ public:
ExprResult ActOnDecltypeExpression(Expr *E);
bool ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
- const DeclSpec &DS,
+ const DeclSpec &DS,
SourceLocation ColonColonLoc);
bool IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
@@ -3681,7 +3916,7 @@ public:
/// including this new type).
///
/// \param TemplateKWLoc the location of the 'template' keyword, if any.
- /// \param TemplateName The template name.
+ /// \param TemplateName the template name.
/// \param TemplateNameLoc The location of the template name.
/// \param LAngleLoc The location of the opening angle bracket ('<').
/// \param TemplateArgs The template arguments.
@@ -3696,7 +3931,7 @@ public:
bool ActOnCXXNestedNameSpecifier(Scope *S,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
- TemplateTy Template,
+ TemplateTy TemplateName,
SourceLocation TemplateNameLoc,
SourceLocation LAngleLoc,
ASTTemplateArgsPtr TemplateArgs,
@@ -3759,17 +3994,14 @@ public:
/// \brief Create a new lambda closure type.
CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange,
bool KnownDependent = false);
-
+
/// \brief Start the definition of a lambda expression.
CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class,
SourceRange IntroducerRange,
TypeSourceInfo *MethodType,
SourceLocation EndLoc,
- llvm::ArrayRef<ParmVarDecl *> Params,
- llvm::Optional<unsigned> ManglingNumber
- = llvm::Optional<unsigned>(),
- Decl *ContextDecl = 0);
-
+ llvm::ArrayRef<ParmVarDecl *> Params);
+
/// \brief Introduce the scope for a lambda expression.
sema::LambdaScopeInfo *enterLambdaScope(CXXMethodDecl *CallOperator,
SourceRange IntroducerRange,
@@ -3777,16 +4009,20 @@ public:
bool ExplicitParams,
bool ExplicitResultType,
bool Mutable);
-
+
/// \brief Note that we have finished the explicit captures for the
/// given lambda.
void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI);
-
+
/// \brief Introduce the lambda parameters into scope.
void addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope);
-
+
+ /// \brief Deduce a block or lambda's return type based on the return
+ /// statements present in the body.
+ void deduceClosureReturnType(sema::CapturingScopeInfo &CSI);
+
/// ActOnStartOfLambdaDefinition - This is called just before we start
- /// parsing the body of a lambda; it analyzes the explicit captures and
+ /// parsing the body of a lambda; it analyzes the explicit captures and
/// arguments, and sets up various data-structures for the body of the
/// lambda.
void ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
@@ -3800,10 +4036,10 @@ public:
/// ActOnLambdaExpr - This is called when the body of a lambda expression
/// was successfully completed.
ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
- Scope *CurScope,
+ Scope *CurScope,
bool IsInstantiation = false);
- /// \brief Define the "body" of the conversion from a lambda object to a
+ /// \brief Define the "body" of the conversion from a lambda object to a
/// function pointer.
///
/// This routine doesn't actually define a sensible body; rather, it fills
@@ -3813,7 +4049,7 @@ public:
void DefineImplicitLambdaToFunctionPointerConversion(
SourceLocation CurrentLoc, CXXConversionDecl *Conv);
- /// \brief Define the "body" of the conversion from a lambda object to a
+ /// \brief Define the "body" of the conversion from a lambda object to a
/// block pointer.
///
/// This routine doesn't actually define a sensible body; rather, it fills
@@ -3832,26 +4068,33 @@ public:
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
Expr **Strings,
unsigned NumStrings);
-
+
ExprResult BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S);
-
- /// BuildObjCNumericLiteral - builds an ObjCNumericLiteral AST node for the
+
+ /// BuildObjCNumericLiteral - builds an ObjCBoxedExpr AST node for the
/// numeric literal expression. Type of the expression will be "NSNumber *"
/// or "id" if NSNumber is unavailable.
ExprResult BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number);
ExprResult ActOnObjCBoolLiteral(SourceLocation AtLoc, SourceLocation ValueLoc,
bool Value);
ExprResult BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements);
-
+
+ /// BuildObjCBoxedExpr - builds an ObjCBoxedExpr AST node for the
+ /// '@' prefixed parenthesized expression. The type of the expression will
+ /// either be "NSNumber *" or "NSString *" depending on the type of
+ /// ValueType, which is allowed to be a built-in numeric type or
+ /// "char *" or "const char *".
+ ExprResult BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr);
+
ExprResult BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr,
Expr *IndexExpr,
ObjCMethodDecl *getterMethod,
ObjCMethodDecl *setterMethod);
-
- ExprResult BuildObjCDictionaryLiteral(SourceRange SR,
+
+ ExprResult BuildObjCDictionaryLiteral(SourceRange SR,
ObjCDictionaryElement *Elements,
unsigned NumElements);
-
+
ExprResult BuildObjCEncodeExpression(SourceLocation AtLoc,
TypeSourceInfo *EncodedTypeInfo,
SourceLocation RParenLoc);
@@ -3865,18 +4108,19 @@ public:
ParsedType Ty,
SourceLocation RParenLoc);
- // ParseObjCSelectorExpression - Build selector expression for @selector
+ /// ParseObjCSelectorExpression - Build selector expression for \@selector
ExprResult ParseObjCSelectorExpression(Selector Sel,
SourceLocation AtLoc,
SourceLocation SelLoc,
SourceLocation LParenLoc,
SourceLocation RParenLoc);
- // ParseObjCProtocolExpression - Build protocol expression for @protocol
+ /// ParseObjCProtocolExpression - Build protocol expression for \@protocol
ExprResult ParseObjCProtocolExpression(IdentifierInfo * ProtocolName,
SourceLocation AtLoc,
SourceLocation ProtoLoc,
SourceLocation LParenLoc,
+ SourceLocation ProtoIdLoc,
SourceLocation RParenLoc);
//===--------------------------------------------------------------------===//
@@ -3907,7 +4151,7 @@ public:
Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
Expr *BitfieldWidth, const VirtSpecifiers &VS,
- bool HasDeferredInit);
+ InClassInitStyle InitStyle);
void ActOnCXXInClassMemberInitializer(Decl *VarDecl, SourceLocation EqualLoc,
Expr *Init);
@@ -4004,6 +4248,11 @@ public:
void MarkVTableUsed(SourceLocation Loc, CXXRecordDecl *Class,
bool DefinitionRequired = false);
+ /// \brief Mark the exception specifications of all virtual member functions
+ /// in the given class as needed.
+ void MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
+ const CXXRecordDecl *RD);
+
/// MarkVirtualMembersReferenced - Will mark all members of the given
/// CXXRecordDecl referenced.
void MarkVirtualMembersReferenced(SourceLocation Loc,
@@ -4047,6 +4296,11 @@ public:
Expr *AssertExpr,
Expr *AssertMessageExpr,
SourceLocation RParenLoc);
+ Decl *BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
+ Expr *AssertExpr,
+ StringLiteral *AssertMessageExpr,
+ SourceLocation RParenLoc,
+ bool Failed);
FriendDecl *CheckFriendTypeDecl(SourceLocation Loc,
SourceLocation FriendLoc,
@@ -4067,12 +4321,7 @@ public:
Decl *ActOnConversionDeclarator(CXXConversionDecl *Conversion);
void CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record);
- void CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *Ctor);
- void CheckExplicitlyDefaultedCopyConstructor(CXXConstructorDecl *Ctor);
- void CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *Method);
- void CheckExplicitlyDefaultedMoveConstructor(CXXConstructorDecl *Ctor);
- void CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *Method);
- void CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *Dtor);
+ void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD);
//===--------------------------------------------------------------------===//
// C++ Derived Classes
@@ -4130,12 +4379,12 @@ public:
bool CheckPureMethod(CXXMethodDecl *Method, SourceRange InitRange);
- /// CheckOverrideControl - Check C++0x override control semantics.
- void CheckOverrideControl(const Decl *D);
+ /// CheckOverrideControl - Check C++11 override control semantics.
+ void CheckOverrideControl(Decl *D);
/// CheckForFunctionMarkedFinal - Checks whether a virtual member function
/// overrides a virtual member function marked 'final', according to
- /// C++0x [class.virtual]p3.
+ /// C++11 [class.virtual]p4.
bool CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New,
const CXXMethodDecl *Old);
@@ -4178,9 +4427,7 @@ public:
CXXDestructorDecl *Dtor,
const PartialDiagnostic &PDiag,
QualType objectType = QualType());
- AccessResult CheckDirectMemberAccess(SourceLocation Loc,
- NamedDecl *D,
- const PartialDiagnostic &PDiag);
+ AccessResult CheckFriendAccess(NamedDecl *D);
AccessResult CheckMemberOperatorAccess(SourceLocation Loc,
Expr *ObjectExpr,
Expr *ArgExpr,
@@ -4206,36 +4453,10 @@ public:
void HandleDelayedAccessCheck(sema::DelayedDiagnostic &DD, Decl *Ctx);
- /// A flag to suppress access checking.
- bool SuppressAccessChecking;
-
/// \brief When true, access checking violations are treated as SFINAE
/// failures rather than hard errors.
bool AccessCheckingSFINAE;
- /// \brief RAII object used to temporarily suppress access checking.
- class SuppressAccessChecksRAII {
- Sema &S;
- bool SuppressingAccess;
-
- public:
- SuppressAccessChecksRAII(Sema &S, bool Suppress)
- : S(S), SuppressingAccess(Suppress) {
- if (Suppress) S.ActOnStartSuppressingAccessChecks();
- }
- ~SuppressAccessChecksRAII() {
- done();
- }
- void done() {
- if (!SuppressingAccess) return;
- S.ActOnStopSuppressingAccessChecks();
- SuppressingAccess = false;
- }
- };
-
- void ActOnStartSuppressingAccessChecks();
- void ActOnStopSuppressingAccessChecks();
-
enum AbstractDiagSelID {
AbstractNone = -1,
AbstractReturnType,
@@ -4246,7 +4467,31 @@ public:
};
bool RequireNonAbstractType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD);
+ TypeDiagnoser &Diagnoser);
+ template<typename T1>
+ bool RequireNonAbstractType(SourceLocation Loc, QualType T,
+ unsigned DiagID,
+ const T1 &Arg1) {
+ BoundTypeDiagnoser1<T1> Diagnoser(DiagID, Arg1);
+ return RequireNonAbstractType(Loc, T, Diagnoser);
+ }
+
+ template<typename T1, typename T2>
+ bool RequireNonAbstractType(SourceLocation Loc, QualType T,
+ unsigned DiagID,
+ const T1 &Arg1, const T2 &Arg2) {
+ BoundTypeDiagnoser2<T1, T2> Diagnoser(DiagID, Arg1, Arg2);
+ return RequireNonAbstractType(Loc, T, Diagnoser);
+ }
+
+ template<typename T1, typename T2, typename T3>
+ bool RequireNonAbstractType(SourceLocation Loc, QualType T,
+ unsigned DiagID,
+ const T1 &Arg1, const T2 &Arg2, const T3 &Arg3) {
+ BoundTypeDiagnoser3<T1, T2, T3> Diagnoser(DiagID, Arg1, Arg2, Arg3);
+ return RequireNonAbstractType(Loc, T, Diagnoser);
+ }
+
void DiagnoseAbstractType(const CXXRecordDecl *RD);
bool RequireNonAbstractType(SourceLocation Loc, QualType T, unsigned DiagID,
@@ -4263,9 +4508,9 @@ public:
//===--------------------------------------------------------------------===//
// C++ Templates [C++ 14]
//
- void FilterAcceptableTemplateNames(LookupResult &R,
+ void FilterAcceptableTemplateNames(LookupResult &R,
bool AllowFunctionTemplates = true);
- bool hasAnyAcceptableTemplateNames(LookupResult &R,
+ bool hasAnyAcceptableTemplateNames(LookupResult &R,
bool AllowFunctionTemplates = true);
void LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS,
@@ -4638,7 +4883,7 @@ public:
ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
const CXXScopeSpec &SS,
SourceLocation TemplateLoc,
- TemplateTy Template,
+ TemplateTy TemplateName,
SourceLocation TemplateNameLoc,
SourceLocation LAngleLoc,
ASTTemplateArgsPtr TemplateArgs,
@@ -4730,7 +4975,13 @@ public:
UPPC_IfExists,
/// \brief Microsoft __if_not_exists.
- UPPC_IfNotExists
+ UPPC_IfNotExists,
+
+ /// \brief Lambda expression.
+ UPPC_Lambda,
+
+ /// \brief Block expression,
+ UPPC_Block
};
/// \brief Diagnose unexpanded parameter packs.
@@ -4741,7 +4992,9 @@ public:
/// parameter packs.
///
/// \param Unexpanded the set of unexpanded parameter packs.
- void DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ bool DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
UnexpandedParameterPackContext UPPC,
ArrayRef<UnexpandedParameterPack> Unexpanded);
@@ -4922,9 +5175,6 @@ public:
/// \param Unexpanded The set of unexpanded parameter packs within the
/// pattern.
///
- /// \param NumUnexpanded The number of unexpanded parameter packs in
- /// \p Unexpanded.
- ///
/// \param ShouldExpand Will be set to \c true if the transformer should
/// expand the corresponding pack expansions into separate arguments. When
/// set, \c NumExpansions must also be set.
@@ -4957,10 +5207,11 @@ public:
/// \brief Determine the number of arguments in the given pack expansion
/// type.
///
- /// This routine already assumes that the pack expansion type can be
- /// expanded and that the number of arguments in the expansion is
+ /// This routine assumes that the number of arguments in the expansion is
/// consistent across all of the unexpanded parameter packs in its pattern.
- unsigned getNumArgumentsInExpansion(QualType T,
+ ///
+ /// Returns an empty Optional if the type can't be expanded.
+ llvm::Optional<unsigned> getNumArgumentsInExpansion(QualType T,
const MultiLevelTemplateArgumentList &TemplateArgs);
/// \brief Determine whether the given declarator contains any unexpanded
@@ -5366,16 +5617,14 @@ public:
/// template-id.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
TemplateDecl *Template,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange = SourceRange());
/// \brief Note that we are instantiating a default argument in a
/// template-id.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
FunctionTemplateDecl *FunctionTemplate,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
ActiveTemplateInstantiation::InstantiationKind Kind,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());
@@ -5385,15 +5634,13 @@ public:
/// specialization.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
ClassTemplatePartialSpecializationDecl *PartialSpec,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
ParmVarDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange = SourceRange());
/// \brief Note that we are substituting prior template arguments into a
@@ -5401,15 +5648,13 @@ public:
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
NamedDecl *Template,
NonTypeTemplateParmDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange);
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
NamedDecl *Template,
TemplateTemplateParmDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange);
/// \brief Note that we are checking the default template argument
@@ -5417,8 +5662,7 @@ public:
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
TemplateDecl *Template,
NamedDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
+ ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange);
@@ -5456,6 +5700,14 @@ public:
/// diagnostics that will be suppressed.
llvm::Optional<sema::TemplateDeductionInfo *> isSFINAEContext() const;
+ /// \brief Determines whether we are currently in a context that
+ /// is not evaluated as per C++ [expr] p5.
+ bool isUnevaluatedContext() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().Context == Sema::Unevaluated;
+ }
+
/// \brief RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
/// deduction.`
@@ -5705,7 +5957,7 @@ public:
SourceLocation EndProtoLoc,
AttributeList *AttrList);
- Decl *ActOnCompatiblityAlias(
+ Decl *ActOnCompatibilityAlias(
SourceLocation AtCompatibilityAliasLoc,
IdentifierInfo *AliasName, SourceLocation AliasLocation,
IdentifierInfo *ClassName, SourceLocation ClassLocation);
@@ -5768,28 +6020,27 @@ public:
/// be modified to be consistent with \arg PropertyTy.
void CheckObjCPropertyAttributes(Decl *PropertyPtrTy,
SourceLocation Loc,
- unsigned &Attributes);
+ unsigned &Attributes,
+ bool propertyInPrimaryClass);
/// Process the specified property declaration and create decls for the
/// setters and getters as needed.
/// \param property The property declaration being processed
- /// \param DC The semantic container for the property
+ /// \param CD The semantic container for the property
/// \param redeclaredProperty Declaration for property if redeclared
/// in class extension.
/// \param lexicalDC Container for redeclaredProperty.
void ProcessPropertyDecl(ObjCPropertyDecl *property,
- ObjCContainerDecl *DC,
+ ObjCContainerDecl *CD,
ObjCPropertyDecl *redeclaredProperty = 0,
ObjCContainerDecl *lexicalDC = 0);
+
void DiagnosePropertyMismatch(ObjCPropertyDecl *Property,
ObjCPropertyDecl *SuperProperty,
const IdentifierInfo *Name);
void ComparePropertiesInBaseAndSuper(ObjCInterfaceDecl *IDecl);
- void CompareMethodParamsInBaseAndSuper(Decl *IDecl,
- ObjCMethodDecl *MethodDecl,
- bool IsInstance);
void CompareProperties(Decl *CDecl, Decl *MergeProtocols);
@@ -5855,14 +6106,6 @@ public:
AttributeList *AttrList, tok::ObjCKeywordKind MethodImplKind,
bool isVariadic, bool MethodDefinition);
- // Helper method for ActOnClassMethod/ActOnInstanceMethod.
- // Will search "local" class/category implementations for a method decl.
- // Will also search in class's root looking for instance method.
- // Returns 0 if no method is found.
- ObjCMethodDecl *LookupPrivateClassMethod(Selector Sel,
- ObjCInterfaceDecl *CDecl);
- ObjCMethodDecl *LookupPrivateInstanceMethod(Selector Sel,
- ObjCInterfaceDecl *ClassDecl);
ObjCMethodDecl *LookupMethodInQualifiedType(Selector Sel,
const ObjCObjectPointerType *OPT,
bool IsInstance);
@@ -5988,9 +6231,16 @@ public:
const ObjCMethodDecl *Overridden,
bool IsImplementation);
- /// \brief Check whether the given method overrides any methods in its class,
- /// calling \c CheckObjCMethodOverride for each overridden method.
- bool CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC);
+ /// \brief Describes the compatibility of a result type with its method.
+ enum ResultTypeCompatibilityKind {
+ RTC_Compatible,
+ RTC_Incompatible,
+ RTC_Unknown
+ };
+
+ void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
+ ObjCInterfaceDecl *CurrentClass,
+ ResultTypeCompatibilityKind RTC);
enum PragmaOptionsAlignKind {
POAK_Native, // #pragma options align=native
@@ -6001,7 +6251,7 @@ public:
POAK_Reset // #pragma options align=reset
};
- /// ActOnPragmaOptionsAlign - Called on well formed #pragma options align.
+ /// ActOnPragmaOptionsAlign - Called on well formed \#pragma options align.
void ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind,
SourceLocation PragmaLoc,
SourceLocation KindLoc);
@@ -6018,7 +6268,7 @@ public:
PMSST_ON // #pragms ms_struct on
};
- /// ActOnPragmaPack - Called on well formed #pragma pack(...).
+ /// ActOnPragmaPack - Called on well formed \#pragma pack(...).
void ActOnPragmaPack(PragmaPackKind Kind,
IdentifierInfo *Name,
Expr *Alignment,
@@ -6026,15 +6276,15 @@ public:
SourceLocation LParenLoc,
SourceLocation RParenLoc);
- /// ActOnPragmaMSStruct - Called on well formed #pragms ms_struct [on|off].
+ /// ActOnPragmaMSStruct - Called on well formed \#pragma ms_struct [on|off].
void ActOnPragmaMSStruct(PragmaMSStructKind Kind);
- /// ActOnPragmaUnused - Called on well-formed '#pragma unused'.
+ /// ActOnPragmaUnused - Called on well-formed '\#pragma unused'.
void ActOnPragmaUnused(const Token &Identifier,
Scope *curScope,
SourceLocation PragmaLoc);
- /// ActOnPragmaVisibility - Called on well formed #pragma GCC visibility... .
+ /// ActOnPragmaVisibility - Called on well formed \#pragma GCC visibility... .
void ActOnPragmaVisibility(const IdentifierInfo* VisType,
SourceLocation PragmaLoc);
@@ -6042,20 +6292,20 @@ public:
SourceLocation Loc);
void DeclApplyPragmaWeak(Scope *S, NamedDecl *ND, WeakInfo &W);
- /// ActOnPragmaWeakID - Called on well formed #pragma weak ident.
+ /// ActOnPragmaWeakID - Called on well formed \#pragma weak ident.
void ActOnPragmaWeakID(IdentifierInfo* WeakName,
SourceLocation PragmaLoc,
SourceLocation WeakNameLoc);
- /// ActOnPragmaRedefineExtname - Called on well formed
- /// #pragma redefine_extname oldname newname.
+ /// ActOnPragmaRedefineExtname - Called on well formed
+ /// \#pragma redefine_extname oldname newname.
void ActOnPragmaRedefineExtname(IdentifierInfo* WeakName,
IdentifierInfo* AliasName,
SourceLocation PragmaLoc,
SourceLocation WeakNameLoc,
SourceLocation AliasNameLoc);
- /// ActOnPragmaWeakAlias - Called on well formed #pragma weak ident = ident.
+ /// ActOnPragmaWeakAlias - Called on well formed \#pragma weak ident = ident.
void ActOnPragmaWeakAlias(IdentifierInfo* WeakName,
IdentifierInfo* AliasName,
SourceLocation PragmaLoc,
@@ -6063,11 +6313,11 @@ public:
SourceLocation AliasNameLoc);
/// ActOnPragmaFPContract - Called on well formed
- /// #pragma {STDC,OPENCL} FP_CONTRACT
+ /// \#pragma {STDC,OPENCL} FP_CONTRACT
void ActOnPragmaFPContract(tok::OnOffSwitch OOS);
/// AddAlignmentAttributesForRecord - Adds any needed alignment attributes to
- /// a the record decl, to handle '#pragma pack' and '#pragma options align'.
+ /// a the record decl, to handle '\#pragma pack' and '\#pragma options align'.
void AddAlignmentAttributesForRecord(RecordDecl *RD);
/// AddMsStructLayoutForRecord - Adds ms_struct layout attribute to record.
@@ -6081,25 +6331,27 @@ public:
void PushNamespaceVisibilityAttr(const VisibilityAttr *Attr,
SourceLocation Loc);
- /// AddPushedVisibilityAttribute - If '#pragma GCC visibility' was used,
+ /// AddPushedVisibilityAttribute - If '\#pragma GCC visibility' was used,
/// add an appropriate visibility attribute.
void AddPushedVisibilityAttribute(Decl *RD);
/// PopPragmaVisibility - Pop the top element of the visibility stack; used
- /// for '#pragma GCC visibility' and visibility attributes on namespaces.
+ /// for '\#pragma GCC visibility' and visibility attributes on namespaces.
void PopPragmaVisibility(bool IsNamespaceEnd, SourceLocation EndLoc);
/// FreeVisContext - Deallocate and null out VisContext.
void FreeVisContext();
/// AddCFAuditedAttribute - Check whether we're currently within
- /// '#pragma clang arc_cf_code_audited' and, if so, consider adding
+ /// '\#pragma clang arc_cf_code_audited' and, if so, consider adding
/// the appropriate attribute.
void AddCFAuditedAttribute(Decl *D);
/// AddAlignedAttr - Adds an aligned attribute to a particular declaration.
- void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E);
- void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T);
+ void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E,
+ bool isDeclSpec);
+ void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T,
+ bool isDeclSpec);
/// \brief The kind of conversion being performed.
enum CheckedConversionKind {
@@ -6164,6 +6416,21 @@ public:
VariadicDoesNotApply
};
+ VariadicCallType getVariadicCallType(FunctionDecl *FDecl,
+ const FunctionProtoType *Proto,
+ Expr *Fn);
+
+ // Used for determining in which context a type is allowed to be passed to a
+ // vararg function.
+ enum VarArgKind {
+ VAK_Valid,
+ VAK_ValidInCXX11,
+ VAK_Invalid
+ };
+
+ // Determines which VarArgKind fits an expression.
+ VarArgKind isValidVarArgType(const QualType &Ty);
+
/// GatherArgumentsForCall - Collector argument expressions for various
/// form of call prototypes.
bool GatherArgumentsForCall(SourceLocation CallLoc,
@@ -6176,10 +6443,14 @@ public:
bool AllowExplicit = false);
// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but
- // will warn if the resulting type is not a POD type.
+ // will create a runtime trap if the resulting type is not a POD type.
ExprResult DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
FunctionDecl *FDecl);
+ /// Checks to see if the given expression is a valid argument to a variadic
+ /// function, issuing a diagnostic and returning NULL if not.
+ bool variadicArgumentPODCheck(const Expr *E, VariadicCallType CT);
+
// UsualArithmeticConversions - performs the UsualUnaryConversions on it's
// operands and then handles various conversions that are common to binary
// operators (C99 6.3.1.8). If both operands aren't arithmetic, this
@@ -6269,6 +6540,11 @@ public:
Expr *SrcExpr, AssignmentAction Action,
bool *Complained = 0);
+ /// DiagnoseAssignmentEnum - Warn if assignment to enum is a constant
+ /// integer not in the range of enum values.
+ void DiagnoseAssignmentEnum(QualType DstType, QualType SrcType,
+ Expr *SrcExpr);
+
/// CheckAssignmentConstraints - Perform type checking for assignment,
/// argument passing, variable initialization, and function return values.
/// C99 6.5.16.
@@ -6434,7 +6710,7 @@ public:
/// \brief Force an expression with unknown-type to an expression of the
/// given type.
ExprResult forceUnknownAnyToType(Expr *E, QualType ToType);
-
+
// CheckVectorCast - check type constraints for vectors.
// Since vectors are an extension, there are no C standard reference for this.
// We allow casting between vectors and integer datatypes of the same size.
@@ -6540,20 +6816,29 @@ public:
/// in the global scope.
bool CheckObjCDeclScope(Decl *D);
+ /// \brief Abstract base class used for diagnosing integer constant
+ /// expression violations.
+ class VerifyICEDiagnoser {
+ public:
+ bool Suppress;
+
+ VerifyICEDiagnoser(bool Suppress = false) : Suppress(Suppress) { }
+
+ virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) =0;
+ virtual void diagnoseFold(Sema &S, SourceLocation Loc, SourceRange SR);
+ virtual ~VerifyICEDiagnoser() { }
+ };
+
/// VerifyIntegerConstantExpression - Verifies that an expression is an ICE,
/// and reports the appropriate diagnostics. Returns false on success.
/// Can optionally return the value of the expression.
ExprResult VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
- const PartialDiagnostic &Diag,
- bool AllowFold,
- const PartialDiagnostic &FoldDiag);
+ VerifyICEDiagnoser &Diagnoser,
+ bool AllowFold = true);
ExprResult VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
- const PartialDiagnostic &Diag,
- bool AllowFold = true) {
- return VerifyIntegerConstantExpression(E, Result, Diag, AllowFold,
- PDiag(0));
- }
- ExprResult VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result = 0);
+ unsigned DiagID,
+ bool AllowFold = true);
+ ExprResult VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result=0);
/// VerifyBitField - verifies that a bit field expression is an ICE and has
/// the correct width, and that the field type is valid.
@@ -6745,15 +7030,39 @@ private:
const ArraySubscriptExpr *ASE=0,
bool AllowOnePastEnd=true, bool IndexNegated=false);
void CheckArrayAccess(const Expr *E);
- bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall);
- bool CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation loc,
+ // Used to grab the relevant information from a FormatAttr and a
+ // FunctionDeclaration.
+ struct FormatStringInfo {
+ unsigned FormatIdx;
+ unsigned FirstDataArg;
+ bool HasVAListArg;
+ };
+
+ bool getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember,
+ FormatStringInfo *FSI);
+ bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
+ const FunctionProtoType *Proto);
+ bool CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation loc,
Expr **Args, unsigned NumArgs);
- bool CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall);
+ bool CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall,
+ const FunctionProtoType *Proto);
+ void CheckConstructorCall(FunctionDecl *FDecl,
+ Expr **Args,
+ unsigned NumArgs,
+ const FunctionProtoType *Proto,
+ SourceLocation Loc);
+
+ void checkCall(NamedDecl *FDecl, Expr **Args, unsigned NumArgs,
+ unsigned NumProtoArgs, bool IsMemberFunction,
+ SourceLocation Loc, SourceRange Range,
+ VariadicCallType CallType);
+
bool CheckObjCString(Expr *Arg);
ExprResult CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
+ bool CheckMipsBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool SemaBuiltinVAStart(CallExpr *TheCall);
bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
@@ -6783,23 +7092,36 @@ private:
FST_Unknown
};
static FormatStringType GetFormatStringType(const FormatAttr *Format);
- bool SemaCheckStringLiteral(const Expr *E, Expr **Args, unsigned NumArgs,
- bool HasVAListArg, unsigned format_idx,
- unsigned firstDataArg, FormatStringType Type,
- bool inFunctionCall = true);
+
+ enum StringLiteralCheckType {
+ SLCT_NotALiteral,
+ SLCT_UncheckedLiteral,
+ SLCT_CheckedLiteral
+ };
+
+ StringLiteralCheckType checkFormatStringExpr(const Expr *E,
+ Expr **Args, unsigned NumArgs,
+ bool HasVAListArg,
+ unsigned format_idx,
+ unsigned firstDataArg,
+ FormatStringType Type,
+ VariadicCallType CallType,
+ bool inFunctionCall = true);
void CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr,
Expr **Args, unsigned NumArgs, bool HasVAListArg,
unsigned format_idx, unsigned firstDataArg,
- FormatStringType Type, bool inFunctionCall);
+ FormatStringType Type, bool inFunctionCall,
+ VariadicCallType CallType);
- void CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall);
- void CheckFormatArguments(const FormatAttr *Format, Expr **Args,
+ bool CheckFormatArguments(const FormatAttr *Format, Expr **Args,
unsigned NumArgs, bool IsCXXMember,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange Range);
- void CheckFormatArguments(Expr **Args, unsigned NumArgs,
+ bool CheckFormatArguments(Expr **Args, unsigned NumArgs,
bool HasVAListArg, unsigned format_idx,
unsigned firstDataArg, FormatStringType Type,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange range);
void CheckNonNullArguments(const NonNullAttr *NonNull,
diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h
index c16823a..273374d 100644
--- a/include/clang/Sema/Template.h
+++ b/include/clang/Sema/Template.h
@@ -152,10 +152,11 @@ namespace clang {
/// \brief Construct an integral non-type template argument that
/// has been deduced, possibly from an array bound.
- DeducedTemplateArgument(const llvm::APSInt &Value,
+ DeducedTemplateArgument(ASTContext &Ctx,
+ const llvm::APSInt &Value,
QualType ValueType,
bool DeducedFromArrayBound)
- : TemplateArgument(Value, ValueType),
+ : TemplateArgument(Ctx, Value, ValueType),
DeducedFromArrayBound(DeducedFromArrayBound) { }
/// \brief For a non-type template argument, determine whether the
@@ -371,8 +372,10 @@ namespace clang {
public:
TemplateDeclInstantiator(Sema &SemaRef, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs)
- : SemaRef(SemaRef), SubstIndex(SemaRef, -1), Owner(Owner),
- TemplateArgs(TemplateArgs), LateAttrs(0), StartingScope(0) { }
+ : SemaRef(SemaRef),
+ SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
+ Owner(Owner), TemplateArgs(TemplateArgs), LateAttrs(0), StartingScope(0)
+ { }
// FIXME: Once we get closer to completion, replace these manually-written
// declarations with automatically-generated ones from
diff --git a/include/clang/Sema/TemplateDeduction.h b/include/clang/Sema/TemplateDeduction.h
index 100d56e..4c2d876 100644
--- a/include/clang/Sema/TemplateDeduction.h
+++ b/include/clang/Sema/TemplateDeduction.h
@@ -39,6 +39,9 @@ class TemplateDeductionInfo {
/// deduction is occurring.
SourceLocation Loc;
+ /// \brief Have we suppressed an error during deduction?
+ bool HasSFINAEDiagnostic;
+
/// \brief Warnings (and follow-on notes) that were suppressed due to
/// SFINAE while performing template argument deduction.
SmallVector<PartialDiagnosticAt, 4> SuppressedDiagnostics;
@@ -49,7 +52,7 @@ class TemplateDeductionInfo {
public:
TemplateDeductionInfo(ASTContext &Context, SourceLocation Loc)
- : Context(Context), Deduced(0), Loc(Loc) { }
+ : Context(Context), Deduced(0), Loc(Loc), HasSFINAEDiagnostic(false) { }
~TemplateDeductionInfo() {
// FIXME: if (Deduced) Deduced->Destroy(Context);
@@ -68,6 +71,15 @@ public:
return Result;
}
+ /// \brief Take ownership of the SFINAE diagnostic.
+ void takeSFINAEDiagnostic(PartialDiagnosticAt &PD) {
+ assert(HasSFINAEDiagnostic);
+ PD.first = SuppressedDiagnostics.front().first;
+ PD.second.swap(SuppressedDiagnostics.front().second);
+ SuppressedDiagnostics.clear();
+ HasSFINAEDiagnostic = false;
+ }
+
/// \brief Provide a new template argument list that contains the
/// results of template argument deduction.
void reset(TemplateArgumentList *NewDeduced) {
@@ -75,10 +87,31 @@ public:
Deduced = NewDeduced;
}
+ /// \brief Is a SFINAE diagnostic available?
+ bool hasSFINAEDiagnostic() const {
+ return HasSFINAEDiagnostic;
+ }
+
+ /// \brief Set the diagnostic which caused the SFINAE failure.
+ void addSFINAEDiagnostic(SourceLocation Loc, PartialDiagnostic PD) {
+ // Only collect the first diagnostic.
+ if (HasSFINAEDiagnostic)
+ return;
+ SuppressedDiagnostics.clear();
+ SuppressedDiagnostics.push_back(
+ std::make_pair(Loc, PartialDiagnostic::NullDiagnostic()));
+ SuppressedDiagnostics.back().second.swap(PD);
+ HasSFINAEDiagnostic = true;
+ }
+
/// \brief Add a new diagnostic to the set of diagnostics
void addSuppressedDiagnostic(SourceLocation Loc,
- const PartialDiagnostic &PD) {
- SuppressedDiagnostics.push_back(std::make_pair(Loc, PD));
+ PartialDiagnostic PD) {
+ if (HasSFINAEDiagnostic)
+ return;
+ SuppressedDiagnostics.push_back(
+ std::make_pair(Loc, PartialDiagnostic::NullDiagnostic()));
+ SuppressedDiagnostics.back().second.swap(PD);
}
/// \brief Iterator over the set of suppressed diagnostics.
diff --git a/include/clang/Sema/Weak.h b/include/clang/Sema/Weak.h
index d36b970..6d1b64b 100644
--- a/include/clang/Sema/Weak.h
+++ b/include/clang/Sema/Weak.h
@@ -21,7 +21,7 @@ namespace clang {
class IdentifierInfo;
-/// \brief Captures information about a #pragma weak directive.
+/// \brief Captures information about a \#pragma weak directive.
class WeakInfo {
IdentifierInfo *alias; // alias (optional)
SourceLocation loc; // for diagnostics
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index f9bb892..dbe6e5a 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -207,7 +207,10 @@ namespace clang {
PREPROCESSOR_DETAIL_BLOCK_ID,
/// \brief The block containing the submodule structure.
- SUBMODULE_BLOCK_ID
+ SUBMODULE_BLOCK_ID,
+
+ /// \brief The block containing comments.
+ COMMENTS_BLOCK_ID
};
/// \brief Record types that occur within the AST block itself.
@@ -405,7 +408,7 @@ namespace clang {
/// sets.
CXX_BASE_SPECIFIER_OFFSETS = 37,
- /// \brief Record code for #pragma diagnostic mappings.
+ /// \brief Record code for \#pragma diagnostic mappings.
DIAG_PRAGMA_MAPPINGS = 38,
/// \brief Record code for special CUDA declarations.
@@ -417,7 +420,7 @@ namespace clang {
/// \brief The directory that the PCH was originally created in.
ORIGINAL_PCH_DIR = 41,
- /// \brief Record code for floating point #pragma options.
+ /// \brief Record code for floating point \#pragma options.
FP_PRAGMA_OPTIONS = 42,
/// \brief Record code for enabled OpenCL extensions.
@@ -441,7 +444,7 @@ namespace clang {
MODULE_OFFSET_MAP = 47,
/// \brief Record code for the source manager line table information,
- /// which stores information about #line directives.
+ /// which stores information about \#line directives.
SOURCE_MANAGER_LINE_TABLE = 48,
/// \brief Record code for map of Objective-C class definition IDs to the
@@ -469,7 +472,7 @@ namespace clang {
///
/// This array can only be interpreted properly using the Objective-C
/// categories map.
- OBJC_CATEGORIES
+ OBJC_CATEGORIES = 54
};
/// \brief Record types used within a source manager block.
@@ -500,8 +503,8 @@ namespace clang {
PP_MACRO_OBJECT_LIKE = 1,
/// \brief A function-like macro definition.
- /// [PP_MACRO_FUNCTION_LIKE, <ObjectLikeStuff>, IsC99Varargs, IsGNUVarars,
- /// NumArgs, ArgIdentInfoID* ]
+ /// [PP_MACRO_FUNCTION_LIKE, \<ObjectLikeStuff>, IsC99Varargs,
+ /// IsGNUVarars, NumArgs, ArgIdentInfoID* ]
PP_MACRO_FUNCTION_LIKE = 2,
/// \brief Describes one token.
@@ -545,7 +548,12 @@ namespace clang {
/// \brief Specifies a required feature.
SUBMODULE_REQUIRES = 7
};
-
+
+ /// \brief Record types used within a comments block.
+ enum CommentRecordTypes {
+ COMMENTS_RAW_COMMENT = 0
+ };
+
/// \defgroup ASTAST AST file AST constants
///
/// The constants in this group describe various components of the
@@ -557,7 +565,7 @@ namespace clang {
///
/// These type IDs correspond to predefined types in the AST
/// context, such as built-in types (int) and special place-holder
- /// types (the <overload> and <dependent> type markers). Such
+ /// types (the \<overload> and \<dependent> type markers). Such
/// types are never actually serialized, since they will be built
/// by the AST context when it is created.
enum PredefinedTypeIDs {
@@ -632,7 +640,9 @@ namespace clang {
/// \brief ARC's unbridged-cast placeholder type.
PREDEF_TYPE_ARC_UNBRIDGED_CAST = 34,
/// \brief The pseudo-object placeholder type.
- PREDEF_TYPE_PSEUDO_OBJECT = 35
+ PREDEF_TYPE_PSEUDO_OBJECT = 35,
+ /// \brief The __va_list_tag placeholder type.
+ PREDEF_TYPE_VA_LIST_TAG = 36
};
/// \brief The number of predefined type IDs that are reserved for
@@ -738,28 +748,26 @@ namespace clang {
/// The constants in this enumeration are indices into the
/// SPECIAL_TYPES record.
enum SpecialTypeIDs {
- /// \brief __builtin_va_list
- SPECIAL_TYPE_BUILTIN_VA_LIST = 0,
/// \brief CFConstantString type
- SPECIAL_TYPE_CF_CONSTANT_STRING = 1,
+ SPECIAL_TYPE_CF_CONSTANT_STRING = 0,
/// \brief C FILE typedef type
- SPECIAL_TYPE_FILE = 2,
+ SPECIAL_TYPE_FILE = 1,
/// \brief C jmp_buf typedef type
- SPECIAL_TYPE_JMP_BUF = 3,
+ SPECIAL_TYPE_JMP_BUF = 2,
/// \brief C sigjmp_buf typedef type
- SPECIAL_TYPE_SIGJMP_BUF = 4,
+ SPECIAL_TYPE_SIGJMP_BUF = 3,
/// \brief Objective-C "id" redefinition type
- SPECIAL_TYPE_OBJC_ID_REDEFINITION = 5,
+ SPECIAL_TYPE_OBJC_ID_REDEFINITION = 4,
/// \brief Objective-C "Class" redefinition type
- SPECIAL_TYPE_OBJC_CLASS_REDEFINITION = 6,
+ SPECIAL_TYPE_OBJC_CLASS_REDEFINITION = 5,
/// \brief Objective-C "SEL" redefinition type
- SPECIAL_TYPE_OBJC_SEL_REDEFINITION = 7,
+ SPECIAL_TYPE_OBJC_SEL_REDEFINITION = 6,
/// \brief C ucontext_t typedef type
- SPECIAL_TYPE_UCONTEXT_T = 8
+ SPECIAL_TYPE_UCONTEXT_T = 7
};
/// \brief The number of special type IDs.
- const unsigned NumSpecialTypeIDs = 9;
+ const unsigned NumSpecialTypeIDs = 8;
/// \brief Predefined declaration IDs.
///
@@ -793,14 +801,17 @@ namespace clang {
PREDEF_DECL_UNSIGNED_INT_128_ID = 7,
/// \brief The internal 'instancetype' typedef.
- PREDEF_DECL_OBJC_INSTANCETYPE_ID = 8
+ PREDEF_DECL_OBJC_INSTANCETYPE_ID = 8,
+
+ /// \brief The internal '__builtin_va_list' typedef.
+ PREDEF_DECL_BUILTIN_VA_LIST_ID = 9
};
/// \brief The number of declaration IDs that are predefined.
///
/// For more information about predefined declarations, see the
/// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants.
- const unsigned int NUM_PREDEF_DECL_IDS = 9;
+ const unsigned int NUM_PREDEF_DECL_IDS = 10;
/// \brief Record codes for each kind of declaration.
///
@@ -862,7 +873,7 @@ namespace clang {
/// in the order in which those declarations were added to the
/// declaration context. This data is used when iterating over
/// the contents of a DeclContext, e.g., via
- /// DeclContext::decls_begin()/DeclContext::decls_end().
+ /// DeclContext::decls_begin() and DeclContext::decls_end().
DECL_CONTEXT_LEXICAL,
/// \brief A record that stores the set of declarations that are
/// visible from a given DeclContext.
@@ -1066,7 +1077,7 @@ namespace clang {
/// \brief An ObjCStringLiteral record.
EXPR_OBJC_STRING_LITERAL,
- EXPR_OBJC_NUMERIC_LITERAL,
+ EXPR_OBJC_BOXED_EXPRESSION,
EXPR_OBJC_ARRAY_LITERAL,
EXPR_OBJC_DICTIONARY_LITERAL,
@@ -1089,7 +1100,7 @@ namespace clang {
EXPR_OBJC_MESSAGE_EXPR,
/// \brief An ObjCIsa Expr record.
EXPR_OBJC_ISA,
- /// \breif An ObjCIndirectCopyRestoreExpr record.
+ /// \brief An ObjCIndirectCopyRestoreExpr record.
EXPR_OBJC_INDIRECT_COPY_RESTORE,
/// \brief An ObjCForCollectionStmt record.
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index a9d0fc3..f0b7275 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -199,6 +199,8 @@ class ASTReader
public ExternalSLocEntrySource
{
public:
+ typedef SmallVector<uint64_t, 64> RecordData;
+
enum ASTReadResult { Success, Failure, IgnorePCH };
/// \brief Types of AST files.
friend class PCHValidator;
@@ -454,7 +456,7 @@ private:
/// consumer eagerly.
SmallVector<uint64_t, 16> ExternalDefinitions;
- /// \brief The IDs of all tentative definitions stored in the the chain.
+ /// \brief The IDs of all tentative definitions stored in the chain.
///
/// Sema keeps track of all tentative definitions in a TU because it has to
/// complete them and pass them on to CodeGen. Thus, tentative definitions in
@@ -591,11 +593,14 @@ private:
/// indicates how many separate module file load operations have occurred.
unsigned CurrentGeneration;
+ typedef llvm::DenseMap<unsigned, SwitchCase *> SwitchCaseMapTy;
/// \brief Mapping from switch-case IDs in the chain to switch-case statements
///
/// Statements usually don't have IDs, but switch cases need them, so that the
/// switch statement can refer to them.
- std::map<unsigned, SwitchCase *> SwitchCaseStmts;
+ SwitchCaseMapTy SwitchCaseStmts;
+
+ SwitchCaseMapTy *CurrSwitchCaseStmts;
/// \brief The number of stat() calls that hit/missed the stat
/// cache.
@@ -798,7 +803,7 @@ private:
llvm::BitstreamCursor &SLocCursorForID(int ID);
SourceLocation getImportLocation(ModuleFile *F);
ASTReadResult ReadSubmoduleBlock(ModuleFile &F);
- bool ParseLanguageOptions(const SmallVectorImpl<uint64_t> &Record);
+ bool ParseLanguageOptions(const RecordData &Record);
struct RecordLocation {
RecordLocation(ModuleFile *M, uint64_t O)
@@ -821,19 +826,19 @@ private:
RecordLocation getLocalBitOffset(uint64_t GlobalOffset);
uint64_t getGlobalBitOffset(ModuleFile &M, uint32_t LocalOffset);
- /// \brief Returns the first preprocessed entity ID that ends after \arg BLoc.
+ /// \brief Returns the first preprocessed entity ID that ends after BLoc.
serialization::PreprocessedEntityID
findBeginPreprocessedEntity(SourceLocation BLoc) const;
- /// \brief Returns the first preprocessed entity ID that begins after \arg
- /// ELoc.
+ /// \brief Returns the first preprocessed entity ID that begins after ELoc.
serialization::PreprocessedEntityID
findEndPreprocessedEntity(SourceLocation ELoc) const;
- /// \brief \arg SLocMapI points at a chunk of a module that contains no
- /// preprocessed entities or the entities it contains are not the ones we are
- /// looking for. Find the next module that contains entities and return the ID
+ /// \brief Find the next module that contains entities and return the ID
/// of the first entry.
+ /// \arg SLocMapI points at a chunk of a module that contains no
+ /// preprocessed entities or the entities it contains are not the
+ /// ones we are looking for.
serialization::PreprocessedEntityID
findNextPreprocessedEntity(
GlobalSLocOffsetMapType::const_iterator SLocMapI) const;
@@ -859,8 +864,6 @@ private:
ASTReader(const ASTReader&); // do not implement
ASTReader &operator=(const ASTReader &); // do not implement
public:
- typedef SmallVector<uint64_t, 64> RecordData;
-
/// \brief Load the AST file and validate its contents against the given
/// Preprocessor.
///
@@ -907,8 +910,8 @@ public:
///
/// \param Mod The module whose names should be made visible.
///
- /// \param Visibility The level of visibility to give the names in the module.
- /// Visibility can only be increased over time.
+ /// \param NameVisibility The level of visibility to give the names in the
+ /// module. Visibility can only be increased over time.
void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind NameVisibility);
@@ -1498,6 +1501,13 @@ public:
SwitchCase *getSwitchCaseWithID(unsigned ID);
void ClearSwitchCaseIDs();
+
+ /// \brief Cursors for comments blocks.
+ SmallVector<std::pair<llvm::BitstreamCursor,
+ serialization::ModuleFile *>, 8> CommentsCursors;
+
+ /// \brief Loads comments ranges.
+ void ReadComments();
};
/// \brief Helper class that saves the current stream position and
diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h
index e693f17..d038d58 100644
--- a/include/clang/Serialization/ASTWriter.h
+++ b/include/clang/Serialization/ASTWriter.h
@@ -110,6 +110,10 @@ private:
/// serialization, rather than just queueing updates.
bool WritingAST;
+ /// \brief Indicates that we are done serializing the collection of decls
+ /// and types to emit.
+ bool DoneWritingDeclsAndTypes;
+
/// \brief Indicates that the AST contained compiler errors.
bool ASTHasCompilerErrors;
@@ -296,6 +300,10 @@ private:
/// it.
llvm::SmallPtrSet<const DeclContext *, 16> UpdatedDeclContexts;
+ /// \brief Keeps track of visible decls that were added in DeclContexts
+ /// coming from another AST file.
+ SmallVector<const Decl *, 16> UpdatingVisibleDecls;
+
typedef llvm::SmallPtrSet<const Decl *, 16> DeclsToRewriteTy;
/// \brief Decls that will be replaced in the current dependent AST file.
DeclsToRewriteTy DeclsToRewrite;
@@ -337,7 +345,7 @@ private:
SmallVector<Stmt *, 16> *CollectedStmts;
/// \brief Mapping from SwitchCase statements to IDs.
- std::map<SwitchCase *, unsigned> SwitchCaseIDs;
+ llvm::DenseMap<SwitchCase *, unsigned> SwitchCaseIDs;
/// \brief The number of statements written to the AST file.
unsigned NumStatements;
@@ -414,11 +422,12 @@ private:
uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC);
void WriteTypeDeclOffsets();
void WriteFileDeclIDsMap();
+ void WriteComments();
void WriteSelectors(Sema &SemaRef);
void WriteReferencedSelectorsPool(Sema &SemaRef);
void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver &IdResolver,
bool IsModule);
- void WriteAttributes(const AttrVec &Attrs, RecordDataImpl &Record);
+ void WriteAttributes(ArrayRef<const Attr*> Attrs, RecordDataImpl &Record);
void ResolveDeclUpdatesBlocks();
void WriteDeclUpdatesBlocks();
void WriteDeclReplacementsBlock();
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
index 2b699a8..5ee52cc 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
@@ -101,20 +101,27 @@ protected:
/// Used for clients to tell if the report's configuration has changed
/// since the last time they checked.
unsigned ConfigurationChangeToken;
+
+ /// When set, this flag disables all callstack pruning from a diagnostic
+ /// path. This is useful for some reports that want maximum fidelty
+ /// when reporting an issue.
+ bool DoNotPrunePath;
public:
BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode)
: BT(bt), DeclWithIssue(0), Description(desc), ErrorNode(errornode),
- ConfigurationChangeToken(0) {}
+ ConfigurationChangeToken(0), DoNotPrunePath(false) {}
BugReport(BugType& bt, StringRef shortDesc, StringRef desc,
const ExplodedNode *errornode)
: BT(bt), DeclWithIssue(0), ShortDescription(shortDesc), Description(desc),
- ErrorNode(errornode), ConfigurationChangeToken(0) {}
+ ErrorNode(errornode), ConfigurationChangeToken(0),
+ DoNotPrunePath(false) {}
BugReport(BugType& bt, StringRef desc, PathDiagnosticLocation l)
: BT(bt), DeclWithIssue(0), Description(desc), Location(l), ErrorNode(0),
- ConfigurationChangeToken(0) {}
+ ConfigurationChangeToken(0),
+ DoNotPrunePath(false) {}
/// \brief Create a BugReport with a custom uniqueing location.
///
@@ -142,6 +149,13 @@ public:
return ShortDescription.empty() ? Description : ShortDescription;
}
+ /// Indicates whether or not any path pruning should take place
+ /// when generating a PathDiagnostic from this BugReport.
+ bool shouldPrunePath() const { return !DoNotPrunePath; }
+
+ /// Disable all path pruning when generating a PathDiagnostic.
+ void disablePathPruning() { DoNotPrunePath = true; }
+
void markInteresting(SymbolRef sym);
void markInteresting(const MemRegion *R);
void markInteresting(SVal V);
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
index 7e665ce..f53c15f 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
@@ -226,13 +226,11 @@ public:
namespace bugreporter {
-BugReporterVisitor *getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
- const Stmt *S,
- BugReport *R);
+void addTrackNullOrUndefValueVisitor(const ExplodedNode *N, const Stmt *S,
+ BugReport *R);
const Stmt *GetDerefExpr(const ExplodedNode *N);
const Stmt *GetDenomExpr(const ExplodedNode *N);
-const Stmt *GetCalleeExpr(const ExplodedNode *N);
const Stmt *GetRetValExpr(const ExplodedNode *N);
} // end namespace clang
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
index 5a8a1c7..2e7abfa 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
@@ -148,6 +148,15 @@ public:
assert(Range.isValid());
}
+ /// Create a location at an explicit offset in the source.
+ ///
+ /// This should only be used if there are no more appropriate constructors.
+ PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm)
+ : K(SingleLocK), S(0), D(0), SM(&sm), Loc(loc, sm), Range(genRange()) {
+ assert(Loc.isValid());
+ assert(Range.isValid());
+ }
+
/// Create a location corresponding to the given declaration.
static PathDiagnosticLocation create(const Decl *D,
const SourceManager &SM) {
@@ -163,6 +172,14 @@ public:
const SourceManager &SM,
const LocationOrAnalysisDeclContext LAC);
+ /// Create a location for the end of the statement.
+ ///
+ /// If the statement is a CompoundStatement, the location will point to the
+ /// closing brace instead of following it.
+ static PathDiagnosticLocation createEnd(const Stmt *S,
+ const SourceManager &SM,
+ const LocationOrAnalysisDeclContext LAC);
+
/// Create the location for the operator of the binary expression.
/// Assumes the statement has a valid location.
static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
@@ -333,10 +350,17 @@ public:
};
-class PathPieces :
- public std::deque<IntrusiveRefCntPtr<PathDiagnosticPiece> > {
+class PathPieces : public std::deque<IntrusiveRefCntPtr<PathDiagnosticPiece> > {
+ void flattenTo(PathPieces &Primary, PathPieces &Current,
+ bool ShouldFlattenMacros) const;
public:
- ~PathPieces();
+ ~PathPieces();
+
+ PathPieces flatten(bool ShouldFlattenMacros) const {
+ PathPieces Result;
+ flattenTo(Result, Result, ShouldFlattenMacros);
+ return Result;
+ }
};
class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
@@ -362,7 +386,7 @@ public:
/// \brief Interface for classes constructing Stack hints.
///
/// If a PathDiagnosticEvent occurs in a different frame than the final
-/// diagnostic the hints can be used to summarise the effect of the call.
+/// diagnostic the hints can be used to summarize the effect of the call.
class StackHintGenerator {
public:
virtual ~StackHintGenerator() = 0;
@@ -510,7 +534,7 @@ public:
}
static PathDiagnosticCallPiece *construct(const ExplodedNode *N,
- const CallExit &CE,
+ const CallExitEnd &CE,
const SourceManager &SM);
static PathDiagnosticCallPiece *construct(PathPieces &pieces,
@@ -637,6 +661,8 @@ public:
void pushActivePath(PathPieces *p) { pathStack.push_back(p); }
void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); }
+
+ bool isWithinCall() const { return !pathStack.empty(); }
// PathDiagnostic();
PathDiagnostic(const Decl *DeclWithIssue,
diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h
index 76d8c15..3214d96 100644
--- a/include/clang/StaticAnalyzer/Core/Checker.h
+++ b/include/clang/StaticAnalyzer/Core/Checker.h
@@ -122,7 +122,7 @@ public:
class PreObjCMessage {
template <typename CHECKER>
- static void _checkObjCMessage(void *checker, const ObjCMessage &msg,
+ static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg,
CheckerContext &C) {
((const CHECKER *)checker)->checkPreObjCMessage(msg, C);
}
@@ -137,7 +137,7 @@ public:
class PostObjCMessage {
template <typename CHECKER>
- static void _checkObjCMessage(void *checker, const ObjCMessage &msg,
+ static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg,
CheckerContext &C) {
((const CHECKER *)checker)->checkPostObjCMessage(msg, C);
}
@@ -150,6 +150,36 @@ public:
}
};
+class PreCall {
+ template <typename CHECKER>
+ static void _checkCall(void *checker, const CallEvent &msg,
+ CheckerContext &C) {
+ ((const CHECKER *)checker)->checkPreCall(msg, C);
+ }
+
+public:
+ template <typename CHECKER>
+ static void _register(CHECKER *checker, CheckerManager &mgr) {
+ mgr._registerForPreCall(
+ CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>));
+ }
+};
+
+class PostCall {
+ template <typename CHECKER>
+ static void _checkCall(void *checker, const CallEvent &msg,
+ CheckerContext &C) {
+ ((const CHECKER *)checker)->checkPostCall(msg, C);
+ }
+
+public:
+ template <typename CHECKER>
+ static void _register(CHECKER *checker, CheckerManager &mgr) {
+ mgr._registerForPostCall(
+ CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>));
+ }
+};
+
class Location {
template <typename CHECKER>
static void _checkLocation(void *checker,
@@ -266,7 +296,7 @@ class RegionChanges {
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) {
+ const CallEvent *Call) {
return ((const CHECKER *)checker)->checkRegionChanges(state, invalidated,
Explicits, Regions, Call);
}
@@ -372,16 +402,14 @@ template <typename CHECK1, typename CHECK2=check::_VoidCheck,
typename CHECK11=check::_VoidCheck,typename CHECK12=check::_VoidCheck,
typename CHECK13=check::_VoidCheck,typename CHECK14=check::_VoidCheck,
typename CHECK15=check::_VoidCheck,typename CHECK16=check::_VoidCheck,
- typename CHECK17=check::_VoidCheck,typename CHECK18=check::_VoidCheck>
+ typename CHECK17=check::_VoidCheck,typename CHECK18=check::_VoidCheck,
+ typename CHECK19=check::_VoidCheck,typename CHECK20=check::_VoidCheck,
+ typename CHECK21=check::_VoidCheck,typename CHECK22=check::_VoidCheck,
+ typename CHECK23=check::_VoidCheck,typename CHECK24=check::_VoidCheck>
class Checker;
template <>
-class Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck>
+class Checker<check::_VoidCheck>
: public CheckerBase
{
virtual void anchor();
@@ -393,19 +421,22 @@ template <typename CHECK1, typename CHECK2, typename CHECK3, typename CHECK4,
typename CHECK5, typename CHECK6, typename CHECK7, typename CHECK8,
typename CHECK9, typename CHECK10,typename CHECK11,typename CHECK12,
typename CHECK13,typename CHECK14,typename CHECK15,typename CHECK16,
- typename CHECK17,typename CHECK18>
+ typename CHECK17,typename CHECK18,typename CHECK19,typename CHECK20,
+ typename CHECK21,typename CHECK22,typename CHECK23,typename CHECK24>
class Checker
: public CHECK1,
- public Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7, CHECK8,
- CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,CHECK14,CHECK15,
- CHECK16,CHECK17,CHECK18> {
+ public Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7,
+ CHECK8, CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,
+ CHECK14,CHECK15,CHECK16,CHECK17,CHECK18,CHECK19,
+ CHECK20,CHECK21,CHECK22,CHECK23,CHECK24> {
public:
template <typename CHECKER>
static void _register(CHECKER *checker, CheckerManager &mgr) {
CHECK1::_register(checker, mgr);
- Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7, CHECK8,
- CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,CHECK14,CHECK15,
- CHECK16,CHECK17,CHECK18>::_register(checker, mgr);
+ Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7,
+ CHECK8, CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,
+ CHECK14,CHECK15,CHECK16,CHECK17,CHECK18,CHECK19,
+ CHECK20,CHECK21,CHECK22,CHECK23,CHECK24>::_register(checker, mgr);
}
};
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h
index d215f99..e11b6d5 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -33,7 +33,8 @@ namespace ento {
class AnalysisManager;
class BugReporter;
class CheckerContext;
- class ObjCMessage;
+ class SimpleCall;
+ class ObjCMethodCall;
class SVal;
class ExplodedNode;
class ExplodedNodeSet;
@@ -44,12 +45,6 @@ namespace ento {
class MemRegion;
class SymbolReaper;
-class GraphExpander {
-public:
- virtual ~GraphExpander();
- virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) = 0;
-};
-
template <typename T> class CheckerFn;
template <typename RET, typename P1, typename P2, typename P3, typename P4,
@@ -207,7 +202,7 @@ public:
/// \brief Run checkers for pre-visiting obj-c messages.
void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const ObjCMessage &msg,
+ const ObjCMethodCall &msg,
ExprEngine &Eng) {
runCheckersForObjCMessage(/*isPreVisit=*/true, Dst, Src, msg, Eng);
}
@@ -215,16 +210,39 @@ public:
/// \brief Run checkers for post-visiting obj-c messages.
void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const ObjCMessage &msg,
- ExprEngine &Eng) {
- runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng);
+ const ObjCMethodCall &msg,
+ ExprEngine &Eng,
+ bool wasInlined = false) {
+ runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng,
+ wasInlined);
}
/// \brief Run checkers for visiting obj-c messages.
void runCheckersForObjCMessage(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const ObjCMessage &msg, ExprEngine &Eng);
+ const ObjCMethodCall &msg, ExprEngine &Eng,
+ bool wasInlined = false);
+
+ /// \brief Run checkers for pre-visiting obj-c messages.
+ void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
+ const CallEvent &Call, ExprEngine &Eng) {
+ runCheckersForCallEvent(/*isPreVisit=*/true, Dst, Src, Call, Eng);
+ }
+
+ /// \brief Run checkers for post-visiting obj-c messages.
+ void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
+ const CallEvent &Call, ExprEngine &Eng,
+ bool wasInlined = false) {
+ runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng,
+ wasInlined);
+ }
+
+ /// \brief Run checkers for visiting obj-c messages.
+ void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call, ExprEngine &Eng,
+ bool wasInlined = false);
/// \brief Run checkers for load/store of a location.
void runCheckersForLocation(ExplodedNodeSet &Dst,
@@ -272,7 +290,8 @@ public:
void runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper, const Stmt *S,
- ExprEngine &Eng);
+ ExprEngine &Eng,
+ ProgramPoint::Kind K);
/// \brief True if at least one checker wants to check region changes.
bool wantsRegionChangeUpdate(ProgramStateRef state);
@@ -286,24 +305,25 @@ public:
/// For example, in the case of a function call, these would be arguments.
/// \param Regions The transitive closure of accessible regions,
/// i.e. all regions that may have been touched by this change.
- /// \param The call expression wrapper if the regions are invalidated by a
- /// call.
- ProgramStateRef
+ /// \param Call The call expression wrapper if the regions are invalidated
+ /// by a call.
+ ProgramStateRef
runCheckersForRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call);
+ const CallEvent *Call);
/// \brief Run checkers for handling assumptions on symbolic values.
ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state,
SVal Cond, bool Assumption);
/// \brief Run checkers for evaluating a call.
+ ///
+ /// Warning: Currently, the CallEvent MUST come from a CallExpr!
void runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const CallExpr *CE, ExprEngine &Eng,
- GraphExpander *defaultEval = 0);
+ const CallEvent &CE, ExprEngine &Eng);
/// \brief Run checkers for the entire Translation Unit.
void runCheckersOnEndOfTranslationUnit(const TranslationUnitDecl *TU,
@@ -342,8 +362,11 @@ public:
typedef CheckerFn<void (const Stmt *, CheckerContext &)> CheckStmtFunc;
- typedef CheckerFn<void (const ObjCMessage &, CheckerContext &)>
+ typedef CheckerFn<void (const ObjCMethodCall &, CheckerContext &)>
CheckObjCMessageFunc;
+
+ typedef CheckerFn<void (const CallEvent &, CheckerContext &)>
+ CheckCallFunc;
typedef CheckerFn<void (const SVal &location, bool isLoad,
const Stmt *S,
@@ -372,7 +395,7 @@ public:
const StoreManager::InvalidatedSymbols *symbols,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call)>
+ const CallEvent *Call)>
CheckRegionChangesFunc;
typedef CheckerFn<bool (ProgramStateRef)> WantsRegionChangeUpdateFunc;
@@ -402,6 +425,9 @@ public:
void _registerForPreObjCMessage(CheckObjCMessageFunc checkfn);
void _registerForPostObjCMessage(CheckObjCMessageFunc checkfn);
+ void _registerForPreCall(CheckCallFunc checkfn);
+ void _registerForPostCall(CheckCallFunc checkfn);
+
void _registerForLocation(CheckLocationFunc checkfn);
void _registerForBind(CheckBindFunc checkfn);
@@ -523,6 +549,9 @@ private:
std::vector<CheckObjCMessageFunc> PreObjCMessageCheckers;
std::vector<CheckObjCMessageFunc> PostObjCMessageCheckers;
+ std::vector<CheckCallFunc> PreCallCheckers;
+ std::vector<CheckCallFunc> PostCallCheckers;
+
std::vector<CheckLocationFunc> LocationCheckers;
std::vector<CheckBindFunc> BindCheckers;
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h
new file mode 100644
index 0000000..e1ff17b
--- /dev/null
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h
@@ -0,0 +1,106 @@
+//== APSIntType.h - Simple record of the type of APSInts --------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SA_CORE_APSINTTYPE_H
+#define LLVM_CLANG_SA_CORE_APSINTTYPE_H
+
+#include "llvm/ADT/APSInt.h"
+
+namespace clang {
+namespace ento {
+
+/// \brief A record of the "type" of an APSInt, used for conversions.
+class APSIntType {
+ uint32_t BitWidth;
+ bool IsUnsigned;
+
+public:
+ APSIntType(uint32_t Width, bool Unsigned)
+ : BitWidth(Width), IsUnsigned(Unsigned) {}
+
+ /* implicit */ APSIntType(const llvm::APSInt &Value)
+ : BitWidth(Value.getBitWidth()), IsUnsigned(Value.isUnsigned()) {}
+
+ uint32_t getBitWidth() const { return BitWidth; }
+ bool isUnsigned() const { return IsUnsigned; }
+
+ /// \brief Convert a given APSInt, in place, to match this type.
+ ///
+ /// This behaves like a C cast: converting 255u8 (0xFF) to s16 gives
+ /// 255 (0x00FF), and converting -1s8 (0xFF) to u16 gives 65535 (0xFFFF).
+ void apply(llvm::APSInt &Value) const {
+ // Note the order here. We extend first to preserve the sign, if this value
+ // is signed, /then/ match the signedness of the result type.
+ Value = Value.extOrTrunc(BitWidth);
+ Value.setIsUnsigned(IsUnsigned);
+ }
+
+ /// Convert and return a new APSInt with the given value, but this
+ /// type's bit width and signedness.
+ ///
+ /// \see apply
+ llvm::APSInt convert(const llvm::APSInt &Value) const LLVM_READONLY {
+ llvm::APSInt Result(Value, Value.isUnsigned());
+ apply(Result);
+ return Result;
+ }
+
+ /// Returns an all-zero value for this type.
+ llvm::APSInt getZeroValue() const LLVM_READONLY {
+ return llvm::APSInt(BitWidth, IsUnsigned);
+ }
+
+ /// Returns the minimum value for this type.
+ llvm::APSInt getMinValue() const LLVM_READONLY {
+ return llvm::APSInt::getMinValue(BitWidth, IsUnsigned);
+ }
+
+ /// Returns the maximum value for this type.
+ llvm::APSInt getMaxValue() const LLVM_READONLY {
+ return llvm::APSInt::getMaxValue(BitWidth, IsUnsigned);
+ }
+
+ /// Used to classify whether a value is representable using this type.
+ ///
+ /// \see testInRange
+ enum RangeTestResultKind {
+ RTR_Below = -1, ///< Value is less than the minimum representable value.
+ RTR_Within = 0, ///< Value is representable using this type.
+ RTR_Above = 1 ///< Value is greater than the maximum representable value.
+ };
+
+ /// Tests whether a given value is losslessly representable using this type.
+ ///
+ /// Note that signedness conversions will be rejected, even with the same bit
+ /// pattern. For example, -1s8 is not in range for 'unsigned char' (u8).
+ RangeTestResultKind testInRange(const llvm::APSInt &Val) const LLVM_READONLY;
+
+ bool operator==(const APSIntType &Other) const {
+ return BitWidth == Other.BitWidth && IsUnsigned == Other.IsUnsigned;
+ }
+
+ /// \brief Provide an ordering for finding a common conversion type.
+ ///
+ /// Unsigned integers are considered to be better conversion types than
+ /// signed integers of the same width.
+ bool operator<(const APSIntType &Other) const {
+ if (BitWidth < Other.BitWidth)
+ return true;
+ if (BitWidth > Other.BitWidth)
+ return false;
+ if (!IsUnsigned && Other.IsUnsigned)
+ return true;
+ return false;
+ }
+};
+
+} // end ento namespace
+} // end clang namespace
+
+#endif
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
index d01644ba..1cc53d4 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
@@ -41,8 +41,6 @@ class AnalysisManager : public BugReporterData {
CheckerManager *CheckerMgr;
- enum AnalysisScope { ScopeTU, ScopeDecl } AScope;
-
/// \brief The maximum number of exploded nodes the analyzer will generate.
unsigned MaxNodes;
@@ -93,7 +91,7 @@ public:
bool vizdot, bool vizubi, AnalysisPurgeMode purge,
bool eager, bool trim,
bool useUnoptimizedCFG,
- bool addImplicitDtors, bool addInitializers,
+ bool addImplicitDtors,
bool eagerlyTrimEGraph,
AnalysisIPAMode ipa,
unsigned inlineMaxStack,
@@ -171,7 +169,7 @@ public:
bool shouldEagerlyAssume() const { return EagerlyAssume; }
- bool shouldInlineCall() const { return (IPAMode == Inlining); }
+ bool shouldInlineCall() const { return (IPAMode != None); }
CFG *getCFG(Decl const *D) {
return AnaCtxMgr.getContext(D)->getCFG();
@@ -190,10 +188,6 @@ public:
return AnaCtxMgr.getContext(D);
}
- AnalysisDeclContext *getAnalysisDeclContext(const Decl *D, idx::TranslationUnit *TU) {
- return AnaCtxMgr.getContext(D, TU);
- }
-
};
} // enAnaCtxMgrspace
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
index 9a699f9..b4a9de7 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
@@ -16,6 +16,7 @@
#ifndef LLVM_CLANG_GR_BASICVALUEFACTORY_H
#define LLVM_CLANG_GR_BASICVALUEFACTORY_H
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
@@ -86,28 +87,30 @@ public:
const llvm::APSInt& getValue(uint64_t X, unsigned BitWidth, bool isUnsigned);
const llvm::APSInt& getValue(uint64_t X, QualType T);
+ /// Returns the type of the APSInt used to store values of the given QualType.
+ APSIntType getAPSIntType(QualType T) const {
+ assert(T->isIntegerType() || Loc::isLocType(T));
+ return APSIntType(Ctx.getTypeSize(T),
+ !T->isSignedIntegerOrEnumerationType());
+ }
+
/// Convert - Create a new persistent APSInt with the same value as 'From'
/// but with the bitwidth and signedness of 'To'.
const llvm::APSInt &Convert(const llvm::APSInt& To,
const llvm::APSInt& From) {
-
- if (To.isUnsigned() == From.isUnsigned() &&
- To.getBitWidth() == From.getBitWidth())
+ APSIntType TargetType(To);
+ if (TargetType == APSIntType(From))
return From;
- return getValue(From.getSExtValue(), To.getBitWidth(), To.isUnsigned());
+ return getValue(TargetType.convert(From));
}
const llvm::APSInt &Convert(QualType T, const llvm::APSInt &From) {
- assert(T->isIntegerType() || Loc::isLocType(T));
- unsigned bitwidth = Ctx.getTypeSize(T);
- bool isUnsigned
- = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T);
-
- if (isUnsigned == From.isUnsigned() && bitwidth == From.getBitWidth())
+ APSIntType TargetType = getAPSIntType(T);
+ if (TargetType == APSIntType(From))
return From;
- return getValue(From.getSExtValue(), bitwidth, isUnsigned);
+ return getValue(TargetType.convert(From));
}
const llvm::APSInt& getIntValue(uint64_t X, bool isUnsigned) {
@@ -116,25 +119,19 @@ public:
}
inline const llvm::APSInt& getMaxValue(const llvm::APSInt &v) {
- return getValue(llvm::APSInt::getMaxValue(v.getBitWidth(), v.isUnsigned()));
+ return getValue(APSIntType(v).getMaxValue());
}
inline const llvm::APSInt& getMinValue(const llvm::APSInt &v) {
- return getValue(llvm::APSInt::getMinValue(v.getBitWidth(), v.isUnsigned()));
+ return getValue(APSIntType(v).getMinValue());
}
inline const llvm::APSInt& getMaxValue(QualType T) {
- assert(T->isIntegerType() || Loc::isLocType(T));
- bool isUnsigned
- = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T);
- return getValue(llvm::APSInt::getMaxValue(Ctx.getTypeSize(T), isUnsigned));
+ return getValue(getAPSIntType(T).getMaxValue());
}
inline const llvm::APSInt& getMinValue(QualType T) {
- assert(T->isIntegerType() || Loc::isLocType(T));
- bool isUnsigned
- = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T);
- return getValue(llvm::APSInt::getMinValue(Ctx.getTypeSize(T), isUnsigned));
+ return getValue(getAPSIntType(T).getMinValue());
}
inline const llvm::APSInt& Add1(const llvm::APSInt& V) {
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
new file mode 100644
index 0000000..47edfe9
--- /dev/null
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -0,0 +1,962 @@
+//===- CallEvent.h - Wrapper for all function and method calls ----*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines CallEvent and its subclasses, which represent path-
+/// sensitive instances of different kinds of function and method calls
+/// (C, C++, and Objective-C).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_CALL
+#define LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_CALL
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/PointerIntPair.h"
+
+namespace clang {
+class ProgramPoint;
+class ProgramPointTag;
+
+namespace ento {
+
+enum CallEventKind {
+ CE_Function,
+ CE_Block,
+ CE_BEG_SIMPLE_CALLS = CE_Function,
+ CE_END_SIMPLE_CALLS = CE_Block,
+ CE_CXXMember,
+ CE_CXXMemberOperator,
+ CE_CXXDestructor,
+ CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember,
+ CE_END_CXX_INSTANCE_CALLS = CE_CXXDestructor,
+ CE_CXXConstructor,
+ CE_CXXAllocator,
+ CE_BEG_FUNCTION_CALLS = CE_Function,
+ CE_END_FUNCTION_CALLS = CE_CXXAllocator,
+ CE_ObjCMessage
+};
+
+class CallEvent;
+class CallEventManager;
+
+template<typename T = CallEvent>
+class CallEventRef : public IntrusiveRefCntPtr<const T> {
+public:
+ CallEventRef(const T *Call) : IntrusiveRefCntPtr<const T>(Call) {}
+ CallEventRef(const CallEventRef &Orig) : IntrusiveRefCntPtr<const T>(Orig) {}
+
+ CallEventRef<T> cloneWithState(ProgramStateRef State) const {
+ return this->getPtr()->template cloneWithState<T>(State);
+ }
+
+ // Allow implicit conversions to a superclass type, since CallEventRef
+ // behaves like a pointer-to-const.
+ template <typename SuperT>
+ operator CallEventRef<SuperT> () const {
+ return this->getPtr();
+ }
+};
+
+/// \brief Defines the runtime definition of the called function.
+class RuntimeDefinition {
+ /// The Declaration of the function which will be called at runtime.
+ /// 0 if not available.
+ const Decl *D;
+
+ /// The region representing an object (ObjC/C++) on which the method is
+ /// called. With dynamic dispatch, the method definition depends on the
+ /// runtime type of this object. 0 when there is no dynamic dispatch.
+ const MemRegion *R;
+
+public:
+ RuntimeDefinition(): D(0), R(0) {}
+ RuntimeDefinition(const Decl *InD): D(InD), R(0) {}
+ RuntimeDefinition(const Decl *InD, const MemRegion *InR): D(InD), R(InR) {}
+ const Decl *getDecl() { return D; }
+ const MemRegion *getDispatchRegion() { return R; }
+ bool mayHaveOtherDefinitions() { return R != 0; }
+};
+
+/// \brief Represents an abstract call to a function or method along a
+/// particular path.
+///
+/// CallEvents are created through the factory methods of CallEventManager.
+///
+/// CallEvents should always be cheap to create and destroy. In order for
+/// CallEventManager to be able to re-use CallEvent-sized memory blocks,
+/// subclasses of CallEvent may not add any data members to the base class.
+/// Use the "Data" and "Location" fields instead.
+class CallEvent {
+public:
+ typedef CallEventKind Kind;
+
+private:
+ ProgramStateRef State;
+ const LocationContext *LCtx;
+ llvm::PointerUnion<const Expr *, const Decl *> Origin;
+
+ // DO NOT IMPLEMENT
+ CallEvent &operator=(const CallEvent &);
+
+protected:
+ // This is user data for subclasses.
+ const void *Data;
+
+ // This is user data for subclasses.
+ // This should come right before RefCount, so that the two fields can be
+ // packed together on LP64 platforms.
+ SourceLocation Location;
+
+private:
+ mutable unsigned RefCount;
+
+ template <typename T> friend struct llvm::IntrusiveRefCntPtrInfo;
+ void Retain() const { ++RefCount; }
+ void Release() const;
+
+protected:
+ friend class CallEventManager;
+
+ CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx)
+ : State(state), LCtx(lctx), Origin(E), RefCount(0) {}
+
+ CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx)
+ : State(state), LCtx(lctx), Origin(D), RefCount(0) {}
+
+ // DO NOT MAKE PUBLIC
+ CallEvent(const CallEvent &Original)
+ : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin),
+ Data(Original.Data), Location(Original.Location), RefCount(0) {}
+
+
+ ProgramStateRef getState() const {
+ return State;
+ }
+
+ const LocationContext *getLocationContext() const {
+ return LCtx;
+ }
+
+
+ /// Copies this CallEvent, with vtable intact, into a new block of memory.
+ virtual void cloneTo(void *Dest) const = 0;
+
+ /// \brief Get the value of arbitrary expressions at this point in the path.
+ SVal getSVal(const Stmt *S) const {
+ return getState()->getSVal(S, getLocationContext());
+ }
+
+
+ typedef SmallVectorImpl<const MemRegion *> RegionList;
+
+ /// \brief Used to specify non-argument regions that will be invalidated as a
+ /// result of this call.
+ virtual void getExtraInvalidatedRegions(RegionList &Regions) const {}
+
+ virtual QualType getDeclaredResultType() const = 0;
+
+public:
+ virtual ~CallEvent() {}
+
+ /// \brief Returns the kind of call this is.
+ virtual Kind getKind() const = 0;
+
+ /// \brief Returns the declaration of the function or method that will be
+ /// called. May be null.
+ virtual const Decl *getDecl() const {
+ return Origin.dyn_cast<const Decl *>();
+ }
+
+ /// \brief Returns the definition of the function or method that will be
+ /// called.
+ virtual RuntimeDefinition getRuntimeDefinition() const = 0;
+
+ /// \brief Returns the expression whose value will be the result of this call.
+ /// May be null.
+ const Expr *getOriginExpr() const {
+ return Origin.dyn_cast<const Expr *>();
+ }
+
+ /// \brief Returns the number of arguments (explicit and implicit).
+ ///
+ /// Note that this may be greater than the number of parameters in the
+ /// callee's declaration, and that it may include arguments not written in
+ /// the source.
+ virtual unsigned getNumArgs() const = 0;
+
+ /// \brief Returns true if the callee is known to be from a system header.
+ bool isInSystemHeader() const {
+ const Decl *D = getDecl();
+ if (!D)
+ return false;
+
+ SourceLocation Loc = D->getLocation();
+ if (Loc.isValid()) {
+ const SourceManager &SM =
+ getState()->getStateManager().getContext().getSourceManager();
+ return SM.isInSystemHeader(D->getLocation());
+ }
+
+ // Special case for implicitly-declared global operator new/delete.
+ // These should be considered system functions.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ return FD->isOverloadedOperator() && FD->isImplicit() && FD->isGlobal();
+
+ return false;
+ }
+
+ /// \brief Returns a source range for the entire call, suitable for
+ /// outputting in diagnostics.
+ virtual SourceRange getSourceRange() const {
+ return getOriginExpr()->getSourceRange();
+ }
+
+ /// \brief Returns the value of a given argument at the time of the call.
+ virtual SVal getArgSVal(unsigned Index) const;
+
+ /// \brief Returns the expression associated with a given argument.
+ /// May be null if this expression does not appear in the source.
+ virtual const Expr *getArgExpr(unsigned Index) const { return 0; }
+
+ /// \brief Returns the source range for errors associated with this argument.
+ ///
+ /// May be invalid if the argument is not written in the source.
+ virtual SourceRange getArgSourceRange(unsigned Index) const;
+
+ /// \brief Returns the result type, adjusted for references.
+ QualType getResultType() const;
+
+ /// \brief Returns true if any of the arguments appear to represent callbacks.
+ bool hasNonZeroCallbackArg() const;
+
+ /// \brief Returns true if any of the arguments are known to escape to long-
+ /// term storage, even if this method will not modify them.
+ // NOTE: The exact semantics of this are still being defined!
+ // We don't really want a list of hardcoded exceptions in the long run,
+ // but we don't want duplicated lists of known APIs in the short term either.
+ virtual bool argumentsMayEscape() const {
+ return hasNonZeroCallbackArg();
+ }
+
+ /// \brief Returns an appropriate ProgramPoint for this call.
+ ProgramPoint getProgramPoint(bool IsPreVisit = false,
+ const ProgramPointTag *Tag = 0) const;
+
+ /// \brief Returns a new state with all argument regions invalidated.
+ ///
+ /// This accepts an alternate state in case some processing has already
+ /// occurred.
+ ProgramStateRef invalidateRegions(unsigned BlockCount,
+ ProgramStateRef Orig = 0) const;
+
+ typedef std::pair<Loc, SVal> FrameBindingTy;
+ typedef SmallVectorImpl<FrameBindingTy> BindingsTy;
+
+ /// Populates the given SmallVector with the bindings in the callee's stack
+ /// frame at the start of this call.
+ virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const = 0;
+
+ /// Returns a copy of this CallEvent, but using the given state.
+ template <typename T>
+ CallEventRef<T> cloneWithState(ProgramStateRef NewState) const;
+
+ /// Returns a copy of this CallEvent, but using the given state.
+ CallEventRef<> cloneWithState(ProgramStateRef NewState) const {
+ return cloneWithState<CallEvent>(NewState);
+ }
+
+ /// \brief Returns true if this is a statement that can be considered for
+ /// inlining.
+ ///
+ /// FIXME: This should go away once CallEvents are cheap and easy to
+ /// construct from ExplodedNodes.
+ static bool mayBeInlined(const Stmt *S);
+
+ // Iterator access to formal parameters and their types.
+private:
+ typedef std::const_mem_fun_t<QualType, ParmVarDecl> get_type_fun;
+
+public:
+ typedef const ParmVarDecl * const *param_iterator;
+
+ /// Returns an iterator over the call's formal parameters.
+ ///
+ /// If UseDefinitionParams is set, this will return the parameter decls
+ /// used in the callee's definition (suitable for inlining). Most of the
+ /// time it is better to use the decl found by name lookup, which likely
+ /// carries more annotations.
+ ///
+ /// Remember that the number of formal parameters may not match the number
+ /// of arguments for all calls. However, the first parameter will always
+ /// correspond with the argument value returned by \c getArgSVal(0).
+ ///
+ /// If the call has no accessible declaration (or definition, if
+ /// \p UseDefinitionParams is set), \c param_begin() will be equal to
+ /// \c param_end().
+ virtual param_iterator param_begin() const =0;
+ /// \sa param_begin()
+ virtual param_iterator param_end() const = 0;
+
+ typedef llvm::mapped_iterator<param_iterator, get_type_fun>
+ param_type_iterator;
+
+ /// Returns an iterator over the types of the call's formal parameters.
+ ///
+ /// This uses the callee decl found by default name lookup rather than the
+ /// definition because it represents a public interface, and probably has
+ /// more annotations.
+ param_type_iterator param_type_begin() const {
+ return llvm::map_iterator(param_begin(),
+ get_type_fun(&ParmVarDecl::getType));
+ }
+ /// \sa param_type_begin()
+ param_type_iterator param_type_end() const {
+ return llvm::map_iterator(param_end(), get_type_fun(&ParmVarDecl::getType));
+ }
+
+ // For debugging purposes only
+ void dump(raw_ostream &Out) const;
+ LLVM_ATTRIBUTE_USED void dump() const { dump(llvm::errs()); }
+
+ static bool classof(const CallEvent *) { return true; }
+};
+
+
+/// \brief Represents a call to any sort of function that might have a
+/// FunctionDecl.
+class AnyFunctionCall : public CallEvent {
+protected:
+ AnyFunctionCall(const Expr *E, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CallEvent(E, St, LCtx) {}
+ AnyFunctionCall(const Decl *D, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CallEvent(D, St, LCtx) {}
+ AnyFunctionCall(const AnyFunctionCall &Other) : CallEvent(Other) {}
+
+ virtual QualType getDeclaredResultType() const;
+
+public:
+ // This function is overridden by subclasses, but they must return
+ // a FunctionDecl.
+ virtual const FunctionDecl *getDecl() const {
+ return cast<FunctionDecl>(CallEvent::getDecl());
+ }
+
+ virtual RuntimeDefinition getRuntimeDefinition() const {
+ const FunctionDecl *FD = getDecl();
+ // Note that hasBody() will fill FD with the definition FunctionDecl.
+ if (FD && FD->hasBody(FD))
+ return RuntimeDefinition(FD);
+ return RuntimeDefinition();
+ }
+
+ virtual bool argumentsMayEscape() const;
+
+ virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const;
+
+ virtual param_iterator param_begin() const;
+ virtual param_iterator param_end() const;
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() >= CE_BEG_FUNCTION_CALLS &&
+ CA->getKind() <= CE_END_FUNCTION_CALLS;
+ }
+};
+
+/// \brief Represents a call to a non-C++ function, written as a CallExpr.
+class SimpleCall : public AnyFunctionCall {
+protected:
+ SimpleCall(const CallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : AnyFunctionCall(CE, St, LCtx) {}
+ SimpleCall(const SimpleCall &Other) : AnyFunctionCall(Other) {}
+
+public:
+ virtual const CallExpr *getOriginExpr() const {
+ return cast<CallExpr>(AnyFunctionCall::getOriginExpr());
+ }
+
+ virtual const FunctionDecl *getDecl() const;
+
+ virtual unsigned getNumArgs() const { return getOriginExpr()->getNumArgs(); }
+
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ return getOriginExpr()->getArg(Index);
+ }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() >= CE_BEG_SIMPLE_CALLS &&
+ CA->getKind() <= CE_END_SIMPLE_CALLS;
+ }
+};
+
+/// \brief Represents a C function or static C++ member function call.
+///
+/// Example: \c fun()
+class FunctionCall : public SimpleCall {
+ friend class CallEventManager;
+
+protected:
+ FunctionCall(const CallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : SimpleCall(CE, St, LCtx) {}
+
+ FunctionCall(const FunctionCall &Other) : SimpleCall(Other) {}
+ virtual void cloneTo(void *Dest) const { new (Dest) FunctionCall(*this); }
+
+public:
+ virtual Kind getKind() const { return CE_Function; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_Function;
+ }
+};
+
+/// \brief Represents a call to a block.
+///
+/// Example: <tt>^{ /* ... */ }()</tt>
+class BlockCall : public SimpleCall {
+ friend class CallEventManager;
+
+protected:
+ BlockCall(const CallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : SimpleCall(CE, St, LCtx) {}
+
+ BlockCall(const BlockCall &Other) : SimpleCall(Other) {}
+ virtual void cloneTo(void *Dest) const { new (Dest) BlockCall(*this); }
+
+ virtual void getExtraInvalidatedRegions(RegionList &Regions) const;
+
+ virtual QualType getDeclaredResultType() const;
+
+public:
+ /// \brief Returns the region associated with this instance of the block.
+ ///
+ /// This may be NULL if the block's origin is unknown.
+ const BlockDataRegion *getBlockRegion() const;
+
+ /// \brief Gets the declaration of the block.
+ ///
+ /// This is not an override of getDecl() because AnyFunctionCall has already
+ /// assumed that it's a FunctionDecl.
+ const BlockDecl *getBlockDecl() const {
+ const BlockDataRegion *BR = getBlockRegion();
+ if (!BR)
+ return 0;
+ return BR->getDecl();
+ }
+
+ virtual RuntimeDefinition getRuntimeDefinition() const {
+ return RuntimeDefinition(getBlockDecl());
+ }
+
+ virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const;
+
+ virtual param_iterator param_begin() const;
+ virtual param_iterator param_end() const;
+
+ virtual Kind getKind() const { return CE_Block; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_Block;
+ }
+};
+
+/// \brief Represents a non-static C++ member function call, no matter how
+/// it is written.
+class CXXInstanceCall : public AnyFunctionCall {
+protected:
+ virtual void getExtraInvalidatedRegions(RegionList &Regions) const;
+
+ CXXInstanceCall(const CallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : AnyFunctionCall(CE, St, LCtx) {}
+ CXXInstanceCall(const FunctionDecl *D, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : AnyFunctionCall(D, St, LCtx) {}
+
+
+ CXXInstanceCall(const CXXInstanceCall &Other) : AnyFunctionCall(Other) {}
+
+public:
+ /// \brief Returns the expression representing the implicit 'this' object.
+ virtual const Expr *getCXXThisExpr() const { return 0; }
+
+ /// \brief Returns the value of the implicit 'this' object.
+ virtual SVal getCXXThisVal() const {
+ const Expr *Base = getCXXThisExpr();
+ // FIXME: This doesn't handle an overloaded ->* operator.
+ if (!Base)
+ return UnknownVal();
+ return getSVal(Base);
+ }
+
+ virtual const FunctionDecl *getDecl() const;
+
+ virtual RuntimeDefinition getRuntimeDefinition() const;
+
+ virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const;
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() >= CE_BEG_CXX_INSTANCE_CALLS &&
+ CA->getKind() <= CE_END_CXX_INSTANCE_CALLS;
+ }
+};
+
+/// \brief Represents a non-static C++ member function call.
+///
+/// Example: \c obj.fun()
+class CXXMemberCall : public CXXInstanceCall {
+ friend class CallEventManager;
+
+protected:
+ CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CXXInstanceCall(CE, St, LCtx) {}
+
+ CXXMemberCall(const CXXMemberCall &Other) : CXXInstanceCall(Other) {}
+ virtual void cloneTo(void *Dest) const { new (Dest) CXXMemberCall(*this); }
+
+public:
+ virtual const CXXMemberCallExpr *getOriginExpr() const {
+ return cast<CXXMemberCallExpr>(CXXInstanceCall::getOriginExpr());
+ }
+
+ virtual unsigned getNumArgs() const {
+ if (const CallExpr *CE = getOriginExpr())
+ return CE->getNumArgs();
+ return 0;
+ }
+
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ return getOriginExpr()->getArg(Index);
+ }
+
+ virtual const Expr *getCXXThisExpr() const;
+
+ virtual Kind getKind() const { return CE_CXXMember; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_CXXMember;
+ }
+};
+
+/// \brief Represents a C++ overloaded operator call where the operator is
+/// implemented as a non-static member function.
+///
+/// Example: <tt>iter + 1</tt>
+class CXXMemberOperatorCall : public CXXInstanceCall {
+ friend class CallEventManager;
+
+protected:
+ CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CXXInstanceCall(CE, St, LCtx) {}
+
+ CXXMemberOperatorCall(const CXXMemberOperatorCall &Other)
+ : CXXInstanceCall(Other) {}
+ virtual void cloneTo(void *Dest) const {
+ new (Dest) CXXMemberOperatorCall(*this);
+ }
+
+public:
+ virtual const CXXOperatorCallExpr *getOriginExpr() const {
+ return cast<CXXOperatorCallExpr>(CXXInstanceCall::getOriginExpr());
+ }
+
+ virtual unsigned getNumArgs() const {
+ return getOriginExpr()->getNumArgs() - 1;
+ }
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ return getOriginExpr()->getArg(Index + 1);
+ }
+
+ virtual const Expr *getCXXThisExpr() const;
+
+ virtual Kind getKind() const { return CE_CXXMemberOperator; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_CXXMemberOperator;
+ }
+};
+
+/// \brief Represents an implicit call to a C++ destructor.
+///
+/// This can occur at the end of a scope (for automatic objects), at the end
+/// of a full-expression (for temporaries), or as part of a delete.
+class CXXDestructorCall : public CXXInstanceCall {
+ friend class CallEventManager;
+
+protected:
+ /// Creates an implicit destructor.
+ ///
+ /// \param DD The destructor that will be called.
+ /// \param Trigger The statement whose completion causes this destructor call.
+ /// \param Target The object region to be destructed.
+ /// \param St The path-sensitive state at this point in the program.
+ /// \param LCtx The location context at this point in the program.
+ CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
+ const MemRegion *Target, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CXXInstanceCall(DD, St, LCtx) {
+ Data = Target;
+ Location = Trigger->getLocEnd();
+ }
+
+ CXXDestructorCall(const CXXDestructorCall &Other) : CXXInstanceCall(Other) {}
+ virtual void cloneTo(void *Dest) const { new (Dest) CXXDestructorCall(*this); }
+
+public:
+ virtual SourceRange getSourceRange() const { return Location; }
+ virtual unsigned getNumArgs() const { return 0; }
+
+ /// \brief Returns the value of the implicit 'this' object.
+ virtual SVal getCXXThisVal() const;
+
+ virtual Kind getKind() const { return CE_CXXDestructor; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_CXXDestructor;
+ }
+};
+
+/// \brief Represents a call to a C++ constructor.
+///
+/// Example: \c T(1)
+class CXXConstructorCall : public AnyFunctionCall {
+ friend class CallEventManager;
+
+protected:
+ /// Creates a constructor call.
+ ///
+ /// \param CE The constructor expression as written in the source.
+ /// \param Target The region where the object should be constructed. If NULL,
+ /// a new symbolic region will be used.
+ /// \param St The path-sensitive state at this point in the program.
+ /// \param LCtx The location context at this point in the program.
+ CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *target,
+ ProgramStateRef St, const LocationContext *LCtx)
+ : AnyFunctionCall(CE, St, LCtx) {
+ Data = target;
+ }
+
+ CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){}
+ virtual void cloneTo(void *Dest) const { new (Dest) CXXConstructorCall(*this); }
+
+ virtual void getExtraInvalidatedRegions(RegionList &Regions) const;
+
+public:
+ virtual const CXXConstructExpr *getOriginExpr() const {
+ return cast<CXXConstructExpr>(AnyFunctionCall::getOriginExpr());
+ }
+
+ virtual const CXXConstructorDecl *getDecl() const {
+ return getOriginExpr()->getConstructor();
+ }
+
+ virtual unsigned getNumArgs() const { return getOriginExpr()->getNumArgs(); }
+
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ return getOriginExpr()->getArg(Index);
+ }
+
+ /// \brief Returns the value of the implicit 'this' object.
+ SVal getCXXThisVal() const;
+
+ virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const;
+
+ virtual Kind getKind() const { return CE_CXXConstructor; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_CXXConstructor;
+ }
+};
+
+/// \brief Represents the memory allocation call in a C++ new-expression.
+///
+/// This is a call to "operator new".
+class CXXAllocatorCall : public AnyFunctionCall {
+ friend class CallEventManager;
+
+protected:
+ CXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : AnyFunctionCall(E, St, LCtx) {}
+
+ CXXAllocatorCall(const CXXAllocatorCall &Other) : AnyFunctionCall(Other) {}
+ virtual void cloneTo(void *Dest) const { new (Dest) CXXAllocatorCall(*this); }
+
+public:
+ virtual const CXXNewExpr *getOriginExpr() const {
+ return cast<CXXNewExpr>(AnyFunctionCall::getOriginExpr());
+ }
+
+ virtual const FunctionDecl *getDecl() const {
+ return getOriginExpr()->getOperatorNew();
+ }
+
+ virtual unsigned getNumArgs() const {
+ return getOriginExpr()->getNumPlacementArgs() + 1;
+ }
+
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ // The first argument of an allocator call is the size of the allocation.
+ if (Index == 0)
+ return 0;
+ return getOriginExpr()->getPlacementArg(Index - 1);
+ }
+
+ virtual Kind getKind() const { return CE_CXXAllocator; }
+
+ static bool classof(const CallEvent *CE) {
+ return CE->getKind() == CE_CXXAllocator;
+ }
+};
+
+/// \brief Represents the ways an Objective-C message send can occur.
+//
+// Note to maintainers: OCM_Message should always be last, since it does not
+// need to fit in the Data field's low bits.
+enum ObjCMessageKind {
+ OCM_PropertyAccess,
+ OCM_Subscript,
+ OCM_Message
+};
+
+/// \brief Represents any expression that calls an Objective-C method.
+///
+/// This includes all of the kinds listed in ObjCMessageKind.
+class ObjCMethodCall : public CallEvent {
+ friend class CallEventManager;
+
+ const PseudoObjectExpr *getContainingPseudoObjectExpr() const;
+
+protected:
+ ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CallEvent(Msg, St, LCtx) {
+ Data = 0;
+ }
+
+ ObjCMethodCall(const ObjCMethodCall &Other) : CallEvent(Other) {}
+ virtual void cloneTo(void *Dest) const { new (Dest) ObjCMethodCall(*this); }
+
+ virtual void getExtraInvalidatedRegions(RegionList &Regions) const;
+
+ virtual QualType getDeclaredResultType() const;
+
+ /// Check if the selector may have multiple definitions (may have overrides).
+ virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
+ Selector Sel) const;
+
+public:
+ virtual const ObjCMessageExpr *getOriginExpr() const {
+ return cast<ObjCMessageExpr>(CallEvent::getOriginExpr());
+ }
+ virtual const ObjCMethodDecl *getDecl() const {
+ return getOriginExpr()->getMethodDecl();
+ }
+ virtual unsigned getNumArgs() const {
+ return getOriginExpr()->getNumArgs();
+ }
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ return getOriginExpr()->getArg(Index);
+ }
+
+ bool isInstanceMessage() const {
+ return getOriginExpr()->isInstanceMessage();
+ }
+ ObjCMethodFamily getMethodFamily() const {
+ return getOriginExpr()->getMethodFamily();
+ }
+ Selector getSelector() const {
+ return getOriginExpr()->getSelector();
+ }
+
+ virtual SourceRange getSourceRange() const;
+
+ /// \brief Returns the value of the receiver at the time of this call.
+ SVal getReceiverSVal() const;
+
+ /// \brief Get the interface for the receiver.
+ ///
+ /// This works whether this is an instance message or a class message.
+ /// However, it currently just uses the static type of the receiver.
+ const ObjCInterfaceDecl *getReceiverInterface() const {
+ return getOriginExpr()->getReceiverInterface();
+ }
+
+ ObjCMessageKind getMessageKind() const;
+
+ bool isSetter() const {
+ switch (getMessageKind()) {
+ case OCM_Message:
+ llvm_unreachable("This is not a pseudo-object access!");
+ case OCM_PropertyAccess:
+ return getNumArgs() > 0;
+ case OCM_Subscript:
+ return getNumArgs() > 1;
+ }
+ llvm_unreachable("Unknown message kind");
+ }
+
+ virtual RuntimeDefinition getRuntimeDefinition() const;
+
+ virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const;
+
+ virtual param_iterator param_begin() const;
+ virtual param_iterator param_end() const;
+
+ virtual Kind getKind() const { return CE_ObjCMessage; }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_ObjCMessage;
+ }
+};
+
+
+/// \brief Manages the lifetime of CallEvent objects.
+///
+/// CallEventManager provides a way to create arbitrary CallEvents "on the
+/// stack" as if they were value objects by keeping a cache of CallEvent-sized
+/// memory blocks. The CallEvents created by CallEventManager are only valid
+/// for the lifetime of the OwnedCallEvent that holds them; right now these
+/// objects cannot be copied and ownership cannot be transferred.
+class CallEventManager {
+ friend class CallEvent;
+
+ llvm::BumpPtrAllocator &Alloc;
+ SmallVector<void *, 8> Cache;
+
+ void reclaim(const void *Memory) {
+ Cache.push_back(const_cast<void *>(Memory));
+ }
+
+ /// Returns memory that can be initialized as a CallEvent.
+ void *allocate() {
+ if (Cache.empty())
+ return Alloc.Allocate<FunctionCall>();
+ else
+ return Cache.pop_back_val();
+ }
+
+ template <typename T, typename Arg>
+ T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx) {
+ return new (allocate()) T(A, St, LCtx);
+ }
+
+ template <typename T, typename Arg1, typename Arg2>
+ T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx) {
+ return new (allocate()) T(A1, A2, St, LCtx);
+ }
+
+ template <typename T, typename Arg1, typename Arg2, typename Arg3>
+ T *create(Arg1 A1, Arg2 A2, Arg3 A3, ProgramStateRef St,
+ const LocationContext *LCtx) {
+ return new (allocate()) T(A1, A2, A3, St, LCtx);
+ }
+
+public:
+ CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {}
+
+
+ CallEventRef<>
+ getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State);
+
+
+ CallEventRef<>
+ getSimpleCall(const CallExpr *E, ProgramStateRef State,
+ const LocationContext *LCtx);
+
+ CallEventRef<ObjCMethodCall>
+ getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ return create<ObjCMethodCall>(E, State, LCtx);
+ }
+
+ CallEventRef<CXXConstructorCall>
+ getCXXConstructorCall(const CXXConstructExpr *E, const MemRegion *Target,
+ ProgramStateRef State, const LocationContext *LCtx) {
+ return create<CXXConstructorCall>(E, Target, State, LCtx);
+ }
+
+ CallEventRef<CXXDestructorCall>
+ getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
+ const MemRegion *Target, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ return create<CXXDestructorCall>(DD, Trigger, Target, State, LCtx);
+ }
+
+ CallEventRef<CXXAllocatorCall>
+ getCXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ return create<CXXAllocatorCall>(E, State, LCtx);
+ }
+};
+
+
+template <typename T>
+CallEventRef<T> CallEvent::cloneWithState(ProgramStateRef NewState) const {
+ assert(isa<T>(*this) && "Cloning to unrelated type");
+ assert(sizeof(T) == sizeof(CallEvent) && "Subclasses may not add fields");
+
+ if (NewState == State)
+ return cast<T>(this);
+
+ CallEventManager &Mgr = State->getStateManager().getCallEventManager();
+ T *Copy = static_cast<T *>(Mgr.allocate());
+ cloneTo(Copy);
+ assert(Copy->getKind() == this->getKind() && "Bad copy");
+
+ Copy->State = NewState;
+ return Copy;
+}
+
+inline void CallEvent::Release() const {
+ assert(RefCount > 0 && "Reference count is already zero.");
+ --RefCount;
+
+ if (RefCount > 0)
+ return;
+
+ CallEventManager &Mgr = State->getStateManager().getCallEventManager();
+ Mgr.reclaim(this);
+
+ this->~CallEvent();
+}
+
+} // end namespace ento
+} // end namespace clang
+
+namespace llvm {
+ // Support isa<>, cast<>, and dyn_cast<> for CallEventRef.
+ template<class T> struct simplify_type< clang::ento::CallEventRef<T> > {
+ typedef const T *SimpleType;
+
+ static SimpleType
+ getSimplifiedValue(const clang::ento::CallEventRef<T>& Val) {
+ return Val.getPtr();
+ }
+ };
+}
+
+#endif
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
index b051d33..8c8e82c 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -92,6 +92,10 @@ public:
return Pred->getLocationContext();
}
+ const StackFrameContext *getStackFrame() const {
+ return Pred->getStackFrame();
+ }
+
BugReporter &getBugReporter() {
return Eng.getBugReporter();
}
@@ -132,6 +136,11 @@ public:
return 0;
}
+ /// \brief Get the value of arbitrary expressions at this point in the path.
+ SVal getSVal(const Stmt *S) const {
+ return getState()->getSVal(S, getLocationContext());
+ }
+
/// \brief Generates a new transition in the program state graph
/// (ExplodedGraph). Uses the default CheckerContext predecessor node.
///
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
index 59136fc..e75cdd8 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
@@ -65,7 +65,7 @@ private:
/// WList - A set of queued nodes that need to be processed by the
/// worklist algorithm. It is up to the implementation of WList to decide
/// the order that nodes are processed.
- WorkList* WList;
+ OwningPtr<WorkList> WList;
/// BCounterFactory - A factory object for created BlockCounter objects.
/// These are used to record for key nodes in the ExplodedGraph the
@@ -104,23 +104,18 @@ private:
CoreEngine(const CoreEngine&); // Do not implement.
CoreEngine& operator=(const CoreEngine&);
- ExplodedNode *generateCallExitNode(ExplodedNode *N);
+ ExplodedNode *generateCallExitBeginNode(ExplodedNode *N);
public:
- /// Construct a CoreEngine object to analyze the provided CFG using
- /// a DFS exploration of the exploded graph.
+ /// Construct a CoreEngine object to analyze the provided CFG.
CoreEngine(SubEngine& subengine, SetOfConstDecls *VisitedCallees,
FunctionSummariesTy *FS)
: SubEng(subengine), G(new ExplodedGraph()),
- WList(WorkList::makeBFS()),
+ WList(WorkList::makeDFS()),
BCounterFactory(G->getAllocator()),
AnalyzedCallees(VisitedCallees),
FunctionSummaries(FS){}
- ~CoreEngine() {
- delete WList;
- }
-
/// getGraph - Returns the exploded graph.
ExplodedGraph& getGraph() { return *G.get(); }
@@ -156,7 +151,7 @@ public:
blocksAborted.push_back(std::make_pair(block, node));
}
- WorkList *getWorkList() const { return WList; }
+ WorkList *getWorkList() const { return WList.get(); }
BlocksExhausted::const_iterator blocks_exhausted_begin() const {
return blocksExhausted.begin();
@@ -188,10 +183,10 @@ public:
// TODO: Turn into a calss.
struct NodeBuilderContext {
- CoreEngine &Eng;
+ const CoreEngine &Eng;
const CFGBlock *Block;
ExplodedNode *Pred;
- NodeBuilderContext(CoreEngine &E, const CFGBlock *B, ExplodedNode *N)
+ NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N)
: Eng(E), Block(B), Pred(N) { assert(B); assert(!N->isSink()); }
ExplodedNode *getPred() const { return Pred; }
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
index 46fbb88..1052d94 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
@@ -133,6 +133,10 @@ public:
return getLocation().getLocationContext();
}
+ const StackFrameContext *getStackFrame() const {
+ return getLocationContext()->getCurrentStackFrame();
+ }
+
const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); }
CFG &getCFG() const { return *getLocationContext()->getCFG(); }
@@ -151,7 +155,7 @@ public:
static void Profile(llvm::FoldingSetNodeID &ID,
const ProgramPoint &Loc,
- ProgramStateRef state,
+ const ProgramStateRef &state,
bool IsSink) {
ID.Add(Loc);
ID.AddPointer(state.getPtr());
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 2a21a03..4addb9d 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -41,8 +41,8 @@ class ObjCForCollectionStmt;
namespace ento {
class AnalysisManager;
-class CallOrObjCMessage;
-class ObjCMessage;
+class CallEvent;
+class SimpleCall;
class ExprEngine : public SubEngine {
AnalysisManager &AMgr;
@@ -151,6 +151,25 @@ public:
ExplodedGraph& getGraph() { return G; }
const ExplodedGraph& getGraph() const { return G; }
+ /// \brief Run the analyzer's garbage collection - remove dead symbols and
+ /// bindings.
+ ///
+ /// \param Node - The predecessor node, from which the processing should
+ /// start.
+ /// \param Out - The returned set of output nodes.
+ /// \param ReferenceStmt - Run garbage collection using the symbols,
+ /// which are live before the given statement.
+ /// \param LC - The location context of the ReferenceStmt.
+ /// \param DiagnosticStmt - the statement used to associate the diagnostic
+ /// message, if any warnings should occur while removing the dead (leaks
+ /// are usually reported here).
+ /// \param K - In some cases it is possible to use PreStmt kind. (Do
+ /// not use it unless you know what you are doing.)
+ void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out,
+ const Stmt *ReferenceStmt, const LocationContext *LC,
+ const Stmt *DiagnosticStmt,
+ ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind);
+
/// processCFGElement - Called by CoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a CFG element.
void processCFGElement(const CFGElement E, ExplodedNode *Pred,
@@ -199,7 +218,8 @@ public:
/// Generate the entry node of the callee.
void processCallEnter(CallEnter CE, ExplodedNode *Pred);
- /// Generate the first post callsite node.
+ /// Generate the sequence of nodes that simulate the call exit and the post
+ /// visit for CallExpr.
void processCallExit(ExplodedNode *Pred);
/// Called by CoreEngine when the analysis worklist has terminated.
@@ -220,7 +240,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call);
+ const CallEvent *Call);
/// printState - Called by ProgramStateManager to print checker-specific data.
void printState(raw_ostream &Out, ProgramStateRef State,
@@ -265,6 +285,10 @@ public:
/// VisitAsmStmt - Transfer function logic for inline asm.
void VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst);
+
+ /// VisitMSAsmStmt - Transfer function logic for MS inline asm.
+ void VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst);
/// VisitBlockExpr - Transfer function logic for BlockExprs.
void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
@@ -323,7 +347,7 @@ public:
void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
- void VisitObjCMessage(const ObjCMessage &msg, ExplodedNode *Pred,
+ void VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
/// VisitReturnStmt - Transfer function logic for return statements.
@@ -353,13 +377,10 @@ public:
void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
ExplodedNodeSet & Dst);
- void VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr,
- ExplodedNode *Pred, ExplodedNodeSet &Dst);
-
- void VisitCXXConstructExpr(const CXXConstructExpr *E, const MemRegion *Dest,
- ExplodedNode *Pred, ExplodedNodeSet &Dst);
+ void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst);
- void VisitCXXDestructor(const CXXDestructorDecl *DD,
+ void VisitCXXDestructor(QualType ObjectType,
const MemRegion *Dest, const Stmt *S,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
@@ -373,13 +394,6 @@ public:
void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
-
- /// Synthesize CXXThisRegion.
- const CXXThisRegion *getCXXThisRegion(const CXXRecordDecl *RD,
- const StackFrameContext *SFC);
-
- const CXXThisRegion *getCXXThisRegion(const CXXMethodDecl *decl,
- const StackFrameContext *frameCtx);
/// evalEagerlyAssume - Given the nodes in 'Src', eagerly assume symbolic
/// expressions of the form 'x != 0' and generate new nodes (stored in Dst)
@@ -416,19 +430,6 @@ public:
}
protected:
- void evalObjCMessage(StmtNodeBuilder &Bldr, const ObjCMessage &msg,
- ExplodedNode *Pred, ProgramStateRef state,
- bool GenSink);
-
- ProgramStateRef invalidateArguments(ProgramStateRef State,
- const CallOrObjCMessage &Call,
- const LocationContext *LC);
-
- ProgramStateRef MarkBranch(ProgramStateRef state,
- const Stmt *Terminator,
- const LocationContext *LCtx,
- bool branchTaken);
-
/// evalBind - Handle the semantics of binding a value to a specific location.
/// This method is used by evalStore, VisitDeclStmt, and others.
void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred,
@@ -455,6 +456,21 @@ public:
void evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *StoreE,
ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val,
const ProgramPointTag *tag = 0);
+
+ /// \brief Create a new state in which the call return value is binded to the
+ /// call origin expression.
+ ProgramStateRef bindReturnValue(const CallEvent &Call,
+ const LocationContext *LCtx,
+ ProgramStateRef State);
+
+ /// Evaluate a call, running pre- and post-call checks and allowing checkers
+ /// to be responsible for handling the evaluation of the call itself.
+ void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ const CallEvent &Call);
+
+ /// \brief Default implementation of call evaluation.
+ void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred,
+ const CallEvent &Call);
private:
void evalLoadCommon(ExplodedNodeSet &Dst,
const Expr *NodeEx, /* Eventually will be a CFGStmt */
@@ -474,8 +490,20 @@ private:
ProgramStateRef St, SVal location,
const ProgramPointTag *tag, bool isLoad);
- bool shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred);
- bool InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExplodedNode *Pred);
+ bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred);
+ bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr,
+ ExplodedNode *Pred, ProgramStateRef State);
+
+ /// \brief Conservatively evaluate call by invalidating regions and binding
+ /// a conjured return value.
+ void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
+ ExplodedNode *Pred, ProgramStateRef State);
+
+ /// \brief Either inline or process the call conservatively (or both), based
+ /// on DynamicDispatchBifurcation data.
+ void BifurcateCall(const MemRegion *BifurReg,
+ const CallEvent &Call, const Decl *D, NodeBuilder &Bldr,
+ ExplodedNode *Pred);
bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC);
};
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
index 42adff3..cf4a692 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
@@ -14,15 +14,16 @@
#ifndef LLVM_CLANG_GR_FUNCTIONSUMMARY_H
#define LLVM_CLANG_GR_FUNCTIONSUMMARY_H
+#include <deque>
#include "clang/AST/Decl.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/BitVector.h"
namespace clang {
namespace ento {
-typedef llvm::SmallPtrSet<Decl*, 24> SetOfDecls;
-typedef llvm::SmallPtrSet<const Decl*, 24> SetOfConstDecls;
+typedef std::deque<Decl*> SetOfDecls;
+typedef llvm::DenseSet<const Decl*> SetOfConstDecls;
class FunctionSummariesTy {
struct FunctionSummary {
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
index 87bc0df..01218ef 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
@@ -16,6 +16,7 @@
#ifndef LLVM_CLANG_GR_MEMREGION_H
#define LLVM_CLANG_GR_MEMREGION_H
+#include "clang/AST/ASTContext.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/ExprObjC.h"
@@ -51,11 +52,21 @@ class RegionOffset {
int64_t Offset;
public:
- RegionOffset(const MemRegion *r) : R(r), Offset(0) {}
+ enum { Symbolic = INT64_MAX };
+
+ RegionOffset() : R(0) {}
RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {}
const MemRegion *getRegion() const { return R; }
- int64_t getOffset() const { return Offset; }
+
+ bool hasSymbolicOffset() const { return Offset == Symbolic; }
+
+ int64_t getOffset() const {
+ assert(!hasSymbolicOffset());
+ return Offset;
+ }
+
+ bool isValid() const { return R; }
};
//===----------------------------------------------------------------------===//
@@ -127,7 +138,7 @@ public:
const MemRegion *getBaseRegion() const;
- const MemRegion *StripCasts() const;
+ const MemRegion *StripCasts(bool StripBaseCasts = true) const;
bool hasGlobalsOrParametersStorage() const;
@@ -147,8 +158,11 @@ public:
void dump() const;
+ /// \brief Returns true if this region can be printed in a user-friendly way.
+ virtual bool canPrintPretty() const;
+
/// \brief Print the region for use in diagnostics.
- virtual void dumpPretty(raw_ostream &os) const;
+ virtual void printPretty(raw_ostream &os) const;
Kind getKind() const { return kind; }
@@ -197,8 +211,9 @@ public:
}
};
-/// \class The region of the static variables within the current CodeTextRegion
+/// \brief The region of the static variables within the current CodeTextRegion
/// scope.
+///
/// Currently, only the static locals are placed there, so we know that these
/// variables do not get invalidated by calls to other functions.
class StaticGlobalSpaceRegion : public GlobalsSpaceRegion {
@@ -221,7 +236,7 @@ public:
}
};
-/// \class The region for all the non-static global variables.
+/// \brief The region for all the non-static global variables.
///
/// This class is further split into subclasses for efficient implementation of
/// invalidating a set of related global values as is done in
@@ -236,8 +251,6 @@ protected:
public:
- void dumpToStream(raw_ostream &os) const;
-
static bool classof(const MemRegion *R) {
Kind k = R->getKind();
return k >= BEG_NON_STATIC_GLOBAL_MEMSPACES &&
@@ -245,7 +258,7 @@ public:
}
};
-/// \class The region containing globals which are defined in system/external
+/// \brief The region containing globals which are defined in system/external
/// headers and are considered modifiable by system calls (ex: errno).
class GlobalSystemSpaceRegion : public NonStaticGlobalSpaceRegion {
friend class MemRegionManager;
@@ -262,7 +275,7 @@ public:
}
};
-/// \class The region containing globals which are considered not to be modified
+/// \brief The region containing globals which are considered not to be modified
/// or point to data which could be modified as a result of a function call
/// (system or internal). Ex: Const global scalars would be modeled as part of
/// this region. This region also includes most system globals since they have
@@ -282,7 +295,7 @@ public:
}
};
-/// \class The region containing globals which can be modified by calls to
+/// \brief The region containing globals which can be modified by calls to
/// "internally" defined functions - (for now just) functions other then system
/// calls.
class GlobalInternalSpaceRegion : public NonStaticGlobalSpaceRegion {
@@ -307,6 +320,9 @@ class HeapSpaceRegion : public MemSpaceRegion {
HeapSpaceRegion(MemRegionManager *mgr)
: MemSpaceRegion(mgr, HeapSpaceRegionKind) {}
public:
+
+ void dumpToStream(raw_ostream &os) const;
+
static bool classof(const MemRegion *R) {
return R->getKind() == HeapSpaceRegionKind;
}
@@ -318,6 +334,9 @@ class UnknownSpaceRegion : public MemSpaceRegion {
UnknownSpaceRegion(MemRegionManager *mgr)
: MemSpaceRegion(mgr, UnknownSpaceRegionKind) {}
public:
+
+ void dumpToStream(raw_ostream &os) const;
+
static bool classof(const MemRegion *R) {
return R->getKind() == UnknownSpaceRegionKind;
}
@@ -351,6 +370,9 @@ class StackLocalsSpaceRegion : public StackSpaceRegion {
StackLocalsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc)
: StackSpaceRegion(mgr, StackLocalsSpaceRegionKind, sfc) {}
public:
+
+ void dumpToStream(raw_ostream &os) const;
+
static bool classof(const MemRegion *R) {
return R->getKind() == StackLocalsSpaceRegionKind;
}
@@ -363,6 +385,9 @@ private:
StackArgumentsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc)
: StackSpaceRegion(mgr, StackArgumentsSpaceRegionKind, sfc) {}
public:
+
+ void dumpToStream(raw_ostream &os) const;
+
static bool classof(const MemRegion *R) {
return R->getKind() == StackArgumentsSpaceRegionKind;
}
@@ -478,6 +503,8 @@ public:
return T.getTypePtrOrNull() ? T.getDesugaredType(Context) : T;
}
+ DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const;
+
static bool classof(const MemRegion* R) {
unsigned k = R->getKind();
return k >= BEG_TYPED_VALUE_REGIONS && k <= END_TYPED_VALUE_REGIONS;
@@ -579,25 +606,37 @@ class BlockDataRegion : public SubRegion {
const BlockTextRegion *BC;
const LocationContext *LC; // Can be null */
void *ReferencedVars;
+ void *OriginalVars;
BlockDataRegion(const BlockTextRegion *bc, const LocationContext *lc,
const MemRegion *sreg)
- : SubRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), ReferencedVars(0) {}
+ : SubRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc),
+ ReferencedVars(0), OriginalVars(0) {}
-public:
+public:
const BlockTextRegion *getCodeRegion() const { return BC; }
const BlockDecl *getDecl() const { return BC->getDecl(); }
class referenced_vars_iterator {
const MemRegion * const *R;
+ const MemRegion * const *OriginalR;
public:
- explicit referenced_vars_iterator(const MemRegion * const *r) : R(r) {}
+ explicit referenced_vars_iterator(const MemRegion * const *r,
+ const MemRegion * const *originalR)
+ : R(r), OriginalR(originalR) {}
operator const MemRegion * const *() const {
return R;
}
-
+
+ const MemRegion *getCapturedRegion() const {
+ return *R;
+ }
+ const MemRegion *getOriginalRegion() const {
+ return *OriginalR;
+ }
+
const VarRegion* operator*() const {
return cast<VarRegion>(*R);
}
@@ -610,6 +649,7 @@ public:
}
referenced_vars_iterator &operator++() {
++R;
+ ++OriginalR;
return *this;
}
};
@@ -781,8 +821,6 @@ public:
const Decl *getDecl() const { return D; }
void Profile(llvm::FoldingSetNodeID& ID) const;
- DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const;
-
static bool classof(const MemRegion* R) {
unsigned k = R->getKind();
return k >= BEG_DECL_REGIONS && k <= END_DECL_REGIONS;
@@ -819,7 +857,8 @@ public:
return R->getKind() == VarRegionKind;
}
- void dumpPretty(raw_ostream &os) const;
+ bool canPrintPretty() const;
+ void printPretty(raw_ostream &os) const;
};
/// CXXThisRegion - Represents the region for the implicit 'this' parameter
@@ -878,7 +917,9 @@ public:
}
void dumpToStream(raw_ostream &os) const;
- void dumpPretty(raw_ostream &os) const;
+
+ bool canPrintPretty() const;
+ void printPretty(raw_ostream &os) const;
};
class ObjCIvarRegion : public DeclRegion {
@@ -1106,8 +1147,11 @@ public:
const CXXThisRegion *getCXXThisRegion(QualType thisPointerTy,
const LocationContext *LC);
- /// getSymbolicRegion - Retrieve or create a "symbolic" memory region.
- const SymbolicRegion* getSymbolicRegion(SymbolRef sym);
+ /// \brief Retrieve or create a "symbolic" memory region.
+ const SymbolicRegion* getSymbolicRegion(SymbolRef Sym);
+
+ /// \brief Return a unique symbolic region belonging to heap memory space.
+ const SymbolicRegion *getSymbolicHeapRegion(SymbolRef sym);
const StringRegion *getStringRegion(const StringLiteral* Str);
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
deleted file mode 100644
index d8aec09..0000000
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
+++ /dev/null
@@ -1,293 +0,0 @@
-//===- ObjCMessage.h - Wrapper for ObjC messages and dot syntax ---*- C++ -*--//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines ObjCMessage which serves as a common wrapper for ObjC
-// message expressions or implicit messages for loading/storing ObjC properties.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_OBJCMESSAGE
-#define LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_OBJCMESSAGE
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/AST/ExprObjC.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/Basic/SourceManager.h"
-#include "llvm/ADT/PointerUnion.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Compiler.h"
-
-namespace clang {
-namespace ento {
-using llvm::StrInStrNoCase;
-
-/// \brief Represents both explicit ObjC message expressions and implicit
-/// messages that are sent for handling properties in dot syntax.
-class ObjCMessage {
- const ObjCMessageExpr *Msg;
- const ObjCPropertyRefExpr *PE;
- const bool IsPropSetter;
-public:
- ObjCMessage() : Msg(0), PE(0), IsPropSetter(false) {}
-
- ObjCMessage(const ObjCMessageExpr *E, const ObjCPropertyRefExpr *pe = 0,
- bool isSetter = false)
- : Msg(E), PE(pe), IsPropSetter(isSetter) {
- assert(E && "should not be initialized with null expression");
- }
-
- bool isValid() const { return Msg; }
-
- bool isPureMessageExpr() const { return !PE; }
-
- bool isPropertyGetter() const { return PE && !IsPropSetter; }
-
- bool isPropertySetter() const {
- return IsPropSetter;
- }
-
- const Expr *getMessageExpr() const {
- return Msg;
- }
-
- QualType getType(ASTContext &ctx) const {
- return Msg->getType();
- }
-
- QualType getResultType(ASTContext &ctx) const {
- if (const ObjCMethodDecl *MD = Msg->getMethodDecl())
- return MD->getResultType();
- return getType(ctx);
- }
-
- ObjCMethodFamily getMethodFamily() const {
- return Msg->getMethodFamily();
- }
-
- Selector getSelector() const {
- return Msg->getSelector();
- }
-
- const Expr *getInstanceReceiver() const {
- return Msg->getInstanceReceiver();
- }
-
- SVal getInstanceReceiverSVal(ProgramStateRef State,
- const LocationContext *LC) const {
- if (!isInstanceMessage())
- return UndefinedVal();
- if (const Expr *Ex = getInstanceReceiver())
- return State->getSValAsScalarOrLoc(Ex, LC);
-
- // An instance message with no expression means we are sending to super.
- // In this case the object reference is the same as 'self'.
- const ImplicitParamDecl *SelfDecl = LC->getSelfDecl();
- assert(SelfDecl && "No message receiver Expr, but not in an ObjC method");
- return State->getSVal(State->getRegion(SelfDecl, LC));
- }
-
- bool isInstanceMessage() const {
- return Msg->isInstanceMessage();
- }
-
- const ObjCMethodDecl *getMethodDecl() const {
- return Msg->getMethodDecl();
- }
-
- const ObjCInterfaceDecl *getReceiverInterface() const {
- return Msg->getReceiverInterface();
- }
-
- SourceLocation getSuperLoc() const {
- if (PE)
- return PE->getReceiverLocation();
- return Msg->getSuperLoc();
- }
-
- SourceRange getSourceRange() const LLVM_READONLY {
- if (PE)
- return PE->getSourceRange();
- return Msg->getSourceRange();
- }
-
- unsigned getNumArgs() const {
- return Msg->getNumArgs();
- }
-
- SVal getArgSVal(unsigned i,
- const LocationContext *LCtx,
- ProgramStateRef state) const {
- assert(i < getNumArgs() && "Invalid index for argument");
- return state->getSVal(Msg->getArg(i), LCtx);
- }
-
- QualType getArgType(unsigned i) const {
- assert(i < getNumArgs() && "Invalid index for argument");
- return Msg->getArg(i)->getType();
- }
-
- const Expr *getArgExpr(unsigned i) const {
- assert(i < getNumArgs() && "Invalid index for argument");
- return Msg->getArg(i);
- }
-
- SourceRange getArgSourceRange(unsigned i) const {
- const Expr *argE = getArgExpr(i);
- return argE->getSourceRange();
- }
-
- SourceRange getReceiverSourceRange() const {
- if (PE) {
- if (PE->isObjectReceiver())
- return PE->getBase()->getSourceRange();
- }
- else {
- return Msg->getReceiverRange();
- }
-
- // FIXME: This isn't a range.
- return PE->getReceiverLocation();
- }
-};
-
-/// \brief Common wrapper for a call expression, ObjC message, or C++
-/// constructor, mainly to provide a common interface for their arguments.
-class CallOrObjCMessage {
- llvm::PointerUnion<const CallExpr *, const CXXConstructExpr *> CallE;
- ObjCMessage Msg;
- ProgramStateRef State;
- const LocationContext *LCtx;
-public:
- CallOrObjCMessage(const CallExpr *callE, ProgramStateRef state,
- const LocationContext *lctx)
- : CallE(callE), State(state), LCtx(lctx) {}
- CallOrObjCMessage(const CXXConstructExpr *consE, ProgramStateRef state,
- const LocationContext *lctx)
- : CallE(consE), State(state), LCtx(lctx) {}
- CallOrObjCMessage(const ObjCMessage &msg, ProgramStateRef state,
- const LocationContext *lctx)
- : CallE((CallExpr *)0), Msg(msg), State(state), LCtx(lctx) {}
-
- QualType getResultType(ASTContext &ctx) const;
-
- bool isFunctionCall() const {
- return CallE && CallE.is<const CallExpr *>();
- }
-
- bool isCXXConstructExpr() const {
- return CallE && CallE.is<const CXXConstructExpr *>();
- }
-
- bool isObjCMessage() const {
- return !CallE;
- }
-
- bool isCXXCall() const {
- const CallExpr *ActualCallE = CallE.dyn_cast<const CallExpr *>();
- return ActualCallE && isa<CXXMemberCallExpr>(ActualCallE);
- }
-
- /// Check if the callee is declared in the system header.
- bool isInSystemHeader() const {
- if (const Decl *FD = getDecl()) {
- const SourceManager &SM =
- State->getStateManager().getContext().getSourceManager();
- return SM.isInSystemHeader(FD->getLocation());
- }
- return false;
- }
-
- const Expr *getOriginExpr() const {
- if (!CallE)
- return Msg.getMessageExpr();
- if (const CXXConstructExpr *Ctor =
- CallE.dyn_cast<const CXXConstructExpr *>())
- return Ctor;
- return CallE.get<const CallExpr *>();
- }
-
- SVal getFunctionCallee() const;
- SVal getCXXCallee() const;
- SVal getInstanceMessageReceiver(const LocationContext *LC) const;
-
- /// Get the declaration of the function or method.
- const Decl *getDecl() const;
-
- unsigned getNumArgs() const {
- if (!CallE)
- return Msg.getNumArgs();
- if (const CXXConstructExpr *Ctor =
- CallE.dyn_cast<const CXXConstructExpr *>())
- return Ctor->getNumArgs();
- return CallE.get<const CallExpr *>()->getNumArgs();
- }
-
- SVal getArgSVal(unsigned i) const {
- assert(i < getNumArgs());
- if (!CallE)
- return Msg.getArgSVal(i, LCtx, State);
- return State->getSVal(getArg(i), LCtx);
- }
-
- const Expr *getArg(unsigned i) const {
- assert(i < getNumArgs());
- if (!CallE)
- return Msg.getArgExpr(i);
- if (const CXXConstructExpr *Ctor =
- CallE.dyn_cast<const CXXConstructExpr *>())
- return Ctor->getArg(i);
- return CallE.get<const CallExpr *>()->getArg(i);
- }
-
- SourceRange getArgSourceRange(unsigned i) const {
- assert(i < getNumArgs());
- if (CallE)
- return getArg(i)->getSourceRange();
- return Msg.getArgSourceRange(i);
- }
-
- SourceRange getReceiverSourceRange() const {
- assert(isObjCMessage());
- return Msg.getReceiverSourceRange();
- }
-
- /// \brief Check if the name corresponds to a CoreFoundation or CoreGraphics
- /// function that allows objects to escape.
- ///
- /// Many methods allow a tracked object to escape. For example:
- ///
- /// CFMutableDictionaryRef x = CFDictionaryCreateMutable(..., customDeallocator);
- /// CFDictionaryAddValue(y, key, x);
- ///
- /// We handle this and similar cases with the following heuristic. If the
- /// function name contains "InsertValue", "SetValue", "AddValue",
- /// "AppendValue", or "SetAttribute", then we assume that arguments may
- /// escape.
- //
- // TODO: To reduce false negatives here, we should track the container
- // allocation site and check if a proper deallocator was set there.
- static bool isCFCGAllowingEscape(StringRef FName) {
- if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G'))
- if (StrInStrNoCase(FName, "InsertValue") != StringRef::npos||
- StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
- StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
- StrInStrNoCase(FName, "WithData") != StringRef::npos ||
- StrInStrNoCase(FName, "AppendValue") != StringRef::npos||
- StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) {
- return true;
- }
- return false;
- }
-};
-
-}
-}
-
-#endif
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index 360d648..d36aa1b 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -35,7 +35,8 @@ class ASTContext;
namespace ento {
-class CallOrObjCMessage;
+class CallEvent;
+class CallEventManager;
typedef ConstraintManager* (*ConstraintManagerCreator)(ProgramStateManager&,
SubEngine&);
@@ -49,13 +50,34 @@ template <typename T> struct ProgramStatePartialTrait;
template <typename T> struct ProgramStateTrait {
typedef typename T::data_type data_type;
- static inline void *GDMIndex() { return &T::TagInt; }
static inline void *MakeVoidPtr(data_type D) { return (void*) D; }
static inline data_type MakeData(void *const* P) {
return P ? (data_type) *P : (data_type) 0;
}
};
+/// \class Stores the dynamic type information.
+/// Information about type of an object at runtime. This is used by dynamic
+/// dispatch implementation.
+class DynamicTypeInfo {
+ QualType T;
+ bool CanBeASubClass;
+
+public:
+ DynamicTypeInfo() : T(QualType()) {}
+ DynamicTypeInfo(QualType WithType, bool CanBeSub = true):
+ T(WithType), CanBeASubClass(CanBeSub) {}
+ QualType getType() { return T; }
+ bool canBeASubClass() { return CanBeASubClass; }
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ T.Profile(ID);
+ ID.AddInteger((unsigned)CanBeASubClass);
+ }
+ bool operator==(const DynamicTypeInfo &X) const {
+ return T == X.T && CanBeASubClass == X.CanBeASubClass;
+ }
+};
+
/// \class ProgramState
/// ProgramState - This class encapsulates:
///
@@ -220,12 +242,12 @@ public:
const Expr *E, unsigned BlockCount,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols *IS = 0,
- const CallOrObjCMessage *Call = 0) const;
+ const CallEvent *Call = 0) const;
/// enterStackFrame - Returns the state for entry to the given stack frame,
/// preserving the current state.
- ProgramStateRef enterStackFrame(const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx) const;
+ ProgramStateRef enterStackFrame(const CallEvent &Call,
+ const StackFrameContext *CalleeCtx) const;
/// Get the lvalue for a variable reference.
Loc getLValue(const VarDecl *D, const LocationContext *LC) const;
@@ -239,6 +261,9 @@ public:
/// Get the lvalue for a field reference.
SVal getLValue(const FieldDecl *decl, SVal Base) const;
+ /// Get the lvalue for an indirect field reference.
+ SVal getLValue(const IndirectFieldDecl *decl, SVal Base) const;
+
/// Get the lvalue for an array index.
SVal getLValue(QualType ElementType, SVal Idx, SVal Base) const;
@@ -310,6 +335,20 @@ public:
bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const;
bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const;
+ /// \brief Get dynamic type information for a region.
+ DynamicTypeInfo getDynamicTypeInfo(const MemRegion *Reg) const;
+
+ /// \brief Set dynamic type information of the region; return the new state.
+ ProgramStateRef setDynamicTypeInfo(const MemRegion *Reg,
+ DynamicTypeInfo NewTy) const;
+
+ /// \brief Set dynamic type information of the region; return the new state.
+ ProgramStateRef setDynamicTypeInfo(const MemRegion *Reg,
+ QualType NewTy,
+ bool CanBeSubClassed = true) const {
+ return setDynamicTypeInfo(Reg, DynamicTypeInfo(NewTy, CanBeSubClassed));
+ }
+
//==---------------------------------------------------------------------==//
// Accessing the Generic Data Map (GDM).
//==---------------------------------------------------------------------==//
@@ -382,7 +421,7 @@ private:
const Expr *E, unsigned BlockCount,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
};
//===----------------------------------------------------------------------===//
@@ -412,6 +451,9 @@ private:
/// Object that manages the data for all created SVals.
OwningPtr<SValBuilder> svalBuilder;
+ /// Manages memory for created CallEvents.
+ OwningPtr<CallEventManager> CallEventMgr;
+
/// A BumpPtrAllocator to allocate states.
llvm::BumpPtrAllocator &Alloc;
@@ -423,28 +465,7 @@ public:
StoreManagerCreator CreateStoreManager,
ConstraintManagerCreator CreateConstraintManager,
llvm::BumpPtrAllocator& alloc,
- SubEngine &subeng)
- : Eng(&subeng),
- EnvMgr(alloc),
- GDMFactory(alloc),
- svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
- Alloc(alloc) {
- StoreMgr.reset((*CreateStoreManager)(*this));
- ConstraintMgr.reset((*CreateConstraintManager)(*this, subeng));
- }
-
- ProgramStateManager(ASTContext &Ctx,
- StoreManagerCreator CreateStoreManager,
- ConstraintManager* ConstraintManagerPtr,
- llvm::BumpPtrAllocator& alloc)
- : Eng(0),
- EnvMgr(alloc),
- GDMFactory(alloc),
- svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
- Alloc(alloc) {
- StoreMgr.reset((*CreateStoreManager)(*this));
- ConstraintMgr.reset(ConstraintManagerPtr);
- }
+ SubEngine &subeng);
~ProgramStateManager();
@@ -480,6 +501,8 @@ public:
return svalBuilder->getRegionManager();
}
+ CallEventManager &getCallEventManager() { return *CallEventMgr; }
+
StoreManager& getStoreManager() { return *StoreMgr; }
ConstraintManager& getConstraintManager() { return *ConstraintMgr; }
SubEngine* getOwningEngine() { return Eng; }
@@ -650,6 +673,18 @@ inline SVal ProgramState::getLValue(const FieldDecl *D, SVal Base) const {
return getStateManager().StoreMgr->getLValueField(D, Base);
}
+inline SVal ProgramState::getLValue(const IndirectFieldDecl *D,
+ SVal Base) const {
+ StoreManager &SM = *getStateManager().StoreMgr;
+ for (IndirectFieldDecl::chain_iterator I = D->chain_begin(),
+ E = D->chain_end();
+ I != E; ++I) {
+ Base = SM.getLValueField(cast<FieldDecl>(*I), Base);
+ }
+
+ return Base;
+}
+
inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{
if (NonLoc *N = dyn_cast<NonLoc>(&Idx))
return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base);
@@ -672,7 +707,7 @@ ProgramState::getSValAsScalarOrLoc(const Stmt *S,
const LocationContext *LCtx) const {
if (const Expr *Ex = dyn_cast<Expr>(S)) {
QualType T = Ex->getType();
- if (Ex->isLValue() || Loc::isLocType(T) || T->isIntegerType())
+ if (Ex->isGLValue() || Loc::isLocType(T) || T->isIntegerType())
return getSVal(S, LCtx);
}
@@ -765,14 +800,12 @@ CB ProgramState::scanReachableSymbols(const MemRegion * const *beg,
/// \class ScanReachableSymbols
/// A Utility class that allows to visit the reachable symbols using a custom
/// SymbolVisitor.
-class ScanReachableSymbols : public SubRegionMap::Visitor {
- virtual void anchor();
+class ScanReachableSymbols {
typedef llvm::DenseMap<const void*, unsigned> VisitedItems;
VisitedItems visited;
ProgramStateRef state;
SymbolVisitor &visitor;
- OwningPtr<SubRegionMap> SRM;
public:
ScanReachableSymbols(ProgramStateRef st, SymbolVisitor& v)
@@ -782,11 +815,6 @@ public:
bool scan(SVal val);
bool scan(const MemRegion *R);
bool scan(const SymExpr *sym);
-
- // From SubRegionMap::Visitor.
- bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) {
- return scan(SubRegion);
- }
};
} // end GR namespace
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
index 4ad36f9..83c3a56 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_GR_SVALBUILDER
#define LLVM_CLANG_GR_SVALBUILDER
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
@@ -78,7 +79,7 @@ public:
// FIXME: Remove the second disjunct when we support symbolic
// truncation/extension.
return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) ||
- (Ty2->isIntegerType() && Ty2->isIntegerType()));
+ (Ty1->isIntegerType() && Ty2->isIntegerType()));
}
SVal evalCast(SVal val, QualType castTy, QualType originalType);
@@ -107,12 +108,9 @@ public:
/// that value is returned. Otherwise, returns NULL.
virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal val) = 0;
- /// Handles generation of the value in case the builder is not smart enough to
- /// handle the given binary expression. Depending on the state, decides to
- /// either keep the expression or forget the history and generate an
- /// UnknownVal.
- SVal makeGenericVal(ProgramStateRef state, BinaryOperator::Opcode op,
- NonLoc lhs, NonLoc rhs, QualType resultTy);
+ /// Constructs a symbolic expression for two non-location values.
+ SVal makeSymExprValNN(ProgramStateRef state, BinaryOperator::Opcode op,
+ NonLoc lhs, NonLoc rhs, QualType resultTy);
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type);
@@ -185,6 +183,12 @@ public:
const LocationContext *LCtx,
QualType type,
unsigned visitCount);
+ /// \brief Conjure a symbol representing heap allocated memory region.
+ ///
+ /// Note, the expression should represent a location.
+ DefinedOrUnknownSVal getConjuredHeapSymbolVal(const Expr *E,
+ const LocationContext *LCtx,
+ unsigned Count);
DefinedOrUnknownSVal getDerivedRegionValueSymbolVal(
SymbolRef parentSymbol, const TypedValueRegion *region);
@@ -307,6 +311,13 @@ public:
return loc::ConcreteInt(BasicVals.getValue(integer));
}
+ /// Return a memory region for the 'this' object reference.
+ loc::MemRegionVal getCXXThis(const CXXMethodDecl *D,
+ const StackFrameContext *SFC);
+
+ /// Return a memory region for the 'this' object reference.
+ loc::MemRegionVal getCXXThis(const CXXRecordDecl *D,
+ const StackFrameContext *SFC);
};
SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
index ce3eb1d..e0b5f64 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
@@ -68,7 +68,6 @@ protected:
public:
explicit SVal() : Data(0), Kind(0) {}
- ~SVal() {}
/// BufferTy - A temporary buffer to hold a set of SVals.
typedef SmallVector<SVal,5> BufferTy;
@@ -164,13 +163,10 @@ public:
class UndefinedVal : public SVal {
public:
UndefinedVal() : SVal(UndefinedKind) {}
- UndefinedVal(const void *D) : SVal(UndefinedKind, D) {}
static inline bool classof(const SVal* V) {
return V->getBaseKind() == UndefinedKind;
}
-
- const void *getData() const { return Data; }
};
class DefinedOrUnknownSVal : public SVal {
@@ -326,16 +322,22 @@ class LocAsInteger : public NonLoc {
public:
Loc getLoc() const {
- return cast<Loc>(((std::pair<SVal, uintptr_t>*) Data)->first);
+ const std::pair<SVal, uintptr_t> *D =
+ static_cast<const std::pair<SVal, uintptr_t> *>(Data);
+ return cast<Loc>(D->first);
}
const Loc& getPersistentLoc() const {
- const SVal& V = ((std::pair<SVal, uintptr_t>*) Data)->first;
+ const std::pair<SVal, uintptr_t> *D =
+ static_cast<const std::pair<SVal, uintptr_t> *>(Data);
+ const SVal& V = D->first;
return cast<Loc>(V);
}
unsigned getNumBits() const {
- return ((std::pair<SVal, unsigned>*) Data)->second;
+ const std::pair<SVal, uintptr_t> *D =
+ static_cast<const std::pair<SVal, uintptr_t> *>(Data);
+ return D->second;
}
// Implement isa<T> support.
@@ -401,7 +403,7 @@ public:
namespace loc {
-enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind, ObjCPropRefKind };
+enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind };
class GotoLabel : public Loc {
public:
@@ -431,7 +433,7 @@ public:
}
/// \brief Get the underlining region and strip casts.
- const MemRegion* stripCasts() const;
+ const MemRegion* stripCasts(bool StripBaseCasts = true) const;
template <typename REGION>
const REGION* getRegionAs() const {
@@ -480,28 +482,6 @@ public:
}
};
-/// \brief Pseudo-location SVal used by the ExprEngine to simulate a "load" or
-/// "store" of an ObjC property for the dot syntax.
-class ObjCPropRef : public Loc {
-public:
- explicit ObjCPropRef(const ObjCPropertyRefExpr *E)
- : Loc(ObjCPropRefKind, E) {}
-
- const ObjCPropertyRefExpr *getPropRefExpr() const {
- return static_cast<const ObjCPropertyRefExpr *>(Data);
- }
-
- // Implement isa<T> support.
- static inline bool classof(const SVal* V) {
- return V->getBaseKind() == LocKind &&
- V->getSubKind() == ObjCPropRefKind;
- }
-
- static inline bool classof(const Loc* V) {
- return V->getSubKind() == ObjCPropRefKind;
- }
-};
-
} // end ento::loc namespace
} // end GR namespace
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
index 5315f4b..138a590 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
@@ -29,10 +29,10 @@ class StackFrameContext;
namespace ento {
-class CallOrObjCMessage;
+class CallEvent;
class ProgramState;
class ProgramStateManager;
-class SubRegionMap;
+class ScanReachableSymbols;
class StoreManager {
protected:
@@ -49,7 +49,7 @@ public:
virtual ~StoreManager() {}
/// Return the value bound to specified location in a given state.
- /// \param[in] state The analysis state.
+ /// \param[in] store The analysis state.
/// \param[in] loc The symbolic memory location.
/// \param[in] T An optional type that provides a hint indicating the
/// expected type of the returned value. This is used if the value is
@@ -58,12 +58,12 @@ public:
virtual SVal getBinding(Store store, Loc loc, QualType T = QualType()) = 0;
/// Return a state with the specified value bound to the given location.
- /// \param[in] state The analysis state.
+ /// \param[in] store The analysis state.
/// \param[in] loc The symbolic memory location.
/// \param[in] val The value to bind to location \c loc.
- /// \return A pointer to a ProgramState object that contains the same bindings as
- /// \c state with the addition of having the value specified by \c val bound
- /// to the location given for \c loc.
+ /// \return A pointer to a ProgramState object that contains the same
+ /// bindings as \c state with the addition of having the value specified
+ /// by \c val bound to the location given for \c loc.
virtual StoreRef Bind(Store store, Loc loc, SVal val) = 0;
virtual StoreRef BindDefault(Store store, const MemRegion *R, SVal V);
@@ -85,11 +85,6 @@ public:
/// used to query and manipulate MemRegion objects.
MemRegionManager& getRegionManager() { return MRMgr; }
- /// getSubRegionMap - Returns an opaque map object that clients can query
- /// to get the subregions of a given MemRegion object. It is the
- // caller's responsibility to 'delete' the returned map.
- virtual SubRegionMap *getSubRegionMap(Store store) = 0;
-
virtual Loc getLValueVar(const VarDecl *VD, const LocationContext *LC) {
return svalBuilder.makeLoc(MRMgr.getVarRegion(VD, LC));
}
@@ -120,7 +115,10 @@ public:
virtual SVal ArrayToPointer(Loc Array) = 0;
/// Evaluates DerivedToBase casts.
- virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType) = 0;
+ SVal evalDerivedToBase(SVal derived, const CastExpr *Cast);
+
+ /// Evaluates a derived-to-base cast through a single level of derivation.
+ virtual SVal evalDerivedToBase(SVal derived, QualType derivedPtrType) = 0;
/// \brief Evaluates C++ dynamic_cast cast.
/// The callback may result in the following 3 scenarios:
@@ -133,15 +131,6 @@ public:
virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType,
bool &Failed) = 0;
- class CastResult {
- ProgramStateRef state;
- const MemRegion *region;
- public:
- ProgramStateRef getState() const { return state; }
- const MemRegion* getRegion() const { return region; }
- CastResult(ProgramStateRef s, const MemRegion* r = 0) : state(s), region(r){}
- };
-
const ElementRegion *GetElementZeroRegion(const MemRegion *R, QualType T);
/// castRegion - Used by ExprEngine::VisitCast to handle casts from
@@ -176,8 +165,7 @@ public:
/// invalidate additional regions that may have changed based on accessing
/// the given regions. Optionally, invalidates non-static globals as well.
/// \param[in] store The initial store
- /// \param[in] Begin A pointer to the first region to invalidate.
- /// \param[in] End A pointer just past the last region to invalidate.
+ /// \param[in] Regions The regions to invalidate.
/// \param[in] E The current statement being evaluated. Used to conjure
/// symbols to mark the values of invalidated regions.
/// \param[in] Count The current block count. Used to conjure
@@ -186,7 +174,7 @@ public:
/// accessible. Pass \c NULL if this information will not be used.
/// \param[in] Call The call expression which will be used to determine which
/// globals should get invalidated.
- /// \param[in,out] Regions A vector to fill with any regions being
+ /// \param[in,out] Invalidated A vector to fill with any regions being
/// invalidated. This should include any regions explicitly invalidated
/// even if they do not currently have bindings. Pass \c NULL if this
/// information will not be used.
@@ -195,14 +183,20 @@ public:
const Expr *E, unsigned Count,
const LocationContext *LCtx,
InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call,
+ const CallEvent *Call,
InvalidatedRegions *Invalidated) = 0;
/// enterStackFrame - Let the StoreManager to do something when execution
/// engine is about to execute into a callee.
- virtual StoreRef enterStackFrame(ProgramStateRef state,
- const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx);
+ StoreRef enterStackFrame(Store store,
+ const CallEvent &Call,
+ const StackFrameContext *CalleeCtx);
+
+ /// Finds the transitive closure of symbols within the given region.
+ ///
+ /// Returns false if the visitor aborted the scan.
+ virtual bool scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Visitor) = 0;
virtual void print(Store store, raw_ostream &Out,
const char* nl, const char *sep) = 0;
@@ -275,24 +269,6 @@ inline StoreRef &StoreRef::operator=(StoreRef const &newStore) {
return *this;
}
-// FIXME: Do we still need this?
-/// SubRegionMap - An abstract interface that represents a queryable map
-/// between MemRegion objects and their subregions.
-class SubRegionMap {
- virtual void anchor();
-public:
- virtual ~SubRegionMap() {}
-
- class Visitor {
- virtual void anchor();
- public:
- virtual ~Visitor() {}
- virtual bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) = 0;
- };
-
- virtual bool iterSubRegions(const MemRegion *region, Visitor& V) const = 0;
-};
-
// FIXME: Do we need to pass ProgramStateManager anymore?
StoreManager *CreateRegionStoreManager(ProgramStateManager& StMgr);
StoreManager *CreateFieldsOnlyRegionStoreManager(ProgramStateManager& StMgr);
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
index baf57d4..68b81f1 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
@@ -105,7 +105,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) = 0;
+ const CallEvent *Call) = 0;
inline ProgramStateRef
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
index c7de7ef..5d27f86 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
@@ -94,6 +94,8 @@ public:
return symbol_iterator(this);
}
static symbol_iterator symbol_end() { return symbol_iterator(); }
+
+ unsigned computeComplexity() const;
};
typedef const SymExpr* SymbolRef;
@@ -553,6 +555,7 @@ public:
BasicValueFactory &getBasicVals() { return BV; }
};
+/// \brief A class responsible for cleaning up unused symbols.
class SymbolReaper {
enum SymbolStatus {
NotProcessed,
@@ -569,21 +572,26 @@ class SymbolReaper {
RegionSetTy RegionRoots;
- const LocationContext *LCtx;
+ const StackFrameContext *LCtx;
const Stmt *Loc;
SymbolManager& SymMgr;
StoreRef reapedStore;
llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache;
public:
+ /// \brief Construct a reaper object, which removes everything which is not
+ /// live before we execute statement s in the given location context.
+ ///
+ /// If the statement is NULL, everything is this and parent contexts is
+ /// considered live.
SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr,
StoreManager &storeMgr)
- : LCtx(ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {}
+ : LCtx(ctx->getCurrentStackFrame()), Loc(s), SymMgr(symmgr),
+ reapedStore(0, storeMgr) {}
~SymbolReaper() {}
const LocationContext *getLocationContext() const { return LCtx; }
- const Stmt *getCurrentStatement() const { return Loc; }
bool isLive(SymbolRef sym);
bool isLiveRegion(const MemRegion *region);
diff --git a/include/clang/Tooling/ArgumentsAdjusters.h b/include/clang/Tooling/ArgumentsAdjusters.h
new file mode 100644
index 0000000..492ddd2
--- /dev/null
+++ b/include/clang/Tooling/ArgumentsAdjusters.h
@@ -0,0 +1,59 @@
+//===--- ArgumentsAdjusters.h - Command line arguments adjuster -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares base abstract class ArgumentsAdjuster and its descendants.
+// These classes are intended to modify command line arguments obtained from
+// a compilation database before they are used to run a frontend action.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_ARGUMENTSADJUSTERS_H
+#define LLVM_CLANG_TOOLING_ARGUMENTSADJUSTERS_H
+
+#include <string>
+#include <vector>
+
+namespace clang {
+
+namespace tooling {
+
+/// \brief A sequence of command line arguments.
+typedef std::vector<std::string> CommandLineArguments;
+
+/// \brief Abstract interface for a command line adjusters.
+///
+/// This abstract interface describes a command line argument adjuster,
+/// which is responsible for command line arguments modification before
+/// the arguments are used to run a frontend action.
+class ArgumentsAdjuster {
+ virtual void anchor();
+public:
+ /// \brief Returns adjusted command line arguments.
+ ///
+ /// \param Args Input sequence of command line arguments.
+ ///
+ /// \returns Modified sequence of command line arguments.
+ virtual CommandLineArguments Adjust(const CommandLineArguments &Args) = 0;
+ virtual ~ArgumentsAdjuster() {
+ }
+};
+
+/// \brief Syntax check only command line adjuster.
+///
+/// This class implements ArgumentsAdjuster interface and converts input
+/// command line arguments to the "syntax check only" variant.
+class ClangSyntaxOnlyAdjuster : public ArgumentsAdjuster {
+ virtual CommandLineArguments Adjust(const CommandLineArguments &Args);
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_ARGUMENTSADJUSTERS_H
+
diff --git a/include/clang/Tooling/CommandLineClangTool.h b/include/clang/Tooling/CommandLineClangTool.h
new file mode 100644
index 0000000..c29c302
--- /dev/null
+++ b/include/clang/Tooling/CommandLineClangTool.h
@@ -0,0 +1,80 @@
+//===- CommandLineClangTool.h - command-line clang tools driver -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the CommandLineClangTool class used to run clang
+// tools as separate command-line applications with a consistent common
+// interface for handling compilation database and input files.
+//
+// It provides a common subset of command-line options, common algorithm
+// for locating a compilation database and source files, and help messages
+// for the basic command-line interface.
+//
+// It creates a CompilationDatabase, initializes a ClangTool and runs a
+// user-specified FrontendAction over all TUs in which the given files are
+// compiled.
+//
+// This class uses the Clang Tooling infrastructure, see
+// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+// for details on setting it up with LLVM source tree.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_CLANG_INCLUDE_CLANG_TOOLING_COMMANDLINECLANGTOOL_H
+#define LLVM_TOOLS_CLANG_INCLUDE_CLANG_TOOLING_COMMANDLINECLANGTOOL_H
+
+#include "llvm/Support/CommandLine.h"
+#include "clang/Tooling/CompilationDatabase.h"
+
+namespace clang {
+
+namespace tooling {
+
+class CompilationDatabase;
+class FrontendActionFactory;
+
+/// \brief A common driver for command-line Clang tools.
+///
+/// Parses a common subset of command-line arguments, locates and loads a
+/// compilation commands database, runs a tool with user-specified action. It
+/// also contains a help message for the common command-line options.
+/// An example of usage:
+/// @code
+/// int main(int argc, const char **argv) {
+/// CommandLineClangTool Tool;
+/// cl::extrahelp MoreHelp("\nMore help text...");
+/// Tool.initialize(argc, argv);
+/// return Tool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>());
+/// }
+/// @endcode
+///
+class CommandLineClangTool {
+public:
+ /// Sets up command-line options and help messages.
+ /// Add your own help messages after constructing this tool.
+ CommandLineClangTool();
+
+ /// Parses command-line, initializes a compilation database.
+ /// This method exits program in case of error.
+ void initialize(int argc, const char **argv);
+
+ /// Runs a clang tool with an action created by \c ActionFactory.
+ int run(FrontendActionFactory *ActionFactory);
+
+private:
+ llvm::OwningPtr<CompilationDatabase> Compilations;
+ llvm::cl::opt<std::string> BuildPath;
+ llvm::cl::list<std::string> SourcePaths;
+ llvm::cl::extrahelp MoreHelp;
+};
+
+} // namespace tooling
+
+} // namespace clang
+
+#endif // LLVM_TOOLS_CLANG_INCLUDE_CLANG_TOOLING_COMMANDLINECLANGTOOL_H
diff --git a/include/clang/Tooling/CompilationDatabase.h b/include/clang/Tooling/CompilationDatabase.h
index 625c8ec..f78ffae 100644
--- a/include/clang/Tooling/CompilationDatabase.h
+++ b/include/clang/Tooling/CompilationDatabase.h
@@ -81,6 +81,20 @@ public:
static CompilationDatabase *loadFromDirectory(StringRef BuildDirectory,
std::string &ErrorMessage);
+ /// \brief Tries to detect a compilation database location and load it.
+ ///
+ /// Looks for a compilation database in all parent paths of file 'SourceFile'
+ /// by calling loadFromDirectory.
+ static CompilationDatabase *autoDetectFromSource(StringRef SourceFile,
+ std::string &ErrorMessage);
+
+ /// \brief Tries to detect a compilation database location and load it.
+ ///
+ /// Looks for a compilation database in directory 'SourceDir' and all
+ /// its parent paths by calling loadFromDirectory.
+ static CompilationDatabase *autoDetectFromDirectory(StringRef SourceDir,
+ std::string &ErrorMessage);
+
/// \brief Returns all compile commands in which the specified file was
/// compiled.
///
@@ -92,6 +106,9 @@ public:
/// lines for a.cc and b.cc and only the first command line for t.cc.
virtual std::vector<CompileCommand> getCompileCommands(
StringRef FilePath) const = 0;
+
+ /// \brief Returns the list of all files available in the compilation database.
+ virtual std::vector<std::string> getAllFiles() const = 0;
};
/// \brief A compilation database that returns a single compile command line.
@@ -141,6 +158,11 @@ public:
virtual std::vector<CompileCommand> getCompileCommands(
StringRef FilePath) const;
+ /// \brief Returns the list of all files available in the compilation database.
+ ///
+ /// Note: This is always an empty list for the fixed compilation database.
+ virtual std::vector<std::string> getAllFiles() const;
+
private:
/// This is built up to contain a single entry vector to be returned from
/// getCompileCommands after adding the positional argument.
@@ -187,6 +209,11 @@ public:
virtual std::vector<CompileCommand> getCompileCommands(
StringRef FilePath) const;
+ /// \brief Returns the list of all files available in the compilation database.
+ ///
+ /// These are the 'file' entries of the JSON objects.
+ virtual std::vector<std::string> getAllFiles() const;
+
private:
/// \brief Constructs a JSON compilation database on a memory buffer.
JSONCompilationDatabase(llvm::MemoryBuffer *Database)
@@ -215,4 +242,3 @@ private:
} // end namespace clang
#endif // LLVM_CLANG_TOOLING_COMPILATION_DATABASE_H
-
diff --git a/include/clang/Tooling/Refactoring.h b/include/clang/Tooling/Refactoring.h
new file mode 100644
index 0000000..0e42a0e
--- /dev/null
+++ b/include/clang/Tooling/Refactoring.h
@@ -0,0 +1,151 @@
+//===--- Refactoring.h - Framework for clang refactoring tools --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Interfaces supporting refactorings that span multiple translation units.
+// While single translation unit refactorings are supported via the Rewriter,
+// when refactoring multiple translation units changes must be stored in a
+// SourceManager independent form, duplicate changes need to be removed, and
+// all changes must be applied at once at the end of the refactoring so that
+// the code is always parseable.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTORING_H
+#define LLVM_CLANG_TOOLING_REFACTORING_H
+
+#include "llvm/ADT/StringRef.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Tooling.h"
+#include <set>
+#include <string>
+
+namespace clang {
+
+class Rewriter;
+class SourceLocation;
+
+namespace tooling {
+
+/// \brief A text replacement.
+///
+/// Represents a SourceManager independent replacement of a range of text in a
+/// specific file.
+class Replacement {
+public:
+ /// \brief Creates an invalid (not applicable) replacement.
+ Replacement();
+
+ /// \brief Creates a replacement of the range [Offset, Offset+Length) in
+ /// FilePath with ReplacementText.
+ ///
+ /// \param FilePath A source file accessible via a SourceManager.
+ /// \param Offset The byte offset of the start of the range in the file.
+ /// \param Length The length of the range in bytes.
+ Replacement(llvm::StringRef FilePath, unsigned Offset,
+ unsigned Length, llvm::StringRef ReplacementText);
+
+ /// \brief Creates a Replacement of the range [Start, Start+Length) with
+ /// ReplacementText.
+ Replacement(SourceManager &Sources, SourceLocation Start, unsigned Length,
+ llvm::StringRef ReplacementText);
+
+ /// \brief Creates a Replacement of the given range with ReplacementText.
+ Replacement(SourceManager &Sources, const CharSourceRange &Range,
+ llvm::StringRef ReplacementText);
+
+ /// \brief Creates a Replacement of the node with ReplacementText.
+ template <typename Node>
+ Replacement(SourceManager &Sources, const Node &NodeToReplace,
+ llvm::StringRef ReplacementText);
+
+ /// \brief Returns whether this replacement can be applied to a file.
+ ///
+ /// Only replacements that are in a valid file can be applied.
+ bool isApplicable() const;
+
+ /// \brief Accessors.
+ /// @{
+ StringRef getFilePath() const { return FilePath; }
+ unsigned getOffset() const { return Offset; }
+ unsigned getLength() const { return Length; }
+ /// @}
+
+ /// \brief Applies the replacement on the Rewriter.
+ bool apply(Rewriter &Rewrite) const;
+
+ /// \brief Returns a human readable string representation.
+ std::string toString() const;
+
+ /// \brief Comparator to be able to use Replacement in std::set for uniquing.
+ class Less {
+ public:
+ bool operator()(const Replacement &R1, const Replacement &R2) const;
+ };
+
+ private:
+ void setFromSourceLocation(SourceManager &Sources, SourceLocation Start,
+ unsigned Length, llvm::StringRef ReplacementText);
+ void setFromSourceRange(SourceManager &Sources, const CharSourceRange &Range,
+ llvm::StringRef ReplacementText);
+
+ std::string FilePath;
+ unsigned Offset;
+ unsigned Length;
+ std::string ReplacementText;
+};
+
+/// \brief A set of Replacements.
+/// FIXME: Change to a vector and deduplicate in the RefactoringTool.
+typedef std::set<Replacement, Replacement::Less> Replacements;
+
+/// \brief Apply all replacements on the Rewriter.
+///
+/// If at least one Apply returns false, ApplyAll returns false. Every
+/// Apply will be executed independently of the result of other
+/// Apply operations.
+bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite);
+
+/// \brief A tool to run refactorings.
+///
+/// This is a refactoring specific version of \see ClangTool.
+/// All text replacements added to getReplacements() during the run of the
+/// tool will be applied and saved after all translation units have been
+/// processed.
+class RefactoringTool {
+public:
+ /// \see ClangTool::ClangTool.
+ RefactoringTool(const CompilationDatabase &Compilations,
+ ArrayRef<std::string> SourcePaths);
+
+ /// \brief Returns a set of replacements. All replacements added during the
+ /// run of the tool will be applied after all translation units have been
+ /// processed.
+ Replacements &getReplacements();
+
+ /// \see ClangTool::run.
+ int run(FrontendActionFactory *ActionFactory);
+
+private:
+ ClangTool Tool;
+ Replacements Replace;
+};
+
+template <typename Node>
+Replacement::Replacement(SourceManager &Sources, const Node &NodeToReplace,
+ llvm::StringRef ReplacementText) {
+ const CharSourceRange Range =
+ CharSourceRange::getTokenRange(NodeToReplace->getSourceRange());
+ setFromSourceRange(Sources, Range, ReplacementText);
+}
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // end namespace LLVM_CLANG_TOOLING_REFACTORING_H
+
diff --git a/include/clang/Tooling/RefactoringCallbacks.h b/include/clang/Tooling/RefactoringCallbacks.h
new file mode 100644
index 0000000..c500f35
--- /dev/null
+++ b/include/clang/Tooling/RefactoringCallbacks.h
@@ -0,0 +1,90 @@
+//===--- RefactoringCallbacks.h - Structural query framework ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides callbacks to make common kinds of refactorings easy.
+//
+// The general idea is to construct a matcher expression that describes a
+// subtree match on the AST and then replace the corresponding source code
+// either by some specific text or some other AST node.
+//
+// Example:
+// int main(int argc, char **argv) {
+// ClangTool Tool(argc, argv);
+// MatchFinder Finder;
+// ReplaceStmtWithText Callback("integer", "42");
+// Finder.AddMatcher(id("integer", expression(integerLiteral())), Callback);
+// return Tool.run(newFrontendActionFactory(&Finder));
+// }
+//
+// This will replace all integer literals with "42".
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTORING_CALLBACKS_H
+#define LLVM_CLANG_TOOLING_REFACTORING_CALLBACKS_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+namespace tooling {
+
+/// \brief Base class for RefactoringCallbacks.
+///
+/// Collects \c tooling::Replacements while running.
+class RefactoringCallback : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ RefactoringCallback();
+ Replacements &getReplacements();
+
+protected:
+ Replacements Replace;
+};
+
+/// \brief Replace the text of the statement bound to \c FromId with the text in
+/// \c ToText.
+class ReplaceStmtWithText : public RefactoringCallback {
+public:
+ ReplaceStmtWithText(StringRef FromId, StringRef ToText);
+ virtual void run(const ast_matchers::MatchFinder::MatchResult &Result);
+
+private:
+ std::string FromId;
+ std::string ToText;
+};
+
+/// \brief Replace the text of the statement bound to \c FromId with the text of
+/// the statement bound to \c ToId.
+class ReplaceStmtWithStmt : public RefactoringCallback {
+public:
+ ReplaceStmtWithStmt(StringRef FromId, StringRef ToId);
+ virtual void run(const ast_matchers::MatchFinder::MatchResult &Result);
+
+private:
+ std::string FromId;
+ std::string ToId;
+};
+
+/// \brief Replace an if-statement bound to \c Id with the outdented text of its
+/// body, choosing the consequent or the alternative based on whether
+/// \c PickTrueBranch is true.
+class ReplaceIfStmtWithItsBody : public RefactoringCallback {
+public:
+ ReplaceIfStmtWithItsBody(StringRef Id, bool PickTrueBranch);
+ virtual void run(const ast_matchers::MatchFinder::MatchResult &Result);
+
+private:
+ std::string Id;
+ const bool PickTrueBranch;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTORING_CALLBACKS_H
diff --git a/include/clang/Tooling/Tooling.h b/include/clang/Tooling/Tooling.h
index 868eae3..e06705f 100644
--- a/include/clang/Tooling/Tooling.h
+++ b/include/clang/Tooling/Tooling.h
@@ -15,7 +15,7 @@
// all TUs in which the given files are compiled.
//
// It is also possible to run a FrontendAction over a snippet of code by
-// calling runSyntaxOnlyToolOnCode, which is useful for unit testing.
+// calling runToolOnCode, which is useful for unit testing.
//
// Applications that need more fine grained control over how to run
// multiple FrontendActions over code can use ToolInvocation.
@@ -35,6 +35,9 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LLVM.h"
#include "clang/Driver/Util.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CompilationDatabase.h"
#include <string>
#include <vector>
@@ -50,8 +53,6 @@ class FrontendAction;
namespace tooling {
-class CompilationDatabase;
-
/// \brief Interface to generate clang::FrontendActions.
class FrontendActionFactory {
public:
@@ -74,18 +75,19 @@ template <typename T>
FrontendActionFactory *newFrontendActionFactory();
/// \brief Returns a new FrontendActionFactory for any type that provides an
-/// implementation of newFrontendAction().
+/// implementation of newASTConsumer().
///
-/// FactoryT must implement: FrontendAction *newFrontendAction().
+/// FactoryT must implement: ASTConsumer *newASTConsumer().
///
/// Example:
-/// struct ProvidesFrontendActions {
-/// FrontendAction *newFrontendAction();
+/// struct ProvidesASTConsumers {
+/// clang::ASTConsumer *newASTConsumer();
/// } Factory;
/// FrontendActionFactory *FactoryAdapter =
/// newFrontendActionFactory(&Factory);
template <typename FactoryT>
-FrontendActionFactory *newFrontendActionFactory(FactoryT *ActionFactory);
+inline FrontendActionFactory *newFrontendActionFactory(
+ FactoryT *ConsumerFactory);
/// \brief Runs (and deletes) the tool on 'Code' with the -fsyntax-only flag.
///
@@ -102,7 +104,10 @@ class ToolInvocation {
public:
/// \brief Create a tool invocation.
///
- /// \param CommandLine The command line arguments to clang.
+ /// \param CommandLine The command line arguments to clang. Note that clang
+ /// uses its binary name (CommandLine[0]) to locate its builtin headers.
+ /// Callers have to ensure that they are installed in a compatible location
+ /// (see clang driver implementation) or mapped in via mapVirtualFile.
/// \param ToolAction The action to be executed. Class takes ownership.
/// \param Files The FileManager used for the execution. Class does not take
/// ownership.
@@ -126,8 +131,7 @@ class ToolInvocation {
bool runInvocation(const char *BinaryName,
clang::driver::Compilation *Compilation,
clang::CompilerInvocation *Invocation,
- const clang::driver::ArgStringList &CC1Args,
- clang::FrontendAction *ToolAction);
+ const clang::driver::ArgStringList &CC1Args);
std::vector<std::string> CommandLine;
llvm::OwningPtr<FrontendAction> ToolAction;
@@ -139,6 +143,10 @@ class ToolInvocation {
/// \brief Utility to run a FrontendAction over a set of files.
///
/// This class is written to be usable for command line utilities.
+/// By default the class uses ClangSyntaxOnlyAdjuster to modify
+/// command line arguments before the arguments are used to run
+/// a frontend action. One could install another command line
+/// arguments adjuster by call setArgumentsAdjuster() method.
class ClangTool {
public:
/// \brief Constructs a clang tool to run over a list of files.
@@ -156,6 +164,11 @@ class ClangTool {
/// \param Content A null terminated buffer of the file's content.
void mapVirtualFile(StringRef FilePath, StringRef Content);
+ /// \brief Install command line arguments adjuster.
+ ///
+ /// \param Adjuster Command line arguments adjuster.
+ void setArgumentsAdjuster(ArgumentsAdjuster *Adjuster);
+
/// Runs a frontend action over all files specified in the command line.
///
/// \param ActionFactory Factory generating the frontend actions. The function
@@ -169,13 +182,14 @@ class ClangTool {
FileManager &getFiles() { return Files; }
private:
- // We store command lines as pair (file name, command line).
- typedef std::pair< std::string, std::vector<std::string> > CommandLine;
- std::vector<CommandLine> CommandLines;
+ // We store compile commands as pair (file name, compile command).
+ std::vector< std::pair<std::string, CompileCommand> > CompileCommands;
FileManager Files;
// Contains a list of pairs (<file name>, <file content>).
std::vector< std::pair<StringRef, StringRef> > MappedFileContents;
+
+ llvm::OwningPtr<ArgumentsAdjuster> ArgsAdjuster;
};
template <typename T>
@@ -189,25 +203,54 @@ FrontendActionFactory *newFrontendActionFactory() {
}
template <typename FactoryT>
-FrontendActionFactory *newFrontendActionFactory(FactoryT *ActionFactory) {
+inline FrontendActionFactory *newFrontendActionFactory(
+ FactoryT *ConsumerFactory) {
class FrontendActionFactoryAdapter : public FrontendActionFactory {
public:
- explicit FrontendActionFactoryAdapter(FactoryT *ActionFactory)
- : ActionFactory(ActionFactory) {}
+ explicit FrontendActionFactoryAdapter(FactoryT *ConsumerFactory)
+ : ConsumerFactory(ConsumerFactory) {}
virtual clang::FrontendAction *create() {
- return ActionFactory->newFrontendAction();
+ return new ConsumerFactoryAdaptor(ConsumerFactory);
}
private:
- FactoryT *ActionFactory;
+ class ConsumerFactoryAdaptor : public clang::ASTFrontendAction {
+ public:
+ ConsumerFactoryAdaptor(FactoryT *ConsumerFactory)
+ : ConsumerFactory(ConsumerFactory) {}
+
+ clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &,
+ llvm::StringRef) {
+ return ConsumerFactory->newASTConsumer();
+ }
+
+ private:
+ FactoryT *ConsumerFactory;
+ };
+ FactoryT *ConsumerFactory;
};
- return new FrontendActionFactoryAdapter(ActionFactory);
+ return new FrontendActionFactoryAdapter(ConsumerFactory);
}
+/// \brief Returns the absolute path of \c File, by prepending it with
+/// the current directory if \c File is not absolute.
+///
+/// Otherwise returns \c File.
+/// If 'File' starts with "./", the returned path will not contain the "./".
+/// Otherwise, the returned path will contain the literal path-concatenation of
+/// the current directory and \c File.
+///
+/// The difference to llvm::sys::fs::make_absolute is that we prefer
+/// ::getenv("PWD") if available.
+/// FIXME: Make this functionality available from llvm::sys::fs and delete
+/// this function.
+///
+/// \param File Either an absolute or relative path.
+std::string getAbsolutePath(StringRef File);
+
} // end namespace tooling
} // end namespace clang
#endif // LLVM_CLANG_TOOLING_TOOLING_H
-
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp
index 9354dc3..f291dec 100644
--- a/lib/ARCMigrate/ARCMT.cpp
+++ b/lib/ARCMigrate/ARCMT.cpp
@@ -91,11 +91,40 @@ namespace {
class CaptureDiagnosticConsumer : public DiagnosticConsumer {
DiagnosticsEngine &Diags;
+ DiagnosticConsumer &DiagClient;
CapturedDiagList &CapturedDiags;
+ bool HasBegunSourceFile;
public:
CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
- CapturedDiagList &capturedDiags)
- : Diags(diags), CapturedDiags(capturedDiags) { }
+ DiagnosticConsumer &client,
+ CapturedDiagList &capturedDiags)
+ : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
+ HasBegunSourceFile(false) { }
+
+ virtual void BeginSourceFile(const LangOptions &Opts,
+ const Preprocessor *PP) {
+ // Pass BeginSourceFile message onto DiagClient on first call.
+ // The corresponding EndSourceFile call will be made from an
+ // explicit call to FinishCapture.
+ if (!HasBegunSourceFile) {
+ DiagClient.BeginSourceFile(Opts, PP);
+ HasBegunSourceFile = true;
+ }
+ }
+
+ void FinishCapture() {
+ // Call EndSourceFile on DiagClient on completion of capture to
+ // enable VerifyDiagnosticConsumer to check diagnostics *after*
+ // it has received the diagnostic list.
+ if (HasBegunSourceFile) {
+ DiagClient.EndSourceFile();
+ HasBegunSourceFile = false;
+ }
+ }
+
+ virtual ~CaptureDiagnosticConsumer() {
+ assert(!HasBegunSourceFile && "FinishCapture not called!");
+ }
virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
const Diagnostic &Info) {
@@ -195,8 +224,19 @@ createInvocationForMigration(CompilerInvocation &origCI) {
CInvok->getLangOpts()->ObjCAutoRefCount = true;
CInvok->getLangOpts()->setGC(LangOptions::NonGC);
CInvok->getDiagnosticOpts().ErrorLimit = 0;
- CInvok->getDiagnosticOpts().Warnings.push_back(
- "error=arc-unsafe-retained-assign");
+ CInvok->getDiagnosticOpts().PedanticErrors = 0;
+
+ // Ignore -Werror flags when migrating.
+ std::vector<std::string> WarnOpts;
+ for (std::vector<std::string>::iterator
+ I = CInvok->getDiagnosticOpts().Warnings.begin(),
+ E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
+ if (!StringRef(*I).startswith("error"))
+ WarnOpts.push_back(*I);
+ }
+ WarnOpts.push_back("error=arc-unsafe-retained-assign");
+ CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts);
+
CInvok->getLangOpts()->ObjCRuntimeHasWeak = HasARCRuntime(origCI);
return CInvok.take();
@@ -249,13 +289,15 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
// Filter of all diagnostics.
- CaptureDiagnosticConsumer errRec(*Diags, capturedDiags);
+ CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
OwningPtr<ASTUnit> Unit(
ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
- if (!Unit)
+ if (!Unit) {
+ errRec.FinishCapture();
return true;
+ }
// Don't filter diagnostics anymore.
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
@@ -267,6 +309,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
capturedDiags.reportDiagnostics(*Diags);
DiagClient->EndSourceFile();
+ errRec.FinishCapture();
return true;
}
@@ -304,6 +347,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
capturedDiags.reportDiagnostics(*Diags);
DiagClient->EndSourceFile();
+ errRec.FinishCapture();
// If we are migrating code that gets the '-fobjc-arc' flag, make sure
// to remove it so that we don't get errors from normal compilation.
@@ -480,13 +524,12 @@ public:
class RewritesApplicator : public TransformActions::RewriteReceiver {
Rewriter &rewriter;
- ASTContext &Ctx;
MigrationProcess::RewriteListener *Listener;
public:
RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
MigrationProcess::RewriteListener *listener)
- : rewriter(rewriter), Ctx(ctx), Listener(listener) {
+ : rewriter(rewriter), Listener(listener) {
if (Listener)
Listener->start(ctx);
}
@@ -553,7 +596,7 @@ bool MigrationProcess::applyTransform(TransformFn trans,
new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
// Filter of all diagnostics.
- CaptureDiagnosticConsumer errRec(*Diags, capturedDiags);
+ CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
OwningPtr<ARCMTMacroTrackerAction> ASTAction;
@@ -562,8 +605,10 @@ bool MigrationProcess::applyTransform(TransformFn trans,
OwningPtr<ASTUnit> Unit(
ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
ASTAction.get()));
- if (!Unit)
+ if (!Unit) {
+ errRec.FinishCapture();
return true;
+ }
Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
// Don't filter diagnostics anymore.
@@ -576,6 +621,7 @@ bool MigrationProcess::applyTransform(TransformFn trans,
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
capturedDiags.reportDiagnostics(*Diags);
DiagClient->EndSourceFile();
+ errRec.FinishCapture();
return true;
}
@@ -599,6 +645,7 @@ bool MigrationProcess::applyTransform(TransformFn trans,
}
DiagClient->EndSourceFile();
+ errRec.FinishCapture();
if (DiagClient->getNumErrors())
return true;
diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt
index fcb7f72..f602fc8 100644
--- a/lib/ARCMigrate/CMakeLists.txt
+++ b/lib/ARCMigrate/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend clangRewrite)
-
add_clang_library(clangARCMigrate
ARCMT.cpp
ARCMTActions.cpp
@@ -25,5 +23,19 @@ add_clang_library(clangARCMigrate
add_dependencies(clangARCMigrate
ClangAttrClasses
ClangAttrList
+ ClangAttrParsedAttrList
+ ClangCommentNodes
ClangDeclNodes
- ClangStmtNodes)
+ ClangDiagnosticCommon
+ ClangDiagnosticGroups
+ ClangDiagnosticSema
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangARCMigrate
+ clangBasic
+ clangAST
+ clangParse
+ clangFrontend
+ clangRewrite
+ )
diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp
index 474ce7d..e9b49b3 100644
--- a/lib/ARCMigrate/FileRemapper.cpp
+++ b/lib/ARCMigrate/FileRemapper.cpp
@@ -77,7 +77,9 @@ bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
StringRef fromFilename = lines[idx];
unsigned long long timeModified;
- lines[idx+1].getAsInteger(10, timeModified);
+ if (lines[idx+1].getAsInteger(10, timeModified))
+ return report("Invalid file data: '" + lines[idx+1] + "' not a number",
+ Diag);
StringRef toFilename = lines[idx+2];
const FileEntry *origFE = FileMgr->getFile(fromFilename);
diff --git a/lib/ARCMigrate/Internals.h b/lib/ARCMigrate/Internals.h
index 59177c4..935fc9b 100644
--- a/lib/ARCMigrate/Internals.h
+++ b/lib/ARCMigrate/Internals.h
@@ -12,6 +12,7 @@
#include "clang/ARCMigrate/ARCMT.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
namespace clang {
class Sema;
@@ -144,6 +145,7 @@ public:
Sema &SemaRef;
TransformActions &TA;
std::vector<SourceLocation> &ARCMTMacroLocs;
+ llvm::Optional<bool> EnableCFBridgeFns;
MigrationPass(ASTContext &Ctx, LangOptions::GCMode OrigGCMode,
Sema &sema, TransformActions &TA,
@@ -157,6 +159,8 @@ public:
void setNSAllocReallocError(bool val) { MigOptions.NoNSAllocReallocError = val; }
bool noFinalizeRemoval() const { return MigOptions.NoFinalizeRemoval; }
void setNoFinalizeRemoval(bool val) {MigOptions.NoFinalizeRemoval = val; }
+
+ bool CFBridgingFunctionsDefined();
};
static inline StringRef getARCMTMacroName() {
diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp
index e635274..0098f97 100644
--- a/lib/ARCMigrate/ObjCMT.cpp
+++ b/lib/ARCMigrate/ObjCMT.cpp
@@ -10,6 +10,7 @@
#include "clang/ARCMigrate/ARCMTActions.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/NSAPI.h"
#include "clang/AST/ASTConsumer.h"
@@ -209,6 +210,7 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
}
bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
+ CI.getDiagnostics().setIgnoreAllWarnings(true);
CI.getPreprocessorOpts().DetailedRecord = true;
CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true;
return true;
diff --git a/lib/ARCMigrate/TransAPIUses.cpp b/lib/ARCMigrate/TransAPIUses.cpp
index aaa82d8..5336f85 100644
--- a/lib/ARCMigrate/TransAPIUses.cpp
+++ b/lib/ARCMigrate/TransAPIUses.cpp
@@ -19,6 +19,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
diff --git a/lib/ARCMigrate/TransARCAssign.cpp b/lib/ARCMigrate/TransARCAssign.cpp
index cfa6da1..b83f85a 100644
--- a/lib/ARCMigrate/TransARCAssign.cpp
+++ b/lib/ARCMigrate/TransARCAssign.cpp
@@ -23,6 +23,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
diff --git a/lib/ARCMigrate/TransAutoreleasePool.cpp b/lib/ARCMigrate/TransAutoreleasePool.cpp
index 8787724..5205ce4 100644
--- a/lib/ARCMigrate/TransAutoreleasePool.cpp
+++ b/lib/ARCMigrate/TransAutoreleasePool.cpp
@@ -29,6 +29,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include <map>
@@ -75,7 +76,7 @@ public:
&pass.Ctx.Idents.get("drain"));
}
- void transformBody(Stmt *body) {
+ void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
TraverseStmt(body);
}
diff --git a/lib/ARCMigrate/TransBlockObjCVariable.cpp b/lib/ARCMigrate/TransBlockObjCVariable.cpp
index 3be8132..2a79c9a 100644
--- a/lib/ARCMigrate/TransBlockObjCVariable.cpp
+++ b/lib/ARCMigrate/TransBlockObjCVariable.cpp
@@ -9,7 +9,7 @@
//
// rewriteBlockObjCVariable:
//
-// Adding __block to an obj-c variable could be either because the the variable
+// Adding __block to an obj-c variable could be either because the variable
// is used for output storage or the user wanted to break a retain cycle.
// This transformation checks whether a reference of the variable for the block
// is actually needed (it is assigned to or its address is taken) or not.
@@ -27,6 +27,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
using namespace clang;
@@ -37,7 +38,6 @@ namespace {
class RootBlockObjCVarRewriter :
public RecursiveASTVisitor<RootBlockObjCVarRewriter> {
- MigrationPass &Pass;
llvm::DenseSet<VarDecl *> &VarsToChange;
class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> {
@@ -71,9 +71,8 @@ class RootBlockObjCVarRewriter :
};
public:
- RootBlockObjCVarRewriter(MigrationPass &pass,
- llvm::DenseSet<VarDecl *> &VarsToChange)
- : Pass(pass), VarsToChange(VarsToChange) { }
+ RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
+ : VarsToChange(VarsToChange) { }
bool VisitBlockDecl(BlockDecl *block) {
SmallVector<VarDecl *, 4> BlockVars;
@@ -111,16 +110,14 @@ private:
};
class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> {
- MigrationPass &Pass;
llvm::DenseSet<VarDecl *> &VarsToChange;
public:
- BlockObjCVarRewriter(MigrationPass &pass,
- llvm::DenseSet<VarDecl *> &VarsToChange)
- : Pass(pass), VarsToChange(VarsToChange) { }
+ BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
+ : VarsToChange(VarsToChange) { }
bool TraverseBlockDecl(BlockDecl *block) {
- RootBlockObjCVarRewriter(Pass, VarsToChange).TraverseDecl(block);
+ RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block);
return true;
}
};
@@ -131,7 +128,7 @@ void BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) {
MigrationPass &Pass = BodyCtx.getMigrationContext().Pass;
llvm::DenseSet<VarDecl *> VarsToChange;
- BlockObjCVarRewriter trans(Pass, VarsToChange);
+ BlockObjCVarRewriter trans(VarsToChange);
trans.TraverseStmt(BodyCtx.getTopStmt());
for (llvm::DenseSet<VarDecl *>::iterator
diff --git a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
index 0fb7141..552cb2f 100644
--- a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
+++ b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
@@ -21,6 +21,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceManager.h"
@@ -44,7 +45,7 @@ static bool isEmptyARCMTMacroStatement(NullStmt *S,
SourceManager &SM = Ctx.getSourceManager();
std::vector<SourceLocation>::iterator
I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc,
- SourceManager::LocBeforeThanCompare(SM));
+ BeforeThanCompare<SourceLocation>(SM));
--I;
SourceLocation
AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
@@ -210,8 +211,8 @@ static void cleanupDeallocOrFinalize(MigrationPass &pass) {
ObjCMethodDecl *DeallocM = 0;
ObjCMethodDecl *FinalizeM = 0;
for (ObjCImplementationDecl::instmeth_iterator
- MI = (*I)->instmeth_begin(),
- ME = (*I)->instmeth_end(); MI != ME; ++MI) {
+ MI = I->instmeth_begin(),
+ ME = I->instmeth_end(); MI != ME; ++MI) {
ObjCMethodDecl *MD = *MI;
if (!MD->hasBody())
continue;
diff --git a/lib/ARCMigrate/TransGCAttrs.cpp b/lib/ARCMigrate/TransGCAttrs.cpp
index 9f6066e..eec7306 100644
--- a/lib/ARCMigrate/TransGCAttrs.cpp
+++ b/lib/ARCMigrate/TransGCAttrs.cpp
@@ -9,12 +9,13 @@
#include "Transforms.h"
#include "Internals.h"
-#include "clang/Lex/Lexer.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
-#include "llvm/Support/SaveAndRestore.h"
+#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/TinyPtrVector.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace arcmt;
@@ -136,7 +137,7 @@ public:
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
for (CXXRecordDecl::method_iterator
MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) {
- if ((*MI)->isOutOfLine())
+ if (MI->isOutOfLine())
return true;
}
return false;
@@ -166,7 +167,7 @@ public:
for (Decl::redecl_iterator
I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I)
- if (!isInMainFile((*I)->getLocation()))
+ if (!isInMainFile(I->getLocation()))
return false;
return true;
diff --git a/lib/ARCMigrate/TransGCCalls.cpp b/lib/ARCMigrate/TransGCCalls.cpp
index 1be9020..2ec480c 100644
--- a/lib/ARCMigrate/TransGCCalls.cpp
+++ b/lib/ARCMigrate/TransGCCalls.cpp
@@ -9,6 +9,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
@@ -20,13 +21,12 @@ namespace {
class GCCollectableCallsChecker :
public RecursiveASTVisitor<GCCollectableCallsChecker> {
MigrationContext &MigrateCtx;
- ParentMap &PMap;
IdentifierInfo *NSMakeCollectableII;
IdentifierInfo *CFMakeCollectableII;
public:
- GCCollectableCallsChecker(MigrationContext &ctx, ParentMap &map)
- : MigrateCtx(ctx), PMap(map) {
+ GCCollectableCallsChecker(MigrationContext &ctx)
+ : MigrateCtx(ctx) {
IdentifierTable &Ids = MigrateCtx.Pass.Ctx.Idents;
NSMakeCollectableII = &Ids.get("NSMakeCollectable");
CFMakeCollectableII = &Ids.get("CFMakeCollectable");
@@ -78,7 +78,6 @@ public:
} // anonymous namespace
void GCCollectableCallsTraverser::traverseBody(BodyContext &BodyCtx) {
- GCCollectableCallsChecker(BodyCtx.getMigrationContext(),
- BodyCtx.getParentMap())
+ GCCollectableCallsChecker(BodyCtx.getMigrationContext())
.TraverseStmt(BodyCtx.getTopStmt());
}
diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp
index cc85fe2..fdd6e88 100644
--- a/lib/ARCMigrate/TransProperties.cpp
+++ b/lib/ARCMigrate/TransProperties.cpp
@@ -309,17 +309,8 @@ private:
if (RE->getDecl() != Ivar)
return true;
- if (ObjCMessageExpr *
- ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
- if (ME->getMethodFamily() == OMF_retain)
+ if (isPlusOneAssign(E))
return false;
-
- ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
- while (implCE && implCE->getCastKind() == CK_BitCast)
- implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
-
- if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
- return false;
}
return true;
diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
index 11a6553..91d2b39 100644
--- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
+++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
@@ -19,10 +19,11 @@
#include "Transforms.h"
#include "Internals.h"
-#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
-#include "clang/Lex/Lexer.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
@@ -49,7 +50,7 @@ public:
Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
}
- void transformBody(Stmt *body) {
+ void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
collectRemovables(body, Removables);
StmtMap.reset(new ParentMap(body));
@@ -64,14 +65,16 @@ public:
return true;
case OMF_autorelease:
if (isRemovable(E)) {
- // An unused autorelease is badness. If we remove it the receiver
- // will likely die immediately while previously it was kept alive
- // by the autorelease pool. This is bad practice in general, leave it
- // and emit an error to force the user to restructure his code.
- Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
- "message; its receiver may be destroyed immediately",
- E->getLocStart(), E->getSourceRange());
- return true;
+ if (!isCommonUnusedAutorelease(E)) {
+ // An unused autorelease is badness. If we remove it the receiver
+ // will likely die immediately while previously it was kept alive
+ // by the autorelease pool. This is bad practice in general, leave it
+ // and emit an error to force the user to restructure his code.
+ Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
+ "message; its receiver may be destroyed immediately",
+ E->getLocStart(), E->getSourceRange());
+ return true;
+ }
}
// Pass through.
case OMF_retain:
@@ -156,6 +159,80 @@ public:
}
private:
+ /// \brief Checks for idioms where an unused -autorelease is common.
+ ///
+ /// Currently only returns true for this idiom which is common in property
+ /// setters:
+ ///
+ /// [backingValue autorelease];
+ /// backingValue = [newValue retain]; // in general a +1 assign
+ ///
+ bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
+ Expr *Rec = E->getInstanceReceiver();
+ if (!Rec)
+ return false;
+
+ Decl *RefD = getReferencedDecl(Rec);
+ if (!RefD)
+ return false;
+
+ Stmt *OuterS = E, *InnerS;
+ do {
+ InnerS = OuterS;
+ OuterS = StmtMap->getParent(InnerS);
+ }
+ while (OuterS && (isa<ParenExpr>(OuterS) ||
+ isa<CastExpr>(OuterS) ||
+ isa<ExprWithCleanups>(OuterS)));
+
+ if (!OuterS)
+ return false;
+
+ // Find next statement after the -autorelease.
+
+ Stmt::child_iterator currChildS = OuterS->child_begin();
+ Stmt::child_iterator childE = OuterS->child_end();
+ for (; currChildS != childE; ++currChildS) {
+ if (*currChildS == InnerS)
+ break;
+ }
+ if (currChildS == childE)
+ return false;
+ ++currChildS;
+ if (currChildS == childE)
+ return false;
+
+ Stmt *nextStmt = *currChildS;
+ if (!nextStmt)
+ return false;
+ nextStmt = nextStmt->IgnoreImplicit();
+
+ // Check for "RefD = [+1 retained object];".
+
+ if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) {
+ if (RefD != getReferencedDecl(Bop->getLHS()))
+ return false;
+ if (isPlusOneAssign(Bop))
+ return true;
+ }
+ return false;
+ }
+
+ Decl *getReferencedDecl(Expr *E) {
+ if (!E)
+ return 0;
+
+ E = E->IgnoreParenCasts();
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ return DRE->getDecl();
+ if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
+ return ME->getMemberDecl();
+ if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))
+ return IRE->getDecl();
+
+ return 0;
+ }
+
/// \brief Check if the retain/release is due to a GCD/XPC macro that are
/// defined as:
///
diff --git a/lib/ARCMigrate/TransUnbridgedCasts.cpp b/lib/ARCMigrate/TransUnbridgedCasts.cpp
index 48437c7..ac18b5d 100644
--- a/lib/ARCMigrate/TransUnbridgedCasts.cpp
+++ b/lib/ARCMigrate/TransUnbridgedCasts.cpp
@@ -12,7 +12,7 @@
// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer
// is from a file-level variable, __bridge cast is used to convert it.
// For the result of a function call that we know is +1/+0,
-// __bridge/__bridge_transfer is used.
+// __bridge/CFBridgingRelease is used.
//
// NSString *str = (NSString *)kUTTypePlainText;
// str = b ? kUTTypeRTF : kUTTypePlainText;
@@ -21,8 +21,8 @@
// ---->
// NSString *str = (__bridge NSString *)kUTTypePlainText;
// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
-// NSString *_uuidString = (__bridge_transfer NSString *)
-// CFUUIDCreateString(kCFAllocatorDefault, _uuid);
+// NSString *_uuidString = (NSString *)
+// CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
//
// For a C pointer to ObjC, for casting 'self', __bridge is used.
//
@@ -35,9 +35,11 @@
#include "Transforms.h"
#include "Internals.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
-#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
@@ -50,13 +52,15 @@ class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
MigrationPass &Pass;
IdentifierInfo *SelfII;
OwningPtr<ParentMap> StmtMap;
+ Decl *ParentD;
public:
- UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) {
+ UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass), ParentD(0) {
SelfII = &Pass.Ctx.Idents.get("self");
}
- void transformBody(Stmt *body) {
+ void transformBody(Stmt *body, Decl *ParentD) {
+ this->ParentD = ParentD;
StmtMap.reset(new ParentMap(body));
TraverseStmt(body);
}
@@ -155,6 +159,21 @@ private:
}
}
}
+
+ // If returning an ivar or a member of an ivar from a +0 method, use
+ // a __bridge cast.
+ Expr *base = inner->IgnoreParenImpCasts();
+ while (isa<MemberExpr>(base))
+ base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts();
+ if (isa<ObjCIvarRefExpr>(base) &&
+ isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) {
+ if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) {
+ if (!method->hasAttr<NSReturnsRetainedAttr>()) {
+ castToObjCObject(E, /*retained=*/false);
+ return;
+ }
+ }
+ }
}
void castToObjCObject(CastExpr *E, bool retained) {
@@ -191,22 +210,48 @@ private:
TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
E->getLocStart());
- if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) {
- TA.insertAfterToken(CCE->getLParenLoc(), bridge);
- } else {
- SourceLocation insertLoc = E->getSubExpr()->getLocStart();
- SmallString<128> newCast;
- newCast += '(';
- newCast += bridge;
- newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());
- newCast += ')';
-
- if (isa<ParenExpr>(E->getSubExpr())) {
- TA.insert(insertLoc, newCast.str());
+ if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) {
+ if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) {
+ TA.insertAfterToken(CCE->getLParenLoc(), bridge);
} else {
+ SourceLocation insertLoc = E->getSubExpr()->getLocStart();
+ SmallString<128> newCast;
newCast += '(';
- TA.insert(insertLoc, newCast.str());
- TA.insertAfterToken(E->getLocEnd(), ")");
+ newCast += bridge;
+ newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());
+ newCast += ')';
+
+ if (isa<ParenExpr>(E->getSubExpr())) {
+ TA.insert(insertLoc, newCast.str());
+ } else {
+ newCast += '(';
+ TA.insert(insertLoc, newCast.str());
+ TA.insertAfterToken(E->getLocEnd(), ")");
+ }
+ }
+ } else {
+ assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained);
+ SmallString<32> BridgeCall;
+
+ Expr *WrapE = E->getSubExpr();
+ SourceLocation InsertLoc = WrapE->getLocStart();
+
+ SourceManager &SM = Pass.Ctx.getSourceManager();
+ char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1));
+ if (Lexer::isIdentifierBodyChar(PrevChar, Pass.Ctx.getLangOpts()))
+ BridgeCall += ' ';
+
+ if (Kind == OBC_BridgeTransfer)
+ BridgeCall += "CFBridgingRelease";
+ else
+ BridgeCall += "CFBridgingRetain";
+
+ if (isa<ParenExpr>(WrapE)) {
+ TA.insert(InsertLoc, BridgeCall);
+ } else {
+ BridgeCall += '(';
+ TA.insert(InsertLoc, BridgeCall);
+ TA.insertAfterToken(WrapE->getLocEnd(), ")");
}
}
}
diff --git a/lib/ARCMigrate/TransUnusedInitDelegate.cpp b/lib/ARCMigrate/TransUnusedInitDelegate.cpp
index 60ed32a..3057e39 100644
--- a/lib/ARCMigrate/TransUnusedInitDelegate.cpp
+++ b/lib/ARCMigrate/TransUnusedInitDelegate.cpp
@@ -22,6 +22,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
@@ -40,7 +41,7 @@ public:
UnusedInitRewriter(MigrationPass &pass)
: Body(0), Pass(pass) { }
- void transformBody(Stmt *body) {
+ void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
collectRemovables(body, Removables);
TraverseStmt(body);
diff --git a/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp b/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp
index d1f08aa..a07596d 100644
--- a/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp
+++ b/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp
@@ -15,6 +15,7 @@
#include "Transforms.h"
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
using namespace clang;
using namespace arcmt;
diff --git a/lib/ARCMigrate/TransformActions.cpp b/lib/ARCMigrate/TransformActions.cpp
index 0ecfeb5..783db1c 100644
--- a/lib/ARCMigrate/TransformActions.cpp
+++ b/lib/ARCMigrate/TransformActions.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "Internals.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/SourceManager.h"
diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp
index d342d1a..1175c36 100644
--- a/lib/ARCMigrate/Transforms.cpp
+++ b/lib/ARCMigrate/Transforms.cpp
@@ -9,11 +9,14 @@
#include "Transforms.h"
#include "Internals.h"
-#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtVisitor.h"
-#include "clang/Lex/Lexer.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/DenseSet.h"
#include <map>
@@ -24,6 +27,13 @@ using namespace trans;
ASTTraverser::~ASTTraverser() { }
+bool MigrationPass::CFBridgingFunctionsDefined() {
+ if (!EnableCFBridgeFns.hasValue())
+ EnableCFBridgeFns = SemaRef.isKnownName("CFBridgingRetain") &&
+ SemaRef.isKnownName("CFBridgingRelease");
+ return *EnableCFBridgeFns;
+}
+
//===----------------------------------------------------------------------===//
// Helpers.
//===----------------------------------------------------------------------===//
@@ -56,6 +66,47 @@ bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
return true;
}
+bool trans::isPlusOneAssign(const BinaryOperator *E) {
+ if (E->getOpcode() != BO_Assign)
+ return false;
+
+ if (const ObjCMessageExpr *
+ ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
+ if (ME->getMethodFamily() == OMF_retain)
+ return true;
+
+ if (const CallExpr *
+ callE = dyn_cast<CallExpr>(E->getRHS()->IgnoreParenCasts())) {
+ if (const FunctionDecl *FD = callE->getDirectCallee()) {
+ if (FD->getAttr<CFReturnsRetainedAttr>())
+ return true;
+
+ if (FD->isGlobal() &&
+ FD->getIdentifier() &&
+ FD->getParent()->isTranslationUnit() &&
+ FD->getLinkage() == ExternalLinkage &&
+ ento::cocoa::isRefType(callE->getType(), "CF",
+ FD->getIdentifier()->getName())) {
+ StringRef fname = FD->getIdentifier()->getName();
+ if (fname.endswith("Retain") ||
+ fname.find("Create") != StringRef::npos ||
+ fname.find("Copy") != StringRef::npos) {
+ return true;
+ }
+ }
+ }
+ }
+
+ const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
+ while (implCE && implCE->getCastKind() == CK_BitCast)
+ implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
+
+ if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
+ return true;
+
+ return false;
+}
+
/// \brief 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
@@ -472,8 +523,8 @@ static void GCRewriteFinalize(MigrationPass &pass) {
for (impl_iterator I = impl_iterator(DC->decls_begin()),
E = impl_iterator(DC->decls_end()); I != E; ++I) {
for (ObjCImplementationDecl::instmeth_iterator
- MI = (*I)->instmeth_begin(),
- ME = (*I)->instmeth_end(); MI != ME; ++MI) {
+ MI = I->instmeth_begin(),
+ ME = I->instmeth_end(); MI != ME; ++MI) {
ObjCMethodDecl *MD = *MI;
if (!MD->hasBody())
continue;
diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h
index 445c3e5..5d4ac94 100644
--- a/lib/ARCMigrate/Transforms.h
+++ b/lib/ARCMigrate/Transforms.h
@@ -13,6 +13,7 @@
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/ParentMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/SaveAndRestore.h"
namespace clang {
class Decl;
@@ -154,6 +155,8 @@ public:
bool canApplyWeak(ASTContext &Ctx, QualType type,
bool AllowOnUnknownClass = false);
+bool isPlusOneAssign(const BinaryOperator *E);
+
/// \brief 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
@@ -174,15 +177,22 @@ StringRef getNilString(ASTContext &Ctx);
template <typename BODY_TRANS>
class BodyTransform : public RecursiveASTVisitor<BodyTransform<BODY_TRANS> > {
MigrationPass &Pass;
+ Decl *ParentD;
+ typedef RecursiveASTVisitor<BodyTransform<BODY_TRANS> > base;
public:
- BodyTransform(MigrationPass &pass) : Pass(pass) { }
+ BodyTransform(MigrationPass &pass) : Pass(pass), ParentD(0) { }
bool TraverseStmt(Stmt *rootS) {
if (rootS)
- BODY_TRANS(Pass).transformBody(rootS);
+ BODY_TRANS(Pass).transformBody(rootS, ParentD);
return true;
}
+
+ bool TraverseObjCMethodDecl(ObjCMethodDecl *D) {
+ SaveAndRestore<Decl *> SetParent(ParentD, D);
+ return base::TraverseObjCMethodDecl(D);
+ }
};
typedef llvm::DenseSet<Expr *> ExprSet;
diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp
index a31b3c5..a74ef14 100644
--- a/lib/AST/APValue.cpp
+++ b/lib/AST/APValue.cpp
@@ -467,9 +467,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{
FI != RD->field_end(); ++FI) {
if (!First)
Out << ", ";
- if ((*FI)->isUnnamedBitfield()) continue;
- getStructField((*FI)->getFieldIndex()).
- printPretty(Out, Ctx, (*FI)->getType());
+ if (FI->isUnnamedBitfield()) continue;
+ getStructField(FI->getFieldIndex()).
+ printPretty(Out, Ctx, FI->getType());
First = false;
}
Out << '}';
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index cb4d336..ad48dff 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/CommentCommandTraits.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@@ -53,6 +54,218 @@ enum FloatingRank {
HalfRank, FloatRank, DoubleRank, LongDoubleRank
};
+RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
+ if (!CommentsLoaded && ExternalSource) {
+ ExternalSource->ReadComments();
+ CommentsLoaded = true;
+ }
+
+ assert(D);
+
+ // User can not attach documentation to implicit declarations.
+ if (D->isImplicit())
+ return NULL;
+
+ // TODO: handle comments for function parameters properly.
+ if (isa<ParmVarDecl>(D))
+ return NULL;
+
+ // TODO: we could look up template parameter documentation in the template
+ // documentation.
+ if (isa<TemplateTypeParmDecl>(D) ||
+ isa<NonTypeTemplateParmDecl>(D) ||
+ isa<TemplateTemplateParmDecl>(D))
+ return NULL;
+
+ ArrayRef<RawComment *> RawComments = Comments.getComments();
+
+ // If there are no comments anywhere, we won't find anything.
+ if (RawComments.empty())
+ return NULL;
+
+ // Find declaration location.
+ // For Objective-C declarations we generally don't expect to have multiple
+ // declarators, thus use declaration starting location as the "declaration
+ // location".
+ // For all other declarations multiple declarators are used quite frequently,
+ // so we use the location of the identifier as the "declaration location".
+ SourceLocation DeclLoc;
+ if (isa<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) ||
+ isa<ObjCPropertyDecl>(D) ||
+ isa<RedeclarableTemplateDecl>(D) ||
+ isa<ClassTemplateSpecializationDecl>(D))
+ DeclLoc = D->getLocStart();
+ else
+ DeclLoc = D->getLocation();
+
+ // If the declaration doesn't map directly to a location in a file, we
+ // can't find the comment.
+ if (DeclLoc.isInvalid() || !DeclLoc.isFileID())
+ return NULL;
+
+ // Find the comment that occurs just after this declaration.
+ ArrayRef<RawComment *>::iterator Comment;
+ {
+ // When searching for comments during parsing, the comment we are looking
+ // for is usually among the last two comments we parsed -- check them
+ // first.
+ RawComment CommentAtDeclLoc(SourceMgr, SourceRange(DeclLoc));
+ BeforeThanCompare<RawComment> Compare(SourceMgr);
+ ArrayRef<RawComment *>::iterator MaybeBeforeDecl = RawComments.end() - 1;
+ bool Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc);
+ if (!Found && RawComments.size() >= 2) {
+ MaybeBeforeDecl--;
+ Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc);
+ }
+
+ if (Found) {
+ Comment = MaybeBeforeDecl + 1;
+ assert(Comment == std::lower_bound(RawComments.begin(), RawComments.end(),
+ &CommentAtDeclLoc, Compare));
+ } else {
+ // Slow path.
+ Comment = std::lower_bound(RawComments.begin(), RawComments.end(),
+ &CommentAtDeclLoc, Compare);
+ }
+ }
+
+ // Decompose the location for the declaration and find the beginning of the
+ // file buffer.
+ std::pair<FileID, unsigned> DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc);
+
+ // First check whether we have a trailing comment.
+ if (Comment != RawComments.end() &&
+ (*Comment)->isDocumentation() && (*Comment)->isTrailingComment() &&
+ (isa<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(D))) {
+ std::pair<FileID, unsigned> CommentBeginDecomp
+ = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getBegin());
+ // Check that Doxygen trailing comment comes after the declaration, starts
+ // on the same line and in the same file as the declaration.
+ if (DeclLocDecomp.first == CommentBeginDecomp.first &&
+ SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second)
+ == SourceMgr.getLineNumber(CommentBeginDecomp.first,
+ CommentBeginDecomp.second)) {
+ (*Comment)->setDecl(D);
+ return *Comment;
+ }
+ }
+
+ // The comment just after the declaration was not a trailing comment.
+ // Let's look at the previous comment.
+ if (Comment == RawComments.begin())
+ return NULL;
+ --Comment;
+
+ // Check that we actually have a non-member Doxygen comment.
+ if (!(*Comment)->isDocumentation() || (*Comment)->isTrailingComment())
+ return NULL;
+
+ // Decompose the end of the comment.
+ std::pair<FileID, unsigned> CommentEndDecomp
+ = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getEnd());
+
+ // If the comment and the declaration aren't in the same file, then they
+ // aren't related.
+ if (DeclLocDecomp.first != CommentEndDecomp.first)
+ return NULL;
+
+ // Get the corresponding buffer.
+ bool Invalid = false;
+ const char *Buffer = SourceMgr.getBufferData(DeclLocDecomp.first,
+ &Invalid).data();
+ if (Invalid)
+ return NULL;
+
+ // Extract text between the comment and declaration.
+ StringRef Text(Buffer + CommentEndDecomp.second,
+ DeclLocDecomp.second - CommentEndDecomp.second);
+
+ // There should be no other declarations or preprocessor directives between
+ // comment and declaration.
+ if (Text.find_first_of(",;{}#@") != StringRef::npos)
+ return NULL;
+
+ (*Comment)->setDecl(D);
+ return *Comment;
+}
+
+const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const {
+ // If we have a 'templated' declaration for a template, adjust 'D' to
+ // refer to the actual template.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate())
+ D = FTD;
+ } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
+ if (const ClassTemplateDecl *CTD = RD->getDescribedClassTemplate())
+ D = CTD;
+ }
+ // FIXME: Alias templates?
+
+ // Check whether we have cached a comment for this declaration already.
+ {
+ llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
+ RedeclComments.find(D);
+ if (Pos != RedeclComments.end()) {
+ const RawCommentAndCacheFlags &Raw = Pos->second;
+ if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl)
+ return Raw.getRaw();
+ }
+ }
+
+ // Search for comments attached to declarations in the redeclaration chain.
+ const RawComment *RC = NULL;
+ for (Decl::redecl_iterator I = D->redecls_begin(),
+ E = D->redecls_end();
+ I != E; ++I) {
+ llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
+ RedeclComments.find(*I);
+ if (Pos != RedeclComments.end()) {
+ const RawCommentAndCacheFlags &Raw = Pos->second;
+ if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) {
+ RC = Raw.getRaw();
+ break;
+ }
+ } else {
+ RC = getRawCommentForDeclNoCache(*I);
+ RawCommentAndCacheFlags Raw;
+ if (RC) {
+ Raw.setRaw(RC);
+ Raw.setKind(RawCommentAndCacheFlags::FromDecl);
+ } else
+ Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl);
+ RedeclComments[*I] = Raw;
+ if (RC)
+ break;
+ }
+ }
+
+ // If we found a comment, it should be a documentation comment.
+ assert(!RC || RC->isDocumentation());
+
+ // Update cache for every declaration in the redeclaration chain.
+ RawCommentAndCacheFlags Raw;
+ Raw.setRaw(RC);
+ Raw.setKind(RawCommentAndCacheFlags::FromRedecl);
+
+ for (Decl::redecl_iterator I = D->redecls_begin(),
+ E = D->redecls_end();
+ I != E; ++I) {
+ RawCommentAndCacheFlags &R = RedeclComments[*I];
+ if (R.getKind() == RawCommentAndCacheFlags::NoCommentInDecl)
+ R = Raw;
+ }
+
+ return RC;
+}
+
+comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const {
+ const RawComment *RC = getRawCommentForAnyRedecl(D);
+ if (!RC)
+ return NULL;
+
+ return RC->getParsed(*this);
+}
+
void
ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
TemplateTemplateParmDecl *Parm) {
@@ -206,7 +419,10 @@ static const LangAS::Map *getAddressSpaceMap(const TargetInfo &T,
static const unsigned FakeAddrSpaceMap[] = {
1, // opencl_global
2, // opencl_local
- 3 // opencl_constant
+ 3, // opencl_constant
+ 4, // cuda_device
+ 5, // cuda_constant
+ 6 // cuda_shared
};
return &FakeAddrSpaceMap;
} else {
@@ -226,6 +442,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM,
SubstTemplateTemplateParmPacks(this_()),
GlobalNestedNameSpecifier(0),
Int128Decl(0), UInt128Decl(0),
+ BuiltinVaListDecl(0),
ObjCIdDecl(0), ObjCSelDecl(0), ObjCClassDecl(0), ObjCProtocolClassDecl(0),
CFConstantStringTypeDecl(0), ObjCInstanceTypeDecl(0),
FILEDecl(0),
@@ -240,6 +457,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM,
BuiltinInfo(builtins),
DeclarationNames(*this),
ExternalSource(0), Listener(0),
+ Comments(SM), CommentsLoaded(false),
LastSDM(0, 0),
UniqueBlockByRefTypeID(0)
{
@@ -436,6 +654,8 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) {
} else // C99
WCharTy = getFromTargetType(Target.getWCharType());
+ WIntTy = getFromTargetType(Target.getWIntType());
+
if (LangOpts.CPlusPlus) // C++0x 3.9.1p5, extension for C++
InitBuiltinType(Char16Ty, BuiltinType::Char16);
else // C99
@@ -473,8 +693,6 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) {
DoubleComplexTy = getComplexType(DoubleTy);
LongDoubleComplexTy = getComplexType(LongDoubleTy);
- BuiltinVaListType = QualType();
-
// Builtin types for 'id', 'Class', and 'SEL'.
InitBuiltinType(ObjCBuiltinIdTy, BuiltinType::ObjCId);
InitBuiltinType(ObjCBuiltinClassTy, BuiltinType::ObjCClass);
@@ -494,6 +712,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) {
// half type (OpenCL 6.1.1.1) / ARM NEON __fp16
InitBuiltinType(HalfTy, BuiltinType::Half);
+
+ // Builtin type used to help define __builtin_va_list.
+ VaListTagTy = QualType();
}
DiagnosticsEngine &ASTContext::getDiagnostics() const {
@@ -881,6 +1102,10 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
Align = llvm::NextPowerOf2(Align);
Width = llvm::RoundUpToAlignment(Width, Align);
}
+ // Adjust the alignment based on the target max.
+ uint64_t TargetVectorAlign = Target->getMaxVectorAlign();
+ if (TargetVectorAlign && TargetVectorAlign < Align)
+ Align = TargetVectorAlign;
break;
}
@@ -1337,14 +1562,6 @@ void ASTContext::setBlockVarCopyInits(VarDecl*VD, Expr* Init) {
BlockVarCopyInits[VD] = Init;
}
-/// \brief Allocate an uninitialized TypeSourceInfo.
-///
-/// The caller should initialize the memory held by TypeSourceInfo using
-/// the TypeLoc wrappers.
-///
-/// \param T the type that will be the basis for type source info. This type
-/// should refer to how the declarator was written in source code, not to
-/// what type semantic analysis resolved the declarator to.
TypeSourceInfo *ASTContext::CreateTypeSourceInfo(QualType T,
unsigned DataSize) const {
if (!DataSize)
@@ -2187,15 +2404,18 @@ ASTContext::getFunctionType(QualType ResultTy,
// - exception types
// - consumed-arguments flags
// Instead of the exception types, there could be a noexcept
- // expression.
+ // expression, or information used to resolve the exception
+ // specification.
size_t Size = sizeof(FunctionProtoType) +
NumArgs * sizeof(QualType);
- if (EPI.ExceptionSpecType == EST_Dynamic)
+ if (EPI.ExceptionSpecType == EST_Dynamic) {
Size += EPI.NumExceptions * sizeof(QualType);
- else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
+ } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
Size += sizeof(Expr*);
} else if (EPI.ExceptionSpecType == EST_Uninstantiated) {
Size += 2 * sizeof(FunctionDecl*);
+ } else if (EPI.ExceptionSpecType == EST_Unevaluated) {
+ Size += sizeof(FunctionDecl*);
}
if (EPI.ConsumedArguments)
Size += NumArgs * sizeof(bool);
@@ -2730,10 +2950,17 @@ QualType ASTContext::getPackExpansionType(QualType Pattern,
QualType Canon;
if (!Pattern.isCanonical()) {
- Canon = getPackExpansionType(getCanonicalType(Pattern), NumExpansions);
-
- // Find the insert position again.
- PackExpansionTypes.FindNodeOrInsertPos(ID, InsertPos);
+ Canon = getCanonicalType(Pattern);
+ // The canonical type might not contain an unexpanded parameter pack, if it
+ // contains an alias template specialization which ignores one of its
+ // parameters.
+ if (Canon->containsUnexpandedParameterPack()) {
+ Canon = getPackExpansionType(getCanonicalType(Pattern), NumExpansions);
+
+ // Find the insert position again, in case we inserted an element into
+ // PackExpansionTypes and invalidated our insert position.
+ PackExpansionTypes.FindNodeOrInsertPos(ID, InsertPos);
+ }
}
T = new (*this) PackExpansionType(Pattern, Canon, NumExpansions);
@@ -2950,7 +3177,7 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
if (Canon) {
// We already have a "canonical" version of an equivalent, dependent
// decltype type. Use that as our canonical type.
- dt = new (*this, TypeAlignment) DecltypeType(e, DependentTy,
+ dt = new (*this, TypeAlignment) DecltypeType(e, UnderlyingType,
QualType((DecltypeType*)Canon, 0));
} else {
// Build a new, canonical typeof(expr) type.
@@ -3331,8 +3558,7 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
Arg.getNumTemplateExpansions());
case TemplateArgument::Integral:
- return TemplateArgument(*Arg.getAsIntegral(),
- getCanonicalType(Arg.getIntegralType()));
+ return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));
case TemplateArgument::Type:
return TemplateArgument(getCanonicalType(Arg.getAsType()));
@@ -3471,7 +3697,7 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const {
VAT->getBracketsRange()));
}
-QualType ASTContext::getAdjustedParameterType(QualType T) {
+QualType ASTContext::getAdjustedParameterType(QualType T) const {
// C99 6.7.5.3p7:
// A declaration of a parameter as "array of type" shall be
// adjusted to "qualified pointer to type", where the type
@@ -3490,7 +3716,7 @@ QualType ASTContext::getAdjustedParameterType(QualType T) {
return T;
}
-QualType ASTContext::getSignatureParameterType(QualType T) {
+QualType ASTContext::getSignatureParameterType(QualType T) const {
T = getVariableArrayDecayedType(T);
T = getAdjustedParameterType(T);
return T.getUnqualifiedType();
@@ -3809,7 +4035,7 @@ QualType ASTContext::getCFConstantStringType() const {
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false);
+ ICIS_NoInit);
Field->setAccess(AS_public);
CFConstantStringTypeDecl->addDecl(Field);
}
@@ -3853,7 +4079,7 @@ QualType ASTContext::getBlockDescriptorType() const {
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false);
+ ICIS_NoInit);
Field->setAccess(AS_public);
T->addDecl(Field);
}
@@ -3896,7 +4122,7 @@ QualType ASTContext::getBlockDescriptorExtendedType() const {
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false);
+ ICIS_NoInit);
Field->setAccess(AS_public);
T->addDecl(Field);
}
@@ -3972,7 +4198,7 @@ ASTContext::BuildByRefType(StringRef DeclName, QualType Ty) const {
&Idents.get(FieldNames[i]),
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0, /*Mutable=*/false,
- /*HasInit=*/false);
+ ICIS_NoInit);
Field->setAccess(AS_public);
T->addDecl(Field);
}
@@ -4045,6 +4271,8 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const {
E = Decl->param_end(); PI != E; ++PI) {
QualType PType = (*PI)->getType();
CharUnits sz = getObjCEncodingTypeSize(PType);
+ if (sz.isZero())
+ continue;
assert (sz.isPositive() && "BlockExpr - Incomplete param type");
ParmOffset += sz;
}
@@ -4086,8 +4314,8 @@ bool ASTContext::getObjCEncodingForFunctionDecl(const FunctionDecl *Decl,
QualType PType = (*PI)->getType();
CharUnits sz = getObjCEncodingTypeSize(PType);
if (sz.isZero())
- return true;
-
+ continue;
+
assert (sz.isPositive() &&
"getObjCEncodingForFunctionDecl - Incomplete param type");
ParmOffset += sz;
@@ -4155,8 +4383,8 @@ bool ASTContext::getObjCEncodingForMethodDecl(const ObjCMethodDecl *Decl,
QualType PType = (*PI)->getType();
CharUnits sz = getObjCEncodingTypeSize(PType);
if (sz.isZero())
- return true;
-
+ continue;
+
assert (sz.isPositive() &&
"getObjCEncodingForMethodDecl - Incomplete param type");
ParmOffset += sz;
@@ -4387,7 +4615,7 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S,
// information is not especially sensible, but we're stuck with it for
// compatibility with GCC, although providing it breaks anything that
// actually uses runtime introspection and wants to work on both runtimes...
- if (!Ctx->getLangOpts().NeXTRuntime) {
+ if (Ctx->getLangOpts().ObjCRuntime.isGNUFamily()) {
const RecordDecl *RD = FD->getParent();
const ASTRecordLayout &RL = Ctx->getASTRecordLayout(RD);
S += llvm::utostr(RL.getFieldOffset(FD->getFieldIndex()));
@@ -4563,7 +4791,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S,
// Special case bit-fields.
if (Field->isBitField()) {
getObjCEncodingForTypeImpl(Field->getType(), S, false, true,
- (*Field));
+ *Field);
} else {
QualType qt = Field->getType();
getLegacyIntegralTypeEncoding(qt);
@@ -4746,7 +4974,7 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl,
CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl();
if (base->isEmpty())
continue;
- uint64_t offs = layout.getBaseClassOffsetInBits(base);
+ uint64_t offs = toBits(layout.getBaseClassOffset(base));
FieldOrBaseOffsets.insert(FieldOrBaseOffsets.upper_bound(offs),
std::make_pair(offs, base));
}
@@ -4769,7 +4997,7 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl,
CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl();
if (base->isEmpty())
continue;
- uint64_t offs = layout.getVBaseClassOffsetInBits(base);
+ uint64_t offs = toBits(layout.getVBaseClassOffset(base));
if (FieldOrBaseOffsets.find(offs) == FieldOrBaseOffsets.end())
FieldOrBaseOffsets.insert(FieldOrBaseOffsets.end(),
std::make_pair(offs, base));
@@ -4787,11 +5015,8 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl,
std::multimap<uint64_t, NamedDecl *>::iterator
CurLayObj = FieldOrBaseOffsets.begin();
- if ((CurLayObj != FieldOrBaseOffsets.end() && CurLayObj->first != 0) ||
- (CurLayObj == FieldOrBaseOffsets.end() &&
- CXXRec && CXXRec->isDynamicClass())) {
- assert(CXXRec && CXXRec->isDynamicClass() &&
- "Offset 0 was empty but no VTable ?");
+ if (CXXRec && CXXRec->isDynamicClass() &&
+ (CurLayObj == FieldOrBaseOffsets.end() || CurLayObj->first != 0)) {
if (FD) {
S += "\"_vptr$";
std::string recname = CXXRec->getNameAsString();
@@ -4877,12 +5102,6 @@ void ASTContext::getObjCEncodingForTypeQualifier(Decl::ObjCDeclQualifier QT,
S += 'V';
}
-void ASTContext::setBuiltinVaListType(QualType T) {
- assert(BuiltinVaListType.isNull() && "__builtin_va_list type already set!");
-
- BuiltinVaListType = T;
-}
-
TypedefDecl *ASTContext::getObjCIdDecl() const {
if (!ObjCIdDecl) {
QualType T = getObjCObjectType(ObjCBuiltinIdTy, 0, 0);
@@ -4936,6 +5155,241 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const {
return ObjCProtocolClassDecl;
}
+//===----------------------------------------------------------------------===//
+// __builtin_va_list Construction Functions
+//===----------------------------------------------------------------------===//
+
+static TypedefDecl *CreateCharPtrBuiltinVaListDecl(const ASTContext *Context) {
+ // typedef char* __builtin_va_list;
+ QualType CharPtrType = Context->getPointerType(Context->CharTy);
+ TypeSourceInfo *TInfo
+ = Context->getTrivialTypeSourceInfo(CharPtrType);
+
+ TypedefDecl *VaListTypeDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__builtin_va_list"),
+ TInfo);
+ return VaListTypeDecl;
+}
+
+static TypedefDecl *CreateVoidPtrBuiltinVaListDecl(const ASTContext *Context) {
+ // typedef void* __builtin_va_list;
+ QualType VoidPtrType = Context->getPointerType(Context->VoidTy);
+ TypeSourceInfo *TInfo
+ = Context->getTrivialTypeSourceInfo(VoidPtrType);
+
+ TypedefDecl *VaListTypeDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__builtin_va_list"),
+ TInfo);
+ return VaListTypeDecl;
+}
+
+static TypedefDecl *CreatePowerABIBuiltinVaListDecl(const ASTContext *Context) {
+ // typedef struct __va_list_tag {
+ RecordDecl *VaListTagDecl;
+
+ VaListTagDecl = CreateRecordDecl(*Context, TTK_Struct,
+ Context->getTranslationUnitDecl(),
+ &Context->Idents.get("__va_list_tag"));
+ VaListTagDecl->startDefinition();
+
+ const size_t NumFields = 5;
+ QualType FieldTypes[NumFields];
+ const char *FieldNames[NumFields];
+
+ // unsigned char gpr;
+ FieldTypes[0] = Context->UnsignedCharTy;
+ FieldNames[0] = "gpr";
+
+ // unsigned char fpr;
+ FieldTypes[1] = Context->UnsignedCharTy;
+ FieldNames[1] = "fpr";
+
+ // unsigned short reserved;
+ FieldTypes[2] = Context->UnsignedShortTy;
+ FieldNames[2] = "reserved";
+
+ // void* overflow_arg_area;
+ FieldTypes[3] = Context->getPointerType(Context->VoidTy);
+ FieldNames[3] = "overflow_arg_area";
+
+ // void* reg_save_area;
+ FieldTypes[4] = Context->getPointerType(Context->VoidTy);
+ FieldNames[4] = "reg_save_area";
+
+ // Create fields
+ for (unsigned i = 0; i < NumFields; ++i) {
+ FieldDecl *Field = FieldDecl::Create(*Context, VaListTagDecl,
+ SourceLocation(),
+ SourceLocation(),
+ &Context->Idents.get(FieldNames[i]),
+ FieldTypes[i], /*TInfo=*/0,
+ /*BitWidth=*/0,
+ /*Mutable=*/false,
+ ICIS_NoInit);
+ Field->setAccess(AS_public);
+ VaListTagDecl->addDecl(Field);
+ }
+ VaListTagDecl->completeDefinition();
+ QualType VaListTagType = Context->getRecordType(VaListTagDecl);
+ Context->VaListTagTy = VaListTagType;
+
+ // } __va_list_tag;
+ TypedefDecl *VaListTagTypedefDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__va_list_tag"),
+ Context->getTrivialTypeSourceInfo(VaListTagType));
+ QualType VaListTagTypedefType =
+ Context->getTypedefType(VaListTagTypedefDecl);
+
+ // typedef __va_list_tag __builtin_va_list[1];
+ llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1);
+ QualType VaListTagArrayType
+ = Context->getConstantArrayType(VaListTagTypedefType,
+ Size, ArrayType::Normal, 0);
+ TypeSourceInfo *TInfo
+ = Context->getTrivialTypeSourceInfo(VaListTagArrayType);
+ TypedefDecl *VaListTypedefDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__builtin_va_list"),
+ TInfo);
+
+ return VaListTypedefDecl;
+}
+
+static TypedefDecl *
+CreateX86_64ABIBuiltinVaListDecl(const ASTContext *Context) {
+ // typedef struct __va_list_tag {
+ RecordDecl *VaListTagDecl;
+ VaListTagDecl = CreateRecordDecl(*Context, TTK_Struct,
+ Context->getTranslationUnitDecl(),
+ &Context->Idents.get("__va_list_tag"));
+ VaListTagDecl->startDefinition();
+
+ const size_t NumFields = 4;
+ QualType FieldTypes[NumFields];
+ const char *FieldNames[NumFields];
+
+ // unsigned gp_offset;
+ FieldTypes[0] = Context->UnsignedIntTy;
+ FieldNames[0] = "gp_offset";
+
+ // unsigned fp_offset;
+ FieldTypes[1] = Context->UnsignedIntTy;
+ FieldNames[1] = "fp_offset";
+
+ // void* overflow_arg_area;
+ FieldTypes[2] = Context->getPointerType(Context->VoidTy);
+ FieldNames[2] = "overflow_arg_area";
+
+ // void* reg_save_area;
+ FieldTypes[3] = Context->getPointerType(Context->VoidTy);
+ FieldNames[3] = "reg_save_area";
+
+ // Create fields
+ for (unsigned i = 0; i < NumFields; ++i) {
+ FieldDecl *Field = FieldDecl::Create(const_cast<ASTContext &>(*Context),
+ VaListTagDecl,
+ SourceLocation(),
+ SourceLocation(),
+ &Context->Idents.get(FieldNames[i]),
+ FieldTypes[i], /*TInfo=*/0,
+ /*BitWidth=*/0,
+ /*Mutable=*/false,
+ ICIS_NoInit);
+ Field->setAccess(AS_public);
+ VaListTagDecl->addDecl(Field);
+ }
+ VaListTagDecl->completeDefinition();
+ QualType VaListTagType = Context->getRecordType(VaListTagDecl);
+ Context->VaListTagTy = VaListTagType;
+
+ // } __va_list_tag;
+ TypedefDecl *VaListTagTypedefDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__va_list_tag"),
+ Context->getTrivialTypeSourceInfo(VaListTagType));
+ QualType VaListTagTypedefType =
+ Context->getTypedefType(VaListTagTypedefDecl);
+
+ // typedef __va_list_tag __builtin_va_list[1];
+ llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1);
+ QualType VaListTagArrayType
+ = Context->getConstantArrayType(VaListTagTypedefType,
+ Size, ArrayType::Normal,0);
+ TypeSourceInfo *TInfo
+ = Context->getTrivialTypeSourceInfo(VaListTagArrayType);
+ TypedefDecl *VaListTypedefDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__builtin_va_list"),
+ TInfo);
+
+ return VaListTypedefDecl;
+}
+
+static TypedefDecl *CreatePNaClABIBuiltinVaListDecl(const ASTContext *Context) {
+ // typedef int __builtin_va_list[4];
+ llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 4);
+ QualType IntArrayType
+ = Context->getConstantArrayType(Context->IntTy,
+ Size, ArrayType::Normal, 0);
+ TypedefDecl *VaListTypedefDecl
+ = TypedefDecl::Create(const_cast<ASTContext &>(*Context),
+ Context->getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get("__builtin_va_list"),
+ Context->getTrivialTypeSourceInfo(IntArrayType));
+
+ return VaListTypedefDecl;
+}
+
+static TypedefDecl *CreateVaListDecl(const ASTContext *Context,
+ TargetInfo::BuiltinVaListKind Kind) {
+ switch (Kind) {
+ case TargetInfo::CharPtrBuiltinVaList:
+ return CreateCharPtrBuiltinVaListDecl(Context);
+ case TargetInfo::VoidPtrBuiltinVaList:
+ return CreateVoidPtrBuiltinVaListDecl(Context);
+ case TargetInfo::PowerABIBuiltinVaList:
+ return CreatePowerABIBuiltinVaListDecl(Context);
+ case TargetInfo::X86_64ABIBuiltinVaList:
+ return CreateX86_64ABIBuiltinVaListDecl(Context);
+ case TargetInfo::PNaClABIBuiltinVaList:
+ return CreatePNaClABIBuiltinVaListDecl(Context);
+ }
+
+ llvm_unreachable("Unhandled __builtin_va_list type kind");
+}
+
+TypedefDecl *ASTContext::getBuiltinVaListDecl() const {
+ if (!BuiltinVaListDecl)
+ BuiltinVaListDecl = CreateVaListDecl(this, Target->getBuiltinVaListKind());
+
+ return BuiltinVaListDecl;
+}
+
+QualType ASTContext::getVaListTagType() const {
+ // Force the creation of VaListTagTy by building the __builtin_va_list
+ // declaration.
+ if (VaListTagTy.isNull())
+ (void) getBuiltinVaListDecl();
+
+ return VaListTagTy;
+}
+
void ASTContext::setObjCConstantStringInterface(ObjCInterfaceDecl *Decl) {
assert(ObjCConstantStringType.isNull() &&
"'NSConstantString' type already set!");
@@ -6412,6 +6866,19 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
VectorType::GenericVector);
break;
}
+ case 'E': {
+ char *End;
+
+ unsigned NumElements = strtoul(Str, &End, 10);
+ assert(End != Str && "Missing vector size");
+
+ Str = End;
+
+ QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE,
+ false);
+ Type = Context.getExtVectorType(ElementType, NumElements);
+ break;
+ }
case 'X': {
QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE,
false);
@@ -6712,9 +7179,15 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return true;
}
-CallingConv ASTContext::getDefaultMethodCallConv() {
+CallingConv ASTContext::getDefaultCXXMethodCallConv(bool isVariadic) {
// Pass through to the C++ ABI object
- return ABI->getDefaultMethodCallConv();
+ return ABI->getDefaultMethodCallConv(isVariadic);
+}
+
+CallingConv ASTContext::getCanonicalCallConv(CallingConv CC) const {
+ if (CC == CC_C && !LangOpts.MRTD && getTargetInfo().getCXXABI() != CXXABI_Microsoft)
+ return CC_Default;
+ return CC;
}
bool ASTContext::isNearlyEmpty(const CXXRecordDecl *RD) const {
diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp
index ca4fe26..35fcd41 100644
--- a/lib/AST/ASTDiagnostic.cpp
+++ b/lib/AST/ASTDiagnostic.cpp
@@ -14,7 +14,11 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -225,6 +229,11 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty,
return S;
}
+static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType,
+ QualType ToType, bool PrintTree,
+ bool PrintFromType, bool ElideType,
+ bool ShowColors, std::string &S);
+
void clang::FormatASTNodeDiagnosticArgument(
DiagnosticsEngine::ArgumentKind Kind,
intptr_t Val,
@@ -244,6 +253,33 @@ void clang::FormatASTNodeDiagnosticArgument(
switch (Kind) {
default: llvm_unreachable("unknown ArgumentKind");
+ case DiagnosticsEngine::ak_qualtype_pair: {
+ TemplateDiffTypes &TDT = *reinterpret_cast<TemplateDiffTypes*>(Val);
+ QualType FromType =
+ QualType::getFromOpaquePtr(reinterpret_cast<void*>(TDT.FromType));
+ QualType ToType =
+ QualType::getFromOpaquePtr(reinterpret_cast<void*>(TDT.ToType));
+
+ if (FormatTemplateTypeDiff(Context, FromType, ToType, TDT.PrintTree,
+ TDT.PrintFromType, TDT.ElideType,
+ TDT.ShowColors, S)) {
+ NeedQuotes = !TDT.PrintTree;
+ TDT.TemplateDiffUsed = true;
+ break;
+ }
+
+ // Don't fall-back during tree printing. The caller will handle
+ // this case.
+ if (TDT.PrintTree)
+ return;
+
+ // Attempting to do a templete diff on non-templates. Set the variables
+ // and continue with regular type printing of the appropriate type.
+ Val = TDT.PrintFromType ? TDT.FromType : TDT.ToType;
+ ModLen = 0;
+ ArgLen = 0;
+ // Fall through
+ }
case DiagnosticsEngine::ak_qualtype: {
assert(ModLen == 0 && ArgLen == 0 &&
"Invalid modifier for QualType argument");
@@ -329,3 +365,901 @@ void clang::FormatASTNodeDiagnosticArgument(
if (NeedQuotes)
Output.push_back('\'');
}
+
+/// TemplateDiff - A class that constructs a pretty string for a pair of
+/// QualTypes. For the pair of types, a diff tree will be created containing
+/// all the information about the templates and template arguments. Afterwards,
+/// the tree is transformed to a string according to the options passed in.
+namespace {
+class TemplateDiff {
+ /// Context - The ASTContext which is used for comparing template arguments.
+ ASTContext &Context;
+
+ /// Policy - Used during expression printing.
+ PrintingPolicy Policy;
+
+ /// ElideType - Option to elide identical types.
+ bool ElideType;
+
+ /// PrintTree - Format output string as a tree.
+ bool PrintTree;
+
+ /// ShowColor - Diagnostics support color, so bolding will be used.
+ bool ShowColor;
+
+ /// FromType - When single type printing is selected, this is the type to be
+ /// be printed. When tree printing is selected, this type will show up first
+ /// in the tree.
+ QualType FromType;
+
+ /// ToType - The type that FromType is compared to. Only in tree printing
+ /// will this type be outputed.
+ QualType ToType;
+
+ /// Str - Storage for the output stream.
+ llvm::SmallString<128> Str;
+
+ /// OS - The stream used to construct the output strings.
+ llvm::raw_svector_ostream OS;
+
+ /// IsBold - Keeps track of the bold formatting for the output string.
+ bool IsBold;
+
+ /// DiffTree - A tree representation the differences between two types.
+ class DiffTree {
+ /// DiffNode - The root node stores the original type. Each child node
+ /// stores template arguments of their parents. For templated types, the
+ /// template decl is also stored.
+ struct DiffNode {
+ /// NextNode - The index of the next sibling node or 0.
+ unsigned NextNode;
+
+ /// ChildNode - The index of the first child node or 0.
+ unsigned ChildNode;
+
+ /// ParentNode - The index of the parent node.
+ unsigned ParentNode;
+
+ /// FromType, ToType - The type arguments.
+ QualType FromType, ToType;
+
+ /// FromExpr, ToExpr - The expression arguments.
+ Expr *FromExpr, *ToExpr;
+
+ /// FromTD, ToTD - The template decl for template template
+ /// arguments or the type arguments that are templates.
+ TemplateDecl *FromTD, *ToTD;
+
+ /// FromDefault, ToDefault - Whether the argument is a default argument.
+ bool FromDefault, ToDefault;
+
+ /// Same - Whether the two arguments evaluate to the same value.
+ bool Same;
+
+ DiffNode(unsigned ParentNode = 0)
+ : NextNode(0), ChildNode(0), ParentNode(ParentNode),
+ FromType(), ToType(), FromExpr(0), ToExpr(0), FromTD(0), ToTD(0),
+ FromDefault(false), ToDefault(false), Same(false) { }
+ };
+
+ /// FlatTree - A flattened tree used to store the DiffNodes.
+ llvm::SmallVector<DiffNode, 16> FlatTree;
+
+ /// CurrentNode - The index of the current node being used.
+ unsigned CurrentNode;
+
+ /// NextFreeNode - The index of the next unused node. Used when creating
+ /// child nodes.
+ unsigned NextFreeNode;
+
+ /// ReadNode - The index of the current node being read.
+ unsigned ReadNode;
+
+ public:
+ DiffTree() :
+ CurrentNode(0), NextFreeNode(1) {
+ FlatTree.push_back(DiffNode());
+ }
+
+ // Node writing functions.
+ /// SetNode - Sets FromTD and ToTD of the current node.
+ void SetNode(TemplateDecl *FromTD, TemplateDecl *ToTD) {
+ FlatTree[CurrentNode].FromTD = FromTD;
+ FlatTree[CurrentNode].ToTD = ToTD;
+ }
+
+ /// SetNode - Sets FromType and ToType of the current node.
+ void SetNode(QualType FromType, QualType ToType) {
+ FlatTree[CurrentNode].FromType = FromType;
+ FlatTree[CurrentNode].ToType = ToType;
+ }
+
+ /// SetNode - Set FromExpr and ToExpr of the current node.
+ void SetNode(Expr *FromExpr, Expr *ToExpr) {
+ FlatTree[CurrentNode].FromExpr = FromExpr;
+ FlatTree[CurrentNode].ToExpr = ToExpr;
+ }
+
+ /// SetSame - Sets the same flag of the current node.
+ void SetSame(bool Same) {
+ FlatTree[CurrentNode].Same = Same;
+ }
+
+ /// SetDefault - Sets FromDefault and ToDefault flags of the current node.
+ void SetDefault(bool FromDefault, bool ToDefault) {
+ FlatTree[CurrentNode].FromDefault = FromDefault;
+ FlatTree[CurrentNode].ToDefault = ToDefault;
+ }
+
+ /// Up - Changes the node to the parent of the current node.
+ void Up() {
+ CurrentNode = FlatTree[CurrentNode].ParentNode;
+ }
+
+ /// AddNode - Adds a child node to the current node, then sets that node
+ /// node as the current node.
+ void AddNode() {
+ FlatTree.push_back(DiffNode(CurrentNode));
+ DiffNode &Node = FlatTree[CurrentNode];
+ if (Node.ChildNode == 0) {
+ // If a child node doesn't exist, add one.
+ Node.ChildNode = NextFreeNode;
+ } else {
+ // If a child node exists, find the last child node and add a
+ // next node to it.
+ unsigned i;
+ for (i = Node.ChildNode; FlatTree[i].NextNode != 0;
+ i = FlatTree[i].NextNode) {
+ }
+ FlatTree[i].NextNode = NextFreeNode;
+ }
+ CurrentNode = NextFreeNode;
+ ++NextFreeNode;
+ }
+
+ // Node reading functions.
+ /// StartTraverse - Prepares the tree for recursive traversal.
+ void StartTraverse() {
+ ReadNode = 0;
+ CurrentNode = NextFreeNode;
+ NextFreeNode = 0;
+ }
+
+ /// Parent - Move the current read node to its parent.
+ void Parent() {
+ ReadNode = FlatTree[ReadNode].ParentNode;
+ }
+
+ /// NodeIsTemplate - Returns true if a template decl is set, and types are
+ /// set.
+ bool NodeIsTemplate() {
+ return (FlatTree[ReadNode].FromTD &&
+ !FlatTree[ReadNode].ToType.isNull()) ||
+ (FlatTree[ReadNode].ToTD && !FlatTree[ReadNode].ToType.isNull());
+ }
+
+ /// NodeIsQualType - Returns true if a Qualtype is set.
+ bool NodeIsQualType() {
+ return !FlatTree[ReadNode].FromType.isNull() ||
+ !FlatTree[ReadNode].ToType.isNull();
+ }
+
+ /// NodeIsExpr - Returns true if an expr is set.
+ bool NodeIsExpr() {
+ return FlatTree[ReadNode].FromExpr || FlatTree[ReadNode].ToExpr;
+ }
+
+ /// NodeIsTemplateTemplate - Returns true if the argument is a template
+ /// template type.
+ bool NodeIsTemplateTemplate() {
+ return FlatTree[ReadNode].FromType.isNull() &&
+ FlatTree[ReadNode].ToType.isNull() &&
+ (FlatTree[ReadNode].FromTD || FlatTree[ReadNode].ToTD);
+ }
+
+ /// GetNode - Gets the FromType and ToType.
+ void GetNode(QualType &FromType, QualType &ToType) {
+ FromType = FlatTree[ReadNode].FromType;
+ ToType = FlatTree[ReadNode].ToType;
+ }
+
+ /// GetNode - Gets the FromExpr and ToExpr.
+ void GetNode(Expr *&FromExpr, Expr *&ToExpr) {
+ FromExpr = FlatTree[ReadNode].FromExpr;
+ ToExpr = FlatTree[ReadNode].ToExpr;
+ }
+
+ /// GetNode - Gets the FromTD and ToTD.
+ void GetNode(TemplateDecl *&FromTD, TemplateDecl *&ToTD) {
+ FromTD = FlatTree[ReadNode].FromTD;
+ ToTD = FlatTree[ReadNode].ToTD;
+ }
+
+ /// NodeIsSame - Returns true the arguments are the same.
+ bool NodeIsSame() {
+ return FlatTree[ReadNode].Same;
+ }
+
+ /// HasChildrend - Returns true if the node has children.
+ bool HasChildren() {
+ return FlatTree[ReadNode].ChildNode != 0;
+ }
+
+ /// MoveToChild - Moves from the current node to its child.
+ void MoveToChild() {
+ ReadNode = FlatTree[ReadNode].ChildNode;
+ }
+
+ /// AdvanceSibling - If there is a next sibling, advance to it and return
+ /// true. Otherwise, return false.
+ bool AdvanceSibling() {
+ if (FlatTree[ReadNode].NextNode == 0)
+ return false;
+
+ ReadNode = FlatTree[ReadNode].NextNode;
+ return true;
+ }
+
+ /// HasNextSibling - Return true if the node has a next sibling.
+ bool HasNextSibling() {
+ return FlatTree[ReadNode].NextNode != 0;
+ }
+
+ /// FromDefault - Return true if the from argument is the default.
+ bool FromDefault() {
+ return FlatTree[ReadNode].FromDefault;
+ }
+
+ /// ToDefault - Return true if the to argument is the default.
+ bool ToDefault() {
+ return FlatTree[ReadNode].ToDefault;
+ }
+
+ /// Empty - Returns true if the tree has no information.
+ bool Empty() {
+ return !FlatTree[0].FromTD && !FlatTree[0].ToTD &&
+ !FlatTree[0].FromExpr && !FlatTree[0].ToExpr &&
+ FlatTree[0].FromType.isNull() && FlatTree[0].ToType.isNull();
+ }
+ };
+
+ DiffTree Tree;
+
+ /// TSTiterator - an iterator that is used to enter a
+ /// TemplateSpecializationType and read TemplateArguments inside template
+ /// parameter packs in order with the rest of the TemplateArguments.
+ struct TSTiterator {
+ typedef const TemplateArgument& reference;
+ typedef const TemplateArgument* pointer;
+
+ /// TST - the template specialization whose arguments this iterator
+ /// traverse over.
+ const TemplateSpecializationType *TST;
+
+ /// Index - the index of the template argument in TST.
+ unsigned Index;
+
+ /// CurrentTA - if CurrentTA is not the same as EndTA, then CurrentTA
+ /// points to a TemplateArgument within a parameter pack.
+ TemplateArgument::pack_iterator CurrentTA;
+
+ /// EndTA - the end iterator of a parameter pack
+ TemplateArgument::pack_iterator EndTA;
+
+ /// TSTiterator - Constructs an iterator and sets it to the first template
+ /// argument.
+ TSTiterator(const TemplateSpecializationType *TST)
+ : TST(TST), Index(0), CurrentTA(0), EndTA(0) {
+ if (isEnd()) return;
+
+ // Set to first template argument. If not a parameter pack, done.
+ TemplateArgument TA = TST->getArg(0);
+ if (TA.getKind() != TemplateArgument::Pack) return;
+
+ // Start looking into the parameter pack.
+ CurrentTA = TA.pack_begin();
+ EndTA = TA.pack_end();
+
+ // Found a valid template argument.
+ if (CurrentTA != EndTA) return;
+
+ // Parameter pack is empty, use the increment to get to a valid
+ // template argument.
+ ++(*this);
+ }
+
+ /// isEnd - Returns true if the iterator is one past the end.
+ bool isEnd() const {
+ return Index == TST->getNumArgs();
+ }
+
+ /// &operator++ - Increment the iterator to the next template argument.
+ TSTiterator &operator++() {
+ assert(!isEnd() && "Iterator incremented past end of arguments.");
+
+ // If in a parameter pack, advance in the parameter pack.
+ if (CurrentTA != EndTA) {
+ ++CurrentTA;
+ if (CurrentTA != EndTA)
+ return *this;
+ }
+
+ // Loop until a template argument is found, or the end is reached.
+ while (true) {
+ // Advance to the next template argument. Break if reached the end.
+ if (++Index == TST->getNumArgs()) break;
+
+ // If the TemplateArgument is not a parameter pack, done.
+ TemplateArgument TA = TST->getArg(Index);
+ if (TA.getKind() != TemplateArgument::Pack) break;
+
+ // Handle parameter packs.
+ CurrentTA = TA.pack_begin();
+ EndTA = TA.pack_end();
+
+ // If the parameter pack is empty, try to advance again.
+ if (CurrentTA != EndTA) break;
+ }
+ return *this;
+ }
+
+ /// operator* - Returns the appropriate TemplateArgument.
+ reference operator*() const {
+ assert(!isEnd() && "Index exceeds number of arguments.");
+ if (CurrentTA == EndTA)
+ return TST->getArg(Index);
+ else
+ return *CurrentTA;
+ }
+
+ /// operator-> - Allow access to the underlying TemplateArgument.
+ pointer operator->() const {
+ return &operator*();
+ }
+ };
+
+ // These functions build up the template diff tree, including functions to
+ // retrieve and compare template arguments.
+
+ static const TemplateSpecializationType * GetTemplateSpecializationType(
+ ASTContext &Context, QualType Ty) {
+ if (const TemplateSpecializationType *TST =
+ Ty->getAs<TemplateSpecializationType>())
+ return TST;
+
+ const RecordType *RT = Ty->getAs<RecordType>();
+
+ if (!RT)
+ return 0;
+
+ const ClassTemplateSpecializationDecl *CTSD =
+ dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
+
+ if (!CTSD)
+ return 0;
+
+ Ty = Context.getTemplateSpecializationType(
+ TemplateName(CTSD->getSpecializedTemplate()),
+ CTSD->getTemplateArgs().data(),
+ CTSD->getTemplateArgs().size(),
+ Ty.getCanonicalType());
+
+ return Ty->getAs<TemplateSpecializationType>();
+ }
+
+ /// DiffTemplate - recursively visits template arguments and stores the
+ /// argument info into a tree.
+ void DiffTemplate(const TemplateSpecializationType *FromTST,
+ const TemplateSpecializationType *ToTST) {
+ // Begin descent into diffing template tree.
+ TemplateParameterList *Params =
+ FromTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters();
+ unsigned TotalArgs = 0;
+ for (TSTiterator FromIter(FromTST), ToIter(ToTST);
+ !FromIter.isEnd() || !ToIter.isEnd(); ++TotalArgs) {
+ Tree.AddNode();
+
+ // Get the parameter at index TotalArgs. If index is larger
+ // than the total number of parameters, then there is an
+ // argument pack, so re-use the last parameter.
+ NamedDecl *ParamND = Params->getParam(
+ (TotalArgs < Params->size()) ? TotalArgs
+ : Params->size() - 1);
+ // Handle Types
+ if (TemplateTypeParmDecl *DefaultTTPD =
+ dyn_cast<TemplateTypeParmDecl>(ParamND)) {
+ QualType FromType, ToType;
+ GetType(FromIter, DefaultTTPD, FromType);
+ GetType(ToIter, DefaultTTPD, ToType);
+ Tree.SetNode(FromType, ToType);
+ Tree.SetDefault(FromIter.isEnd() && !FromType.isNull(),
+ ToIter.isEnd() && !ToType.isNull());
+ if (!FromType.isNull() && !ToType.isNull()) {
+ if (Context.hasSameType(FromType, ToType)) {
+ Tree.SetSame(true);
+ } else {
+ const TemplateSpecializationType *FromArgTST =
+ GetTemplateSpecializationType(Context, FromType);
+ const TemplateSpecializationType *ToArgTST =
+ GetTemplateSpecializationType(Context, ToType);
+
+ if (FromArgTST && ToArgTST) {
+ bool SameTemplate = hasSameTemplate(FromArgTST, ToArgTST);
+ if (SameTemplate) {
+ Tree.SetNode(FromArgTST->getTemplateName().getAsTemplateDecl(),
+ ToArgTST->getTemplateName().getAsTemplateDecl());
+ DiffTemplate(FromArgTST, ToArgTST);
+ }
+ }
+ }
+ }
+ }
+
+ // Handle Expressions
+ if (NonTypeTemplateParmDecl *DefaultNTTPD =
+ dyn_cast<NonTypeTemplateParmDecl>(ParamND)) {
+ Expr *FromExpr, *ToExpr;
+ GetExpr(FromIter, DefaultNTTPD, FromExpr);
+ GetExpr(ToIter, DefaultNTTPD, ToExpr);
+ Tree.SetNode(FromExpr, ToExpr);
+ Tree.SetSame(IsEqualExpr(Context, FromExpr, ToExpr));
+ Tree.SetDefault(FromIter.isEnd() && FromExpr,
+ ToIter.isEnd() && ToExpr);
+ }
+
+ // Handle Templates
+ if (TemplateTemplateParmDecl *DefaultTTPD =
+ dyn_cast<TemplateTemplateParmDecl>(ParamND)) {
+ TemplateDecl *FromDecl, *ToDecl;
+ GetTemplateDecl(FromIter, DefaultTTPD, FromDecl);
+ GetTemplateDecl(ToIter, DefaultTTPD, ToDecl);
+ Tree.SetNode(FromDecl, ToDecl);
+ Tree.SetSame(FromDecl && ToDecl &&
+ FromDecl->getIdentifier() == ToDecl->getIdentifier());
+ }
+
+ if (!FromIter.isEnd()) ++FromIter;
+ if (!ToIter.isEnd()) ++ToIter;
+ Tree.Up();
+ }
+ }
+
+ /// hasSameTemplate - Returns true if both types are specialized from the
+ /// same template declaration. If they come from different template aliases,
+ /// do a parallel ascension search to determine the highest template alias in
+ /// common and set the arguments to them.
+ static bool hasSameTemplate(const TemplateSpecializationType *&FromTST,
+ const TemplateSpecializationType *&ToTST) {
+ // Check the top templates if they are the same.
+ if (FromTST->getTemplateName().getAsTemplateDecl()->getIdentifier() ==
+ ToTST->getTemplateName().getAsTemplateDecl()->getIdentifier())
+ return true;
+
+ // Create vectors of template aliases.
+ SmallVector<const TemplateSpecializationType*, 1> FromTemplateList,
+ ToTemplateList;
+
+ const TemplateSpecializationType *TempToTST = ToTST, *TempFromTST = FromTST;
+ FromTemplateList.push_back(FromTST);
+ ToTemplateList.push_back(ToTST);
+
+ // Dump every template alias into the vectors.
+ while (TempFromTST->isTypeAlias()) {
+ TempFromTST =
+ TempFromTST->getAliasedType()->getAs<TemplateSpecializationType>();
+ if (!TempFromTST)
+ break;
+ FromTemplateList.push_back(TempFromTST);
+ }
+ while (TempToTST->isTypeAlias()) {
+ TempToTST =
+ TempToTST->getAliasedType()->getAs<TemplateSpecializationType>();
+ if (!TempToTST)
+ break;
+ ToTemplateList.push_back(TempToTST);
+ }
+
+ SmallVector<const TemplateSpecializationType*, 1>::reverse_iterator
+ FromIter = FromTemplateList.rbegin(), FromEnd = FromTemplateList.rend(),
+ ToIter = ToTemplateList.rbegin(), ToEnd = ToTemplateList.rend();
+
+ // Check if the lowest template types are the same. If not, return.
+ if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() !=
+ (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier())
+ return false;
+
+ // Begin searching up the template aliases. The bottom most template
+ // matches so move up until one pair does not match. Use the template
+ // right before that one.
+ for (; FromIter != FromEnd && ToIter != ToEnd; ++FromIter, ++ToIter) {
+ if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() !=
+ (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier())
+ break;
+ }
+
+ FromTST = FromIter[-1];
+ ToTST = ToIter[-1];
+
+ return true;
+ }
+
+ /// GetType - Retrieves the template type arguments, including default
+ /// arguments.
+ void GetType(const TSTiterator &Iter, TemplateTypeParmDecl *DefaultTTPD,
+ QualType &ArgType) {
+ ArgType = QualType();
+ bool isVariadic = DefaultTTPD->isParameterPack();
+
+ if (!Iter.isEnd())
+ ArgType = Iter->getAsType();
+ else if (!isVariadic)
+ ArgType = DefaultTTPD->getDefaultArgument();
+ }
+
+ /// GetExpr - Retrieves the template expression argument, including default
+ /// arguments.
+ void GetExpr(const TSTiterator &Iter, NonTypeTemplateParmDecl *DefaultNTTPD,
+ Expr *&ArgExpr) {
+ ArgExpr = 0;
+ bool isVariadic = DefaultNTTPD->isParameterPack();
+
+ if (!Iter.isEnd())
+ ArgExpr = Iter->getAsExpr();
+ else if (!isVariadic)
+ ArgExpr = DefaultNTTPD->getDefaultArgument();
+
+ if (ArgExpr)
+ while (SubstNonTypeTemplateParmExpr *SNTTPE =
+ dyn_cast<SubstNonTypeTemplateParmExpr>(ArgExpr))
+ ArgExpr = SNTTPE->getReplacement();
+ }
+
+ /// GetTemplateDecl - Retrieves the template template arguments, including
+ /// default arguments.
+ void GetTemplateDecl(const TSTiterator &Iter,
+ TemplateTemplateParmDecl *DefaultTTPD,
+ TemplateDecl *&ArgDecl) {
+ ArgDecl = 0;
+ bool isVariadic = DefaultTTPD->isParameterPack();
+
+ TemplateArgument TA = DefaultTTPD->getDefaultArgument().getArgument();
+ TemplateDecl *DefaultTD = TA.getAsTemplate().getAsTemplateDecl();
+
+ if (!Iter.isEnd())
+ ArgDecl = Iter->getAsTemplate().getAsTemplateDecl();
+ else if (!isVariadic)
+ ArgDecl = DefaultTD;
+ }
+
+ /// IsEqualExpr - Returns true if the expressions evaluate to the same value.
+ static bool IsEqualExpr(ASTContext &Context, Expr *FromExpr, Expr *ToExpr) {
+ if (FromExpr == ToExpr)
+ return true;
+
+ if (!FromExpr || !ToExpr)
+ return false;
+
+ FromExpr = FromExpr->IgnoreParens();
+ ToExpr = ToExpr->IgnoreParens();
+
+ DeclRefExpr *FromDRE = dyn_cast<DeclRefExpr>(FromExpr),
+ *ToDRE = dyn_cast<DeclRefExpr>(ToExpr);
+
+ if (FromDRE || ToDRE) {
+ if (!FromDRE || !ToDRE)
+ return false;
+ return FromDRE->getDecl() == ToDRE->getDecl();
+ }
+
+ Expr::EvalResult FromResult, ToResult;
+ if (!FromExpr->EvaluateAsRValue(FromResult, Context) ||
+ !ToExpr->EvaluateAsRValue(ToResult, Context))
+ assert(0 && "Template arguments must be known at compile time.");
+
+ APValue &FromVal = FromResult.Val;
+ APValue &ToVal = ToResult.Val;
+
+ if (FromVal.getKind() != ToVal.getKind()) return false;
+
+ switch (FromVal.getKind()) {
+ case APValue::Int:
+ return FromVal.getInt() == ToVal.getInt();
+ case APValue::LValue: {
+ APValue::LValueBase FromBase = FromVal.getLValueBase();
+ APValue::LValueBase ToBase = ToVal.getLValueBase();
+ if (FromBase.isNull() && ToBase.isNull())
+ return true;
+ if (FromBase.isNull() || ToBase.isNull())
+ return false;
+ return FromBase.get<const ValueDecl*>() ==
+ ToBase.get<const ValueDecl*>();
+ }
+ case APValue::MemberPointer:
+ return FromVal.getMemberPointerDecl() == ToVal.getMemberPointerDecl();
+ default:
+ llvm_unreachable("Unknown template argument expression.");
+ }
+ }
+
+ // These functions converts the tree representation of the template
+ // differences into the internal character vector.
+
+ /// TreeToString - Converts the Tree object into a character stream which
+ /// will later be turned into the output string.
+ void TreeToString(int Indent = 1) {
+ if (PrintTree) {
+ OS << '\n';
+ for (int i = 0; i < Indent; ++i)
+ OS << " ";
+ ++Indent;
+ }
+
+ // Handle cases where the difference is not templates with different
+ // arguments.
+ if (!Tree.NodeIsTemplate()) {
+ if (Tree.NodeIsQualType()) {
+ QualType FromType, ToType;
+ Tree.GetNode(FromType, ToType);
+ PrintTypeNames(FromType, ToType, Tree.FromDefault(), Tree.ToDefault(),
+ Tree.NodeIsSame());
+ return;
+ }
+ if (Tree.NodeIsExpr()) {
+ Expr *FromExpr, *ToExpr;
+ Tree.GetNode(FromExpr, ToExpr);
+ PrintExpr(FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(),
+ Tree.NodeIsSame());
+ return;
+ }
+ if (Tree.NodeIsTemplateTemplate()) {
+ TemplateDecl *FromTD, *ToTD;
+ Tree.GetNode(FromTD, ToTD);
+ PrintTemplateTemplate(FromTD, ToTD, Tree.FromDefault(),
+ Tree.ToDefault(), Tree.NodeIsSame());
+ return;
+ }
+ llvm_unreachable("Unable to deduce template difference.");
+ }
+
+ // Node is root of template. Recurse on children.
+ TemplateDecl *FromTD, *ToTD;
+ Tree.GetNode(FromTD, ToTD);
+
+ assert(Tree.HasChildren() && "Template difference not found in diff tree.");
+
+ OS << FromTD->getNameAsString() << '<';
+ Tree.MoveToChild();
+ unsigned NumElideArgs = 0;
+ do {
+ if (ElideType) {
+ if (Tree.NodeIsSame()) {
+ ++NumElideArgs;
+ continue;
+ }
+ if (NumElideArgs > 0) {
+ PrintElideArgs(NumElideArgs, Indent);
+ NumElideArgs = 0;
+ OS << ", ";
+ }
+ }
+ TreeToString(Indent);
+ if (Tree.HasNextSibling())
+ OS << ", ";
+ } while (Tree.AdvanceSibling());
+ if (NumElideArgs > 0)
+ PrintElideArgs(NumElideArgs, Indent);
+
+ Tree.Parent();
+ OS << ">";
+ }
+
+ // To signal to the text printer that a certain text needs to be bolded,
+ // a special character is injected into the character stream which the
+ // text printer will later strip out.
+
+ /// Bold - Start bolding text.
+ void Bold() {
+ assert(!IsBold && "Attempting to bold text that is already bold.");
+ IsBold = true;
+ if (ShowColor)
+ OS << ToggleHighlight;
+ }
+
+ /// Unbold - Stop bolding text.
+ void Unbold() {
+ assert(IsBold && "Attempting to remove bold from unbold text.");
+ IsBold = false;
+ if (ShowColor)
+ OS << ToggleHighlight;
+ }
+
+ // Functions to print out the arguments and highlighting the difference.
+
+ /// PrintTypeNames - prints the typenames, bolding differences. Will detect
+ /// typenames that are the same and attempt to disambiguate them by using
+ /// canonical typenames.
+ void PrintTypeNames(QualType FromType, QualType ToType,
+ bool FromDefault, bool ToDefault, bool Same) {
+ assert((!FromType.isNull() || !ToType.isNull()) &&
+ "Only one template argument may be missing.");
+
+ if (Same) {
+ OS << FromType.getAsString();
+ return;
+ }
+
+ std::string FromTypeStr = FromType.isNull() ? "(no argument)"
+ : FromType.getAsString();
+ std::string ToTypeStr = ToType.isNull() ? "(no argument)"
+ : ToType.getAsString();
+ // Switch to canonical typename if it is better.
+ // TODO: merge this with other aka printing above.
+ if (FromTypeStr == ToTypeStr) {
+ std::string FromCanTypeStr = FromType.getCanonicalType().getAsString();
+ std::string ToCanTypeStr = ToType.getCanonicalType().getAsString();
+ if (FromCanTypeStr != ToCanTypeStr) {
+ FromTypeStr = FromCanTypeStr;
+ ToTypeStr = ToCanTypeStr;
+ }
+ }
+
+ if (PrintTree) OS << '[';
+ OS << (FromDefault ? "(default) " : "");
+ Bold();
+ OS << FromTypeStr;
+ Unbold();
+ if (PrintTree) {
+ OS << " != " << (ToDefault ? "(default) " : "");
+ Bold();
+ OS << ToTypeStr;
+ Unbold();
+ OS << "]";
+ }
+ return;
+ }
+
+ /// PrintExpr - Prints out the expr template arguments, highlighting argument
+ /// differences.
+ void PrintExpr(const Expr *FromExpr, const Expr *ToExpr,
+ bool FromDefault, bool ToDefault, bool Same) {
+ assert((FromExpr || ToExpr) &&
+ "Only one template argument may be missing.");
+ if (Same) {
+ PrintExpr(FromExpr);
+ } else if (!PrintTree) {
+ OS << (FromDefault ? "(default) " : "");
+ Bold();
+ PrintExpr(FromExpr);
+ Unbold();
+ } else {
+ OS << (FromDefault ? "[(default) " : "[");
+ Bold();
+ PrintExpr(FromExpr);
+ Unbold();
+ OS << " != " << (ToDefault ? "(default) " : "");
+ Bold();
+ PrintExpr(ToExpr);
+ Unbold();
+ OS << ']';
+ }
+ }
+
+ /// PrintExpr - Actual formatting and printing of expressions.
+ void PrintExpr(const Expr *E) {
+ if (!E)
+ OS << "(no argument)";
+ else
+ E->printPretty(OS, Context, 0, Policy); return;
+ }
+
+ /// PrintTemplateTemplate - Handles printing of template template arguments,
+ /// highlighting argument differences.
+ void PrintTemplateTemplate(TemplateDecl *FromTD, TemplateDecl *ToTD,
+ bool FromDefault, bool ToDefault, bool Same) {
+ assert((FromTD || ToTD) && "Only one template argument may be missing.");
+ if (Same) {
+ OS << "template " << FromTD->getNameAsString();
+ } else if (!PrintTree) {
+ OS << (FromDefault ? "(default) template " : "template ");
+ Bold();
+ OS << (FromTD ? FromTD->getNameAsString() : "(no argument)");
+ Unbold();
+ } else {
+ OS << (FromDefault ? "[(default) template " : "[template ");
+ Bold();
+ OS << (FromTD ? FromTD->getNameAsString() : "(no argument)");
+ Unbold();
+ OS << " != " << (ToDefault ? "(default) template " : "template ");
+ Bold();
+ OS << (ToTD ? ToTD->getNameAsString() : "(no argument)");
+ Unbold();
+ OS << ']';
+ }
+ }
+
+ // Prints the appropriate placeholder for elided template arguments.
+ void PrintElideArgs(unsigned NumElideArgs, unsigned Indent) {
+ if (PrintTree) {
+ OS << '\n';
+ for (unsigned i = 0; i < Indent; ++i)
+ OS << " ";
+ }
+ if (NumElideArgs == 0) return;
+ if (NumElideArgs == 1)
+ OS << "[...]";
+ else
+ OS << "[" << NumElideArgs << " * ...]";
+ }
+
+public:
+
+ TemplateDiff(ASTContext &Context, QualType FromType, QualType ToType,
+ bool PrintTree, bool PrintFromType, bool ElideType,
+ bool ShowColor)
+ : Context(Context),
+ Policy(Context.getLangOpts()),
+ ElideType(ElideType),
+ PrintTree(PrintTree),
+ ShowColor(ShowColor),
+ // When printing a single type, the FromType is the one printed.
+ FromType(PrintFromType ? FromType : ToType),
+ ToType(PrintFromType ? ToType : FromType),
+ OS(Str),
+ IsBold(false) {
+ }
+
+ /// DiffTemplate - Start the template type diffing.
+ void DiffTemplate() {
+ const TemplateSpecializationType *FromOrigTST =
+ GetTemplateSpecializationType(Context, FromType);
+ const TemplateSpecializationType *ToOrigTST =
+ GetTemplateSpecializationType(Context, ToType);
+
+ // Only checking templates.
+ if (!FromOrigTST || !ToOrigTST)
+ return;
+
+ // Different base templates.
+ if (!hasSameTemplate(FromOrigTST, ToOrigTST)) {
+ return;
+ }
+
+ Tree.SetNode(FromType, ToType);
+
+ // Same base template, but different arguments.
+ Tree.SetNode(FromOrigTST->getTemplateName().getAsTemplateDecl(),
+ ToOrigTST->getTemplateName().getAsTemplateDecl());
+
+ DiffTemplate(FromOrigTST, ToOrigTST);
+ }
+
+ /// MakeString - When the two types given are templated types with the same
+ /// base template, a string representation of the type difference will be
+ /// loaded into S and return true. Otherwise, return false.
+ bool MakeString(std::string &S) {
+ Tree.StartTraverse();
+ if (Tree.Empty())
+ return false;
+
+ TreeToString();
+ assert(!IsBold && "Bold is applied to end of string.");
+ S = OS.str();
+ return true;
+ }
+}; // end class TemplateDiff
+} // end namespace
+
+/// FormatTemplateTypeDiff - A helper static function to start the template
+/// diff and return the properly formatted string. Returns true if the diff
+/// is successful.
+static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType,
+ QualType ToType, bool PrintTree,
+ bool PrintFromType, bool ElideType,
+ bool ShowColors, std::string &S) {
+ if (PrintTree)
+ PrintFromType = true;
+ TemplateDiff TD(Context, FromType, ToType, PrintTree, PrintFromType,
+ ElideType, ShowColors);
+ TD.DiffTemplate();
+ return TD.MakeString(S);
+}
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 3879907..3e952ac 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -119,7 +119,8 @@ namespace clang {
bool ImportTemplateArguments(const TemplateArgument *FromArgs,
unsigned NumFromArgs,
SmallVectorImpl<TemplateArgument> &ToArgs);
- bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord);
+ bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord,
+ bool Complain = true);
bool IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToRecord);
bool IsStructuralMatch(ClassTemplateDecl *From, ClassTemplateDecl *To);
Decl *VisitDecl(Decl *D);
@@ -201,12 +202,16 @@ namespace {
/// \brief Whether we're being strict about the spelling of types when
/// unifying two types.
bool StrictTypeSpelling;
-
+
+ /// \brief Whether to complain about failures.
+ bool Complain;
+
StructuralEquivalenceContext(ASTContext &C1, ASTContext &C2,
llvm::DenseSet<std::pair<Decl *, Decl *> > &NonEquivalentDecls,
- bool StrictTypeSpelling = false)
+ bool StrictTypeSpelling = false,
+ bool Complain = true)
: C1(C1), C2(C2), NonEquivalentDecls(NonEquivalentDecls),
- StrictTypeSpelling(StrictTypeSpelling) { }
+ StrictTypeSpelling(StrictTypeSpelling), Complain(Complain) { }
/// \brief Determine whether the two declarations are structurally
/// equivalent.
@@ -223,10 +228,16 @@ namespace {
public:
DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID) {
+ if (!Complain)
+ return DiagnosticBuilder::getEmpty();
+
return C1.getDiagnostics().Report(Loc, DiagID);
}
DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID) {
+ if (!Complain)
+ return DiagnosticBuilder::getEmpty();
+
return C2.getDiagnostics().Report(Loc, DiagID);
}
};
@@ -237,45 +248,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2);
-/// \brief Determine if two APInts have the same value, after zero-extending
-/// one of them (if needed!) to ensure that the bit-widths match.
-static bool IsSameValue(const llvm::APInt &I1, const llvm::APInt &I2) {
- if (I1.getBitWidth() == I2.getBitWidth())
- return I1 == I2;
-
- if (I1.getBitWidth() > I2.getBitWidth())
- return I1 == I2.zext(I1.getBitWidth());
-
- return I1.zext(I2.getBitWidth()) == I2;
-}
-
-/// \brief Determine if two APSInts have the same value, zero- or sign-extending
-/// as needed.
-static bool IsSameValue(const llvm::APSInt &I1, const llvm::APSInt &I2) {
- if (I1.getBitWidth() == I2.getBitWidth() && I1.isSigned() == I2.isSigned())
- return I1 == I2;
-
- // Check for a bit-width mismatch.
- if (I1.getBitWidth() > I2.getBitWidth())
- return IsSameValue(I1, I2.extend(I1.getBitWidth()));
- else if (I2.getBitWidth() > I1.getBitWidth())
- return IsSameValue(I1.extend(I2.getBitWidth()), I2);
-
- // We have a signedness mismatch. Turn the signed value into an unsigned
- // value.
- if (I1.isSigned()) {
- if (I1.isNegative())
- return false;
-
- return llvm::APSInt(I1, true) == I2;
- }
-
- if (I2.isNegative())
- return false;
-
- return I1 == llvm::APSInt(I2, true);
-}
-
/// \brief Determine structural equivalence of two expressions.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Expr *E1, Expr *E2) {
@@ -322,7 +294,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Arg2.getIntegralType()))
return false;
- return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral());
+ return llvm::APSInt::isSameValue(Arg1.getAsIntegral(), Arg2.getAsIntegral());
case TemplateArgument::Declaration:
if (!Arg1.getAsDecl() || !Arg2.getAsDecl())
@@ -467,7 +439,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
case Type::ConstantArray: {
const ConstantArrayType *Array1 = cast<ConstantArrayType>(T1);
const ConstantArrayType *Array2 = cast<ConstantArrayType>(T2);
- if (!IsSameValue(Array1->getSize(), Array2->getSize()))
+ if (!llvm::APInt::isSameValue(Array1->getSize(), Array2->getSize()))
return false;
if (!IsArrayStructurallyEquivalent(Context, Array1, Array2))
@@ -1002,9 +974,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
}
// Check the fields for consistency.
- CXXRecordDecl::field_iterator Field2 = D2->field_begin(),
+ RecordDecl::field_iterator Field2 = D2->field_begin(),
Field2End = D2->field_end();
- for (CXXRecordDecl::field_iterator Field1 = D1->field_begin(),
+ for (RecordDecl::field_iterator Field1 = D1->field_begin(),
Field1End = D1->field_end();
Field1 != Field1End;
++Field1, ++Field2) {
@@ -1053,7 +1025,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
llvm::APSInt Val1 = EC1->getInitVal();
llvm::APSInt Val2 = EC2->getInitVal();
- if (!IsSameValue(Val1, Val2) ||
+ if (!llvm::APSInt::isSameValue(Val1, Val2) ||
!IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
<< Context.C2.getTypeDeclType(D2);
@@ -1852,19 +1824,14 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
ToData.HasPublicFields = FromData.HasPublicFields;
ToData.HasMutableFields = FromData.HasMutableFields;
ToData.HasOnlyCMembers = FromData.HasOnlyCMembers;
+ ToData.HasInClassInitializer = FromData.HasInClassInitializer;
ToData.HasTrivialDefaultConstructor = FromData.HasTrivialDefaultConstructor;
ToData.HasConstexprNonCopyMoveConstructor
= FromData.HasConstexprNonCopyMoveConstructor;
ToData.DefaultedDefaultConstructorIsConstexpr
= FromData.DefaultedDefaultConstructorIsConstexpr;
- ToData.DefaultedCopyConstructorIsConstexpr
- = FromData.DefaultedCopyConstructorIsConstexpr;
- ToData.DefaultedMoveConstructorIsConstexpr
- = FromData.DefaultedMoveConstructorIsConstexpr;
ToData.HasConstexprDefaultConstructor
= FromData.HasConstexprDefaultConstructor;
- ToData.HasConstexprCopyConstructor = FromData.HasConstexprCopyConstructor;
- ToData.HasConstexprMoveConstructor = FromData.HasConstexprMoveConstructor;
ToData.HasTrivialCopyConstructor = FromData.HasTrivialCopyConstructor;
ToData.HasTrivialMoveConstructor = FromData.HasTrivialMoveConstructor;
ToData.HasTrivialCopyAssignment = FromData.HasTrivialCopyAssignment;
@@ -1991,7 +1958,7 @@ ASTNodeImporter::ImportTemplateArgument(const TemplateArgument &From) {
QualType ToType = Importer.Import(From.getIntegralType());
if (ToType.isNull())
return TemplateArgument();
- return TemplateArgument(*From.getAsIntegral(), ToType);
+ return TemplateArgument(From, ToType);
}
case TemplateArgument::Declaration:
@@ -2052,10 +2019,11 @@ bool ASTNodeImporter::ImportTemplateArguments(const TemplateArgument *FromArgs,
}
bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord,
- RecordDecl *ToRecord) {
+ RecordDecl *ToRecord, bool Complain) {
StructuralEquivalenceContext Ctx(Importer.getFromContext(),
Importer.getToContext(),
- Importer.getNonEquivalentDecls());
+ Importer.getNonEquivalentDecls(),
+ false, Complain);
return Ctx.IsStructurallyEquivalent(FromRecord, ToRecord);
}
@@ -2335,7 +2303,7 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
// We may already have a record of the same name; try to find and match it.
RecordDecl *AdoptDecl = 0;
- if (!DC->isFunctionOrMethod() && SearchName) {
+ if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
llvm::SmallVector<NamedDecl *, 2> FoundDecls;
DC->localUncachedLookup(SearchName, FoundDecls);
@@ -2351,25 +2319,31 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
if (RecordDecl *FoundRecord = dyn_cast<RecordDecl>(Found)) {
if (RecordDecl *FoundDef = FoundRecord->getDefinition()) {
- if (!D->isCompleteDefinition() || IsStructuralMatch(D, FoundDef)) {
+ if ((SearchName && !D->isCompleteDefinition())
+ || (D->isCompleteDefinition() &&
+ D->isAnonymousStructOrUnion()
+ == FoundDef->isAnonymousStructOrUnion() &&
+ IsStructuralMatch(D, FoundDef))) {
// The record types structurally match, or the "from" translation
// unit only had a forward declaration anyway; call it the same
// function.
// FIXME: For C++, we should also merge methods here.
return Importer.Imported(D, FoundDef);
}
- } else {
+ } else if (!D->isCompleteDefinition()) {
// We have a forward declaration of this type, so adopt that forward
// declaration rather than building a new one.
AdoptDecl = FoundRecord;
continue;
- }
+ } else if (!SearchName) {
+ continue;
+ }
}
ConflictingDecls.push_back(FoundDecls[I]);
}
- if (!ConflictingDecls.empty()) {
+ if (!ConflictingDecls.empty() && SearchName) {
Name = Importer.HandleNameConflict(Name, DC, IDNS,
ConflictingDecls.data(),
ConflictingDecls.size());
@@ -2395,6 +2369,8 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
D2->setQualifierInfo(Importer.Import(D->getQualifierLoc()));
D2->setLexicalDeclContext(LexicalDC);
LexicalDC->addDeclInternal(D2);
+ if (D->isAnonymousStructOrUnion())
+ D2->setAnonymousStructOrUnion(true);
}
Importer.Imported(D, D2);
@@ -2661,7 +2637,7 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
Importer.Import(D->getInnerLocStart()),
Loc, Name.getAsIdentifierInfo(),
T, TInfo, BitWidth, D->isMutable(),
- D->hasInClassInitializer());
+ D->getInClassInitStyle());
ToField->setAccess(D->getAccess());
ToField->setLexicalDeclContext(LexicalDC);
if (ToField->hasInClassInitializer())
@@ -2686,11 +2662,16 @@ Decl *ASTNodeImporter::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
if (IndirectFieldDecl *FoundField
= dyn_cast<IndirectFieldDecl>(FoundDecls[I])) {
if (Importer.IsStructurallyEquivalent(D->getType(),
- FoundField->getType())) {
+ FoundField->getType(),
+ Name)) {
Importer.Imported(D, FoundField);
return FoundField;
}
-
+
+ // If there are more anonymous fields to check, continue.
+ if (!Name && I < N-1)
+ continue;
+
Importer.ToDiag(Loc, diag::err_odr_field_type_inconsistent)
<< Name << D->getType() << FoundField->getType();
Importer.ToDiag(FoundField->getLocation(), diag::note_odr_value_here)
@@ -4665,12 +4646,14 @@ Decl *ASTImporter::Imported(Decl *From, Decl *To) {
return To;
}
-bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To) {
+bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To,
+ bool Complain) {
llvm::DenseMap<const Type *, const Type *>::iterator Pos
= ImportedTypes.find(From.getTypePtr());
if (Pos != ImportedTypes.end() && ToContext.hasSameType(Import(From), To))
return true;
- StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls);
+ StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls,
+ false, Complain);
return Ctx.IsStructurallyEquivalent(From, To);
}
diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt
index 716459a..bcc96f9 100644
--- a/lib/AST/CMakeLists.txt
+++ b/lib/AST/CMakeLists.txt
@@ -1,7 +1,5 @@
set(LLVM_LINK_COMPONENTS support)
-set(LLVM_USED_LIBS clangBasic clangLex)
-
add_clang_library(clangAST
APValue.cpp
ASTConsumer.cpp
@@ -10,6 +8,13 @@ add_clang_library(clangAST
ASTImporter.cpp
AttrImpl.cpp
CXXInheritance.cpp
+ Comment.cpp
+ CommentBriefParser.cpp
+ CommentCommandTraits.cpp
+ CommentDumper.cpp
+ CommentLexer.cpp
+ CommentParser.cpp
+ CommentSema.cpp
Decl.cpp
DeclarationName.cpp
DeclBase.cpp
@@ -35,6 +40,7 @@ add_clang_library(clangAST
NestedNameSpecifier.cpp
NSAPI.cpp
ParentMap.cpp
+ RawCommentList.cpp
RecordLayout.cpp
RecordLayoutBuilder.cpp
SelectorLocationsKind.cpp
@@ -53,5 +59,21 @@ add_clang_library(clangAST
VTTBuilder.cpp
)
-add_dependencies(clangAST ClangARMNeon ClangAttrClasses ClangAttrList
- ClangAttrImpl ClangDiagnosticAST ClangDeclNodes ClangStmtNodes)
+add_dependencies(clangAST
+ ClangARMNeon
+ ClangAttrClasses
+ ClangAttrList
+ ClangAttrImpl
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticAST
+ ClangDiagnosticComment
+ ClangDiagnosticCommon
+ ClangDiagnosticSema
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangAST
+ clangBasic
+ clangLex
+ )
diff --git a/lib/AST/CXXABI.h b/lib/AST/CXXABI.h
index 943c43e..0d9c869 100644
--- a/lib/AST/CXXABI.h
+++ b/lib/AST/CXXABI.h
@@ -32,7 +32,7 @@ public:
virtual unsigned getMemberPointerSize(const MemberPointerType *MPT) const = 0;
/// Returns the default calling convention for C++ methods.
- virtual CallingConv getDefaultMethodCallConv() const = 0;
+ virtual CallingConv getDefaultMethodCallConv(bool isVariadic) const = 0;
// Returns whether the given class is nearly empty, with just virtual pointers
// and no data except possibly virtual bases.
diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp
index 2186730..cf3913b 100644
--- a/lib/AST/CXXInheritance.cpp
+++ b/lib/AST/CXXInheritance.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/DeclCXX.h"
#include <algorithm>
@@ -96,7 +97,7 @@ bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,
Paths);
}
-bool CXXRecordDecl::isVirtuallyDerivedFrom(CXXRecordDecl *Base) const {
+bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const {
if (!getNumVBases())
return false;
@@ -106,8 +107,12 @@ bool CXXRecordDecl::isVirtuallyDerivedFrom(CXXRecordDecl *Base) const {
if (getCanonicalDecl() == Base->getCanonicalDecl())
return false;
- Paths.setOrigin(const_cast<CXXRecordDecl*>(this));
- return lookupInBases(&FindVirtualBaseClass, Base->getCanonicalDecl(), Paths);
+ Paths.setOrigin(const_cast<CXXRecordDecl*>(this));
+
+ const void *BasePtr = static_cast<const void*>(Base->getCanonicalDecl());
+ return lookupInBases(&FindVirtualBaseClass,
+ const_cast<void *>(BasePtr),
+ Paths);
}
static bool BaseIsNot(const CXXRecordDecl *Base, void *OpaqueTarget) {
@@ -160,7 +165,7 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches,
return AllMatches;
}
-bool CXXBasePaths::lookupInBases(ASTContext &Context,
+bool CXXBasePaths::lookupInBases(ASTContext &Context,
const CXXRecordDecl *Record,
CXXRecordDecl::BaseMatchesCallback *BaseMatches,
void *UserData) {
@@ -505,12 +510,17 @@ void FinalOverriderCollector::Collect(const CXXRecordDecl *RD,
CXXFinalOverriderMap *BaseOverriders = &ComputedBaseOverriders;
if (Base->isVirtual()) {
CXXFinalOverriderMap *&MyVirtualOverriders = VirtualOverriders[BaseDecl];
+ BaseOverriders = MyVirtualOverriders;
if (!MyVirtualOverriders) {
MyVirtualOverriders = new CXXFinalOverriderMap;
+
+ // Collect may cause VirtualOverriders to reallocate, invalidating the
+ // MyVirtualOverriders reference. Set BaseOverriders to the right
+ // value now.
+ BaseOverriders = MyVirtualOverriders;
+
Collect(BaseDecl, true, BaseDecl, *MyVirtualOverriders);
}
-
- BaseOverriders = MyVirtualOverriders;
} else
Collect(BaseDecl, false, InVirtualSubobject, ComputedBaseOverriders);
diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp
new file mode 100644
index 0000000..8a711f0
--- /dev/null
+++ b/lib/AST/Comment.cpp
@@ -0,0 +1,264 @@
+//===--- Comment.cpp - Comment AST node implementation --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Comment.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace comments {
+
+const char *Comment::getCommentKindName() const {
+ switch (getCommentKind()) {
+ case NoCommentKind: return "NoCommentKind";
+#define ABSTRACT_COMMENT(COMMENT)
+#define COMMENT(CLASS, PARENT) \
+ case CLASS##Kind: \
+ return #CLASS;
+#include "clang/AST/CommentNodes.inc"
+#undef COMMENT
+#undef ABSTRACT_COMMENT
+ }
+ llvm_unreachable("Unknown comment kind!");
+}
+
+void Comment::dump() const {
+ // It is important that Comment::dump() is defined in a different TU than
+ // Comment::dump(raw_ostream, SourceManager). If both functions were defined
+ // in CommentDumper.cpp, that object file would be removed by linker because
+ // none of its functions are referenced by other object files, despite the
+ // LLVM_ATTRIBUTE_USED.
+ dump(llvm::errs(), NULL);
+}
+
+void Comment::dump(SourceManager &SM) const {
+ dump(llvm::errs(), &SM);
+}
+
+namespace {
+struct good {};
+struct bad {};
+
+template <typename T>
+good implements_child_begin_end(Comment::child_iterator (T::*)() const) {
+ return good();
+}
+
+static inline bad implements_child_begin_end(
+ Comment::child_iterator (Comment::*)() const) {
+ return bad();
+}
+
+#define ASSERT_IMPLEMENTS_child_begin(function) \
+ (void) sizeof(good(implements_child_begin_end(function)))
+
+static inline void CheckCommentASTNodes() {
+#define ABSTRACT_COMMENT(COMMENT)
+#define COMMENT(CLASS, PARENT) \
+ ASSERT_IMPLEMENTS_child_begin(&CLASS::child_begin); \
+ ASSERT_IMPLEMENTS_child_begin(&CLASS::child_end);
+#include "clang/AST/CommentNodes.inc"
+#undef COMMENT
+#undef ABSTRACT_COMMENT
+}
+
+#undef ASSERT_IMPLEMENTS_child_begin
+
+} // end unnamed namespace
+
+Comment::child_iterator Comment::child_begin() const {
+ switch (getCommentKind()) {
+ case NoCommentKind: llvm_unreachable("comment without a kind");
+#define ABSTRACT_COMMENT(COMMENT)
+#define COMMENT(CLASS, PARENT) \
+ case CLASS##Kind: \
+ return static_cast<const CLASS *>(this)->child_begin();
+#include "clang/AST/CommentNodes.inc"
+#undef COMMENT
+#undef ABSTRACT_COMMENT
+ }
+ llvm_unreachable("Unknown comment kind!");
+}
+
+Comment::child_iterator Comment::child_end() const {
+ switch (getCommentKind()) {
+ case NoCommentKind: llvm_unreachable("comment without a kind");
+#define ABSTRACT_COMMENT(COMMENT)
+#define COMMENT(CLASS, PARENT) \
+ case CLASS##Kind: \
+ return static_cast<const CLASS *>(this)->child_end();
+#include "clang/AST/CommentNodes.inc"
+#undef COMMENT
+#undef ABSTRACT_COMMENT
+ }
+ llvm_unreachable("Unknown comment kind!");
+}
+
+bool TextComment::isWhitespaceNoCache() const {
+ for (StringRef::const_iterator I = Text.begin(), E = Text.end();
+ I != E; ++I) {
+ const char C = *I;
+ if (C != ' ' && C != '\n' && C != '\r' &&
+ C != '\t' && C != '\f' && C != '\v')
+ return false;
+ }
+ return true;
+}
+
+bool ParagraphComment::isWhitespaceNoCache() const {
+ for (child_iterator I = child_begin(), E = child_end(); I != E; ++I) {
+ if (const TextComment *TC = dyn_cast<TextComment>(*I)) {
+ if (!TC->isWhitespace())
+ return false;
+ } else
+ return false;
+ }
+ return true;
+}
+
+const char *ParamCommandComment::getDirectionAsString(PassDirection D) {
+ switch (D) {
+ case ParamCommandComment::In:
+ return "[in]";
+ case ParamCommandComment::Out:
+ return "[out]";
+ case ParamCommandComment::InOut:
+ return "[in,out]";
+ }
+ llvm_unreachable("unknown PassDirection");
+}
+
+void DeclInfo::fill() {
+ assert(!IsFilled);
+
+ // Set defaults.
+ Kind = OtherKind;
+ TemplateKind = NotTemplate;
+ IsObjCMethod = false;
+ IsInstanceMethod = false;
+ IsClassMethod = false;
+ ParamVars = ArrayRef<const ParmVarDecl *>();
+ TemplateParameters = NULL;
+
+ if (!ThisDecl) {
+ // If there is no declaration, the defaults is our only guess.
+ IsFilled = true;
+ return;
+ }
+
+ Decl::Kind K = ThisDecl->getKind();
+ switch (K) {
+ default:
+ // Defaults are should be good for declarations we don't handle explicitly.
+ break;
+ case Decl::Function:
+ case Decl::CXXMethod:
+ case Decl::CXXConstructor:
+ case Decl::CXXDestructor:
+ case Decl::CXXConversion: {
+ const FunctionDecl *FD = cast<FunctionDecl>(ThisDecl);
+ Kind = FunctionKind;
+ ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
+ FD->getNumParams());
+ ResultType = FD->getResultType();
+ unsigned NumLists = FD->getNumTemplateParameterLists();
+ if (NumLists != 0) {
+ TemplateKind = TemplateSpecialization;
+ TemplateParameters =
+ FD->getTemplateParameterList(NumLists - 1);
+ }
+
+ if (K == Decl::CXXMethod || K == Decl::CXXConstructor ||
+ K == Decl::CXXDestructor || K == Decl::CXXConversion) {
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(ThisDecl);
+ IsInstanceMethod = MD->isInstance();
+ IsClassMethod = !IsInstanceMethod;
+ }
+ break;
+ }
+ case Decl::ObjCMethod: {
+ const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(ThisDecl);
+ Kind = FunctionKind;
+ ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(),
+ MD->param_size());
+ ResultType = MD->getResultType();
+ IsObjCMethod = true;
+ IsInstanceMethod = MD->isInstanceMethod();
+ IsClassMethod = !IsInstanceMethod;
+ break;
+ }
+ case Decl::FunctionTemplate: {
+ const FunctionTemplateDecl *FTD = cast<FunctionTemplateDecl>(ThisDecl);
+ Kind = FunctionKind;
+ TemplateKind = Template;
+ const FunctionDecl *FD = FTD->getTemplatedDecl();
+ ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
+ FD->getNumParams());
+ ResultType = FD->getResultType();
+ TemplateParameters = FTD->getTemplateParameters();
+ break;
+ }
+ case Decl::ClassTemplate: {
+ const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(ThisDecl);
+ Kind = ClassKind;
+ TemplateKind = Template;
+ TemplateParameters = CTD->getTemplateParameters();
+ break;
+ }
+ case Decl::ClassTemplatePartialSpecialization: {
+ const ClassTemplatePartialSpecializationDecl *CTPSD =
+ cast<ClassTemplatePartialSpecializationDecl>(ThisDecl);
+ Kind = ClassKind;
+ TemplateKind = TemplatePartialSpecialization;
+ TemplateParameters = CTPSD->getTemplateParameters();
+ break;
+ }
+ case Decl::ClassTemplateSpecialization:
+ Kind = ClassKind;
+ TemplateKind = TemplateSpecialization;
+ break;
+ case Decl::Record:
+ case Decl::CXXRecord:
+ Kind = ClassKind;
+ break;
+ case Decl::Var:
+ case Decl::Field:
+ case Decl::EnumConstant:
+ case Decl::ObjCIvar:
+ case Decl::ObjCAtDefsField:
+ Kind = VariableKind;
+ break;
+ case Decl::Namespace:
+ Kind = NamespaceKind;
+ break;
+ case Decl::Typedef:
+ case Decl::TypeAlias:
+ Kind = TypedefKind;
+ break;
+ case Decl::TypeAliasTemplate: {
+ const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(ThisDecl);
+ Kind = TypedefKind;
+ TemplateKind = Template;
+ TemplateParameters = TAT->getTemplateParameters();
+ break;
+ }
+ case Decl::Enum:
+ Kind = EnumKind;
+ break;
+ }
+
+ IsFilled = true;
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/lib/AST/CommentBriefParser.cpp b/lib/AST/CommentBriefParser.cpp
new file mode 100644
index 0000000..0aebc1e
--- /dev/null
+++ b/lib/AST/CommentBriefParser.cpp
@@ -0,0 +1,122 @@
+//===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/CommentBriefParser.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/StringSwitch.h"
+
+namespace clang {
+namespace comments {
+
+namespace {
+/// Convert all whitespace into spaces, remove leading and trailing spaces,
+/// compress multiple spaces into one.
+void cleanupBrief(std::string &S) {
+ bool PrevWasSpace = true;
+ std::string::iterator O = S.begin();
+ for (std::string::iterator I = S.begin(), E = S.end();
+ I != E; ++I) {
+ const char C = *I;
+ if (C == ' ' || C == '\n' || C == '\r' ||
+ C == '\t' || C == '\v' || C == '\f') {
+ if (!PrevWasSpace) {
+ *O++ = ' ';
+ PrevWasSpace = true;
+ }
+ continue;
+ } else {
+ *O++ = C;
+ PrevWasSpace = false;
+ }
+ }
+ if (O != S.begin() && *(O - 1) == ' ')
+ --O;
+
+ S.resize(O - S.begin());
+}
+} // unnamed namespace
+
+BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
+ L(L), Traits(Traits) {
+ // Get lookahead token.
+ ConsumeToken();
+}
+
+std::string BriefParser::Parse() {
+ std::string FirstParagraphOrBrief;
+ std::string ReturnsParagraph;
+ bool InFirstParagraph = true;
+ bool InBrief = false;
+ bool InReturns = false;
+
+ while (Tok.isNot(tok::eof)) {
+ if (Tok.is(tok::text)) {
+ if (InFirstParagraph || InBrief)
+ FirstParagraphOrBrief += Tok.getText();
+ else if (InReturns)
+ ReturnsParagraph += Tok.getText();
+ ConsumeToken();
+ continue;
+ }
+
+ if (Tok.is(tok::command)) {
+ StringRef Name = Tok.getCommandName();
+ if (Traits.isBriefCommand(Name)) {
+ FirstParagraphOrBrief.clear();
+ InBrief = true;
+ ConsumeToken();
+ continue;
+ }
+ if (Traits.isReturnsCommand(Name)) {
+ InReturns = true;
+ ReturnsParagraph += "Returns ";
+ }
+ // Block commands implicitly start a new paragraph.
+ if (Traits.isBlockCommand(Name)) {
+ // We found an implicit paragraph end.
+ InFirstParagraph = false;
+ if (InBrief)
+ break;
+ }
+ }
+
+ if (Tok.is(tok::newline)) {
+ if (InFirstParagraph || InBrief)
+ FirstParagraphOrBrief += ' ';
+ else if (InReturns)
+ ReturnsParagraph += ' ';
+ ConsumeToken();
+
+ if (Tok.is(tok::newline)) {
+ ConsumeToken();
+ // We found a paragraph end.
+ InFirstParagraph = false;
+ InReturns = false;
+ if (InBrief)
+ break;
+ }
+ continue;
+ }
+
+ // We didn't handle this token, so just drop it.
+ ConsumeToken();
+ }
+
+ cleanupBrief(FirstParagraphOrBrief);
+ if (!FirstParagraphOrBrief.empty())
+ return FirstParagraphOrBrief;
+
+ cleanupBrief(ReturnsParagraph);
+ return ReturnsParagraph;
+}
+
+} // end namespace comments
+} // end namespace clang
+
+
diff --git a/lib/AST/CommentCommandTraits.cpp b/lib/AST/CommentCommandTraits.cpp
new file mode 100644
index 0000000..d8ce1f3
--- /dev/null
+++ b/lib/AST/CommentCommandTraits.cpp
@@ -0,0 +1,134 @@
+//===--- CommentCommandTraits.cpp - Comment command properties --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/StringSwitch.h"
+
+namespace clang {
+namespace comments {
+
+// TODO: tablegen
+
+bool CommandTraits::isVerbatimBlockCommand(StringRef BeginName,
+ StringRef &EndName) const {
+ const char *Result = llvm::StringSwitch<const char *>(BeginName)
+ .Case("code", "endcode")
+ .Case("verbatim", "endverbatim")
+ .Case("htmlonly", "endhtmlonly")
+ .Case("latexonly", "endlatexonly")
+ .Case("xmlonly", "endxmlonly")
+ .Case("manonly", "endmanonly")
+ .Case("rtfonly", "endrtfonly")
+
+ .Case("dot", "enddot")
+ .Case("msc", "endmsc")
+
+ .Case("f$", "f$") // Inline LaTeX formula
+ .Case("f[", "f]") // Displayed LaTeX formula
+ .Case("f{", "f}") // LaTeX environment
+
+ .Default(NULL);
+
+ if (Result) {
+ EndName = Result;
+ return true;
+ }
+
+ for (VerbatimBlockCommandVector::const_iterator
+ I = VerbatimBlockCommands.begin(),
+ E = VerbatimBlockCommands.end();
+ I != E; ++I)
+ if (I->BeginName == BeginName) {
+ EndName = I->EndName;
+ return true;
+ }
+
+ return false;
+}
+
+bool CommandTraits::isVerbatimLineCommand(StringRef Name) const {
+ bool Result = isDeclarationCommand(Name) || llvm::StringSwitch<bool>(Name)
+ .Case("defgroup", true)
+ .Case("ingroup", true)
+ .Case("addtogroup", true)
+ .Case("weakgroup", true)
+ .Case("name", true)
+
+ .Case("section", true)
+ .Case("subsection", true)
+ .Case("subsubsection", true)
+ .Case("paragraph", true)
+
+ .Case("mainpage", true)
+ .Case("subpage", true)
+ .Case("ref", true)
+
+ .Default(false);
+
+ if (Result)
+ return true;
+
+ for (VerbatimLineCommandVector::const_iterator
+ I = VerbatimLineCommands.begin(),
+ E = VerbatimLineCommands.end();
+ I != E; ++I)
+ if (I->Name == Name)
+ return true;
+
+ return false;
+}
+
+bool CommandTraits::isDeclarationCommand(StringRef Name) const {
+ return llvm::StringSwitch<bool>(Name)
+ // Doxygen commands.
+ .Case("fn", true)
+ .Case("var", true)
+ .Case("property", true)
+ .Case("typedef", true)
+
+ .Case("overload", true)
+
+ // HeaderDoc commands.
+ .Case("class", true)
+ .Case("interface", true)
+ .Case("protocol", true)
+ .Case("category", true)
+ .Case("template", true)
+ .Case("function", true)
+ .Case("method", true)
+ .Case("callback", true)
+ .Case("var", true)
+ .Case("const", true)
+ .Case("constant", true)
+ .Case("property", true)
+ .Case("struct", true)
+ .Case("union", true)
+ .Case("typedef", true)
+ .Case("enum", true)
+
+ .Default(false);
+}
+
+void CommandTraits::addVerbatimBlockCommand(StringRef BeginName,
+ StringRef EndName) {
+ VerbatimBlockCommand VBC;
+ VBC.BeginName = BeginName;
+ VBC.EndName = EndName;
+ VerbatimBlockCommands.push_back(VBC);
+}
+
+void CommandTraits::addVerbatimLineCommand(StringRef Name) {
+ VerbatimLineCommand VLC;
+ VLC.Name = Name;
+ VerbatimLineCommands.push_back(VLC);
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/lib/AST/CommentDumper.cpp b/lib/AST/CommentDumper.cpp
new file mode 100644
index 0000000..dffc823
--- /dev/null
+++ b/lib/AST/CommentDumper.cpp
@@ -0,0 +1,231 @@
+//===--- CommentDumper.cpp - Dumping implementation for Comment ASTs ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/CommentVisitor.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace comments {
+
+namespace {
+class CommentDumper: public comments::ConstCommentVisitor<CommentDumper> {
+ raw_ostream &OS;
+ SourceManager *SM;
+ unsigned IndentLevel;
+
+public:
+ CommentDumper(raw_ostream &OS, SourceManager *SM) :
+ OS(OS), SM(SM), IndentLevel(0)
+ { }
+
+ void dumpIndent() const {
+ for (unsigned i = 1, e = IndentLevel; i < e; ++i)
+ OS << " ";
+ }
+
+ void dumpLocation(SourceLocation Loc) {
+ if (SM)
+ Loc.print(OS, *SM);
+ }
+
+ void dumpSourceRange(const Comment *C);
+
+ void dumpComment(const Comment *C);
+
+ void dumpSubtree(const Comment *C);
+
+ // Inline content.
+ void visitTextComment(const TextComment *C);
+ void visitInlineCommandComment(const InlineCommandComment *C);
+ void visitHTMLStartTagComment(const HTMLStartTagComment *C);
+ void visitHTMLEndTagComment(const HTMLEndTagComment *C);
+
+ // Block content.
+ void visitParagraphComment(const ParagraphComment *C);
+ void visitBlockCommandComment(const BlockCommandComment *C);
+ void visitParamCommandComment(const ParamCommandComment *C);
+ void visitTParamCommandComment(const TParamCommandComment *C);
+ void visitVerbatimBlockComment(const VerbatimBlockComment *C);
+ void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
+ void visitVerbatimLineComment(const VerbatimLineComment *C);
+
+ void visitFullComment(const FullComment *C);
+};
+
+void CommentDumper::dumpSourceRange(const Comment *C) {
+ if (!SM)
+ return;
+
+ SourceRange SR = C->getSourceRange();
+
+ OS << " <";
+ dumpLocation(SR.getBegin());
+ if (SR.getBegin() != SR.getEnd()) {
+ OS << ", ";
+ dumpLocation(SR.getEnd());
+ }
+ OS << ">";
+}
+
+void CommentDumper::dumpComment(const Comment *C) {
+ dumpIndent();
+ OS << "(" << C->getCommentKindName()
+ << " " << (const void *) C;
+ dumpSourceRange(C);
+}
+
+void CommentDumper::dumpSubtree(const Comment *C) {
+ ++IndentLevel;
+ if (C) {
+ visit(C);
+ for (Comment::child_iterator I = C->child_begin(),
+ E = C->child_end();
+ I != E; ++I) {
+ OS << '\n';
+ dumpSubtree(*I);
+ }
+ OS << ')';
+ } else {
+ dumpIndent();
+ OS << "<<<NULL>>>";
+ }
+ --IndentLevel;
+}
+
+void CommentDumper::visitTextComment(const TextComment *C) {
+ dumpComment(C);
+
+ OS << " Text=\"" << C->getText() << "\"";
+}
+
+void CommentDumper::visitInlineCommandComment(const InlineCommandComment *C) {
+ dumpComment(C);
+
+ OS << " Name=\"" << C->getCommandName() << "\"";
+ switch (C->getRenderKind()) {
+ case InlineCommandComment::RenderNormal:
+ OS << " RenderNormal";
+ break;
+ case InlineCommandComment::RenderBold:
+ OS << " RenderBold";
+ break;
+ case InlineCommandComment::RenderMonospaced:
+ OS << " RenderMonospaced";
+ break;
+ case InlineCommandComment::RenderEmphasized:
+ OS << " RenderEmphasized";
+ break;
+ }
+
+ for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
+ OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\"";
+}
+
+void CommentDumper::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
+ dumpComment(C);
+
+ OS << " Name=\"" << C->getTagName() << "\"";
+ if (C->getNumAttrs() != 0) {
+ OS << " Attrs: ";
+ for (unsigned i = 0, e = C->getNumAttrs(); i != e; ++i) {
+ const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
+ OS << " \"" << Attr.Name << "=\"" << Attr.Value << "\"";
+ }
+ }
+ if (C->isSelfClosing())
+ OS << " SelfClosing";
+}
+
+void CommentDumper::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
+ dumpComment(C);
+
+ OS << " Name=\"" << C->getTagName() << "\"";
+}
+
+void CommentDumper::visitParagraphComment(const ParagraphComment *C) {
+ dumpComment(C);
+}
+
+void CommentDumper::visitBlockCommandComment(const BlockCommandComment *C) {
+ dumpComment(C);
+
+ OS << " Name=\"" << C->getCommandName() << "\"";
+ for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
+ OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\"";
+}
+
+void CommentDumper::visitParamCommandComment(const ParamCommandComment *C) {
+ dumpComment(C);
+
+ OS << " " << ParamCommandComment::getDirectionAsString(C->getDirection());
+
+ if (C->isDirectionExplicit())
+ OS << " explicitly";
+ else
+ OS << " implicitly";
+
+ if (C->hasParamName())
+ OS << " Param=\"" << C->getParamName() << "\"";
+
+ if (C->isParamIndexValid())
+ OS << " ParamIndex=" << C->getParamIndex();
+}
+
+void CommentDumper::visitTParamCommandComment(const TParamCommandComment *C) {
+ dumpComment(C);
+
+ if (C->hasParamName()) {
+ OS << " Param=\"" << C->getParamName() << "\"";
+ }
+
+ if (C->isPositionValid()) {
+ OS << " Position=<";
+ for (unsigned i = 0, e = C->getDepth(); i != e; ++i) {
+ OS << C->getIndex(i);
+ if (i != e - 1)
+ OS << ", ";
+ }
+ OS << ">";
+ }
+}
+
+void CommentDumper::visitVerbatimBlockComment(const VerbatimBlockComment *C) {
+ dumpComment(C);
+
+ OS << " Name=\"" << C->getCommandName() << "\""
+ " CloseName=\"" << C->getCloseName() << "\"";
+}
+
+void CommentDumper::visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C) {
+ dumpComment(C);
+
+ OS << " Text=\"" << C->getText() << "\"";
+}
+
+void CommentDumper::visitVerbatimLineComment(const VerbatimLineComment *C) {
+ dumpComment(C);
+
+ OS << " Text=\"" << C->getText() << "\"";
+}
+
+void CommentDumper::visitFullComment(const FullComment *C) {
+ dumpComment(C);
+}
+
+} // unnamed namespace
+
+void Comment::dump(llvm::raw_ostream &OS, SourceManager *SM) const {
+ CommentDumper D(llvm::errs(), SM);
+ D.dumpSubtree(this);
+ llvm::errs() << '\n';
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/lib/AST/CommentLexer.cpp b/lib/AST/CommentLexer.cpp
new file mode 100644
index 0000000..b6516ec
--- /dev/null
+++ b/lib/AST/CommentLexer.cpp
@@ -0,0 +1,815 @@
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/Basic/ConvertUTF.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+namespace comments {
+
+void Token::dump(const Lexer &L, const SourceManager &SM) const {
+ llvm::errs() << "comments::Token Kind=" << Kind << " ";
+ Loc.dump(SM);
+ llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
+}
+
+namespace {
+bool isHTMLNamedCharacterReferenceCharacter(char C) {
+ return (C >= 'a' && C <= 'z') ||
+ (C >= 'A' && C <= 'Z');
+}
+
+bool isHTMLDecimalCharacterReferenceCharacter(char C) {
+ return C >= '0' && C <= '9';
+}
+
+bool isHTMLHexCharacterReferenceCharacter(char C) {
+ return (C >= '0' && C <= '9') ||
+ (C >= 'a' && C <= 'f') ||
+ (C >= 'A' && C <= 'F');
+}
+} // unnamed namespace
+
+StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
+ return llvm::StringSwitch<StringRef>(Name)
+ .Case("amp", "&")
+ .Case("lt", "<")
+ .Case("gt", ">")
+ .Case("quot", "\"")
+ .Case("apos", "\'")
+ .Default("");
+}
+
+StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
+ unsigned CodePoint = 0;
+ for (unsigned i = 0, e = Name.size(); i != e; ++i) {
+ assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
+ CodePoint *= 10;
+ CodePoint += Name[i] - '0';
+ }
+
+ char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
+ char *ResolvedPtr = Resolved;
+ if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
+ return StringRef(Resolved, ResolvedPtr - Resolved);
+ else
+ return StringRef();
+}
+
+StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
+ unsigned CodePoint = 0;
+ for (unsigned i = 0, e = Name.size(); i != e; ++i) {
+ CodePoint *= 16;
+ const char C = Name[i];
+ assert(isHTMLHexCharacterReferenceCharacter(C));
+ if (C >= '0' && C <= '9')
+ CodePoint += Name[i] - '0';
+ else if (C >= 'a' && C <= 'f')
+ CodePoint += Name[i] - 'a' + 10;
+ else
+ CodePoint += Name[i] - 'A' + 10;
+ }
+
+ char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
+ char *ResolvedPtr = Resolved;
+ if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
+ return StringRef(Resolved, ResolvedPtr - Resolved);
+ else
+ return StringRef();
+}
+
+void Lexer::skipLineStartingDecorations() {
+ // This function should be called only for C comments
+ assert(CommentState == LCS_InsideCComment);
+
+ if (BufferPtr == CommentEnd)
+ return;
+
+ switch (*BufferPtr) {
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v': {
+ const char *NewBufferPtr = BufferPtr;
+ NewBufferPtr++;
+ if (NewBufferPtr == CommentEnd)
+ return;
+
+ char C = *NewBufferPtr;
+ while (C == ' ' || C == '\t' || C == '\f' || C == '\v') {
+ NewBufferPtr++;
+ if (NewBufferPtr == CommentEnd)
+ return;
+ C = *NewBufferPtr;
+ }
+ if (C == '*')
+ BufferPtr = NewBufferPtr + 1;
+ break;
+ }
+ case '*':
+ BufferPtr++;
+ break;
+ }
+}
+
+namespace {
+/// Returns pointer to the first newline character in the string.
+const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ const char C = *BufferPtr;
+ if (C == '\n' || C == '\r')
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
+ if (BufferPtr == BufferEnd)
+ return BufferPtr;
+
+ if (*BufferPtr == '\n')
+ BufferPtr++;
+ else {
+ assert(*BufferPtr == '\r');
+ BufferPtr++;
+ if (BufferPtr != BufferEnd && *BufferPtr == '\n')
+ BufferPtr++;
+ }
+ return BufferPtr;
+}
+
+const char *skipNamedCharacterReference(const char *BufferPtr,
+ const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+const char *skipDecimalCharacterReference(const char *BufferPtr,
+ const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+const char *skipHexCharacterReference(const char *BufferPtr,
+ const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+bool isHTMLIdentifierStartingCharacter(char C) {
+ return (C >= 'a' && C <= 'z') ||
+ (C >= 'A' && C <= 'Z');
+}
+
+bool isHTMLIdentifierCharacter(char C) {
+ return (C >= 'a' && C <= 'z') ||
+ (C >= 'A' && C <= 'Z') ||
+ (C >= '0' && C <= '9');
+}
+
+const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (!isHTMLIdentifierCharacter(*BufferPtr))
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+/// Skip HTML string quoted in single or double quotes. Escaping quotes inside
+/// string allowed.
+///
+/// Returns pointer to closing quote.
+const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
+{
+ const char Quote = *BufferPtr;
+ assert(Quote == '\"' || Quote == '\'');
+
+ BufferPtr++;
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ const char C = *BufferPtr;
+ if (C == Quote && BufferPtr[-1] != '\\')
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+bool isHorizontalWhitespace(char C) {
+ return C == ' ' || C == '\t' || C == '\f' || C == '\v';
+}
+
+bool isWhitespace(char C) {
+ return C == ' ' || C == '\n' || C == '\r' ||
+ C == '\t' || C == '\f' || C == '\v';
+}
+
+const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (!isWhitespace(*BufferPtr))
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
+ return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
+}
+
+bool isCommandNameCharacter(char C) {
+ return (C >= 'a' && C <= 'z') ||
+ (C >= 'A' && C <= 'Z') ||
+ (C >= '0' && C <= '9');
+}
+
+const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (!isCommandNameCharacter(*BufferPtr))
+ return BufferPtr;
+ }
+ return BufferEnd;
+}
+
+/// Return the one past end pointer for BCPL comments.
+/// Handles newlines escaped with backslash or trigraph for backslahs.
+const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
+ const char *CurPtr = BufferPtr;
+ while (CurPtr != BufferEnd) {
+ char C = *CurPtr;
+ while (C != '\n' && C != '\r') {
+ CurPtr++;
+ if (CurPtr == BufferEnd)
+ return BufferEnd;
+ C = *CurPtr;
+ }
+ // We found a newline, check if it is escaped.
+ const char *EscapePtr = CurPtr - 1;
+ while(isHorizontalWhitespace(*EscapePtr))
+ EscapePtr--;
+
+ if (*EscapePtr == '\\' ||
+ (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
+ EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
+ // We found an escaped newline.
+ CurPtr = skipNewline(CurPtr, BufferEnd);
+ } else
+ return CurPtr; // Not an escaped newline.
+ }
+ return BufferEnd;
+}
+
+/// Return the one past end pointer for C comments.
+/// Very dumb, does not handle escaped newlines or trigraphs.
+const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
+ for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
+ if (*BufferPtr == '*') {
+ assert(BufferPtr + 1 != BufferEnd);
+ if (*(BufferPtr + 1) == '/')
+ return BufferPtr;
+ }
+ }
+ llvm_unreachable("buffer end hit before '*/' was seen");
+}
+} // unnamed namespace
+
+void Lexer::lexCommentText(Token &T) {
+ assert(CommentState == LCS_InsideBCPLComment ||
+ CommentState == LCS_InsideCComment);
+
+ switch (State) {
+ case LS_Normal:
+ break;
+ case LS_VerbatimBlockFirstLine:
+ lexVerbatimBlockFirstLine(T);
+ return;
+ case LS_VerbatimBlockBody:
+ lexVerbatimBlockBody(T);
+ return;
+ case LS_VerbatimLineText:
+ lexVerbatimLineText(T);
+ return;
+ case LS_HTMLStartTag:
+ lexHTMLStartTag(T);
+ return;
+ case LS_HTMLEndTag:
+ lexHTMLEndTag(T);
+ return;
+ }
+
+ assert(State == LS_Normal);
+
+ const char *TokenPtr = BufferPtr;
+ assert(TokenPtr < CommentEnd);
+ while (TokenPtr != CommentEnd) {
+ switch(*TokenPtr) {
+ case '\\':
+ case '@': {
+ TokenPtr++;
+ if (TokenPtr == CommentEnd) {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ char C = *TokenPtr;
+ switch (C) {
+ default:
+ break;
+
+ case '\\': case '@': case '&': case '$':
+ case '#': case '<': case '>': case '%':
+ case '\"': case '.': case ':':
+ // This is one of \\ \@ \& \$ etc escape sequences.
+ TokenPtr++;
+ if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
+ // This is the \:: escape sequence.
+ TokenPtr++;
+ }
+ StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
+ formTokenWithChars(T, TokenPtr, tok::text);
+ T.setText(UnescapedText);
+ return;
+ }
+
+ // Don't make zero-length commands.
+ if (!isCommandNameCharacter(*TokenPtr)) {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+
+ TokenPtr = skipCommandName(TokenPtr, CommentEnd);
+ unsigned Length = TokenPtr - (BufferPtr + 1);
+
+ // Hardcoded support for lexing LaTeX formula commands
+ // \f$ \f[ \f] \f{ \f} as a single command.
+ if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
+ C = *TokenPtr;
+ if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') {
+ TokenPtr++;
+ Length++;
+ }
+ }
+
+ const StringRef CommandName(BufferPtr + 1, Length);
+ StringRef EndName;
+
+ if (Traits.isVerbatimBlockCommand(CommandName, EndName)) {
+ setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, EndName);
+ return;
+ }
+ if (Traits.isVerbatimLineCommand(CommandName)) {
+ setupAndLexVerbatimLine(T, TokenPtr);
+ return;
+ }
+ formTokenWithChars(T, TokenPtr, tok::command);
+ T.setCommandName(CommandName);
+ return;
+ }
+
+ case '&':
+ lexHTMLCharacterReference(T);
+ return;
+
+ case '<': {
+ TokenPtr++;
+ if (TokenPtr == CommentEnd) {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ const char C = *TokenPtr;
+ if (isHTMLIdentifierStartingCharacter(C))
+ setupAndLexHTMLStartTag(T);
+ else if (C == '/')
+ setupAndLexHTMLEndTag(T);
+ else
+ formTextToken(T, TokenPtr);
+
+ return;
+ }
+
+ case '\n':
+ case '\r':
+ TokenPtr = skipNewline(TokenPtr, CommentEnd);
+ formTokenWithChars(T, TokenPtr, tok::newline);
+
+ if (CommentState == LCS_InsideCComment)
+ skipLineStartingDecorations();
+ return;
+
+ default: {
+ while (true) {
+ TokenPtr++;
+ if (TokenPtr == CommentEnd)
+ break;
+ const char C = *TokenPtr;
+ if(C == '\n' || C == '\r' ||
+ C == '\\' || C == '@' || C == '&' || C == '<')
+ break;
+ }
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ }
+ }
+}
+
+void Lexer::setupAndLexVerbatimBlock(Token &T,
+ const char *TextBegin,
+ char Marker, StringRef EndName) {
+ VerbatimBlockEndCommandName.clear();
+ VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
+ VerbatimBlockEndCommandName.append(EndName);
+
+ StringRef Name(BufferPtr + 1, TextBegin - (BufferPtr + 1));
+ formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
+ T.setVerbatimBlockName(Name);
+
+ // If there is a newline following the verbatim opening command, skip the
+ // newline so that we don't create an tok::verbatim_block_line with empty
+ // text content.
+ if (BufferPtr != CommentEnd) {
+ const char C = *BufferPtr;
+ if (C == '\n' || C == '\r') {
+ BufferPtr = skipNewline(BufferPtr, CommentEnd);
+ State = LS_VerbatimBlockBody;
+ return;
+ }
+ }
+
+ State = LS_VerbatimBlockFirstLine;
+}
+
+void Lexer::lexVerbatimBlockFirstLine(Token &T) {
+again:
+ assert(BufferPtr < CommentEnd);
+
+ // FIXME: It would be better to scan the text once, finding either the block
+ // end command or newline.
+ //
+ // Extract current line.
+ const char *Newline = findNewline(BufferPtr, CommentEnd);
+ StringRef Line(BufferPtr, Newline - BufferPtr);
+
+ // Look for end command in current line.
+ size_t Pos = Line.find(VerbatimBlockEndCommandName);
+ const char *TextEnd;
+ const char *NextLine;
+ if (Pos == StringRef::npos) {
+ // Current line is completely verbatim.
+ TextEnd = Newline;
+ NextLine = skipNewline(Newline, CommentEnd);
+ } else if (Pos == 0) {
+ // Current line contains just an end command.
+ const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
+ StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
+ formTokenWithChars(T, End, tok::verbatim_block_end);
+ T.setVerbatimBlockName(Name);
+ State = LS_Normal;
+ return;
+ } else {
+ // There is some text, followed by end command. Extract text first.
+ TextEnd = BufferPtr + Pos;
+ NextLine = TextEnd;
+ // If there is only whitespace before end command, skip whitespace.
+ if (isWhitespace(BufferPtr, TextEnd)) {
+ BufferPtr = TextEnd;
+ goto again;
+ }
+ }
+
+ StringRef Text(BufferPtr, TextEnd - BufferPtr);
+ formTokenWithChars(T, NextLine, tok::verbatim_block_line);
+ T.setVerbatimBlockText(Text);
+
+ State = LS_VerbatimBlockBody;
+}
+
+void Lexer::lexVerbatimBlockBody(Token &T) {
+ assert(State == LS_VerbatimBlockBody);
+
+ if (CommentState == LCS_InsideCComment)
+ skipLineStartingDecorations();
+
+ lexVerbatimBlockFirstLine(T);
+}
+
+void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin) {
+ const StringRef Name(BufferPtr + 1, TextBegin - BufferPtr - 1);
+ formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
+ T.setVerbatimLineName(Name);
+
+ State = LS_VerbatimLineText;
+}
+
+void Lexer::lexVerbatimLineText(Token &T) {
+ assert(State == LS_VerbatimLineText);
+
+ // Extract current line.
+ const char *Newline = findNewline(BufferPtr, CommentEnd);
+ const StringRef Text(BufferPtr, Newline - BufferPtr);
+ formTokenWithChars(T, Newline, tok::verbatim_line_text);
+ T.setVerbatimLineText(Text);
+
+ State = LS_Normal;
+}
+
+void Lexer::lexHTMLCharacterReference(Token &T) {
+ const char *TokenPtr = BufferPtr;
+ assert(*TokenPtr == '&');
+ TokenPtr++;
+ if (TokenPtr == CommentEnd) {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ const char *NamePtr;
+ bool isNamed = false;
+ bool isDecimal = false;
+ char C = *TokenPtr;
+ if (isHTMLNamedCharacterReferenceCharacter(C)) {
+ NamePtr = TokenPtr;
+ TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
+ isNamed = true;
+ } else if (C == '#') {
+ TokenPtr++;
+ if (TokenPtr == CommentEnd) {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ C = *TokenPtr;
+ if (isHTMLDecimalCharacterReferenceCharacter(C)) {
+ NamePtr = TokenPtr;
+ TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
+ isDecimal = true;
+ } else if (C == 'x' || C == 'X') {
+ TokenPtr++;
+ NamePtr = TokenPtr;
+ TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
+ } else {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ } else {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
+ *TokenPtr != ';') {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ StringRef Name(NamePtr, TokenPtr - NamePtr);
+ TokenPtr++; // Skip semicolon.
+ StringRef Resolved;
+ if (isNamed)
+ Resolved = resolveHTMLNamedCharacterReference(Name);
+ else if (isDecimal)
+ Resolved = resolveHTMLDecimalCharacterReference(Name);
+ else
+ Resolved = resolveHTMLHexCharacterReference(Name);
+
+ if (Resolved.empty()) {
+ formTextToken(T, TokenPtr);
+ return;
+ }
+ formTokenWithChars(T, TokenPtr, tok::text);
+ T.setText(Resolved);
+ return;
+}
+
+void Lexer::setupAndLexHTMLStartTag(Token &T) {
+ assert(BufferPtr[0] == '<' &&
+ isHTMLIdentifierStartingCharacter(BufferPtr[1]));
+ const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
+
+ StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
+ formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
+ T.setHTMLTagStartName(Name);
+
+ BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
+
+ const char C = *BufferPtr;
+ if (BufferPtr != CommentEnd &&
+ (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
+ State = LS_HTMLStartTag;
+}
+
+void Lexer::lexHTMLStartTag(Token &T) {
+ assert(State == LS_HTMLStartTag);
+
+ const char *TokenPtr = BufferPtr;
+ char C = *TokenPtr;
+ if (isHTMLIdentifierCharacter(C)) {
+ TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
+ StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
+ formTokenWithChars(T, TokenPtr, tok::html_ident);
+ T.setHTMLIdent(Ident);
+ } else {
+ switch (C) {
+ case '=':
+ TokenPtr++;
+ formTokenWithChars(T, TokenPtr, tok::html_equals);
+ break;
+ case '\"':
+ case '\'': {
+ const char *OpenQuote = TokenPtr;
+ TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
+ const char *ClosingQuote = TokenPtr;
+ if (TokenPtr != CommentEnd) // Skip closing quote.
+ TokenPtr++;
+ formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
+ T.setHTMLQuotedString(StringRef(OpenQuote + 1,
+ ClosingQuote - (OpenQuote + 1)));
+ break;
+ }
+ case '>':
+ TokenPtr++;
+ formTokenWithChars(T, TokenPtr, tok::html_greater);
+ State = LS_Normal;
+ return;
+ case '/':
+ TokenPtr++;
+ if (TokenPtr != CommentEnd && *TokenPtr == '>') {
+ TokenPtr++;
+ formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
+ } else
+ formTextToken(T, TokenPtr);
+
+ State = LS_Normal;
+ return;
+ }
+ }
+
+ // Now look ahead and return to normal state if we don't see any HTML tokens
+ // ahead.
+ BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
+ if (BufferPtr == CommentEnd) {
+ State = LS_Normal;
+ return;
+ }
+
+ C = *BufferPtr;
+ if (!isHTMLIdentifierStartingCharacter(C) &&
+ C != '=' && C != '\"' && C != '\'' && C != '>') {
+ State = LS_Normal;
+ return;
+ }
+}
+
+void Lexer::setupAndLexHTMLEndTag(Token &T) {
+ assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
+
+ const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
+ const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
+
+ const char *End = skipWhitespace(TagNameEnd, CommentEnd);
+
+ formTokenWithChars(T, End, tok::html_end_tag);
+ T.setHTMLTagEndName(StringRef(TagNameBegin, TagNameEnd - TagNameBegin));
+
+ if (BufferPtr != CommentEnd && *BufferPtr == '>')
+ State = LS_HTMLEndTag;
+}
+
+void Lexer::lexHTMLEndTag(Token &T) {
+ assert(BufferPtr != CommentEnd && *BufferPtr == '>');
+
+ formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
+ State = LS_Normal;
+}
+
+Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits,
+ SourceLocation FileLoc, const CommentOptions &CommOpts,
+ const char *BufferStart, const char *BufferEnd):
+ Allocator(Allocator), Traits(Traits),
+ BufferStart(BufferStart), BufferEnd(BufferEnd),
+ FileLoc(FileLoc), CommOpts(CommOpts), BufferPtr(BufferStart),
+ CommentState(LCS_BeforeComment), State(LS_Normal) {
+}
+
+void Lexer::lex(Token &T) {
+again:
+ switch (CommentState) {
+ case LCS_BeforeComment:
+ if (BufferPtr == BufferEnd) {
+ formTokenWithChars(T, BufferPtr, tok::eof);
+ return;
+ }
+
+ assert(*BufferPtr == '/');
+ BufferPtr++; // Skip first slash.
+ switch(*BufferPtr) {
+ case '/': { // BCPL comment.
+ BufferPtr++; // Skip second slash.
+
+ if (BufferPtr != BufferEnd) {
+ // Skip Doxygen magic marker, if it is present.
+ // It might be missing because of a typo //< or /*<, or because we
+ // merged this non-Doxygen comment into a bunch of Doxygen comments
+ // around it: /** ... */ /* ... */ /** ... */
+ const char C = *BufferPtr;
+ if (C == '/' || C == '!')
+ BufferPtr++;
+ }
+
+ // Skip less-than symbol that marks trailing comments.
+ // Skip it even if the comment is not a Doxygen one, because //< and /*<
+ // are frequent typos.
+ if (BufferPtr != BufferEnd && *BufferPtr == '<')
+ BufferPtr++;
+
+ CommentState = LCS_InsideBCPLComment;
+ if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
+ State = LS_Normal;
+ CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
+ goto again;
+ }
+ case '*': { // C comment.
+ BufferPtr++; // Skip star.
+
+ // Skip Doxygen magic marker.
+ const char C = *BufferPtr;
+ if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
+ BufferPtr++;
+
+ // Skip less-than symbol that marks trailing comments.
+ if (BufferPtr != BufferEnd && *BufferPtr == '<')
+ BufferPtr++;
+
+ CommentState = LCS_InsideCComment;
+ State = LS_Normal;
+ CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
+ goto again;
+ }
+ default:
+ llvm_unreachable("second character of comment should be '/' or '*'");
+ }
+
+ case LCS_BetweenComments: {
+ // Consecutive comments are extracted only if there is only whitespace
+ // between them. So we can search for the start of the next comment.
+ const char *EndWhitespace = BufferPtr;
+ while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
+ EndWhitespace++;
+
+ // Turn any whitespace between comments (and there is only whitespace
+ // between them -- guaranteed by comment extraction) into a newline. We
+ // have two newlines between C comments in total (first one was synthesized
+ // after a comment).
+ formTokenWithChars(T, EndWhitespace, tok::newline);
+
+ CommentState = LCS_BeforeComment;
+ break;
+ }
+
+ case LCS_InsideBCPLComment:
+ case LCS_InsideCComment:
+ if (BufferPtr != CommentEnd) {
+ lexCommentText(T);
+ break;
+ } else {
+ // Skip C comment closing sequence.
+ if (CommentState == LCS_InsideCComment) {
+ assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
+ BufferPtr += 2;
+ assert(BufferPtr <= BufferEnd);
+
+ // Synthenize newline just after the C comment, regardless if there is
+ // actually a newline.
+ formTokenWithChars(T, BufferPtr, tok::newline);
+
+ CommentState = LCS_BetweenComments;
+ break;
+ } else {
+ // Don't synthesized a newline after BCPL comment.
+ CommentState = LCS_BetweenComments;
+ goto again;
+ }
+ }
+ }
+}
+
+StringRef Lexer::getSpelling(const Token &Tok,
+ const SourceManager &SourceMgr,
+ bool *Invalid) const {
+ SourceLocation Loc = Tok.getLocation();
+ std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
+
+ bool InvalidTemp = false;
+ StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
+ if (InvalidTemp) {
+ *Invalid = true;
+ return StringRef();
+ }
+
+ const char *Begin = File.data() + LocInfo.second;
+ return StringRef(Begin, Tok.getLength());
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp
new file mode 100644
index 0000000..43abf6a
--- /dev/null
+++ b/lib/AST/CommentParser.cpp
@@ -0,0 +1,722 @@
+//===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/CommentParser.h"
+#include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentDiagnostic.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+namespace comments {
+
+/// Re-lexes a sequence of tok::text tokens.
+class TextTokenRetokenizer {
+ llvm::BumpPtrAllocator &Allocator;
+ Parser &P;
+
+ /// This flag is set when there are no more tokens we can fetch from lexer.
+ bool NoMoreInterestingTokens;
+
+ /// Token buffer: tokens we have processed and lookahead.
+ SmallVector<Token, 16> Toks;
+
+ /// A position in \c Toks.
+ struct Position {
+ unsigned CurToken;
+ const char *BufferStart;
+ const char *BufferEnd;
+ const char *BufferPtr;
+ SourceLocation BufferStartLoc;
+ };
+
+ /// Current position in Toks.
+ Position Pos;
+
+ bool isEnd() const {
+ return Pos.CurToken >= Toks.size();
+ }
+
+ /// Sets up the buffer pointers to point to current token.
+ void setupBuffer() {
+ assert(!isEnd());
+ const Token &Tok = Toks[Pos.CurToken];
+
+ Pos.BufferStart = Tok.getText().begin();
+ Pos.BufferEnd = Tok.getText().end();
+ Pos.BufferPtr = Pos.BufferStart;
+ Pos.BufferStartLoc = Tok.getLocation();
+ }
+
+ SourceLocation getSourceLocation() const {
+ const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
+ return Pos.BufferStartLoc.getLocWithOffset(CharNo);
+ }
+
+ char peek() const {
+ assert(!isEnd());
+ assert(Pos.BufferPtr != Pos.BufferEnd);
+ return *Pos.BufferPtr;
+ }
+
+ void consumeChar() {
+ assert(!isEnd());
+ assert(Pos.BufferPtr != Pos.BufferEnd);
+ Pos.BufferPtr++;
+ if (Pos.BufferPtr == Pos.BufferEnd) {
+ Pos.CurToken++;
+ if (isEnd() && !addToken())
+ return;
+
+ assert(!isEnd());
+ setupBuffer();
+ }
+ }
+
+ /// Add a token.
+ /// Returns true on success, false if there are no interesting tokens to
+ /// fetch from lexer.
+ bool addToken() {
+ if (NoMoreInterestingTokens)
+ return false;
+
+ if (P.Tok.is(tok::newline)) {
+ // If we see a single newline token between text tokens, skip it.
+ Token Newline = P.Tok;
+ P.consumeToken();
+ if (P.Tok.isNot(tok::text)) {
+ P.putBack(Newline);
+ NoMoreInterestingTokens = true;
+ return false;
+ }
+ }
+ if (P.Tok.isNot(tok::text)) {
+ NoMoreInterestingTokens = true;
+ return false;
+ }
+
+ Toks.push_back(P.Tok);
+ P.consumeToken();
+ if (Toks.size() == 1)
+ setupBuffer();
+ return true;
+ }
+
+ static bool isWhitespace(char C) {
+ return C == ' ' || C == '\n' || C == '\r' ||
+ C == '\t' || C == '\f' || C == '\v';
+ }
+
+ void consumeWhitespace() {
+ while (!isEnd()) {
+ if (isWhitespace(peek()))
+ consumeChar();
+ else
+ break;
+ }
+ }
+
+ void formTokenWithChars(Token &Result,
+ SourceLocation Loc,
+ const char *TokBegin,
+ unsigned TokLength,
+ StringRef Text) {
+ Result.setLocation(Loc);
+ Result.setKind(tok::text);
+ Result.setLength(TokLength);
+#ifndef NDEBUG
+ Result.TextPtr1 = "<UNSET>";
+ Result.TextLen1 = 7;
+#endif
+ Result.setText(Text);
+ }
+
+public:
+ TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
+ Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
+ Pos.CurToken = 0;
+ addToken();
+ }
+
+ /// Extract a word -- sequence of non-whitespace characters.
+ bool lexWord(Token &Tok) {
+ if (isEnd())
+ return false;
+
+ Position SavedPos = Pos;
+
+ consumeWhitespace();
+ SmallString<32> WordText;
+ const char *WordBegin = Pos.BufferPtr;
+ SourceLocation Loc = getSourceLocation();
+ while (!isEnd()) {
+ const char C = peek();
+ if (!isWhitespace(C)) {
+ WordText.push_back(C);
+ consumeChar();
+ } else
+ break;
+ }
+ const unsigned Length = WordText.size();
+ if (Length == 0) {
+ Pos = SavedPos;
+ return false;
+ }
+
+ char *TextPtr = Allocator.Allocate<char>(Length + 1);
+
+ memcpy(TextPtr, WordText.c_str(), Length + 1);
+ StringRef Text = StringRef(TextPtr, Length);
+
+ formTokenWithChars(Tok, Loc, WordBegin,
+ Pos.BufferPtr - WordBegin, Text);
+ return true;
+ }
+
+ bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
+ if (isEnd())
+ return false;
+
+ Position SavedPos = Pos;
+
+ consumeWhitespace();
+ SmallString<32> WordText;
+ const char *WordBegin = Pos.BufferPtr;
+ SourceLocation Loc = getSourceLocation();
+ bool Error = false;
+ if (!isEnd()) {
+ const char C = peek();
+ if (C == OpenDelim) {
+ WordText.push_back(C);
+ consumeChar();
+ } else
+ Error = true;
+ }
+ char C = '\0';
+ while (!Error && !isEnd()) {
+ C = peek();
+ WordText.push_back(C);
+ consumeChar();
+ if (C == CloseDelim)
+ break;
+ }
+ if (!Error && C != CloseDelim)
+ Error = true;
+
+ if (Error) {
+ Pos = SavedPos;
+ return false;
+ }
+
+ const unsigned Length = WordText.size();
+ char *TextPtr = Allocator.Allocate<char>(Length + 1);
+
+ memcpy(TextPtr, WordText.c_str(), Length + 1);
+ StringRef Text = StringRef(TextPtr, Length);
+
+ formTokenWithChars(Tok, Loc, WordBegin,
+ Pos.BufferPtr - WordBegin, Text);
+ return true;
+ }
+
+ /// Put back tokens that we didn't consume.
+ void putBackLeftoverTokens() {
+ if (isEnd())
+ return;
+
+ bool HavePartialTok = false;
+ Token PartialTok;
+ if (Pos.BufferPtr != Pos.BufferStart) {
+ formTokenWithChars(PartialTok, getSourceLocation(),
+ Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
+ StringRef(Pos.BufferPtr,
+ Pos.BufferEnd - Pos.BufferPtr));
+ HavePartialTok = true;
+ Pos.CurToken++;
+ }
+
+ P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
+ Pos.CurToken = Toks.size();
+
+ if (HavePartialTok)
+ P.putBack(PartialTok);
+ }
+};
+
+Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
+ const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
+ const CommandTraits &Traits):
+ L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
+ Traits(Traits) {
+ consumeToken();
+}
+
+void Parser::parseParamCommandArgs(ParamCommandComment *PC,
+ TextTokenRetokenizer &Retokenizer) {
+ Token Arg;
+ // Check if argument looks like direction specification: [dir]
+ // e.g., [in], [out], [in,out]
+ if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
+ S.actOnParamCommandDirectionArg(PC,
+ Arg.getLocation(),
+ Arg.getEndLocation(),
+ Arg.getText());
+
+ if (Retokenizer.lexWord(Arg))
+ S.actOnParamCommandParamNameArg(PC,
+ Arg.getLocation(),
+ Arg.getEndLocation(),
+ Arg.getText());
+}
+
+void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
+ TextTokenRetokenizer &Retokenizer) {
+ Token Arg;
+ if (Retokenizer.lexWord(Arg))
+ S.actOnTParamCommandParamNameArg(TPC,
+ Arg.getLocation(),
+ Arg.getEndLocation(),
+ Arg.getText());
+}
+
+void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
+ TextTokenRetokenizer &Retokenizer,
+ unsigned NumArgs) {
+ typedef BlockCommandComment::Argument Argument;
+ Argument *Args =
+ new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
+ unsigned ParsedArgs = 0;
+ Token Arg;
+ while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
+ Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
+ Arg.getEndLocation()),
+ Arg.getText());
+ ParsedArgs++;
+ }
+
+ S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
+}
+
+BlockCommandComment *Parser::parseBlockCommand() {
+ assert(Tok.is(tok::command));
+
+ ParamCommandComment *PC;
+ TParamCommandComment *TPC;
+ BlockCommandComment *BC;
+ bool IsParam = false;
+ bool IsTParam = false;
+ unsigned NumArgs = 0;
+ if (Traits.isParamCommand(Tok.getCommandName())) {
+ IsParam = true;
+ PC = S.actOnParamCommandStart(Tok.getLocation(),
+ Tok.getEndLocation(),
+ Tok.getCommandName());
+ } if (Traits.isTParamCommand(Tok.getCommandName())) {
+ IsTParam = true;
+ TPC = S.actOnTParamCommandStart(Tok.getLocation(),
+ Tok.getEndLocation(),
+ Tok.getCommandName());
+ } else {
+ NumArgs = Traits.getBlockCommandNumArgs(Tok.getCommandName());
+ BC = S.actOnBlockCommandStart(Tok.getLocation(),
+ Tok.getEndLocation(),
+ Tok.getCommandName());
+ }
+ consumeToken();
+
+ if (Tok.is(tok::command) && Traits.isBlockCommand(Tok.getCommandName())) {
+ // Block command ahead. We can't nest block commands, so pretend that this
+ // command has an empty argument.
+ ParagraphComment *Paragraph = S.actOnParagraphComment(
+ ArrayRef<InlineContentComment *>());
+ if (IsParam) {
+ S.actOnParamCommandFinish(PC, Paragraph);
+ return PC;
+ } else if (IsTParam) {
+ S.actOnTParamCommandFinish(TPC, Paragraph);
+ return TPC;
+ } else {
+ S.actOnBlockCommandFinish(BC, Paragraph);
+ return BC;
+ }
+ }
+
+ if (IsParam || IsTParam || NumArgs > 0) {
+ // In order to parse command arguments we need to retokenize a few
+ // following text tokens.
+ TextTokenRetokenizer Retokenizer(Allocator, *this);
+
+ if (IsParam)
+ parseParamCommandArgs(PC, Retokenizer);
+ else if (IsTParam)
+ parseTParamCommandArgs(TPC, Retokenizer);
+ else
+ parseBlockCommandArgs(BC, Retokenizer, NumArgs);
+
+ Retokenizer.putBackLeftoverTokens();
+ }
+
+ BlockContentComment *Block = parseParagraphOrBlockCommand();
+ // Since we have checked for a block command, we should have parsed a
+ // paragraph.
+ ParagraphComment *Paragraph = cast<ParagraphComment>(Block);
+ if (IsParam) {
+ S.actOnParamCommandFinish(PC, Paragraph);
+ return PC;
+ } else if (IsTParam) {
+ S.actOnTParamCommandFinish(TPC, Paragraph);
+ return TPC;
+ } else {
+ S.actOnBlockCommandFinish(BC, Paragraph);
+ return BC;
+ }
+}
+
+InlineCommandComment *Parser::parseInlineCommand() {
+ assert(Tok.is(tok::command));
+
+ const Token CommandTok = Tok;
+ consumeToken();
+
+ TextTokenRetokenizer Retokenizer(Allocator, *this);
+
+ Token ArgTok;
+ bool ArgTokValid = Retokenizer.lexWord(ArgTok);
+
+ InlineCommandComment *IC;
+ if (ArgTokValid) {
+ IC = S.actOnInlineCommand(CommandTok.getLocation(),
+ CommandTok.getEndLocation(),
+ CommandTok.getCommandName(),
+ ArgTok.getLocation(),
+ ArgTok.getEndLocation(),
+ ArgTok.getText());
+ } else {
+ IC = S.actOnInlineCommand(CommandTok.getLocation(),
+ CommandTok.getEndLocation(),
+ CommandTok.getCommandName());
+ }
+
+ Retokenizer.putBackLeftoverTokens();
+
+ return IC;
+}
+
+HTMLStartTagComment *Parser::parseHTMLStartTag() {
+ assert(Tok.is(tok::html_start_tag));
+ HTMLStartTagComment *HST =
+ S.actOnHTMLStartTagStart(Tok.getLocation(),
+ Tok.getHTMLTagStartName());
+ consumeToken();
+
+ SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::html_ident: {
+ Token Ident = Tok;
+ consumeToken();
+ if (Tok.isNot(tok::html_equals)) {
+ Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
+ Ident.getHTMLIdent()));
+ continue;
+ }
+ Token Equals = Tok;
+ consumeToken();
+ if (Tok.isNot(tok::html_quoted_string)) {
+ Diag(Tok.getLocation(),
+ diag::warn_doc_html_start_tag_expected_quoted_string)
+ << SourceRange(Equals.getLocation());
+ Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
+ Ident.getHTMLIdent()));
+ while (Tok.is(tok::html_equals) ||
+ Tok.is(tok::html_quoted_string))
+ consumeToken();
+ continue;
+ }
+ Attrs.push_back(HTMLStartTagComment::Attribute(
+ Ident.getLocation(),
+ Ident.getHTMLIdent(),
+ Equals.getLocation(),
+ SourceRange(Tok.getLocation(),
+ Tok.getEndLocation()),
+ Tok.getHTMLQuotedString()));
+ consumeToken();
+ continue;
+ }
+
+ case tok::html_greater:
+ S.actOnHTMLStartTagFinish(HST,
+ S.copyArray(llvm::makeArrayRef(Attrs)),
+ Tok.getLocation(),
+ /* IsSelfClosing = */ false);
+ consumeToken();
+ return HST;
+
+ case tok::html_slash_greater:
+ S.actOnHTMLStartTagFinish(HST,
+ S.copyArray(llvm::makeArrayRef(Attrs)),
+ Tok.getLocation(),
+ /* IsSelfClosing = */ true);
+ consumeToken();
+ return HST;
+
+ case tok::html_equals:
+ case tok::html_quoted_string:
+ Diag(Tok.getLocation(),
+ diag::warn_doc_html_start_tag_expected_ident_or_greater);
+ while (Tok.is(tok::html_equals) ||
+ Tok.is(tok::html_quoted_string))
+ consumeToken();
+ if (Tok.is(tok::html_ident) ||
+ Tok.is(tok::html_greater) ||
+ Tok.is(tok::html_slash_greater))
+ continue;
+
+ S.actOnHTMLStartTagFinish(HST,
+ S.copyArray(llvm::makeArrayRef(Attrs)),
+ SourceLocation(),
+ /* IsSelfClosing = */ false);
+ return HST;
+
+ default:
+ // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
+ S.actOnHTMLStartTagFinish(HST,
+ S.copyArray(llvm::makeArrayRef(Attrs)),
+ SourceLocation(),
+ /* IsSelfClosing = */ false);
+ bool StartLineInvalid;
+ const unsigned StartLine = SourceMgr.getPresumedLineNumber(
+ HST->getLocation(),
+ &StartLineInvalid);
+ bool EndLineInvalid;
+ const unsigned EndLine = SourceMgr.getPresumedLineNumber(
+ Tok.getLocation(),
+ &EndLineInvalid);
+ if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
+ Diag(Tok.getLocation(),
+ diag::warn_doc_html_start_tag_expected_ident_or_greater)
+ << HST->getSourceRange();
+ else {
+ Diag(Tok.getLocation(),
+ diag::warn_doc_html_start_tag_expected_ident_or_greater);
+ Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
+ << HST->getSourceRange();
+ }
+ return HST;
+ }
+ }
+}
+
+HTMLEndTagComment *Parser::parseHTMLEndTag() {
+ assert(Tok.is(tok::html_end_tag));
+ Token TokEndTag = Tok;
+ consumeToken();
+ SourceLocation Loc;
+ if (Tok.is(tok::html_greater)) {
+ Loc = Tok.getLocation();
+ consumeToken();
+ }
+
+ return S.actOnHTMLEndTag(TokEndTag.getLocation(),
+ Loc,
+ TokEndTag.getHTMLTagEndName());
+}
+
+BlockContentComment *Parser::parseParagraphOrBlockCommand() {
+ SmallVector<InlineContentComment *, 8> Content;
+
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::verbatim_block_begin:
+ case tok::verbatim_line_name:
+ case tok::eof:
+ assert(Content.size() != 0);
+ break; // Block content or EOF ahead, finish this parapgaph.
+
+ case tok::command:
+ if (Traits.isBlockCommand(Tok.getCommandName())) {
+ if (Content.size() == 0)
+ return parseBlockCommand();
+ break; // Block command ahead, finish this parapgaph.
+ }
+ if (Traits.isInlineCommand(Tok.getCommandName())) {
+ Content.push_back(parseInlineCommand());
+ continue;
+ }
+
+ // Not a block command, not an inline command ==> an unknown command.
+ Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
+ Tok.getEndLocation(),
+ Tok.getCommandName()));
+ consumeToken();
+ continue;
+
+ case tok::newline: {
+ consumeToken();
+ if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
+ consumeToken();
+ break; // Two newlines -- end of paragraph.
+ }
+ if (Content.size() > 0)
+ Content.back()->addTrailingNewline();
+ continue;
+ }
+
+ // Don't deal with HTML tag soup now.
+ case tok::html_start_tag:
+ Content.push_back(parseHTMLStartTag());
+ continue;
+
+ case tok::html_end_tag:
+ Content.push_back(parseHTMLEndTag());
+ continue;
+
+ case tok::text:
+ Content.push_back(S.actOnText(Tok.getLocation(),
+ Tok.getEndLocation(),
+ Tok.getText()));
+ consumeToken();
+ continue;
+
+ case tok::verbatim_block_line:
+ case tok::verbatim_block_end:
+ case tok::verbatim_line_text:
+ case tok::html_ident:
+ case tok::html_equals:
+ case tok::html_quoted_string:
+ case tok::html_greater:
+ case tok::html_slash_greater:
+ llvm_unreachable("should not see this token");
+ }
+ break;
+ }
+
+ return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
+}
+
+VerbatimBlockComment *Parser::parseVerbatimBlock() {
+ assert(Tok.is(tok::verbatim_block_begin));
+
+ VerbatimBlockComment *VB =
+ S.actOnVerbatimBlockStart(Tok.getLocation(),
+ Tok.getVerbatimBlockName());
+ consumeToken();
+
+ // Don't create an empty line if verbatim opening command is followed
+ // by a newline.
+ if (Tok.is(tok::newline))
+ consumeToken();
+
+ SmallVector<VerbatimBlockLineComment *, 8> Lines;
+ while (Tok.is(tok::verbatim_block_line) ||
+ Tok.is(tok::newline)) {
+ VerbatimBlockLineComment *Line;
+ if (Tok.is(tok::verbatim_block_line)) {
+ Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
+ Tok.getVerbatimBlockText());
+ consumeToken();
+ if (Tok.is(tok::newline)) {
+ consumeToken();
+ }
+ } else {
+ // Empty line, just a tok::newline.
+ Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
+ consumeToken();
+ }
+ Lines.push_back(Line);
+ }
+
+ if (Tok.is(tok::verbatim_block_end)) {
+ S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
+ Tok.getVerbatimBlockName(),
+ S.copyArray(llvm::makeArrayRef(Lines)));
+ consumeToken();
+ } else {
+ // Unterminated \\verbatim block
+ S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
+ S.copyArray(llvm::makeArrayRef(Lines)));
+ }
+
+ return VB;
+}
+
+VerbatimLineComment *Parser::parseVerbatimLine() {
+ assert(Tok.is(tok::verbatim_line_name));
+
+ Token NameTok = Tok;
+ consumeToken();
+
+ SourceLocation TextBegin;
+ StringRef Text;
+ // Next token might not be a tok::verbatim_line_text if verbatim line
+ // starting command comes just before a newline or comment end.
+ if (Tok.is(tok::verbatim_line_text)) {
+ TextBegin = Tok.getLocation();
+ Text = Tok.getVerbatimLineText();
+ } else {
+ TextBegin = NameTok.getEndLocation();
+ Text = "";
+ }
+
+ VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
+ NameTok.getVerbatimLineName(),
+ TextBegin,
+ Text);
+ consumeToken();
+ return VL;
+}
+
+BlockContentComment *Parser::parseBlockContent() {
+ switch (Tok.getKind()) {
+ case tok::text:
+ case tok::command:
+ case tok::html_start_tag:
+ case tok::html_end_tag:
+ return parseParagraphOrBlockCommand();
+
+ case tok::verbatim_block_begin:
+ return parseVerbatimBlock();
+
+ case tok::verbatim_line_name:
+ return parseVerbatimLine();
+
+ case tok::eof:
+ case tok::newline:
+ case tok::verbatim_block_line:
+ case tok::verbatim_block_end:
+ case tok::verbatim_line_text:
+ case tok::html_ident:
+ case tok::html_equals:
+ case tok::html_quoted_string:
+ case tok::html_greater:
+ case tok::html_slash_greater:
+ llvm_unreachable("should not see this token");
+ }
+ llvm_unreachable("bogus token kind");
+}
+
+FullComment *Parser::parseFullComment() {
+ // Skip newlines at the beginning of the comment.
+ while (Tok.is(tok::newline))
+ consumeToken();
+
+ SmallVector<BlockContentComment *, 8> Blocks;
+ while (Tok.isNot(tok::eof)) {
+ Blocks.push_back(parseBlockContent());
+
+ // Skip extra newlines after paragraph end.
+ while (Tok.is(tok::newline))
+ consumeToken();
+ }
+ return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
+}
+
+} // end namespace comments
+} // end namespace clang
diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp
new file mode 100644
index 0000000..c39ee57
--- /dev/null
+++ b/lib/AST/CommentSema.cpp
@@ -0,0 +1,739 @@
+//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentDiagnostic.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/StringSwitch.h"
+
+namespace clang {
+namespace comments {
+
+Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
+ DiagnosticsEngine &Diags, const CommandTraits &Traits) :
+ Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
+ ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
+}
+
+void Sema::setDecl(const Decl *D) {
+ if (!D)
+ return;
+
+ ThisDeclInfo = new (Allocator) DeclInfo;
+ ThisDeclInfo->ThisDecl = D;
+ ThisDeclInfo->IsFilled = false;
+}
+
+ParagraphComment *Sema::actOnParagraphComment(
+ ArrayRef<InlineContentComment *> Content) {
+ return new (Allocator) ParagraphComment(Content);
+}
+
+BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) {
+ return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
+}
+
+void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
+ ArrayRef<BlockCommandComment::Argument> Args) {
+ Command->setArgs(Args);
+}
+
+void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
+ ParagraphComment *Paragraph) {
+ Command->setParagraph(Paragraph);
+ checkBlockCommandEmptyParagraph(Command);
+ checkBlockCommandDuplicate(Command);
+ checkReturnsCommand(Command);
+}
+
+ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) {
+ ParamCommandComment *Command =
+ new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
+
+ if (!isFunctionDecl())
+ Diag(Command->getLocation(),
+ diag::warn_doc_param_not_attached_to_a_function_decl)
+ << Command->getCommandNameRange();
+
+ return Command;
+}
+
+void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg) {
+ ParamCommandComment::PassDirection Direction;
+ std::string ArgLower = Arg.lower();
+ // TODO: optimize: lower Name first (need an API in SmallString for that),
+ // after that StringSwitch.
+ if (ArgLower == "[in]")
+ Direction = ParamCommandComment::In;
+ else if (ArgLower == "[out]")
+ Direction = ParamCommandComment::Out;
+ else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
+ Direction = ParamCommandComment::InOut;
+ else {
+ // Remove spaces.
+ std::string::iterator O = ArgLower.begin();
+ for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
+ I != E; ++I) {
+ const char C = *I;
+ if (C != ' ' && C != '\n' && C != '\r' &&
+ C != '\t' && C != '\v' && C != '\f')
+ *O++ = C;
+ }
+ ArgLower.resize(O - ArgLower.begin());
+
+ bool RemovingWhitespaceHelped = false;
+ if (ArgLower == "[in]") {
+ Direction = ParamCommandComment::In;
+ RemovingWhitespaceHelped = true;
+ } else if (ArgLower == "[out]") {
+ Direction = ParamCommandComment::Out;
+ RemovingWhitespaceHelped = true;
+ } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
+ Direction = ParamCommandComment::InOut;
+ RemovingWhitespaceHelped = true;
+ } else {
+ Direction = ParamCommandComment::In;
+ RemovingWhitespaceHelped = false;
+ }
+
+ SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+ if (RemovingWhitespaceHelped)
+ Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
+ << ArgRange
+ << FixItHint::CreateReplacement(
+ ArgRange,
+ ParamCommandComment::getDirectionAsString(Direction));
+ else
+ Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
+ << ArgRange;
+ }
+ Command->setDirection(Direction, /* Explicit = */ true);
+}
+
+void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg) {
+ // Parser will not feed us more arguments than needed.
+ assert(Command->getNumArgs() == 0);
+
+ if (!Command->isDirectionExplicit()) {
+ // User didn't provide a direction argument.
+ Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
+ }
+ typedef BlockCommandComment::Argument Argument;
+ Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
+ ArgLocEnd),
+ Arg);
+ Command->setArgs(llvm::makeArrayRef(A, 1));
+
+ if (!isFunctionDecl()) {
+ // We already warned that this \\param is not attached to a function decl.
+ return;
+ }
+
+ ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
+
+ // Check that referenced parameter name is in the function decl.
+ const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
+ if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
+ Command->setParamIndex(ResolvedParamIndex);
+ if (ParamVarDocs[ResolvedParamIndex]) {
+ SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+ Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
+ << Arg << ArgRange;
+ ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
+ Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
+ << PrevCommand->getParamNameRange();
+ }
+ ParamVarDocs[ResolvedParamIndex] = Command;
+ return;
+ }
+
+ SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+ Diag(ArgLocBegin, diag::warn_doc_param_not_found)
+ << Arg << ArgRange;
+
+ // No parameters -- can't suggest a correction.
+ if (ParamVars.size() == 0)
+ return;
+
+ unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
+ if (ParamVars.size() == 1) {
+ // If function has only one parameter then only that parameter
+ // can be documented.
+ CorrectedParamIndex = 0;
+ } else {
+ // Do typo correction.
+ CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
+ }
+ if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
+ const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
+ if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
+ Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
+ << CorrectedII->getName()
+ << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
+ }
+
+ return;
+}
+
+void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
+ ParagraphComment *Paragraph) {
+ Command->setParagraph(Paragraph);
+ checkBlockCommandEmptyParagraph(Command);
+}
+
+TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) {
+ TParamCommandComment *Command =
+ new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
+
+ if (!isTemplateOrSpecialization())
+ Diag(Command->getLocation(),
+ diag::warn_doc_tparam_not_attached_to_a_template_decl)
+ << Command->getCommandNameRange();
+
+ return Command;
+}
+
+void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg) {
+ // Parser will not feed us more arguments than needed.
+ assert(Command->getNumArgs() == 0);
+
+ typedef BlockCommandComment::Argument Argument;
+ Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
+ ArgLocEnd),
+ Arg);
+ Command->setArgs(llvm::makeArrayRef(A, 1));
+
+ if (!isTemplateOrSpecialization()) {
+ // We already warned that this \\tparam is not attached to a template decl.
+ return;
+ }
+
+ const TemplateParameterList *TemplateParameters =
+ ThisDeclInfo->TemplateParameters;
+ SmallVector<unsigned, 2> Position;
+ if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
+ Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
+ llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
+ TemplateParameterDocs.find(Arg);
+ if (PrevCommandIt != TemplateParameterDocs.end()) {
+ SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+ Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
+ << Arg << ArgRange;
+ TParamCommandComment *PrevCommand = PrevCommandIt->second;
+ Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
+ << PrevCommand->getParamNameRange();
+ }
+ TemplateParameterDocs[Arg] = Command;
+ return;
+ }
+
+ SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+ Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
+ << Arg << ArgRange;
+
+ if (!TemplateParameters || TemplateParameters->size() == 0)
+ return;
+
+ StringRef CorrectedName;
+ if (TemplateParameters->size() == 1) {
+ const NamedDecl *Param = TemplateParameters->getParam(0);
+ const IdentifierInfo *II = Param->getIdentifier();
+ if (II)
+ CorrectedName = II->getName();
+ } else {
+ CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
+ }
+
+ if (!CorrectedName.empty()) {
+ Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
+ << CorrectedName
+ << FixItHint::CreateReplacement(ArgRange, CorrectedName);
+ }
+
+ return;
+}
+
+void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
+ ParagraphComment *Paragraph) {
+ Command->setParagraph(Paragraph);
+ checkBlockCommandEmptyParagraph(Command);
+}
+
+InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
+ SourceLocation CommandLocEnd,
+ StringRef CommandName) {
+ ArrayRef<InlineCommandComment::Argument> Args;
+ return new (Allocator) InlineCommandComment(
+ CommandLocBegin,
+ CommandLocEnd,
+ CommandName,
+ getInlineCommandRenderKind(CommandName),
+ Args);
+}
+
+InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
+ SourceLocation CommandLocEnd,
+ StringRef CommandName,
+ SourceLocation ArgLocBegin,
+ SourceLocation ArgLocEnd,
+ StringRef Arg) {
+ typedef InlineCommandComment::Argument Argument;
+ Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
+ ArgLocEnd),
+ Arg);
+
+ return new (Allocator) InlineCommandComment(
+ CommandLocBegin,
+ CommandLocEnd,
+ CommandName,
+ getInlineCommandRenderKind(CommandName),
+ llvm::makeArrayRef(A, 1));
+}
+
+InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Name) {
+ ArrayRef<InlineCommandComment::Argument> Args;
+ return new (Allocator) InlineCommandComment(
+ LocBegin, LocEnd, Name,
+ InlineCommandComment::RenderNormal,
+ Args);
+}
+
+TextComment *Sema::actOnText(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef Text) {
+ return new (Allocator) TextComment(LocBegin, LocEnd, Text);
+}
+
+VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
+ StringRef Name) {
+ return new (Allocator) VerbatimBlockComment(
+ Loc,
+ Loc.getLocWithOffset(1 + Name.size()),
+ Name);
+}
+
+VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
+ StringRef Text) {
+ return new (Allocator) VerbatimBlockLineComment(Loc, Text);
+}
+
+void Sema::actOnVerbatimBlockFinish(
+ VerbatimBlockComment *Block,
+ SourceLocation CloseNameLocBegin,
+ StringRef CloseName,
+ ArrayRef<VerbatimBlockLineComment *> Lines) {
+ Block->setCloseName(CloseName, CloseNameLocBegin);
+ Block->setLines(Lines);
+}
+
+VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
+ StringRef Name,
+ SourceLocation TextBegin,
+ StringRef Text) {
+ return new (Allocator) VerbatimLineComment(
+ LocBegin,
+ TextBegin.getLocWithOffset(Text.size()),
+ Name,
+ TextBegin,
+ Text);
+}
+
+HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
+ StringRef TagName) {
+ return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
+}
+
+void Sema::actOnHTMLStartTagFinish(
+ HTMLStartTagComment *Tag,
+ ArrayRef<HTMLStartTagComment::Attribute> Attrs,
+ SourceLocation GreaterLoc,
+ bool IsSelfClosing) {
+ Tag->setAttrs(Attrs);
+ Tag->setGreaterLoc(GreaterLoc);
+ if (IsSelfClosing)
+ Tag->setSelfClosing();
+ else if (!isHTMLEndTagForbidden(Tag->getTagName()))
+ HTMLOpenTags.push_back(Tag);
+}
+
+HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ StringRef TagName) {
+ HTMLEndTagComment *HET =
+ new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
+ if (isHTMLEndTagForbidden(TagName)) {
+ Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
+ << TagName << HET->getSourceRange();
+ return HET;
+ }
+
+ bool FoundOpen = false;
+ for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
+ I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
+ I != E; ++I) {
+ if ((*I)->getTagName() == TagName) {
+ FoundOpen = true;
+ break;
+ }
+ }
+ if (!FoundOpen) {
+ Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
+ << HET->getSourceRange();
+ return HET;
+ }
+
+ while (!HTMLOpenTags.empty()) {
+ const HTMLStartTagComment *HST = HTMLOpenTags.back();
+ HTMLOpenTags.pop_back();
+ StringRef LastNotClosedTagName = HST->getTagName();
+ if (LastNotClosedTagName == TagName)
+ break;
+
+ if (isHTMLEndTagOptional(LastNotClosedTagName))
+ continue;
+
+ bool OpenLineInvalid;
+ const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
+ HST->getLocation(),
+ &OpenLineInvalid);
+ bool CloseLineInvalid;
+ const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
+ HET->getLocation(),
+ &CloseLineInvalid);
+
+ if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
+ Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
+ << HST->getTagName() << HET->getTagName()
+ << HST->getSourceRange() << HET->getSourceRange();
+ else {
+ Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
+ << HST->getTagName() << HET->getTagName()
+ << HST->getSourceRange();
+ Diag(HET->getLocation(), diag::note_doc_html_end_tag)
+ << HET->getSourceRange();
+ }
+ }
+
+ return HET;
+}
+
+FullComment *Sema::actOnFullComment(
+ ArrayRef<BlockContentComment *> Blocks) {
+ return new (Allocator) FullComment(Blocks, ThisDeclInfo);
+}
+
+void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
+ ParagraphComment *Paragraph = Command->getParagraph();
+ if (Paragraph->isWhitespace()) {
+ SourceLocation DiagLoc;
+ if (Command->getNumArgs() > 0)
+ DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
+ if (!DiagLoc.isValid())
+ DiagLoc = Command->getCommandNameRange().getEnd();
+ Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
+ << Command->getCommandName()
+ << Command->getSourceRange();
+ }
+}
+
+void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
+ if (!Traits.isReturnsCommand(Command->getCommandName()))
+ return;
+ if (isFunctionDecl()) {
+ if (ThisDeclInfo->ResultType->isVoidType()) {
+ unsigned DiagKind;
+ switch (ThisDeclInfo->ThisDecl->getKind()) {
+ default:
+ if (ThisDeclInfo->IsObjCMethod)
+ DiagKind = 3;
+ else
+ DiagKind = 0;
+ break;
+ case Decl::CXXConstructor:
+ DiagKind = 1;
+ break;
+ case Decl::CXXDestructor:
+ DiagKind = 2;
+ break;
+ }
+ Diag(Command->getLocation(),
+ diag::warn_doc_returns_attached_to_a_void_function)
+ << Command->getCommandName()
+ << DiagKind
+ << Command->getSourceRange();
+ }
+ return;
+ }
+ Diag(Command->getLocation(),
+ diag::warn_doc_returns_not_attached_to_a_function_decl)
+ << Command->getCommandName()
+ << Command->getSourceRange();
+}
+
+void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
+ StringRef Name = Command->getCommandName();
+ const BlockCommandComment *PrevCommand = NULL;
+ if (Traits.isBriefCommand(Name)) {
+ if (!BriefCommand) {
+ BriefCommand = Command;
+ return;
+ }
+ PrevCommand = BriefCommand;
+ } else if (Traits.isReturnsCommand(Name)) {
+ if (!ReturnsCommand) {
+ ReturnsCommand = Command;
+ return;
+ }
+ PrevCommand = ReturnsCommand;
+ } else {
+ // We don't want to check this command for duplicates.
+ return;
+ }
+ Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
+ << Name
+ << Command->getSourceRange();
+ if (Name == PrevCommand->getCommandName())
+ Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
+ << PrevCommand->getCommandName()
+ << Command->getSourceRange();
+ else
+ Diag(PrevCommand->getLocation(),
+ diag::note_doc_block_command_previous_alias)
+ << PrevCommand->getCommandName()
+ << Name;
+}
+
+bool Sema::isFunctionDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
+}
+
+bool Sema::isTemplateOrSpecialization() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
+}
+
+ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->ParamVars;
+}
+
+void Sema::inspectThisDecl() {
+ ThisDeclInfo->fill();
+ ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
+}
+
+unsigned Sema::resolveParmVarReference(StringRef Name,
+ ArrayRef<const ParmVarDecl *> ParamVars) {
+ for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
+ const IdentifierInfo *II = ParamVars[i]->getIdentifier();
+ if (II && II->getName() == Name)
+ return i;
+ }
+ return ParamCommandComment::InvalidParamIndex;
+}
+
+namespace {
+class SimpleTypoCorrector {
+ StringRef Typo;
+ const unsigned MaxEditDistance;
+
+ const NamedDecl *BestDecl;
+ unsigned BestEditDistance;
+ unsigned BestIndex;
+ unsigned NextIndex;
+
+public:
+ SimpleTypoCorrector(StringRef Typo) :
+ Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
+ BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
+ BestIndex(0), NextIndex(0)
+ { }
+
+ void addDecl(const NamedDecl *ND);
+
+ const NamedDecl *getBestDecl() const {
+ if (BestEditDistance > MaxEditDistance)
+ return NULL;
+
+ return BestDecl;
+ }
+
+ unsigned getBestDeclIndex() const {
+ assert(getBestDecl());
+ return BestIndex;
+ }
+};
+
+void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
+ unsigned CurrIndex = NextIndex++;
+
+ const IdentifierInfo *II = ND->getIdentifier();
+ if (!II)
+ return;
+
+ StringRef Name = II->getName();
+ unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
+ if (MinPossibleEditDistance > 0 &&
+ Typo.size() / MinPossibleEditDistance < 3)
+ return;
+
+ unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
+ if (EditDistance < BestEditDistance) {
+ BestEditDistance = EditDistance;
+ BestDecl = ND;
+ BestIndex = CurrIndex;
+ }
+}
+} // unnamed namespace
+
+unsigned Sema::correctTypoInParmVarReference(
+ StringRef Typo,
+ ArrayRef<const ParmVarDecl *> ParamVars) {
+ SimpleTypoCorrector Corrector(Typo);
+ for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
+ Corrector.addDecl(ParamVars[i]);
+ if (Corrector.getBestDecl())
+ return Corrector.getBestDeclIndex();
+ else
+ return ParamCommandComment::InvalidParamIndex;;
+}
+
+namespace {
+bool ResolveTParamReferenceHelper(
+ StringRef Name,
+ const TemplateParameterList *TemplateParameters,
+ SmallVectorImpl<unsigned> *Position) {
+ for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
+ const NamedDecl *Param = TemplateParameters->getParam(i);
+ const IdentifierInfo *II = Param->getIdentifier();
+ if (II && II->getName() == Name) {
+ Position->push_back(i);
+ return true;
+ }
+
+ if (const TemplateTemplateParmDecl *TTP =
+ dyn_cast<TemplateTemplateParmDecl>(Param)) {
+ Position->push_back(i);
+ if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
+ Position))
+ return true;
+ Position->pop_back();
+ }
+ }
+ return false;
+}
+} // unnamed namespace
+
+bool Sema::resolveTParamReference(
+ StringRef Name,
+ const TemplateParameterList *TemplateParameters,
+ SmallVectorImpl<unsigned> *Position) {
+ Position->clear();
+ if (!TemplateParameters)
+ return false;
+
+ return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
+}
+
+namespace {
+void CorrectTypoInTParamReferenceHelper(
+ const TemplateParameterList *TemplateParameters,
+ SimpleTypoCorrector &Corrector) {
+ for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
+ const NamedDecl *Param = TemplateParameters->getParam(i);
+ Corrector.addDecl(Param);
+
+ if (const TemplateTemplateParmDecl *TTP =
+ dyn_cast<TemplateTemplateParmDecl>(Param))
+ CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
+ Corrector);
+ }
+}
+} // unnamed namespace
+
+StringRef Sema::correctTypoInTParamReference(
+ StringRef Typo,
+ const TemplateParameterList *TemplateParameters) {
+ SimpleTypoCorrector Corrector(Typo);
+ CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
+ if (const NamedDecl *ND = Corrector.getBestDecl()) {
+ const IdentifierInfo *II = ND->getIdentifier();
+ assert(II && "SimpleTypoCorrector should not return this decl");
+ return II->getName();
+ }
+ return StringRef();
+}
+
+InlineCommandComment::RenderKind
+Sema::getInlineCommandRenderKind(StringRef Name) const {
+ assert(Traits.isInlineCommand(Name));
+
+ return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
+ .Case("b", InlineCommandComment::RenderBold)
+ .Cases("c", "p", InlineCommandComment::RenderMonospaced)
+ .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
+ .Default(InlineCommandComment::RenderNormal);
+}
+
+bool Sema::isHTMLEndTagOptional(StringRef Name) {
+ return llvm::StringSwitch<bool>(Name)
+ .Case("p", true)
+ .Case("li", true)
+ .Case("dt", true)
+ .Case("dd", true)
+ .Case("tr", true)
+ .Case("th", true)
+ .Case("td", true)
+ .Case("thead", true)
+ .Case("tfoot", true)
+ .Case("tbody", true)
+ .Case("colgroup", true)
+ .Default(false);
+}
+
+bool Sema::isHTMLEndTagForbidden(StringRef Name) {
+ return llvm::StringSwitch<bool>(Name)
+ .Case("br", true)
+ .Case("hr", true)
+ .Case("img", true)
+ .Case("col", true)
+ .Default(false);
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 53032bc..d5b0be3 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -66,32 +66,6 @@ static llvm::Optional<Visibility> getVisibilityOf(const Decl *D) {
typedef NamedDecl::LinkageInfo LinkageInfo;
-namespace {
-/// Flags controlling the computation of linkage and visibility.
-struct LVFlags {
- const bool ConsiderGlobalVisibility;
- const bool ConsiderVisibilityAttributes;
- const bool ConsiderTemplateParameterTypes;
-
- LVFlags() : ConsiderGlobalVisibility(true),
- ConsiderVisibilityAttributes(true),
- ConsiderTemplateParameterTypes(true) {
- }
-
- LVFlags(bool Global, bool Attributes, bool Parameters) :
- ConsiderGlobalVisibility(Global),
- ConsiderVisibilityAttributes(Attributes),
- ConsiderTemplateParameterTypes(Parameters) {
- }
-
- /// \brief Returns a set of flags that is only useful for computing the
- /// linkage, not the visibility, of a declaration.
- static LVFlags CreateOnlyDeclLinkage() {
- return LVFlags(false, false, false);
- }
-};
-} // end anonymous namespace
-
static LinkageInfo getLVForType(QualType T) {
std::pair<Linkage,Visibility> P = T->getLinkageAndVisibility();
return LinkageInfo(P.first, P.second, T->isVisibilityExplicit());
@@ -131,13 +105,13 @@ getLVForTemplateParameterList(const TemplateParameterList *Params) {
}
/// getLVForDecl - Get the linkage and visibility for the given declaration.
-static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags F);
+static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate);
/// \brief Get the most restrictive linkage for the types and
/// declarations in the given template argument list.
static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args,
unsigned NumArgs,
- LVFlags &F) {
+ bool OnlyTemplate) {
LinkageInfo LV(ExternalLinkage, DefaultVisibility, false);
for (unsigned I = 0; I != NumArgs; ++I) {
@@ -148,7 +122,7 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args,
break;
case TemplateArgument::Type:
- LV.merge(getLVForType(Args[I].getAsType()));
+ LV.mergeWithMin(getLVForType(Args[I].getAsType()));
break;
case TemplateArgument::Declaration:
@@ -156,7 +130,7 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args,
// arguments, valid only in C++0x.
if (Decl *D = Args[I].getAsDecl()) {
if (NamedDecl *ND = dyn_cast<NamedDecl>(D))
- LV = merge(LV, getLVForDecl(ND, F));
+ LV.mergeWithMin(getLVForDecl(ND, OnlyTemplate));
}
break;
@@ -164,13 +138,13 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args,
case TemplateArgument::TemplateExpansion:
if (TemplateDecl *Template
= Args[I].getAsTemplateOrTemplatePattern().getAsTemplateDecl())
- LV.merge(getLVForDecl(Template, F));
+ LV.mergeWithMin(getLVForDecl(Template, OnlyTemplate));
break;
case TemplateArgument::Pack:
LV.mergeWithMin(getLVForTemplateArgumentList(Args[I].pack_begin(),
Args[I].pack_size(),
- F));
+ OnlyTemplate));
break;
}
}
@@ -180,21 +154,50 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args,
static LinkageInfo
getLVForTemplateArgumentList(const TemplateArgumentList &TArgs,
- LVFlags &F) {
- return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), F);
+ bool OnlyTemplate) {
+ return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), OnlyTemplate);
}
-static bool shouldConsiderTemplateLV(const FunctionDecl *fn,
+static bool shouldConsiderTemplateVis(const FunctionDecl *fn,
const FunctionTemplateSpecializationInfo *spec) {
- return !(spec->isExplicitSpecialization() &&
- fn->hasAttr<VisibilityAttr>());
+ return !fn->hasAttr<VisibilityAttr>() || spec->isExplicitSpecialization();
}
-static bool shouldConsiderTemplateLV(const ClassTemplateSpecializationDecl *d) {
- return !(d->isExplicitSpecialization() && d->hasAttr<VisibilityAttr>());
+static bool
+shouldConsiderTemplateVis(const ClassTemplateSpecializationDecl *d) {
+ return !d->hasAttr<VisibilityAttr>() || d->isExplicitSpecialization();
}
-static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
+static bool useInlineVisibilityHidden(const NamedDecl *D) {
+ // FIXME: we should warn if -fvisibility-inlines-hidden is used with c.
+ const LangOptions &Opts = D->getASTContext().getLangOpts();
+ if (!Opts.CPlusPlus || !Opts.InlineVisibilityHidden)
+ return false;
+
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+ if (!FD)
+ return false;
+
+ TemplateSpecializationKind TSK = TSK_Undeclared;
+ if (FunctionTemplateSpecializationInfo *spec
+ = FD->getTemplateSpecializationInfo()) {
+ TSK = spec->getTemplateSpecializationKind();
+ } else if (MemberSpecializationInfo *MSI =
+ FD->getMemberSpecializationInfo()) {
+ TSK = MSI->getTemplateSpecializationKind();
+ }
+
+ const FunctionDecl *Def = 0;
+ // InlineVisibilityHidden only applies to definitions, and
+ // isInlined() only gives meaningful answers on definitions
+ // anyway.
+ return TSK != TSK_ExplicitInstantiationDeclaration &&
+ TSK != TSK_ExplicitInstantiationDefinition &&
+ FD->hasBody(Def) && Def->isInlined();
+}
+
+static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
+ bool OnlyTemplate) {
assert(D->getDeclContext()->getRedeclContext()->isFileContext() &&
"Not a name having namespace scope");
ASTContext &Context = D->getASTContext();
@@ -271,11 +274,10 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// scope and no storage-class specifier, its linkage is
// external.
LinkageInfo LV;
- LV.mergeVisibility(Context.getLangOpts().getVisibilityMode());
- if (F.ConsiderVisibilityAttributes) {
+ if (!OnlyTemplate) {
if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) {
- LV.setVisibility(*Vis, true);
+ LV.mergeVisibility(*Vis, true);
} else {
// If we're declared in a namespace with a visibility attribute,
// use that namespace's visibility, but don't call it explicit.
@@ -285,13 +287,21 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
if (!ND) continue;
if (llvm::Optional<Visibility> Vis = ND->getExplicitVisibility()) {
- LV.setVisibility(*Vis, true);
+ LV.mergeVisibility(*Vis, true);
break;
}
}
}
}
+ if (!OnlyTemplate) {
+ LV.mergeVisibility(Context.getLangOpts().getVisibilityMode());
+ // If we're paying attention to global visibility, apply
+ // -finline-visibility-hidden if this is an inline method.
+ if (!LV.visibilityExplicit() && useInlineVisibilityHidden(D))
+ LV.mergeVisibility(HiddenVisibility, true);
+ }
+
// C++ [basic.link]p4:
// A name having namespace scope has external linkage if it is the
@@ -325,11 +335,11 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
LinkageInfo TypeLV = getLVForType(Var->getType());
if (TypeLV.linkage() != ExternalLinkage)
return LinkageInfo::uniqueExternal();
- LV.mergeVisibilityWithMin(TypeLV);
+ LV.mergeVisibility(TypeLV);
}
if (Var->getStorageClass() == SC_PrivateExtern)
- LV.setVisibility(HiddenVisibility, true);
+ LV.mergeVisibility(HiddenVisibility, true);
if (!Context.getLangOpts().CPlusPlus &&
(Var->getStorageClass() == SC_Extern ||
@@ -345,7 +355,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// is visible, or if the prior declaration specifies no
// linkage, then the identifier has external linkage.
if (const VarDecl *PrevVar = Var->getPreviousDecl()) {
- LinkageInfo PrevLV = getLVForDecl(PrevVar, F);
+ LinkageInfo PrevLV = getLVForDecl(PrevVar, OnlyTemplate);
if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage());
LV.mergeVisibility(PrevLV);
}
@@ -359,7 +369,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// just too painful to make work.
if (Function->getStorageClass() == SC_PrivateExtern)
- LV.setVisibility(HiddenVisibility, true);
+ LV.mergeVisibility(HiddenVisibility, true);
// C99 6.2.2p5:
// If the declaration of an identifier for a function has no
@@ -380,7 +390,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// is visible, or if the prior declaration specifies no
// linkage, then the identifier has external linkage.
if (const FunctionDecl *PrevFunc = Function->getPreviousDecl()) {
- LinkageInfo PrevLV = getLVForDecl(PrevFunc, F);
+ LinkageInfo PrevLV = getLVForDecl(PrevFunc, OnlyTemplate);
if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage());
LV.mergeVisibility(PrevLV);
}
@@ -399,11 +409,16 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// this is an explicit specialization with a visibility attribute.
if (FunctionTemplateSpecializationInfo *specInfo
= Function->getTemplateSpecializationInfo()) {
- if (shouldConsiderTemplateLV(Function, specInfo)) {
- LV.merge(getLVForDecl(specInfo->getTemplate(),
- LVFlags::CreateOnlyDeclLinkage()));
- const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments;
- LV.mergeWithMin(getLVForTemplateArgumentList(templateArgs, F));
+ LinkageInfo TempLV = getLVForDecl(specInfo->getTemplate(), true);
+ const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments;
+ LinkageInfo ArgsLV = getLVForTemplateArgumentList(templateArgs,
+ OnlyTemplate);
+ if (shouldConsiderTemplateVis(Function, specInfo)) {
+ LV.mergeWithMin(TempLV);
+ LV.mergeWithMin(ArgsLV);
+ } else {
+ LV.mergeLinkage(TempLV);
+ LV.mergeLinkage(ArgsLV);
}
}
@@ -422,20 +437,26 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// linkage of the template and template arguments.
if (const ClassTemplateSpecializationDecl *spec
= dyn_cast<ClassTemplateSpecializationDecl>(Tag)) {
- if (shouldConsiderTemplateLV(spec)) {
- // From the template.
- LV.merge(getLVForDecl(spec->getSpecializedTemplate(),
- LVFlags::CreateOnlyDeclLinkage()));
-
- // The arguments at which the template was instantiated.
- const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs();
- LV.mergeWithMin(getLVForTemplateArgumentList(TemplateArgs, F));
+ // From the template.
+ LinkageInfo TempLV = getLVForDecl(spec->getSpecializedTemplate(), true);
+
+ // The arguments at which the template was instantiated.
+ const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs();
+ LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs,
+ OnlyTemplate);
+ if (shouldConsiderTemplateVis(spec)) {
+ LV.mergeWithMin(TempLV);
+ LV.mergeWithMin(ArgsLV);
+ } else {
+ LV.mergeLinkage(TempLV);
+ LV.mergeLinkage(ArgsLV);
}
}
// - an enumerator belonging to an enumeration with external linkage;
} else if (isa<EnumConstantDecl>(D)) {
- LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()), F);
+ LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()),
+ OnlyTemplate);
if (!isExternalLinkage(EnumLV.linkage()))
return LinkageInfo::none();
LV.merge(EnumLV);
@@ -443,9 +464,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
// - a template, unless it is a function template that has
// internal linkage (Clause 14);
} else if (const TemplateDecl *temp = dyn_cast<TemplateDecl>(D)) {
- if (F.ConsiderTemplateParameterTypes)
- LV.merge(getLVForTemplateParameterList(temp->getTemplateParameters()));
-
+ LV.merge(getLVForTemplateParameterList(temp->getTemplateParameters()));
// - a namespace (7.3), unless it is declared within an unnamed
// namespace.
} else if (isa<NamespaceDecl>(D) && !D->isInAnonymousNamespace()) {
@@ -469,7 +488,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) {
return LV;
}
-static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) {
+static LinkageInfo getLVForClassMember(const NamedDecl *D, bool OnlyTemplate) {
// Only certain class members have linkage. Note that fields don't
// really have linkage, but it's convenient to say they do for the
// purposes of calculating linkage of pointer-to-data-member
@@ -482,53 +501,32 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) {
return LinkageInfo::none();
LinkageInfo LV;
- LV.mergeVisibility(D->getASTContext().getLangOpts().getVisibilityMode());
- bool DHasExplicitVisibility = false;
// If we have an explicit visibility attribute, merge that in.
- if (F.ConsiderVisibilityAttributes) {
- if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) {
+ if (!OnlyTemplate) {
+ if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility())
LV.mergeVisibility(*Vis, true);
-
- DHasExplicitVisibility = true;
- }
- }
- // Ignore both global visibility and attributes when computing our
- // parent's visibility if we already have an explicit one.
- LVFlags ClassF = DHasExplicitVisibility ?
- LVFlags::CreateOnlyDeclLinkage() : F;
-
- // If we're paying attention to global visibility, apply
- // -finline-visibility-hidden if this is an inline method.
- //
- // Note that we do this before merging information about
- // the class visibility.
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
- TemplateSpecializationKind TSK = TSK_Undeclared;
- if (FunctionTemplateSpecializationInfo *spec
- = MD->getTemplateSpecializationInfo()) {
- TSK = spec->getTemplateSpecializationKind();
- } else if (MemberSpecializationInfo *MSI =
- MD->getMemberSpecializationInfo()) {
- TSK = MSI->getTemplateSpecializationKind();
- }
-
- const FunctionDecl *Def = 0;
- // InlineVisibilityHidden only applies to definitions, and
- // isInlined() only gives meaningful answers on definitions
- // anyway.
- if (TSK != TSK_ExplicitInstantiationDeclaration &&
- TSK != TSK_ExplicitInstantiationDefinition &&
- F.ConsiderGlobalVisibility &&
- !LV.visibilityExplicit() &&
- MD->getASTContext().getLangOpts().InlineVisibilityHidden &&
- MD->hasBody(Def) && Def->isInlined())
+ // If we're paying attention to global visibility, apply
+ // -finline-visibility-hidden if this is an inline method.
+ //
+ // Note that we do this before merging information about
+ // the class visibility.
+ if (!LV.visibilityExplicit() && useInlineVisibilityHidden(D))
LV.mergeVisibility(HiddenVisibility, true);
}
- // Class members only have linkage if their class has external
- // linkage.
- LV.merge(getLVForDecl(cast<RecordDecl>(D->getDeclContext()), ClassF));
+ // If this class member has an explicit visibility attribute, the only
+ // thing that can change its visibility is the template arguments, so
+ // only look for them when processing the class.
+ bool ClassOnlyTemplate = LV.visibilityExplicit() ? true : OnlyTemplate;
+
+ // If this member has an visibility attribute, ClassF will exclude
+ // attributes on the class or command line options, keeping only information
+ // about the template instantiation. If the member has no visibility
+ // attributes, mergeWithMin behaves like merge, so in both cases mergeWithMin
+ // produces the desired result.
+ LV.mergeWithMin(getLVForDecl(cast<RecordDecl>(D->getDeclContext()),
+ ClassOnlyTemplate));
if (!isExternalLinkage(LV.linkage()))
return LinkageInfo::none();
@@ -536,6 +534,9 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) {
if (LV.linkage() == UniqueExternalLinkage)
return LinkageInfo::uniqueExternal();
+ if (!OnlyTemplate)
+ LV.mergeVisibility(D->getASTContext().getLangOpts().getVisibilityMode());
+
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
// If the type of the function uses a type with unique-external
// linkage, it's not legally usable from outside this translation unit.
@@ -546,12 +547,20 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) {
// the template parameters and arguments.
if (FunctionTemplateSpecializationInfo *spec
= MD->getTemplateSpecializationInfo()) {
- if (shouldConsiderTemplateLV(MD, spec)) {
- LV.mergeWithMin(getLVForTemplateArgumentList(*spec->TemplateArguments,
- F));
- if (F.ConsiderTemplateParameterTypes)
- LV.merge(getLVForTemplateParameterList(
- spec->getTemplate()->getTemplateParameters()));
+ const TemplateArgumentList &TemplateArgs = *spec->TemplateArguments;
+ LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs,
+ OnlyTemplate);
+ TemplateParameterList *TemplateParams =
+ spec->getTemplate()->getTemplateParameters();
+ LinkageInfo ParamsLV = getLVForTemplateParameterList(TemplateParams);
+ if (shouldConsiderTemplateVis(MD, spec)) {
+ LV.mergeWithMin(ArgsLV);
+ if (!OnlyTemplate)
+ LV.mergeWithMin(ParamsLV);
+ } else {
+ LV.mergeLinkage(ArgsLV);
+ if (!OnlyTemplate)
+ LV.mergeLinkage(ParamsLV);
}
}
@@ -561,14 +570,22 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) {
} else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
if (const ClassTemplateSpecializationDecl *spec
= dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
- if (shouldConsiderTemplateLV(spec)) {
- // Merge template argument/parameter information for member
- // class template specializations.
- LV.mergeWithMin(getLVForTemplateArgumentList(spec->getTemplateArgs(),
- F));
- if (F.ConsiderTemplateParameterTypes)
- LV.merge(getLVForTemplateParameterList(
- spec->getSpecializedTemplate()->getTemplateParameters()));
+ // Merge template argument/parameter information for member
+ // class template specializations.
+ const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs();
+ LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs,
+ OnlyTemplate);
+ TemplateParameterList *TemplateParams =
+ spec->getSpecializedTemplate()->getTemplateParameters();
+ LinkageInfo ParamsLV = getLVForTemplateParameterList(TemplateParams);
+ if (shouldConsiderTemplateVis(spec)) {
+ LV.mergeWithMin(ArgsLV);
+ if (!OnlyTemplate)
+ LV.mergeWithMin(ParamsLV);
+ } else {
+ LV.mergeLinkage(ArgsLV);
+ if (!OnlyTemplate)
+ LV.mergeLinkage(ParamsLV);
}
}
@@ -579,8 +596,7 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) {
LinkageInfo TypeLV = getLVForType(VD->getType());
if (TypeLV.linkage() != ExternalLinkage)
LV.mergeLinkage(UniqueExternalLinkage);
- if (!LV.visibilityExplicit())
- LV.mergeVisibility(TypeLV);
+ LV.mergeVisibility(TypeLV);
}
return LV;
@@ -636,18 +652,17 @@ void NamedDecl::ClearLinkageCache() {
Linkage NamedDecl::getLinkage() const {
if (HasCachedLinkage) {
assert(Linkage(CachedLinkage) ==
- getLVForDecl(this, LVFlags::CreateOnlyDeclLinkage()).linkage());
+ getLVForDecl(this, true).linkage());
return Linkage(CachedLinkage);
}
- CachedLinkage = getLVForDecl(this,
- LVFlags::CreateOnlyDeclLinkage()).linkage();
+ CachedLinkage = getLVForDecl(this, true).linkage();
HasCachedLinkage = 1;
return Linkage(CachedLinkage);
}
LinkageInfo NamedDecl::getLinkageAndVisibility() const {
- LinkageInfo LI = getLVForDecl(this, LVFlags());
+ LinkageInfo LI = getLVForDecl(this, false);
assert(!HasCachedLinkage || Linkage(CachedLinkage) == LI.linkage());
HasCachedLinkage = 1;
CachedLinkage = LI.linkage();
@@ -656,9 +671,19 @@ LinkageInfo NamedDecl::getLinkageAndVisibility() const {
llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const {
// Use the most recent declaration of a variable.
- if (const VarDecl *var = dyn_cast<VarDecl>(this))
- return getVisibilityOf(var->getMostRecentDecl());
+ if (const VarDecl *Var = dyn_cast<VarDecl>(this)) {
+ if (llvm::Optional<Visibility> V =
+ getVisibilityOf(Var->getMostRecentDecl()))
+ return V;
+
+ if (Var->isStaticDataMember()) {
+ VarDecl *InstantiatedFrom = Var->getInstantiatedFromStaticDataMember();
+ if (InstantiatedFrom)
+ return getVisibilityOf(InstantiatedFrom);
+ }
+ return llvm::Optional<Visibility>();
+ }
// Use the most recent declaration of a function, and also handle
// function template specializations.
if (const FunctionDecl *fn = dyn_cast<FunctionDecl>(this)) {
@@ -685,6 +710,10 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const {
if (llvm::Optional<Visibility> V = getVisibilityOf(this))
return V;
+ // The visibility of a template is stored in the templated decl.
+ if (const TemplateDecl *TD = dyn_cast<TemplateDecl>(this))
+ return getVisibilityOf(TD->getTemplatedDecl());
+
// If there wasn't explicit visibility there, and this is a
// specialization of a class template, check for visibility
// on the pattern.
@@ -703,7 +732,7 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const {
return llvm::Optional<Visibility>();
}
-static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
+static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate) {
// Objective-C: treat all Objective-C declarations as having external
// linkage.
switch (D->getKind()) {
@@ -738,11 +767,12 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
if (isa<ParmVarDecl>(ContextDecl))
DC = ContextDecl->getDeclContext()->getRedeclContext();
else
- return getLVForDecl(cast<NamedDecl>(ContextDecl), Flags);
+ return getLVForDecl(cast<NamedDecl>(ContextDecl),
+ OnlyTemplate);
}
if (const NamedDecl *ND = dyn_cast<NamedDecl>(DC))
- return getLVForDecl(ND, Flags);
+ return getLVForDecl(ND, OnlyTemplate);
return LinkageInfo::external();
}
@@ -753,7 +783,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
// Handle linkage for namespace-scope names.
if (D->getDeclContext()->getRedeclContext()->isFileContext())
- return getLVForNamespaceScopeDecl(D, Flags);
+ return getLVForNamespaceScopeDecl(D, OnlyTemplate);
// C++ [basic.link]p5:
// In addition, a member function, static data member, a named
@@ -763,7 +793,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
// purposes (7.1.3), has external linkage if the name of the class
// has external linkage.
if (D->getDeclContext()->isRecord())
- return getLVForClassMember(D, Flags);
+ return getLVForClassMember(D, OnlyTemplate);
// C++ [basic.link]p6:
// The name of a function declared in block scope and the name of
@@ -783,13 +813,13 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
return LinkageInfo::uniqueExternal();
LinkageInfo LV;
- if (Flags.ConsiderVisibilityAttributes) {
+ if (!OnlyTemplate) {
if (llvm::Optional<Visibility> Vis = Function->getExplicitVisibility())
- LV.setVisibility(*Vis, true);
+ LV.mergeVisibility(*Vis, true);
}
if (const FunctionDecl *Prev = Function->getPreviousDecl()) {
- LinkageInfo PrevLV = getLVForDecl(Prev, Flags);
+ LinkageInfo PrevLV = getLVForDecl(Prev, OnlyTemplate);
if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage());
LV.mergeVisibility(PrevLV);
}
@@ -806,14 +836,14 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) {
LinkageInfo LV;
if (Var->getStorageClass() == SC_PrivateExtern)
- LV.setVisibility(HiddenVisibility, true);
- else if (Flags.ConsiderVisibilityAttributes) {
+ LV.mergeVisibility(HiddenVisibility, true);
+ else if (!OnlyTemplate) {
if (llvm::Optional<Visibility> Vis = Var->getExplicitVisibility())
- LV.setVisibility(*Vis, true);
+ LV.mergeVisibility(*Vis, true);
}
if (const VarDecl *Prev = Var->getPreviousDecl()) {
- LinkageInfo PrevLV = getLVForDecl(Prev, Flags);
+ LinkageInfo PrevLV = getLVForDecl(Prev, OnlyTemplate);
if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage());
LV.mergeVisibility(PrevLV);
}
@@ -881,9 +911,7 @@ std::string NamedDecl::getQualifiedNameAsString(const PrintingPolicy &P) const {
for (unsigned i = 0; i < NumParams; ++i) {
if (i)
OS << ", ";
- std::string Param;
- FD->getParamDecl(i)->getType().getAsStringInternal(Param, P);
- OS << Param;
+ OS << FD->getParamDecl(i)->getType().stream(P);
}
if (FT->isVariadic()) {
@@ -1672,6 +1700,13 @@ void FunctionDecl::setPure(bool P) {
Parent->markedVirtualFunctionPure();
}
+void FunctionDecl::setConstexpr(bool IC) {
+ IsConstexpr = IC;
+ CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(this);
+ if (IC && CD)
+ CD->getParent()->markedConstructorConstexpr(CD);
+}
+
bool FunctionDecl::isMain() const {
const TranslationUnitDecl *tunit =
dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext());
@@ -2446,15 +2481,15 @@ FieldDecl *FieldDecl::Create(const ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, QualType T,
TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
- bool HasInit) {
+ InClassInitStyle InitStyle) {
return new (C) FieldDecl(Decl::Field, DC, StartLoc, IdLoc, Id, T, TInfo,
- BW, Mutable, HasInit);
+ BW, Mutable, InitStyle);
}
FieldDecl *FieldDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
void *Mem = AllocateDeserializedDecl(C, ID, sizeof(FieldDecl));
return new (Mem) FieldDecl(Field, 0, SourceLocation(), SourceLocation(),
- 0, QualType(), 0, 0, false, false);
+ 0, QualType(), 0, 0, false, ICIS_NoInit);
}
bool FieldDecl::isAnonymousStructOrUnion() const {
@@ -2483,15 +2518,15 @@ unsigned FieldDecl::getFieldIndex() const {
for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I, ++Index) {
- (*I)->CachedFieldIndex = Index + 1;
+ I->CachedFieldIndex = Index + 1;
if (IsMsStruct) {
// Zero-length bitfields following non-bitfield members are ignored.
- if (getASTContext().ZeroBitfieldFollowsNonBitfield((*I), LastFD)) {
+ if (getASTContext().ZeroBitfieldFollowsNonBitfield(*I, LastFD)) {
--Index;
continue;
}
- LastFD = (*I);
+ LastFD = *I;
}
}
@@ -2505,11 +2540,16 @@ SourceRange FieldDecl::getSourceRange() const {
return DeclaratorDecl::getSourceRange();
}
+void FieldDecl::setBitWidth(Expr *Width) {
+ assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() &&
+ "bit width or initializer already set");
+ InitializerOrBitWidth.setPointer(Width);
+}
+
void FieldDecl::setInClassInitializer(Expr *Init) {
- assert(!InitializerOrBitWidth.getPointer() &&
+ assert(!InitializerOrBitWidth.getPointer() && hasInClassInitializer() &&
"bit width or initializer already set");
InitializerOrBitWidth.setPointer(Init);
- InitializerOrBitWidth.setInt(0);
}
//===----------------------------------------------------------------------===//
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 47a0d25..f9ce46d 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -411,23 +411,32 @@ AvailabilityResult Decl::getAvailability(std::string *Message) const {
bool Decl::canBeWeakImported(bool &IsDefinition) const {
IsDefinition = false;
+
+ // Variables, if they aren't definitions.
if (const VarDecl *Var = dyn_cast<VarDecl>(this)) {
if (!Var->hasExternalStorage() || Var->getInit()) {
IsDefinition = true;
return false;
}
+ return true;
+
+ // Functions, if they aren't definitions.
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(this)) {
if (FD->hasBody()) {
IsDefinition = true;
return false;
}
- } else if (isa<ObjCPropertyDecl>(this) || isa<ObjCMethodDecl>(this))
- return false;
- else if (!(getASTContext().getLangOpts().ObjCNonFragileABI &&
- isa<ObjCInterfaceDecl>(this)))
- return false;
+ return true;
- return true;
+ // Objective-C classes, if this is the non-fragile runtime.
+ } else if (isa<ObjCInterfaceDecl>(this) &&
+ getASTContext().getLangOpts().ObjCRuntime.hasWeakClassImport()) {
+ return true;
+
+ // Nothing else.
+ } else {
+ return false;
+ }
}
bool Decl::isWeakImported() const {
@@ -974,10 +983,6 @@ DeclContext::decl_iterator DeclContext::noload_decls_begin() const {
return decl_iterator(FirstDecl);
}
-DeclContext::decl_iterator DeclContext::noload_decls_end() const {
- return decl_iterator();
-}
-
DeclContext::decl_iterator DeclContext::decls_begin() const {
if (hasExternalLexicalStorage())
LoadLexicalDeclsFromExternalStorage();
@@ -985,13 +990,6 @@ DeclContext::decl_iterator DeclContext::decls_begin() const {
return decl_iterator(FirstDecl);
}
-DeclContext::decl_iterator DeclContext::decls_end() const {
- if (hasExternalLexicalStorage())
- LoadLexicalDeclsFromExternalStorage();
-
- return decl_iterator();
-}
-
bool DeclContext::decls_empty() const {
if (hasExternalLexicalStorage())
LoadLexicalDeclsFromExternalStorage();
@@ -1192,34 +1190,31 @@ DeclContext::lookup(DeclarationName Name) {
return I->second.getLookupResult();
}
-DeclContext::lookup_const_result
-DeclContext::lookup(DeclarationName Name) const {
- return const_cast<DeclContext*>(this)->lookup(Name);
-}
-
void DeclContext::localUncachedLookup(DeclarationName Name,
llvm::SmallVectorImpl<NamedDecl *> &Results) {
Results.clear();
// If there's no external storage, just perform a normal lookup and copy
// the results.
- if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage()) {
+ if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage() && Name) {
lookup_result LookupResults = lookup(Name);
Results.insert(Results.end(), LookupResults.first, LookupResults.second);
return;
}
// If we have a lookup table, check there first. Maybe we'll get lucky.
- if (StoredDeclsMap *Map = LookupPtr.getPointer()) {
- StoredDeclsMap::iterator Pos = Map->find(Name);
- if (Pos != Map->end()) {
- Results.insert(Results.end(),
- Pos->second.getLookupResult().first,
- Pos->second.getLookupResult().second);
- return;
+ if (Name) {
+ if (StoredDeclsMap *Map = LookupPtr.getPointer()) {
+ StoredDeclsMap::iterator Pos = Map->find(Name);
+ if (Pos != Map->end()) {
+ Results.insert(Results.end(),
+ Pos->second.getLookupResult().first,
+ Pos->second.getLookupResult().second);
+ return;
+ }
}
}
-
+
// Slow case: grovel through the declarations in our chain looking for
// matches.
for (Decl *D = FirstDecl; D; D = D->getNextDeclInContext()) {
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 114322b..eec2e9d 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -43,13 +43,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true),
HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false),
HasMutableFields(false), HasOnlyCMembers(true),
+ HasInClassInitializer(false),
HasTrivialDefaultConstructor(true),
HasConstexprNonCopyMoveConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
- DefaultedCopyConstructorIsConstexpr(true),
- DefaultedMoveConstructorIsConstexpr(true),
- HasConstexprDefaultConstructor(false), HasConstexprCopyConstructor(false),
- HasConstexprMoveConstructor(false), HasTrivialCopyConstructor(true),
+ HasConstexprDefaultConstructor(false), HasTrivialCopyConstructor(true),
HasTrivialMoveConstructor(true), HasTrivialCopyAssignment(true),
HasTrivialMoveAssignment(true), HasTrivialDestructor(true),
HasIrrelevantDestructor(true),
@@ -62,6 +60,14 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) {
}
+CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
+ return Bases.get(Definition->getASTContext().getExternalSource());
+}
+
+CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getVBasesSlowCase() const {
+ return VBases.get(Definition->getASTContext().getExternalSource());
+}
+
CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, CXXRecordDecl *PrevDecl)
@@ -219,8 +225,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
// In the definition of a constexpr constructor [...]
// -- the class shall not have any virtual base classes
data().DefaultedDefaultConstructorIsConstexpr = false;
- data().DefaultedCopyConstructorIsConstexpr = false;
- data().DefaultedMoveConstructorIsConstexpr = false;
} else {
// C++ [class.ctor]p5:
// A default constructor is trivial [...] if:
@@ -259,25 +263,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
// default constructor is constexpr.
if (!BaseClassDecl->hasConstexprDefaultConstructor())
data().DefaultedDefaultConstructorIsConstexpr = false;
-
- // C++11 [class.copy]p13:
- // If the implicitly-defined constructor would satisfy the requirements
- // of a constexpr constructor, the implicitly-defined constructor is
- // constexpr.
- // C++11 [dcl.constexpr]p4:
- // -- every constructor involved in initializing [...] base class
- // sub-objects shall be a constexpr constructor
- if (!BaseClassDecl->hasConstexprCopyConstructor())
- data().DefaultedCopyConstructorIsConstexpr = false;
- if (BaseClassDecl->hasDeclaredMoveConstructor() ||
- BaseClassDecl->needsImplicitMoveConstructor())
- // FIXME: If the implicit move constructor generated for the base class
- // would be ill-formed, the implicit move constructor generated for the
- // derived class calls the base class' copy constructor.
- data().DefaultedMoveConstructorIsConstexpr &=
- BaseClassDecl->hasConstexprMoveConstructor();
- else if (!BaseClassDecl->hasConstexprCopyConstructor())
- data().DefaultedMoveConstructorIsConstexpr = false;
}
// C++ [class.ctor]p3:
@@ -359,8 +344,8 @@ GetBestOverloadCandidateSimple(
if (Cands[Best].second.compatiblyIncludes(Cands[I].second))
Best = I;
- for (unsigned I = 1; I != N; ++I)
- if (Cands[Best].second.compatiblyIncludes(Cands[I].second))
+ for (unsigned I = 0; I != N; ++I)
+ if (I != Best && Cands[Best].second.compatiblyIncludes(Cands[I].second))
return 0;
return Cands[Best].first;
@@ -469,6 +454,14 @@ void CXXRecordDecl::markedVirtualFunctionPure() {
data().Abstract = true;
}
+void CXXRecordDecl::markedConstructorConstexpr(CXXConstructorDecl *CD) {
+ if (!CD->isCopyOrMoveConstructor())
+ data().HasConstexprNonCopyMoveConstructor = true;
+
+ if (CD->isDefaultConstructor())
+ data().HasConstexprDefaultConstructor = true;
+}
+
void CXXRecordDecl::addedMember(Decl *D) {
if (!D->isImplicit() &&
!isa<FieldDecl>(D) &&
@@ -545,12 +538,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
}
} else if (Constructor->isCopyConstructor()) {
data().DeclaredCopyConstructor = true;
- if (Constructor->isConstexpr())
- data().HasConstexprCopyConstructor = true;
} else if (Constructor->isMoveConstructor()) {
data().DeclaredMoveConstructor = true;
- if (Constructor->isConstexpr())
- data().HasConstexprMoveConstructor = true;
} else
goto NotASpecialMember;
return;
@@ -607,9 +596,6 @@ NotASpecialMember:;
// user-provided [...]
if (UserProvided)
data().HasTrivialCopyConstructor = false;
-
- if (Constructor->isConstexpr())
- data().HasConstexprCopyConstructor = true;
} else if (Constructor->isMoveConstructor()) {
data().UserDeclaredMoveConstructor = true;
data().DeclaredMoveConstructor = true;
@@ -619,9 +605,6 @@ NotASpecialMember:;
// user-provided [...]
if (UserProvided)
data().HasTrivialMoveConstructor = false;
-
- if (Constructor->isConstexpr())
- data().HasConstexprMoveConstructor = true;
}
}
if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor()) {
@@ -663,19 +646,9 @@ NotASpecialMember:;
// C++11 [class.dtor]p5:
// A destructor is trivial if it is not user-provided and if
// -- the destructor is not virtual.
- if (DD->isUserProvided() || DD->isVirtual()) {
+ if (DD->isUserProvided() || DD->isVirtual())
data().HasTrivialDestructor = false;
- // C++11 [dcl.constexpr]p1:
- // The constexpr specifier shall be applied only to [...] the
- // declaration of a static data member of a literal type.
- // C++11 [basic.types]p10:
- // A type is a literal type if it is [...] a class type that [...] has
- // a trivial destructor.
- data().DefaultedDefaultConstructorIsConstexpr = false;
- data().DefaultedCopyConstructorIsConstexpr = false;
- data().DefaultedMoveConstructorIsConstexpr = false;
- }
-
+
return;
}
@@ -792,7 +765,7 @@ NotASpecialMember:;
// that does not explicitly have no lifetime makes the class a non-POD.
// However, we delay setting PlainOldData to false in this case so that
// Sema has a chance to diagnostic causes where the same class will be
- // non-POD with Automatic Reference Counting but a POD without Instant Objects.
+ // non-POD with Automatic Reference Counting but a POD without ARC.
// In this case, the class will become a non-POD class when we complete
// the definition.
ASTContext &Context = getASTContext();
@@ -818,17 +791,19 @@ NotASpecialMember:;
data().HasNonLiteralTypeFieldsOrBases = true;
if (Field->hasInClassInitializer()) {
- // C++0x [class]p5:
+ data().HasInClassInitializer = true;
+
+ // C++11 [class]p5:
// A default constructor is trivial if [...] no non-static data member
// of its class has a brace-or-equal-initializer.
data().HasTrivialDefaultConstructor = false;
- // C++0x [dcl.init.aggr]p1:
+ // C++11 [dcl.init.aggr]p1:
// An aggregate is a [...] class with [...] no
// brace-or-equal-initializers for non-static data members.
data().Aggregate = false;
- // C++0x [class]p10:
+ // C++11 [class]p10:
// A POD struct is [...] a trivial class.
data().PlainOldData = false;
}
@@ -920,31 +895,15 @@ NotASpecialMember:;
// -- every constructor involved in initializing non-static data
// members [...] shall be a constexpr constructor
if (!Field->hasInClassInitializer() &&
- !FieldRec->hasConstexprDefaultConstructor())
+ !FieldRec->hasConstexprDefaultConstructor() && !isUnion())
// The standard requires any in-class initializer to be a constant
// expression. We consider this to be a defect.
data().DefaultedDefaultConstructorIsConstexpr = false;
-
- if (!FieldRec->hasConstexprCopyConstructor())
- data().DefaultedCopyConstructorIsConstexpr = false;
-
- if (FieldRec->hasDeclaredMoveConstructor() ||
- FieldRec->needsImplicitMoveConstructor())
- // FIXME: If the implicit move constructor generated for the member's
- // class would be ill-formed, the implicit move constructor generated
- // for this class calls the member's copy constructor.
- data().DefaultedMoveConstructorIsConstexpr &=
- FieldRec->hasConstexprMoveConstructor();
- else if (!FieldRec->hasConstexprCopyConstructor())
- data().DefaultedMoveConstructorIsConstexpr = false;
}
} else {
// Base element type of field is a non-class type.
- if (!T->isLiteralType()) {
- data().DefaultedDefaultConstructorIsConstexpr = false;
- data().DefaultedCopyConstructorIsConstexpr = false;
- data().DefaultedMoveConstructorIsConstexpr = false;
- } else if (!Field->hasInClassInitializer())
+ if (!T->isLiteralType() ||
+ (!Field->hasInClassInitializer() && !isUnion()))
data().DefaultedDefaultConstructorIsConstexpr = false;
}
@@ -1018,7 +977,7 @@ static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) {
/// Collect the visible conversions of a base class.
///
-/// \param Base a base class of the class we're considering
+/// \param Record a base class of the class we're considering
/// \param InVirtual whether this base class is a virtual base (or a base
/// of a virtual base)
/// \param Access the access along the inheritance path to this base
@@ -1050,8 +1009,10 @@ static void CollectVisibleConversions(ASTContext &Context,
HiddenTypes = &HiddenTypesBuffer;
for (UnresolvedSetIterator I = Cs.begin(), E = Cs.end(); I != E; ++I) {
- bool Hidden =
- !HiddenTypesBuffer.insert(GetConversionType(Context, I.getDecl()));
+ CanQualType ConvType(GetConversionType(Context, I.getDecl()));
+ bool Hidden = ParentHiddenTypes.count(ConvType);
+ if (!Hidden)
+ HiddenTypesBuffer.insert(ConvType);
// If this conversion is hidden and we're in a virtual base,
// remember that it's hidden along some inheritance path.
@@ -1247,13 +1208,16 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
// Objective-C Automatic Reference Counting:
// If a class has a non-static data member of Objective-C pointer
// type (or array thereof), it is a non-POD type and its
- // default constructor (if any), copy constructor, copy assignment
- // operator, and destructor are non-trivial.
+ // default constructor (if any), copy constructor, move constructor,
+ // copy assignment operator, move assignment operator, and destructor are
+ // non-trivial.
struct DefinitionData &Data = data();
Data.PlainOldData = false;
Data.HasTrivialDefaultConstructor = false;
Data.HasTrivialCopyConstructor = false;
+ Data.HasTrivialMoveConstructor = false;
Data.HasTrivialCopyAssignment = false;
+ Data.HasTrivialMoveAssignment = false;
Data.HasTrivialDestructor = false;
Data.HasIrrelevantDestructor = false;
}
@@ -1316,6 +1280,55 @@ bool CXXRecordDecl::mayBeAbstract() const {
void CXXMethodDecl::anchor() { }
+static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD,
+ const CXXMethodDecl *BaseMD) {
+ for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
+ E = DerivedMD->end_overridden_methods(); I != E; ++I) {
+ const CXXMethodDecl *MD = *I;
+ if (MD->getCanonicalDecl() == BaseMD->getCanonicalDecl())
+ return true;
+ if (recursivelyOverrides(MD, BaseMD))
+ return true;
+ }
+ return false;
+}
+
+CXXMethodDecl *
+CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) {
+ if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl())
+ return this;
+
+ // Lookup doesn't work for destructors, so handle them separately.
+ if (isa<CXXDestructorDecl>(this)) {
+ CXXMethodDecl *MD = RD->getDestructor();
+ if (MD && recursivelyOverrides(MD, this))
+ return MD;
+ return NULL;
+ }
+
+ lookup_const_result Candidates = RD->lookup(getDeclName());
+ for (NamedDecl * const * I = Candidates.first; I != Candidates.second; ++I) {
+ CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(*I);
+ if (!MD)
+ continue;
+ if (recursivelyOverrides(MD, this))
+ return MD;
+ }
+
+ for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
+ E = RD->bases_end(); I != E; ++I) {
+ const RecordType *RT = I->getType()->getAs<RecordType>();
+ if (!RT)
+ continue;
+ const CXXRecordDecl *Base = cast<CXXRecordDecl>(RT->getDecl());
+ CXXMethodDecl *T = this->getCorrespondingMethodInClass(Base);
+ if (T)
+ return T;
+ }
+
+ return NULL;
+}
+
CXXMethodDecl *
CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation StartLoc,
@@ -1690,7 +1703,9 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const {
return (getNumParams() == 0 &&
getType()->getAs<FunctionProtoType>()->isVariadic()) ||
(getNumParams() == 1) ||
- (getNumParams() > 1 && getParamDecl(1)->hasDefaultArg());
+ (getNumParams() > 1 &&
+ (getParamDecl(1)->hasDefaultArg() ||
+ getParamDecl(1)->isParameterPack()));
}
bool CXXConstructorDecl::isSpecializationCopyingObject() const {
@@ -1993,15 +2008,17 @@ StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation StaticAssertLoc,
Expr *AssertExpr,
StringLiteral *Message,
- SourceLocation RParenLoc) {
+ SourceLocation RParenLoc,
+ bool Failed) {
return new (C) StaticAssertDecl(DC, StaticAssertLoc, AssertExpr, Message,
- RParenLoc);
+ RParenLoc, Failed);
}
StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C,
unsigned ID) {
void *Mem = AllocateDeserializedDecl(C, ID, sizeof(StaticAssertDecl));
- return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0,SourceLocation());
+ return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0,
+ SourceLocation(), false);
}
static const char *getAccessName(AccessSpecifier AS) {
diff --git a/lib/AST/DeclFriend.cpp b/lib/AST/DeclFriend.cpp
index 6e3bd8d..553d170 100644
--- a/lib/AST/DeclFriend.cpp
+++ b/lib/AST/DeclFriend.cpp
@@ -12,12 +12,18 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
using namespace clang;
void FriendDecl::anchor() { }
+FriendDecl *FriendDecl::getNextFriendSlowCase() {
+ return cast_or_null<FriendDecl>(
+ NextFriend.get(getASTContext().getExternalSource()));
+}
+
FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation L,
FriendUnion Friend,
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index 2370d3c..4d48ad8 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -363,9 +363,12 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel,
return NULL;
}
+// Will search "local" class/category implementations for a method decl.
+// If failed, then we search in class's root for an instance method.
+// Returns 0 if no method is found.
ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod(
const Selector &Sel,
- bool Instance) {
+ bool Instance) const {
// FIXME: Should make sure no callers ever do this.
if (!hasDefinition())
return 0;
@@ -377,7 +380,23 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod(
if (ObjCImplementationDecl *ImpDecl = getImplementation())
Method = Instance ? ImpDecl->getInstanceMethod(Sel)
: ImpDecl->getClassMethod(Sel);
-
+
+ // Look through local category implementations associated with the class.
+ if (!Method)
+ Method = Instance ? getCategoryInstanceMethod(Sel)
+ : getCategoryClassMethod(Sel);
+
+ // Before we give up, check if the selector is an instance method.
+ // But only in the root. This matches gcc's behavior and what the
+ // runtime expects.
+ if (!Instance && !Method && !getSuperClass()) {
+ Method = lookupInstanceMethod(Sel);
+ // Look through local category implementations associated
+ // with the root class.
+ if (!Method)
+ Method = lookupPrivateMethod(Sel, true);
+ }
+
if (!Method && getSuperClass())
return getSuperClass()->lookupPrivateMethod(Sel, Instance);
return Method;
@@ -451,7 +470,8 @@ void ObjCMethodDecl::setMethodParams(ASTContext &C,
if (isImplicit())
return setParamsAndSelLocs(C, Params, ArrayRef<SourceLocation>());
- SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params, EndLoc);
+ SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params,
+ DeclEndLoc);
if (SelLocsKind != SelLoc_NonStandard)
return setParamsAndSelLocs(C, Params, ArrayRef<SourceLocation>());
@@ -523,6 +543,12 @@ ObjCMethodDecl *ObjCMethodDecl::getCanonicalDecl() {
return this;
}
+SourceLocation ObjCMethodDecl::getLocEnd() const {
+ if (Stmt *Body = getBody())
+ return Body->getLocEnd();
+ return DeclEndLoc;
+}
+
ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const {
ObjCMethodFamily family = static_cast<ObjCMethodFamily>(Family);
if (family != static_cast<unsigned>(InvalidObjCMethodFamily))
@@ -767,7 +793,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() {
ObjCIvarDecl *curIvar = 0;
if (!ivar_empty()) {
ObjCInterfaceDecl::ivar_iterator I = ivar_begin(), E = ivar_end();
- data().IvarList = (*I); ++I;
+ data().IvarList = *I; ++I;
for (curIvar = data().IvarList; I != E; curIvar = *I, ++I)
curIvar->setNextIvar(*I);
}
@@ -778,7 +804,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() {
ObjCCategoryDecl::ivar_iterator I = CDecl->ivar_begin(),
E = CDecl->ivar_end();
if (!data().IvarList) {
- data().IvarList = (*I); ++I;
+ data().IvarList = *I; ++I;
curIvar = data().IvarList;
}
for ( ;I != E; curIvar = *I, ++I)
@@ -791,7 +817,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() {
ObjCImplementationDecl::ivar_iterator I = ImplDecl->ivar_begin(),
E = ImplDecl->ivar_end();
if (!data().IvarList) {
- data().IvarList = (*I); ++I;
+ data().IvarList = *I; ++I;
curIvar = data().IvarList;
}
for ( ;I != E; curIvar = *I, ++I)
@@ -915,16 +941,10 @@ ObjCIvarDecl *ObjCIvarDecl::Create(ASTContext &C, ObjCContainerDecl *DC,
// decl contexts, the previously built IvarList must be rebuilt.
ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(DC);
if (!ID) {
- if (ObjCImplementationDecl *IM = dyn_cast<ObjCImplementationDecl>(DC)) {
+ if (ObjCImplementationDecl *IM = dyn_cast<ObjCImplementationDecl>(DC))
ID = IM->getClassInterface();
- if (BW)
- IM->setHasSynthBitfield(true);
- } else {
- ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(DC);
- ID = CD->getClassInterface();
- if (BW)
- CD->setHasSynthBitfield(true);
- }
+ else
+ ID = cast<ObjCCategoryDecl>(DC)->getClassInterface();
}
ID->setIvarList(0);
}
@@ -1169,7 +1189,7 @@ void ObjCImplDecl::setClassInterface(ObjCInterfaceDecl *IFace) {
}
/// FindPropertyImplIvarDecl - This method lookup the ivar in the list of
-/// properties implemented in this category @implementation block and returns
+/// properties implemented in this category \@implementation block and returns
/// the implemented property that uses it.
///
ObjCPropertyImplDecl *ObjCImplDecl::
@@ -1184,8 +1204,8 @@ FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const {
}
/// FindPropertyImplDecl - This method looks up a previous ObjCPropertyImplDecl
-/// added to the list of those properties @synthesized/@dynamic in this
-/// category @implementation block.
+/// added to the list of those properties \@synthesized/\@dynamic in this
+/// category \@implementation block.
///
ObjCPropertyImplDecl *ObjCImplDecl::
FindPropertyImplDecl(IdentifierInfo *Id) const {
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index 74e1c1b..aad0ca1 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -114,6 +114,8 @@ static QualType GetBaseType(QualType T) {
BaseType = FTy->getResultType();
else if (const VectorType *VTy = BaseType->getAs<VectorType>())
BaseType = VTy->getElementType();
+ else if (const ReferenceType *RTy = BaseType->getAs<ReferenceType>())
+ BaseType = RTy->getPointeeType();
else
llvm_unreachable("Unknown declarator!");
}
@@ -173,8 +175,10 @@ void DeclContext::dumpDeclContext() const {
Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false);
}
-void Decl::dump() const {
- print(llvm::errs());
+void Decl::dump(raw_ostream &Out) const {
+ PrintingPolicy Policy = getASTContext().getPrintingPolicy();
+ Policy.Dump = true;
+ print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ true);
}
raw_ostream& DeclPrinter::Indent(unsigned Indentation) {
@@ -322,15 +326,13 @@ void DeclPrinter::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
}
void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) {
- std::string S = D->getNameAsString();
- D->getUnderlyingType().getAsStringInternal(S, Policy);
if (!Policy.SuppressSpecifiers) {
Out << "typedef ";
if (D->isModulePrivate())
Out << "__module_private__ ";
}
- Out << S;
+ D->getUnderlyingType().print(Out, Policy, D->getName());
prettyPrintAttributes(D);
}
@@ -350,11 +352,8 @@ void DeclPrinter::VisitEnumDecl(EnumDecl *D) {
}
Out << *D;
- if (D->isFixed()) {
- std::string Underlying;
- D->getIntegerType().getAsStringInternal(Underlying, Policy);
- Out << " : " << Underlying;
- }
+ if (D->isFixed())
+ Out << " : " << D->getIntegerType().stream(Policy);
if (D->isCompleteDefinition()) {
Out << " {\n";
@@ -441,13 +440,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
Proto += ")";
- if (FT && FT->getTypeQuals()) {
- unsigned TypeQuals = FT->getTypeQuals();
- if (TypeQuals & Qualifiers::Const)
+ if (FT) {
+ if (FT->isConst())
Proto += " const";
- if (TypeQuals & Qualifiers::Volatile)
+ if (FT->isVolatile())
Proto += " volatile";
- if (TypeQuals & Qualifiers::Restrict)
+ if (FT->isRestrict())
Proto += " restrict";
}
@@ -460,9 +458,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (I)
Proto += ", ";
- std::string ExceptionType;
- FT->getExceptionType(I).getAsStringInternal(ExceptionType, SubPolicy);
- Proto += ExceptionType;
+ Proto += FT->getExceptionType(I).getAsString(SubPolicy);;
}
Proto += ")";
} else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) {
@@ -542,12 +538,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
}
}
else
- AFT->getResultType().getAsStringInternal(Proto, Policy);
+ AFT->getResultType().print(Out, Policy, Proto);
} else {
- Ty.getAsStringInternal(Proto, Policy);
+ Ty.print(Out, Policy, Proto);
}
- Out << Proto;
prettyPrintAttributes(D);
if (D->isPure())
@@ -581,9 +576,7 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
- std::string Name = D->getNameAsString();
- D->getType().getAsStringInternal(Name, Policy);
- Out << Name;
+ Out << D->getType().stream(Policy, D->getName());
if (D->isBitField()) {
Out << " : ";
@@ -592,7 +585,10 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
Expr *Init = D->getInClassInitializer();
if (!Policy.SuppressInitializers && Init) {
- Out << " = ";
+ if (D->getInClassInitStyle() == ICIS_ListInit)
+ Out << " ";
+ else
+ Out << " = ";
Init->printPretty(Out, Context, 0, Policy, Indentation);
}
prettyPrintAttributes(D);
@@ -613,12 +609,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
if (!Policy.SuppressSpecifiers && D->isModulePrivate())
Out << "__module_private__ ";
- std::string Name = D->getNameAsString();
QualType T = D->getType();
if (ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D))
T = Parm->getOriginalType();
- T.getAsStringInternal(Name, Policy);
- Out << Name;
+ T.print(Out, Policy, D->getName());
Expr *Init = D->getInit();
if (!Policy.SuppressInitializers && Init) {
bool ImplicitInit = false;
@@ -666,6 +660,8 @@ void DeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) {
// C++ declarations
//----------------------------------------------------------------------------
void DeclPrinter::VisitNamespaceDecl(NamespaceDecl *D) {
+ if (D->isInline())
+ Out << "inline ";
Out << "namespace " << *D << " {\n";
VisitDeclContext(D);
Indent() << "}";
@@ -923,7 +919,7 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) {
Indentation += Policy.Indentation;
for (ObjCInterfaceDecl::ivar_iterator I = OID->ivar_begin(),
E = OID->ivar_end(); I != E; ++I) {
- Indent() << (*I)->getType().getAsString(Policy) << ' ' << **I << ";\n";
+ Indent() << I->getType().getAsString(Policy) << ' ' << **I << ";\n";
}
Indentation -= Policy.Indentation;
Out << "}\n";
diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp
index 4590195..5aebc2b 100644
--- a/lib/AST/DeclTemplate.cpp
+++ b/lib/AST/DeclTemplate.cpp
@@ -145,7 +145,7 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() {
template <class EntryType>
typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType*
RedeclarableTemplateDecl::findSpecializationImpl(
- llvm::FoldingSet<EntryType> &Specs,
+ llvm::FoldingSetVector<EntryType> &Specs,
const TemplateArgument *Args, unsigned NumArgs,
void *&InsertPos) {
typedef SpecEntryTraits<EntryType> SETraits;
@@ -298,13 +298,13 @@ void ClassTemplateDecl::LoadLazySpecializations() {
}
}
-llvm::FoldingSet<ClassTemplateSpecializationDecl> &
+llvm::FoldingSetVector<ClassTemplateSpecializationDecl> &
ClassTemplateDecl::getSpecializations() {
LoadLazySpecializations();
return getCommonPtr()->Specializations;
}
-llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> &
+llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
ClassTemplateDecl::getPartialSpecializations() {
LoadLazySpecializations();
return getCommonPtr()->PartialSpecializations;
@@ -363,11 +363,11 @@ void ClassTemplateDecl::AddPartialSpecialization(
void ClassTemplateDecl::getPartialSpecializations(
SmallVectorImpl<ClassTemplatePartialSpecializationDecl *> &PS) {
- llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> &PartialSpecs
+ llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &PartialSpecs
= getPartialSpecializations();
PS.clear();
PS.resize(PartialSpecs.size());
- for (llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator
+ for (llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator
P = PartialSpecs.begin(), PEnd = PartialSpecs.end();
P != PEnd; ++P) {
assert(!PS[P->getSequenceNumber()]);
@@ -378,7 +378,8 @@ void ClassTemplateDecl::getPartialSpecializations(
ClassTemplatePartialSpecializationDecl *
ClassTemplateDecl::findPartialSpecialization(QualType T) {
ASTContext &Context = getASTContext();
- typedef llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator
+ using llvm::FoldingSetVector;
+ typedef FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator
partial_spec_iterator;
for (partial_spec_iterator P = getPartialSpecializations().begin(),
PEnd = getPartialSpecializations().end();
@@ -394,7 +395,7 @@ ClassTemplatePartialSpecializationDecl *
ClassTemplateDecl::findPartialSpecInstantiatedFromMember(
ClassTemplatePartialSpecializationDecl *D) {
Decl *DCanon = D->getCanonicalDecl();
- for (llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator
+ for (llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator
P = getPartialSpecializations().begin(),
PEnd = getPartialSpecializations().end();
P != PEnd; ++P) {
@@ -868,5 +869,6 @@ ClassScopeFunctionSpecializationDecl::CreateDeserialized(ASTContext &C,
unsigned ID) {
void *Mem = AllocateDeserializedDecl(C, ID,
sizeof(ClassScopeFunctionSpecializationDecl));
- return new (Mem) ClassScopeFunctionSpecializationDecl(0, SourceLocation(), 0);
+ return new (Mem) ClassScopeFunctionSpecializationDecl(0, SourceLocation(), 0,
+ false, TemplateArgumentListInfo());
}
diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp
index 64924ad..28188d9 100644
--- a/lib/AST/DeclarationName.cpp
+++ b/lib/AST/DeclarationName.cpp
@@ -135,33 +135,6 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) {
} // end namespace clang
-DeclarationName::DeclarationName(Selector Sel) {
- if (!Sel.getAsOpaquePtr()) {
- Ptr = 0;
- return;
- }
-
- switch (Sel.getNumArgs()) {
- case 0:
- Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo());
- assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo");
- Ptr |= StoredObjCZeroArgSelector;
- break;
-
- case 1:
- Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo());
- assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo");
- Ptr |= StoredObjCOneArgSelector;
- break;
-
- default:
- Ptr = Sel.InfoPtr & ~Selector::ArgFlags;
- assert((Ptr & PtrMask) == 0 && "Improperly aligned MultiKeywordSelector");
- Ptr |= StoredDeclarationNameExtra;
- break;
- }
-}
-
DeclarationName::NameKind DeclarationName::getNameKind() const {
switch (getStoredNameKind()) {
case StoredIdentifier: return Identifier;
@@ -305,28 +278,10 @@ IdentifierInfo *DeclarationName::getCXXLiteralIdentifier() const {
return 0;
}
-Selector DeclarationName::getObjCSelector() const {
- switch (getNameKind()) {
- case ObjCZeroArgSelector:
- return Selector(reinterpret_cast<IdentifierInfo *>(Ptr & ~PtrMask), 0);
-
- case ObjCOneArgSelector:
- return Selector(reinterpret_cast<IdentifierInfo *>(Ptr & ~PtrMask), 1);
-
- case ObjCMultiArgSelector:
- return Selector(reinterpret_cast<MultiKeywordSelector *>(Ptr & ~PtrMask));
-
- default:
- break;
- }
-
- return Selector();
-}
-
-void *DeclarationName::getFETokenInfoAsVoid() const {
+void *DeclarationName::getFETokenInfoAsVoidSlow() const {
switch (getNameKind()) {
case Identifier:
- return getAsIdentifierInfo()->getFETokenInfo<void>();
+ llvm_unreachable("Handled by getFETokenInfo()");
case CXXConstructorName:
case CXXDestructorName:
@@ -481,12 +436,6 @@ DeclarationNameTable::getCXXLiteralOperatorName(IdentifierInfo *II) {
return DeclarationName(LiteralName);
}
-unsigned
-llvm::DenseMapInfo<clang::DeclarationName>::
-getHashValue(clang::DeclarationName N) {
- return DenseMapInfo<void*>::getHashValue(N.getAsOpaquePtr());
-}
-
DeclarationNameLoc::DeclarationNameLoc(DeclarationName Name) {
switch (Name.getNameKind()) {
case DeclarationName::Identifier:
diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp
index 4c7cd8a..c1432b5 100644
--- a/lib/AST/DumpXML.cpp
+++ b/lib/AST/DumpXML.cpp
@@ -326,7 +326,7 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
}
case TemplateArgument::Integral: {
push("integer");
- setInteger("value", *A.getAsIntegral());
+ setInteger("value", A.getAsIntegral());
completeAttrs();
pop();
break;
@@ -778,7 +778,6 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
// ObjCCategoryDecl
void visitObjCCategoryDeclAttrs(ObjCCategoryDecl *D) {
setFlag("extension", D->IsClassExtension());
- setFlag("synth_bitfield", D->hasSynthBitfield());
}
void visitObjCCategoryDeclChildren(ObjCCategoryDecl *D) {
visitDeclRef("interface", D->getClassInterface());
@@ -804,7 +803,6 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
// ObjCImplementationDecl
void visitObjCImplementationDeclAttrs(ObjCImplementationDecl *D) {
- setFlag("synth_bitfield", D->hasSynthBitfield());
set("identifier", D->getName());
}
void visitObjCImplementationDeclChildren(ObjCImplementationDecl *D) {
@@ -973,9 +971,9 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
}
void visitFunctionProtoTypeAttrs(FunctionProtoType *T) {
- setFlag("const", T->getTypeQuals() & Qualifiers::Const);
- setFlag("volatile", T->getTypeQuals() & Qualifiers::Volatile);
- setFlag("restrict", T->getTypeQuals() & Qualifiers::Restrict);
+ setFlag("const", T->isConst());
+ setFlag("volatile", T->isVolatile());
+ setFlag("restrict", T->isRestrict());
}
void visitFunctionProtoTypeChildren(FunctionProtoType *T) {
push("parameters");
@@ -1024,17 +1022,12 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
};
}
-void Decl::dumpXML() const {
- dumpXML(llvm::errs());
-}
-
void Decl::dumpXML(raw_ostream &out) const {
XMLDumper(out, getASTContext()).dispatch(const_cast<Decl*>(this));
}
#else /* ifndef NDEBUG */
-void Decl::dumpXML() const {}
void Decl::dumpXML(raw_ostream &out) const {}
#endif
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index fcde542..24361ef 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -33,6 +33,21 @@
#include <cstring>
using namespace clang;
+const CXXRecordDecl *Expr::getBestDynamicClassType() const {
+ const Expr *E = ignoreParenBaseCasts();
+
+ QualType DerivedType = E->getType();
+ if (const PointerType *PTy = DerivedType->getAs<PointerType>())
+ DerivedType = PTy->getPointeeType();
+
+ if (DerivedType->isDependentType())
+ return NULL;
+
+ const RecordType *Ty = DerivedType->castAs<RecordType>();
+ Decl *D = Ty->getDecl();
+ return cast<CXXRecordDecl>(D);
+}
+
/// isKnownToHaveBooleanValue - Return true if this is an integer expression
/// that is known to return 0 or 1. This happens for _Bool/bool expressions
/// but also int expressions which are produced by things like comparisons in
@@ -196,7 +211,7 @@ static void computeDeclRefDependence(ASTContext &Ctx, NamedDecl *D, QualType T,
if ((Ctx.getLangOpts().CPlusPlus0x ?
Var->getType()->isLiteralType() :
Var->getType()->isIntegralOrEnumerationType()) &&
- (Var->getType().getCVRQualifiers() == Qualifiers::Const ||
+ (Var->getType().isConstQualified() ||
Var->getType()->isReferenceType())) {
if (const Expr *Init = Var->getAnyInitializer())
if (Init->isValueDependent()) {
@@ -414,9 +429,7 @@ std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) {
if (FT) {
for (unsigned i = 0, e = Decl->getNumParams(); i != e; ++i) {
if (i) POut << ", ";
- std::string Param;
- Decl->getParamDecl(i)->getType().getAsStringInternal(Param, Policy);
- POut << Param;
+ POut << Decl->getParamDecl(i)->getType().stream(Policy);
}
if (FT->isVariadic()) {
@@ -427,10 +440,10 @@ std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) {
POut << ")";
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
- Qualifiers ThisQuals = Qualifiers::fromCVRMask(MD->getTypeQualifiers());
- if (ThisQuals.hasConst())
+ const FunctionType *FT = cast<FunctionType>(MD->getType().getTypePtr());
+ if (FT->isConst())
POut << " const";
- if (ThisQuals.hasVolatile())
+ if (FT->isVolatile())
POut << " volatile";
RefQualifierKind Ref = MD->getRefQualifier();
if (Ref == RQ_LValue)
@@ -545,6 +558,17 @@ void APNumericStorage::setIntValue(ASTContext &C, const llvm::APInt &Val) {
VAL = 0;
}
+IntegerLiteral::IntegerLiteral(ASTContext &C, const llvm::APInt &V,
+ QualType type, SourceLocation l)
+ : Expr(IntegerLiteralClass, type, VK_RValue, OK_Ordinary, false, false,
+ false, false),
+ Loc(l) {
+ assert(type->isIntegerType() && "Illegal type in IntegerLiteral");
+ assert(V.getBitWidth() == C.getIntWidth(type) &&
+ "Integer type is not the correct size for constant.");
+ setValue(C, V);
+}
+
IntegerLiteral *
IntegerLiteral::Create(ASTContext &C, const llvm::APInt &V,
QualType type, SourceLocation l) {
@@ -556,6 +580,23 @@ IntegerLiteral::Create(ASTContext &C, EmptyShell Empty) {
return new (C) IntegerLiteral(Empty);
}
+FloatingLiteral::FloatingLiteral(ASTContext &C, const llvm::APFloat &V,
+ bool isexact, QualType Type, SourceLocation L)
+ : Expr(FloatingLiteralClass, Type, VK_RValue, OK_Ordinary, false, false,
+ false, false), Loc(L) {
+ FloatingLiteralBits.IsIEEE =
+ &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad;
+ FloatingLiteralBits.IsExact = isexact;
+ setValue(C, V);
+}
+
+FloatingLiteral::FloatingLiteral(ASTContext &C, EmptyShell Empty)
+ : Expr(FloatingLiteralClass, Empty) {
+ FloatingLiteralBits.IsIEEE =
+ &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad;
+ FloatingLiteralBits.IsExact = false;
+}
+
FloatingLiteral *
FloatingLiteral::Create(ASTContext &C, const llvm::APFloat &V,
bool isexact, QualType Type, SourceLocation L) {
@@ -635,6 +676,99 @@ StringLiteral *StringLiteral::CreateEmpty(ASTContext &C, unsigned NumStrs) {
return SL;
}
+void StringLiteral::outputString(raw_ostream &OS) {
+ switch (getKind()) {
+ case Ascii: break; // no prefix.
+ case Wide: OS << 'L'; break;
+ case UTF8: OS << "u8"; break;
+ case UTF16: OS << 'u'; break;
+ case UTF32: OS << 'U'; break;
+ }
+ OS << '"';
+ static const char Hex[] = "0123456789ABCDEF";
+
+ unsigned LastSlashX = getLength();
+ for (unsigned I = 0, N = getLength(); I != N; ++I) {
+ switch (uint32_t Char = getCodeUnit(I)) {
+ default:
+ // FIXME: Convert UTF-8 back to codepoints before rendering.
+
+ // Convert UTF-16 surrogate pairs back to codepoints before rendering.
+ // Leave invalid surrogates alone; we'll use \x for those.
+ if (getKind() == UTF16 && I != N - 1 && Char >= 0xd800 &&
+ Char <= 0xdbff) {
+ uint32_t Trail = getCodeUnit(I + 1);
+ if (Trail >= 0xdc00 && Trail <= 0xdfff) {
+ Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00);
+ ++I;
+ }
+ }
+
+ if (Char > 0xff) {
+ // If this is a wide string, output characters over 0xff using \x
+ // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a
+ // codepoint: use \x escapes for invalid codepoints.
+ if (getKind() == Wide ||
+ (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) {
+ // FIXME: Is this the best way to print wchar_t?
+ OS << "\\x";
+ int Shift = 28;
+ while ((Char >> Shift) == 0)
+ Shift -= 4;
+ for (/**/; Shift >= 0; Shift -= 4)
+ OS << Hex[(Char >> Shift) & 15];
+ LastSlashX = I;
+ break;
+ }
+
+ if (Char > 0xffff)
+ OS << "\\U00"
+ << Hex[(Char >> 20) & 15]
+ << Hex[(Char >> 16) & 15];
+ else
+ OS << "\\u";
+ OS << Hex[(Char >> 12) & 15]
+ << Hex[(Char >> 8) & 15]
+ << Hex[(Char >> 4) & 15]
+ << Hex[(Char >> 0) & 15];
+ break;
+ }
+
+ // If we used \x... for the previous character, and this character is a
+ // hexadecimal digit, prevent it being slurped as part of the \x.
+ if (LastSlashX + 1 == I) {
+ switch (Char) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ OS << "\"\"";
+ }
+ }
+
+ assert(Char <= 0xff &&
+ "Characters above 0xff should already have been handled.");
+
+ if (isprint(Char))
+ OS << (char)Char;
+ else // Output anything hard as an octal escape.
+ OS << '\\'
+ << (char)('0' + ((Char >> 6) & 7))
+ << (char)('0' + ((Char >> 3) & 7))
+ << (char)('0' + ((Char >> 0) & 7));
+ break;
+ // Handle some common non-printable cases to make dumps prettier.
+ case '\\': OS << "\\\\"; break;
+ case '"': OS << "\\\""; break;
+ case '\n': OS << "\\n"; break;
+ case '\t': OS << "\\t"; break;
+ case '\a': OS << "\\a"; break;
+ case '\b': OS << "\\b"; break;
+ }
+ }
+ OS << '"';
+}
+
void StringLiteral::setString(ASTContext &C, StringRef Str,
StringKind Kind, bool IsPascal) {
//FIXME: we assume that the string data comes from a target that uses the same
@@ -681,7 +815,8 @@ void StringLiteral::setString(ASTContext &C, StringRef Str,
SourceLocation StringLiteral::
getLocationOfByte(unsigned ByteNo, const SourceManager &SM,
const LangOptions &Features, const TargetInfo &Target) const {
- assert(Kind == StringLiteral::Ascii && "This only works for ASCII strings");
+ assert((Kind == StringLiteral::Ascii || Kind == StringLiteral::UTF8) &&
+ "Only narrow string literals are currently supported");
// Loop over all of the tokens in this string until we find the one that
// contains the byte we're looking for.
@@ -704,14 +839,9 @@ getLocationOfByte(unsigned ByteNo, const SourceManager &SM,
const char *StrData = Buffer.data()+LocInfo.second;
- // Create a langops struct and enable trigraphs. This is sufficient for
- // relexing tokens.
- LangOptions LangOpts;
- LangOpts.Trigraphs = true;
-
// Create a lexer starting at the beginning of this token.
- Lexer TheLexer(StrTokSpellingLoc, Features, Buffer.begin(), StrData,
- Buffer.end());
+ Lexer TheLexer(SM.getLocForStartOfFile(LocInfo.first), Features,
+ Buffer.begin(), StrData, Buffer.end());
Token TheTok;
TheLexer.LexFromRawLexer(TheTok);
@@ -1656,8 +1786,9 @@ Stmt *BlockExpr::getBody() {
/// be warned about if the result is unused. If so, fill in Loc and Ranges
/// with location to warn on and the source range[s] to report with the
/// warning.
-bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
- SourceRange &R2, ASTContext &Ctx) const {
+bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
+ SourceRange &R1, SourceRange &R2,
+ ASTContext &Ctx) const {
// Don't warn if the expr is type dependent. The type could end up
// instantiating to void.
if (isTypeDependent())
@@ -1667,30 +1798,32 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
default:
if (getType()->isVoidType())
return false;
+ WarnE = this;
Loc = getExprLoc();
R1 = getSourceRange();
return true;
case ParenExprClass:
return cast<ParenExpr>(this)->getSubExpr()->
- isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
case GenericSelectionExprClass:
return cast<GenericSelectionExpr>(this)->getResultExpr()->
- isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
case UnaryOperatorClass: {
const UnaryOperator *UO = cast<UnaryOperator>(this);
switch (UO->getOpcode()) {
- default: break;
+ case UO_Plus:
+ case UO_Minus:
+ case UO_AddrOf:
+ case UO_Not:
+ case UO_LNot:
+ case UO_Deref:
+ break;
case UO_PostInc:
case UO_PostDec:
case UO_PreInc:
case UO_PreDec: // ++/--
return false; // Not a warning.
- case UO_Deref:
- // Dereferencing a volatile pointer is a side-effect.
- if (Ctx.getCanonicalType(getType()).isVolatileQualified())
- return false;
- break;
case UO_Real:
case UO_Imag:
// accessing a piece of a volatile complex is a side-effect.
@@ -1699,8 +1832,9 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
return false;
break;
case UO_Extension:
- return UO->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
}
+ WarnE = this;
Loc = UO->getOperatorLoc();
R1 = UO->getSubExpr()->getSourceRange();
return true;
@@ -1719,17 +1853,18 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
dyn_cast<IntegerLiteral>(BO->getRHS()->IgnoreParens()))
if (IE->getValue() == 0)
return false;
- return BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
// Consider '||', '&&' to have side effects if the LHS or RHS does.
case BO_LAnd:
case BO_LOr:
- if (!BO->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx) ||
- !BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx))
+ if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) ||
+ !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx))
return false;
break;
}
if (BO->isAssignmentOp())
return false;
+ WarnE = this;
Loc = BO->getOperatorLoc();
R1 = BO->getLHS()->getSourceRange();
R2 = BO->getRHS()->getSourceRange();
@@ -1745,28 +1880,22 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
// be being used for control flow. Only warn if both the LHS and
// RHS are warnings.
const ConditionalOperator *Exp = cast<ConditionalOperator>(this);
- if (!Exp->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx))
+ if (!Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx))
return false;
if (!Exp->getLHS())
return true;
- return Exp->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
}
case MemberExprClass:
- // If the base pointer or element is to a volatile pointer/field, accessing
- // it is a side effect.
- if (Ctx.getCanonicalType(getType()).isVolatileQualified())
- return false;
+ WarnE = this;
Loc = cast<MemberExpr>(this)->getMemberLoc();
R1 = SourceRange(Loc, Loc);
R2 = cast<MemberExpr>(this)->getBase()->getSourceRange();
return true;
case ArraySubscriptExprClass:
- // If the base pointer or element is to a volatile pointer/field, accessing
- // it is a side effect.
- if (Ctx.getCanonicalType(getType()).isVolatileQualified())
- return false;
+ WarnE = this;
Loc = cast<ArraySubscriptExpr>(this)->getRBracketLoc();
R1 = cast<ArraySubscriptExpr>(this)->getLHS()->getSourceRange();
R2 = cast<ArraySubscriptExpr>(this)->getRHS()->getSourceRange();
@@ -1782,6 +1911,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
const CXXOperatorCallExpr *Op = cast<CXXOperatorCallExpr>(this);
if (Op->getOperator() == OO_EqualEqual ||
Op->getOperator() == OO_ExclaimEqual) {
+ WarnE = this;
Loc = Op->getOperatorLoc();
R1 = Op->getSourceRange();
return true;
@@ -1802,6 +1932,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
// updated to match for QoI.
if (FD->getAttr<WarnUnusedResultAttr>() ||
FD->getAttr<PureAttr>() || FD->getAttr<ConstAttr>()) {
+ WarnE = this;
Loc = CE->getCallee()->getLocStart();
R1 = CE->getCallee()->getSourceRange();
@@ -1826,6 +1957,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
ME->getSelector().getIdentifierInfoForSlot(0) &&
ME->getSelector().getIdentifierInfoForSlot(0)
->getName().startswith("init")) {
+ WarnE = this;
Loc = getExprLoc();
R1 = ME->getSourceRange();
return true;
@@ -1833,6 +1965,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD && MD->getAttr<WarnUnusedResultAttr>()) {
+ WarnE = this;
Loc = getExprLoc();
return true;
}
@@ -1840,6 +1973,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
}
case ObjCPropertyRefExprClass:
+ WarnE = this;
Loc = getExprLoc();
R1 = getSourceRange();
return true;
@@ -1852,6 +1986,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
isa<BinaryOperator>(PO->getSyntacticForm()))
return false;
+ WarnE = this;
Loc = getExprLoc();
R1 = getSourceRange();
return true;
@@ -1866,50 +2001,67 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
const CompoundStmt *CS = cast<StmtExpr>(this)->getSubStmt();
if (!CS->body_empty()) {
if (const Expr *E = dyn_cast<Expr>(CS->body_back()))
- return E->isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
if (const LabelStmt *Label = dyn_cast<LabelStmt>(CS->body_back()))
if (const Expr *E = dyn_cast<Expr>(Label->getSubStmt()))
- return E->isUnusedResultAWarning(Loc, R1, R2, Ctx);
+ return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
}
if (getType()->isVoidType())
return false;
+ WarnE = this;
Loc = cast<StmtExpr>(this)->getLParenLoc();
R1 = getSourceRange();
return true;
}
- case CStyleCastExprClass:
- // If this is an explicit cast to void, allow it. People do this when they
- // think they know what they're doing :).
- if (getType()->isVoidType())
- return false;
- Loc = cast<CStyleCastExpr>(this)->getLParenLoc();
- R1 = cast<CStyleCastExpr>(this)->getSubExpr()->getSourceRange();
- return true;
- case CXXFunctionalCastExprClass: {
- if (getType()->isVoidType())
- return false;
+ case CStyleCastExprClass: {
+ // Ignore an explicit cast to void unless the operand is a non-trivial
+ // volatile lvalue.
const CastExpr *CE = cast<CastExpr>(this);
-
- // If this is a cast to void or a constructor conversion, check the operand.
+ if (CE->getCastKind() == CK_ToVoid) {
+ if (CE->getSubExpr()->isGLValue() &&
+ CE->getSubExpr()->getType().isVolatileQualified()) {
+ const DeclRefExpr *DRE =
+ dyn_cast<DeclRefExpr>(CE->getSubExpr()->IgnoreParens());
+ if (!(DRE && isa<VarDecl>(DRE->getDecl()) &&
+ cast<VarDecl>(DRE->getDecl())->hasLocalStorage())) {
+ return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc,
+ R1, R2, Ctx);
+ }
+ }
+ return false;
+ }
+
+ // If this is a cast to a constructor conversion, check the operand.
// Otherwise, the result of the cast is unused.
- if (CE->getCastKind() == CK_ToVoid ||
- CE->getCastKind() == CK_ConstructorConversion)
- return (cast<CastExpr>(this)->getSubExpr()
- ->isUnusedResultAWarning(Loc, R1, R2, Ctx));
- Loc = cast<CXXFunctionalCastExpr>(this)->getTypeBeginLoc();
- R1 = cast<CXXFunctionalCastExpr>(this)->getSubExpr()->getSourceRange();
+ if (CE->getCastKind() == CK_ConstructorConversion)
+ return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+
+ WarnE = this;
+ if (const CXXFunctionalCastExpr *CXXCE =
+ dyn_cast<CXXFunctionalCastExpr>(this)) {
+ Loc = CXXCE->getTypeBeginLoc();
+ R1 = CXXCE->getSubExpr()->getSourceRange();
+ } else {
+ const CStyleCastExpr *CStyleCE = cast<CStyleCastExpr>(this);
+ Loc = CStyleCE->getLParenLoc();
+ R1 = CStyleCE->getSubExpr()->getSourceRange();
+ }
return true;
}
+ case ImplicitCastExprClass: {
+ const CastExpr *ICE = cast<ImplicitCastExpr>(this);
- case ImplicitCastExprClass:
- // Check the operand, since implicit casts are inserted by Sema
- return (cast<ImplicitCastExpr>(this)
- ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx));
+ // lvalue-to-rvalue conversion on a volatile lvalue is a side-effect.
+ if (ICE->getCastKind() == CK_LValueToRValue &&
+ ICE->getSubExpr()->getType().isVolatileQualified())
+ return false;
+ return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+ }
case CXXDefaultArgExprClass:
return (cast<CXXDefaultArgExpr>(this)
- ->getExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx));
+ ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
case CXXNewExprClass:
// FIXME: In theory, there might be new expressions that don't have side
@@ -1918,10 +2070,10 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
return false;
case CXXBindTemporaryExprClass:
return (cast<CXXBindTemporaryExpr>(this)
- ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx));
+ ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
case ExprWithCleanupsClass:
return (cast<ExprWithCleanups>(this)
- ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx));
+ ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
}
}
@@ -2096,7 +2248,27 @@ Expr *Expr::IgnoreParenLValueCasts() {
}
return E;
}
-
+
+Expr *Expr::ignoreParenBaseCasts() {
+ Expr *E = this;
+ while (true) {
+ if (ParenExpr *P = dyn_cast<ParenExpr>(E)) {
+ E = P->getSubExpr();
+ continue;
+ }
+ if (CastExpr *CE = dyn_cast<CastExpr>(E)) {
+ if (CE->getCastKind() == CK_DerivedToBase ||
+ CE->getCastKind() == CK_UncheckedDerivedToBase ||
+ CE->getCastKind() == CK_NoOp) {
+ E = CE->getSubExpr();
+ continue;
+ }
+ }
+
+ return E;
+ }
+}
+
Expr *Expr::IgnoreParenImpCasts() {
Expr *E = this;
while (true) {
@@ -2267,6 +2439,10 @@ bool Expr::isTemporaryObject(ASTContext &C, const CXXRecordDecl *TempTy) const {
if (isa<MemberExpr>(E))
return false;
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+ if (BO->isPtrMemOp())
+ return false;
+
// - opaque values (all)
if (isa<OpaqueValueExpr>(E))
return false;
@@ -2446,6 +2622,207 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const {
return isEvaluatable(Ctx);
}
+bool Expr::HasSideEffects(const ASTContext &Ctx) const {
+ if (isInstantiationDependent())
+ return true;
+
+ switch (getStmtClass()) {
+ case NoStmtClass:
+ #define ABSTRACT_STMT(Type)
+ #define STMT(Type, Base) case Type##Class:
+ #define EXPR(Type, Base)
+ #include "clang/AST/StmtNodes.inc"
+ llvm_unreachable("unexpected Expr kind");
+
+ case DependentScopeDeclRefExprClass:
+ case CXXUnresolvedConstructExprClass:
+ case CXXDependentScopeMemberExprClass:
+ case UnresolvedLookupExprClass:
+ case UnresolvedMemberExprClass:
+ case PackExpansionExprClass:
+ case SubstNonTypeTemplateParmPackExprClass:
+ llvm_unreachable("shouldn't see dependent / unresolved nodes here");
+
+ case DeclRefExprClass:
+ case ObjCIvarRefExprClass:
+ case PredefinedExprClass:
+ case IntegerLiteralClass:
+ case FloatingLiteralClass:
+ case ImaginaryLiteralClass:
+ case StringLiteralClass:
+ case CharacterLiteralClass:
+ case OffsetOfExprClass:
+ case ImplicitValueInitExprClass:
+ case UnaryExprOrTypeTraitExprClass:
+ case AddrLabelExprClass:
+ case GNUNullExprClass:
+ case CXXBoolLiteralExprClass:
+ case CXXNullPtrLiteralExprClass:
+ case CXXThisExprClass:
+ case CXXScalarValueInitExprClass:
+ case TypeTraitExprClass:
+ case UnaryTypeTraitExprClass:
+ case BinaryTypeTraitExprClass:
+ case ArrayTypeTraitExprClass:
+ case ExpressionTraitExprClass:
+ case CXXNoexceptExprClass:
+ case SizeOfPackExprClass:
+ case ObjCStringLiteralClass:
+ case ObjCEncodeExprClass:
+ case ObjCBoolLiteralExprClass:
+ case CXXUuidofExprClass:
+ case OpaqueValueExprClass:
+ // These never have a side-effect.
+ return false;
+
+ case CallExprClass:
+ case CompoundAssignOperatorClass:
+ case VAArgExprClass:
+ case AtomicExprClass:
+ case StmtExprClass:
+ case CXXOperatorCallExprClass:
+ case CXXMemberCallExprClass:
+ case UserDefinedLiteralClass:
+ case CXXThrowExprClass:
+ case CXXNewExprClass:
+ case CXXDeleteExprClass:
+ case ExprWithCleanupsClass:
+ case CXXBindTemporaryExprClass:
+ case BlockExprClass:
+ case CUDAKernelCallExprClass:
+ // These always have a side-effect.
+ return true;
+
+ case ParenExprClass:
+ case ArraySubscriptExprClass:
+ case MemberExprClass:
+ case ConditionalOperatorClass:
+ case BinaryConditionalOperatorClass:
+ case CompoundLiteralExprClass:
+ case ExtVectorElementExprClass:
+ case DesignatedInitExprClass:
+ case ParenListExprClass:
+ case CXXPseudoDestructorExprClass:
+ case SubstNonTypeTemplateParmExprClass:
+ case MaterializeTemporaryExprClass:
+ case ShuffleVectorExprClass:
+ case AsTypeExprClass:
+ // These have a side-effect if any subexpression does.
+ break;
+
+ case UnaryOperatorClass:
+ if (cast<UnaryOperator>(this)->isIncrementDecrementOp())
+ return true;
+ break;
+
+ case BinaryOperatorClass:
+ if (cast<BinaryOperator>(this)->isAssignmentOp())
+ return true;
+ break;
+
+ case InitListExprClass:
+ // FIXME: The children for an InitListExpr doesn't include the array filler.
+ if (const Expr *E = cast<InitListExpr>(this)->getArrayFiller())
+ if (E->HasSideEffects(Ctx))
+ return true;
+ break;
+
+ case GenericSelectionExprClass:
+ return cast<GenericSelectionExpr>(this)->getResultExpr()->
+ HasSideEffects(Ctx);
+
+ case ChooseExprClass:
+ return cast<ChooseExpr>(this)->getChosenSubExpr(Ctx)->HasSideEffects(Ctx);
+
+ case CXXDefaultArgExprClass:
+ return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(Ctx);
+
+ case CXXDynamicCastExprClass: {
+ // A dynamic_cast expression has side-effects if it can throw.
+ const CXXDynamicCastExpr *DCE = cast<CXXDynamicCastExpr>(this);
+ if (DCE->getTypeAsWritten()->isReferenceType() &&
+ DCE->getCastKind() == CK_Dynamic)
+ return true;
+ } // Fall through.
+ case ImplicitCastExprClass:
+ case CStyleCastExprClass:
+ case CXXStaticCastExprClass:
+ case CXXReinterpretCastExprClass:
+ case CXXConstCastExprClass:
+ case CXXFunctionalCastExprClass: {
+ const CastExpr *CE = cast<CastExpr>(this);
+ if (CE->getCastKind() == CK_LValueToRValue &&
+ CE->getSubExpr()->getType().isVolatileQualified())
+ return true;
+ break;
+ }
+
+ case CXXTypeidExprClass:
+ // typeid might throw if its subexpression is potentially-evaluated, so has
+ // side-effects in that case whether or not its subexpression does.
+ return cast<CXXTypeidExpr>(this)->isPotentiallyEvaluated();
+
+ case CXXConstructExprClass:
+ case CXXTemporaryObjectExprClass: {
+ const CXXConstructExpr *CE = cast<CXXConstructExpr>(this);
+ if (!CE->getConstructor()->isTrivial())
+ return true;
+ // A trivial constructor does not add any side-effects of its own. Just look
+ // at its arguments.
+ break;
+ }
+
+ case LambdaExprClass: {
+ const LambdaExpr *LE = cast<LambdaExpr>(this);
+ for (LambdaExpr::capture_iterator I = LE->capture_begin(),
+ E = LE->capture_end(); I != E; ++I)
+ if (I->getCaptureKind() == LCK_ByCopy)
+ // FIXME: Only has a side-effect if the variable is volatile or if
+ // the copy would invoke a non-trivial copy constructor.
+ return true;
+ return false;
+ }
+
+ case PseudoObjectExprClass: {
+ // Only look for side-effects in the semantic form, and look past
+ // OpaqueValueExpr bindings in that form.
+ const PseudoObjectExpr *PO = cast<PseudoObjectExpr>(this);
+ for (PseudoObjectExpr::const_semantics_iterator I = PO->semantics_begin(),
+ E = PO->semantics_end();
+ I != E; ++I) {
+ const Expr *Subexpr = *I;
+ if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Subexpr))
+ Subexpr = OVE->getSourceExpr();
+ if (Subexpr->HasSideEffects(Ctx))
+ return true;
+ }
+ return false;
+ }
+
+ case ObjCBoxedExprClass:
+ case ObjCArrayLiteralClass:
+ case ObjCDictionaryLiteralClass:
+ case ObjCMessageExprClass:
+ case ObjCSelectorExprClass:
+ case ObjCProtocolExprClass:
+ case ObjCPropertyRefExprClass:
+ case ObjCIsaExprClass:
+ case ObjCIndirectCopyRestoreExprClass:
+ case ObjCSubscriptRefExprClass:
+ case ObjCBridgedCastExprClass:
+ // FIXME: Classify these cases better.
+ return true;
+ }
+
+ // Recurse to children.
+ for (const_child_range SubStmts = children(); SubStmts; ++SubStmts)
+ if (const Stmt *S = *SubStmts)
+ if (cast<Expr>(S)->HasSideEffects(Ctx))
+ return true;
+
+ return false;
+}
+
namespace {
/// \brief Look for a call to a non-trivial function within an expression.
class NonTrivialCallFinder : public EvaluatedExprVisitor<NonTrivialCallFinder>
@@ -2514,7 +2891,7 @@ Expr::isNullPointerConstant(ASTContext &Ctx,
llvm_unreachable("Unexpected value dependent expression!");
case NPC_ValueDependentIsNull:
if (isTypeDependent() || getType()->isIntegralType(Ctx))
- return NPCK_ZeroInteger;
+ return NPCK_ZeroExpression;
else
return NPCK_NotNull;
@@ -2588,7 +2965,12 @@ Expr::isNullPointerConstant(ASTContext &Ctx,
return NPCK_NotNull;
}
- return (EvaluateKnownConstInt(Ctx) == 0) ? NPCK_ZeroInteger : NPCK_NotNull;
+ if (EvaluateKnownConstInt(Ctx) != 0)
+ return NPCK_NotNull;
+
+ if (isa<IntegerLiteral>(this))
+ return NPCK_ZeroLiteral;
+ return NPCK_ZeroExpression;
}
/// \brief If this expression is an l-value for an Objective C
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index 8cf519c..3fa49e0 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Basic/IdentifierTable.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
@@ -23,6 +24,21 @@ using namespace clang;
// Child Iterators for iterating over subexpressions/substatements
//===----------------------------------------------------------------------===//
+bool CXXTypeidExpr::isPotentiallyEvaluated() const {
+ if (isTypeOperand())
+ return false;
+
+ // C++11 [expr.typeid]p3:
+ // When typeid is applied to an expression other than a glvalue of
+ // polymorphic class type, [...] the expression is an unevaluated operand.
+ const Expr *E = getExprOperand();
+ if (const CXXRecordDecl *RD = E->getType()->getAsCXXRecordDecl())
+ if (RD->isPolymorphic() && E->isGLValue())
+ return true;
+
+ return false;
+}
+
QualType CXXTypeidExpr::getTypeOperand() const {
assert(isTypeOperand() && "Cannot call getTypeOperand for typeid(expr)");
return Operand.get<TypeSourceInfo *>()->getType().getNonReferenceType()
@@ -126,13 +142,6 @@ SourceLocation CXXNewExpr::getEndLoc() const {
// CXXDeleteExpr
QualType CXXDeleteExpr::getDestroyedType() const {
const Expr *Arg = getArgument();
- while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
- if (ICE->getCastKind() != CK_UserDefinedConversion &&
- ICE->getType()->isVoidPointerType())
- Arg = ICE->getSubExpr();
- else
- break;
- }
// The type-to-delete may not be a pointer if it's a dependent type.
const QualType ArgType = Arg->getType();
@@ -268,6 +277,7 @@ OverloadExpr::OverloadExpr(StmtClass K, ASTContext &C,
isa<UnresolvedUsingValueDecl>(*I)) {
ExprBits.TypeDependent = true;
ExprBits.ValueDependent = true;
+ ExprBits.InstantiationDependent = true;
}
}
@@ -415,37 +425,37 @@ SourceRange CXXConstructExpr::getSourceRange() const {
return SourceRange(Loc, End);
}
-SourceRange CXXOperatorCallExpr::getSourceRange() const {
+SourceRange CXXOperatorCallExpr::getSourceRangeImpl() const {
OverloadedOperatorKind Kind = getOperator();
if (Kind == OO_PlusPlus || Kind == OO_MinusMinus) {
if (getNumArgs() == 1)
// Prefix operator
- return SourceRange(getOperatorLoc(),
- getArg(0)->getSourceRange().getEnd());
+ return SourceRange(getOperatorLoc(), getArg(0)->getLocEnd());
else
// Postfix operator
- return SourceRange(getArg(0)->getSourceRange().getBegin(),
- getOperatorLoc());
+ return SourceRange(getArg(0)->getLocStart(), getOperatorLoc());
} else if (Kind == OO_Arrow) {
return getArg(0)->getSourceRange();
} else if (Kind == OO_Call) {
- return SourceRange(getArg(0)->getSourceRange().getBegin(), getRParenLoc());
+ return SourceRange(getArg(0)->getLocStart(), getRParenLoc());
} else if (Kind == OO_Subscript) {
- return SourceRange(getArg(0)->getSourceRange().getBegin(), getRParenLoc());
+ return SourceRange(getArg(0)->getLocStart(), getRParenLoc());
} else if (getNumArgs() == 1) {
- return SourceRange(getOperatorLoc(), getArg(0)->getSourceRange().getEnd());
+ return SourceRange(getOperatorLoc(), getArg(0)->getLocEnd());
} else if (getNumArgs() == 2) {
- return SourceRange(getArg(0)->getSourceRange().getBegin(),
- getArg(1)->getSourceRange().getEnd());
+ return SourceRange(getArg(0)->getLocStart(), getArg(1)->getLocEnd());
} else {
- return SourceRange();
+ return getOperatorLoc();
}
}
Expr *CXXMemberCallExpr::getImplicitObjectArgument() const {
- if (const MemberExpr *MemExpr =
- dyn_cast<MemberExpr>(getCallee()->IgnoreParens()))
+ const Expr *Callee = getCallee()->IgnoreParens();
+ if (const MemberExpr *MemExpr = dyn_cast<MemberExpr>(Callee))
return MemExpr->getBase();
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Callee))
+ if (BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI)
+ return BO->getLHS();
// FIXME: Will eventually need to cope with member pointers.
return 0;
@@ -461,7 +471,7 @@ CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const {
}
-CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() {
+CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() const {
Expr* ThisArg = getImplicitObjectArgument();
if (!ThisArg)
return 0;
@@ -556,6 +566,9 @@ bool CXXDynamicCastExpr::isAlwaysNull() const
DestType = DestType->castAs<PointerType>()->getPointeeType();
}
+ if (DestType->isVoidType())
+ return false;
+
const CXXRecordDecl *SrcRD =
cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl());
@@ -796,10 +809,11 @@ LambdaExpr::LambdaExpr(QualType T,
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
- SourceLocation ClosingBrace)
+ SourceLocation ClosingBrace,
+ bool ContainsUnexpandedParameterPack)
: Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary,
T->isDependentType(), T->isDependentType(), T->isDependentType(),
- /*ContainsUnexpandedParameterPack=*/false),
+ ContainsUnexpandedParameterPack),
IntroducerRange(IntroducerRange),
NumCaptures(Captures.size()),
CaptureDefault(CaptureDefault),
@@ -856,7 +870,8 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context,
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
- SourceLocation ClosingBrace) {
+ SourceLocation ClosingBrace,
+ bool ContainsUnexpandedParameterPack) {
// Determine the type of the expression (i.e., the type of the
// function object we're creating).
QualType T = Context.getTypeDeclType(Class);
@@ -869,7 +884,7 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context,
return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault,
Captures, ExplicitParams, ExplicitResultType,
CaptureInits, ArrayIndexVars, ArrayIndexStarts,
- ClosingBrace);
+ ClosingBrace, ContainsUnexpandedParameterPack);
}
LambdaExpr *LambdaExpr::CreateDeserialized(ASTContext &C, unsigned NumCaptures,
@@ -944,7 +959,7 @@ CompoundStmt *LambdaExpr::getBody() const {
}
bool LambdaExpr::isMutable() const {
- return (getCallOperator()->getTypeQualifiers() & Qualifiers::Const) == 0;
+ return !getCallOperator()->isConst();
}
ExprWithCleanups::ExprWithCleanups(Expr *subexpr,
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index b091e19..f16d70b 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -77,6 +77,7 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const {
case Cl::CL_MemberFunction:
case Cl::CL_SubObjCPropertySetting:
case Cl::CL_ClassTemporary:
+ case Cl::CL_ArrayTemporary:
case Cl::CL_ObjCMessageRValue:
case Cl::CL_PRValue: assert(getValueKind() == VK_RValue); break;
}
@@ -87,6 +88,18 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const {
return Classification(kind, modifiable);
}
+/// Classify an expression which creates a temporary, based on its type.
+static Cl::Kinds ClassifyTemporary(QualType T) {
+ if (T->isRecordType())
+ return Cl::CL_ClassTemporary;
+ if (T->isArrayType())
+ return Cl::CL_ArrayTemporary;
+
+ // No special classification: these don't behave differently from normal
+ // prvalues.
+ return Cl::CL_PRValue;
+}
+
static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
// This function takes the first stab at classifying expressions.
const LangOptions &Lang = Ctx.getLangOpts();
@@ -124,10 +137,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
return Cl::CL_LValue;
// C99 6.5.2.5p5 says that compound literals are lvalues.
- // In C++, they're class temporaries.
+ // In C++, they're prvalue temporaries.
case Expr::CompoundLiteralExprClass:
- return Ctx.getLangOpts().CPlusPlus? Cl::CL_ClassTemporary
- : Cl::CL_LValue;
+ return Ctx.getLangOpts().CPlusPlus ? ClassifyTemporary(E->getType())
+ : Cl::CL_LValue;
// Expressions that are prvalues.
case Expr::CXXBoolLiteralExprClass:
@@ -158,7 +171,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::ObjCSelectorExprClass:
case Expr::ObjCProtocolExprClass:
case Expr::ObjCStringLiteralClass:
- case Expr::ObjCNumericLiteralClass:
+ case Expr::ObjCBoxedExprClass:
case Expr::ObjCArrayLiteralClass:
case Expr::ObjCDictionaryLiteralClass:
case Expr::ObjCBoolLiteralExprClass:
@@ -417,7 +430,7 @@ static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T) {
return Cl::CL_LValue;
const RValueReferenceType *RV = T->getAs<RValueReferenceType>();
if (!RV) // Could still be a class temporary, though.
- return T->isRecordType() ? Cl::CL_ClassTemporary : Cl::CL_PRValue;
+ return ClassifyTemporary(T);
return RV->getPointeeType()->isFunctionType() ? Cl::CL_LValue : Cl::CL_XValue;
}
@@ -602,6 +615,7 @@ Expr::LValueClassification Expr::ClassifyLValue(ASTContext &Ctx) const {
case Cl::CL_MemberFunction: return LV_MemberFunction;
case Cl::CL_SubObjCPropertySetting: return LV_SubObjCPropertySetting;
case Cl::CL_ClassTemporary: return LV_ClassTemporary;
+ case Cl::CL_ArrayTemporary: return LV_ArrayTemporary;
case Cl::CL_ObjCMessageRValue: return LV_InvalidMessageExpression;
case Cl::CL_PRValue: return LV_InvalidExpression;
}
@@ -622,6 +636,7 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const {
case Cl::CL_MemberFunction: return MLV_MemberFunction;
case Cl::CL_SubObjCPropertySetting: return MLV_SubObjCPropertySetting;
case Cl::CL_ClassTemporary: return MLV_ClassTemporary;
+ case Cl::CL_ArrayTemporary: return MLV_ArrayTemporary;
case Cl::CL_ObjCMessageRValue: return MLV_InvalidMessageExpression;
case Cl::CL_PRValue:
return VC.getModifiable() == Cl::CM_LValueCast ?
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 66a88b0..06c41a2 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -287,7 +287,9 @@ namespace {
/// parameters' function scope indices.
const APValue *Arguments;
- typedef llvm::DenseMap<const Expr*, APValue> MapTy;
+ // Note that we intentionally use std::map here so that references to
+ // values are stable.
+ typedef std::map<const Expr*, APValue> MapTy;
typedef MapTy::const_iterator temp_iterator;
/// Temporaries - Temporary lvalues materialized within this stack frame.
MapTy Temporaries;
@@ -361,11 +363,6 @@ namespace {
/// NextCallIndex - The next call index to assign.
unsigned NextCallIndex;
- typedef llvm::DenseMap<const OpaqueValueExpr*, APValue> MapTy;
- /// OpaqueValues - Values used as the common expression in a
- /// BinaryConditionalOperator.
- MapTy OpaqueValues;
-
/// BottomFrame - The frame in which evaluation started. This must be
/// initialized after CurrentCall and CallStackDepth.
CallStackFrame BottomFrame;
@@ -394,12 +391,6 @@ namespace {
EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false),
CheckingPotentialConstantExpression(false) {}
- const APValue *getOpaqueValue(const OpaqueValueExpr *e) const {
- MapTy::const_iterator i = OpaqueValues.find(e);
- if (i == OpaqueValues.end()) return 0;
- return &i->second;
- }
-
void setEvaluatingDecl(const VarDecl *VD, APValue &Value) {
EvaluatingDecl = VD;
EvaluatingDeclValue = &Value;
@@ -1072,8 +1063,8 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
}
for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I) {
- if (!CheckConstantExpression(Info, DiagLoc, (*I)->getType(),
- Value.getStructField((*I)->getFieldIndex())))
+ if (!CheckConstantExpression(Info, DiagLoc, I->getType(),
+ Value.getStructField(I->getFieldIndex())))
return false;
}
}
@@ -1160,11 +1151,10 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result,
}
template<typename T>
-static bool HandleOverflow(EvalInfo &Info, const Expr *E,
+static void HandleOverflow(EvalInfo &Info, const Expr *E,
const T &SrcValue, QualType DestType) {
- Info.Diag(E, diag::note_constexpr_overflow)
+ Info.CCEDiag(E, diag::note_constexpr_overflow)
<< SrcValue << DestType;
- return false;
}
static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E,
@@ -1178,7 +1168,7 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E,
bool ignored;
if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored)
& APFloat::opInvalidOp)
- return HandleOverflow(Info, E, Value, DestType);
+ HandleOverflow(Info, E, Value, DestType);
return true;
}
@@ -1190,7 +1180,7 @@ static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
if (Result.convert(Info.Ctx.getFloatTypeSemantics(DestType),
APFloat::rmNearestTiesToEven, &ignored)
& APFloat::opOverflow)
- return HandleOverflow(Info, E, Value, DestType);
+ HandleOverflow(Info, E, Value, DestType);
return true;
}
@@ -1213,7 +1203,7 @@ static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E,
if (Result.convertFromAPInt(Value, Value.isSigned(),
APFloat::rmNearestTiesToEven)
& APFloat::opOverflow)
- return HandleOverflow(Info, E, Value, DestType);
+ HandleOverflow(Info, E, Value, DestType);
return true;
}
@@ -1282,6 +1272,7 @@ static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result,
// Truncate the path to the subobject, and remove any derived-to-base offsets.
const RecordDecl *RD = TruncatedType;
for (unsigned I = TruncatedElements, N = D.Entries.size(); I != N; ++I) {
+ if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
const CXXRecordDecl *Base = getAsBaseClass(D.Entries[I]);
if (isVirtualBaseClass(D.Entries[I]))
@@ -1294,13 +1285,18 @@ static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result,
return true;
}
-static void HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj,
+static bool HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj,
const CXXRecordDecl *Derived,
const CXXRecordDecl *Base,
const ASTRecordLayout *RL = 0) {
- if (!RL) RL = &Info.Ctx.getASTRecordLayout(Derived);
+ if (!RL) {
+ if (Derived->isInvalidDecl()) return false;
+ RL = &Info.Ctx.getASTRecordLayout(Derived);
+ }
+
Obj.getLValueOffset() += RL->getBaseClassOffset(Base);
Obj.addDecl(Info, E, Base, /*Virtual*/ false);
+ return true;
}
static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj,
@@ -1308,10 +1304,8 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj,
const CXXBaseSpecifier *Base) {
const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl();
- if (!Base->isVirtual()) {
- HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl);
- return true;
- }
+ if (!Base->isVirtual())
+ return HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl);
SubobjectDesignator &D = Obj.Designator;
if (D.Invalid)
@@ -1323,6 +1317,7 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj,
return false;
// Find the virtual base class.
+ if (DerivedDecl->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl);
Obj.getLValueOffset() += Layout.getVBaseClassOffset(BaseDecl);
Obj.addDecl(Info, E, BaseDecl, /*Virtual*/ true);
@@ -1331,24 +1326,29 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj,
/// Update LVal to refer to the given field, which must be a member of the type
/// currently described by LVal.
-static void HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal,
+static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal,
const FieldDecl *FD,
const ASTRecordLayout *RL = 0) {
- if (!RL)
+ if (!RL) {
+ if (FD->getParent()->isInvalidDecl()) return false;
RL = &Info.Ctx.getASTRecordLayout(FD->getParent());
+ }
unsigned I = FD->getFieldIndex();
LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I));
LVal.addDecl(Info, E, FD);
+ return true;
}
/// Update LVal to refer to the given indirect field.
-static void HandleLValueIndirectMember(EvalInfo &Info, const Expr *E,
+static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E,
LValue &LVal,
const IndirectFieldDecl *IFD) {
for (IndirectFieldDecl::chain_iterator C = IFD->chain_begin(),
CE = IFD->chain_end(); C != CE; ++C)
- HandleLValueMember(Info, E, LVal, cast<FieldDecl>(*C));
+ if (!HandleLValueMember(Info, E, LVal, cast<FieldDecl>(*C)))
+ return false;
+ return true;
}
/// Get the size of the given type in char units.
@@ -1952,22 +1952,27 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
// The first class in the path is that of the lvalue.
for (unsigned I = 1, N = MemPtr.Path.size(); I != N; ++I) {
const CXXRecordDecl *Base = MemPtr.Path[N - I - 1];
- HandleLValueDirectBase(Info, BO, LV, RD, Base);
+ if (!HandleLValueDirectBase(Info, BO, LV, RD, Base))
+ return 0;
RD = Base;
}
// Finally cast to the class containing the member.
- HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord());
+ if (!HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord()))
+ return 0;
}
// Add the member. Note that we cannot build bound member functions here.
if (IncludeMember) {
- if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl()))
- HandleLValueMember(Info, BO, LV, FD);
- else if (const IndirectFieldDecl *IFD =
- dyn_cast<IndirectFieldDecl>(MemPtr.getDecl()))
- HandleLValueIndirectMember(Info, BO, LV, IFD);
- else
+ if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl())) {
+ if (!HandleLValueMember(Info, BO, LV, FD))
+ return 0;
+ } else if (const IndirectFieldDecl *IFD =
+ dyn_cast<IndirectFieldDecl>(MemPtr.getDecl())) {
+ if (!HandleLValueIndirectMember(Info, BO, LV, IFD))
+ return 0;
+ } else {
llvm_unreachable("can't construct reference to bound member function");
+ }
}
return MemPtr.getDecl();
@@ -2189,6 +2194,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
Result = APValue(APValue::UninitStruct(), RD->getNumBases(),
std::distance(RD->field_begin(), RD->field_end()));
+ if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
bool Success = true;
@@ -2212,11 +2218,13 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
"base class initializers not in expected order");
++BaseIt;
#endif
- HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD,
- BaseType->getAsCXXRecordDecl(), &Layout);
+ if (!HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD,
+ BaseType->getAsCXXRecordDecl(), &Layout))
+ return false;
Value = &Result.getStructBase(BasesSeen++);
} else if (FieldDecl *FD = (*I)->getMember()) {
- HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout);
+ if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout))
+ return false;
if (RD->isUnion()) {
Result = APValue(FD);
Value = &Result.getUnionValue();
@@ -2244,7 +2252,8 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
*Value = APValue(APValue::UninitStruct(), CD->getNumBases(),
std::distance(CD->field_begin(), CD->field_end()));
}
- HandleLValueMember(Info, (*I)->getInit(), Subobject, FD);
+ if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD))
+ return false;
if (CD->isUnion())
Value = &Value->getUnionValue();
else
@@ -2268,107 +2277,6 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
return Success;
}
-namespace {
-class HasSideEffect
- : public ConstStmtVisitor<HasSideEffect, bool> {
- const ASTContext &Ctx;
-public:
-
- HasSideEffect(const ASTContext &C) : Ctx(C) {}
-
- // Unhandled nodes conservatively default to having side effects.
- bool VisitStmt(const Stmt *S) {
- return true;
- }
-
- bool VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); }
- bool VisitGenericSelectionExpr(const GenericSelectionExpr *E) {
- return Visit(E->getResultExpr());
- }
- bool VisitDeclRefExpr(const DeclRefExpr *E) {
- if (Ctx.getCanonicalType(E->getType()).isVolatileQualified())
- return true;
- return false;
- }
- bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) {
- if (Ctx.getCanonicalType(E->getType()).isVolatileQualified())
- return true;
- return false;
- }
-
- // We don't want to evaluate BlockExprs multiple times, as they generate
- // a ton of code.
- bool VisitBlockExpr(const BlockExpr *E) { return true; }
- bool VisitPredefinedExpr(const PredefinedExpr *E) { return false; }
- bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E)
- { return Visit(E->getInitializer()); }
- bool VisitMemberExpr(const MemberExpr *E) { return Visit(E->getBase()); }
- bool VisitIntegerLiteral(const IntegerLiteral *E) { return false; }
- bool VisitFloatingLiteral(const FloatingLiteral *E) { return false; }
- bool VisitStringLiteral(const StringLiteral *E) { return false; }
- bool VisitCharacterLiteral(const CharacterLiteral *E) { return false; }
- bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E)
- { return false; }
- bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E)
- { return Visit(E->getLHS()) || Visit(E->getRHS()); }
- bool VisitChooseExpr(const ChooseExpr *E)
- { return Visit(E->getChosenSubExpr(Ctx)); }
- bool VisitCastExpr(const CastExpr *E) { return Visit(E->getSubExpr()); }
- bool VisitBinAssign(const BinaryOperator *E) { return true; }
- bool VisitCompoundAssignOperator(const BinaryOperator *E) { return true; }
- bool VisitBinaryOperator(const BinaryOperator *E)
- { return Visit(E->getLHS()) || Visit(E->getRHS()); }
- bool VisitUnaryPreInc(const UnaryOperator *E) { return true; }
- bool VisitUnaryPostInc(const UnaryOperator *E) { return true; }
- bool VisitUnaryPreDec(const UnaryOperator *E) { return true; }
- bool VisitUnaryPostDec(const UnaryOperator *E) { return true; }
- bool VisitUnaryDeref(const UnaryOperator *E) {
- if (Ctx.getCanonicalType(E->getType()).isVolatileQualified())
- return true;
- return Visit(E->getSubExpr());
- }
- bool VisitUnaryOperator(const UnaryOperator *E) { return Visit(E->getSubExpr()); }
-
- // Has side effects if any element does.
- bool VisitInitListExpr(const InitListExpr *E) {
- for (unsigned i = 0, e = E->getNumInits(); i != e; ++i)
- if (Visit(E->getInit(i))) return true;
- if (const Expr *filler = E->getArrayFiller())
- return Visit(filler);
- return false;
- }
-
- bool VisitSizeOfPackExpr(const SizeOfPackExpr *) { return false; }
-};
-
-class OpaqueValueEvaluation {
- EvalInfo &info;
- OpaqueValueExpr *opaqueValue;
-
-public:
- OpaqueValueEvaluation(EvalInfo &info, OpaqueValueExpr *opaqueValue,
- Expr *value)
- : info(info), opaqueValue(opaqueValue) {
-
- // If evaluation fails, fail immediately.
- if (!Evaluate(info.OpaqueValues[opaqueValue], info, value)) {
- this->opaqueValue = 0;
- return;
- }
- }
-
- bool hasError() const { return opaqueValue == 0; }
-
- ~OpaqueValueEvaluation() {
- // FIXME: For a recursive constexpr call, an outer stack frame might have
- // been using this opaque value too, and will now have to re-evaluate the
- // source expression.
- if (opaqueValue) info.OpaqueValues.erase(opaqueValue);
- }
-};
-
-} // end anonymous namespace
-
//===----------------------------------------------------------------------===//
// Generic Evaluation
//===----------------------------------------------------------------------===//
@@ -2509,9 +2417,10 @@ public:
}
RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
- // Cache the value of the common expression.
- OpaqueValueEvaluation opaque(Info, E->getOpaqueValue(), E->getCommon());
- if (opaque.hasError())
+ // Evaluate and cache the common expression. We treat it as a temporary,
+ // even though it's not quite the same thing.
+ if (!Evaluate(Info.CurrentCall->Temporaries[E->getOpaqueValue()],
+ Info, E->getCommon()))
return false;
return HandleConditionalOperator(E);
@@ -2545,8 +2454,8 @@ public:
}
RetTy VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
- const APValue *Value = Info.getOpaqueValue(E);
- if (!Value) {
+ APValue &Value = Info.CurrentCall->Temporaries[E];
+ if (Value.isUninit()) {
const Expr *Source = E->getSourceExpr();
if (!Source)
return Error(E);
@@ -2556,7 +2465,7 @@ public:
}
return StmtVisitorTy::Visit(Source);
}
- return DerivedSuccess(*Value, E);
+ return DerivedSuccess(Value, E);
}
RetTy VisitCallExpr(const CallExpr *E) {
@@ -2773,9 +2682,11 @@ public:
assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
(void)BaseTy;
- HandleLValueMember(this->Info, E, Result, FD);
+ if (!HandleLValueMember(this->Info, E, Result, FD))
+ return false;
} else if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(MD)) {
- HandleLValueIndirectMember(this->Info, E, Result, IFD);
+ if (!HandleLValueIndirectMember(this->Info, E, Result, IFD))
+ return false;
} else
return this->Error(E);
@@ -2970,6 +2881,9 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
if (E->isTypeOperand())
return Success(E);
CXXRecordDecl *RD = E->getExprOperand()->getType()->getAsCXXRecordDecl();
+ // FIXME: The standard says "a typeid expression whose operand is of a
+ // polymorphic class type" is not a constant expression, but it probably
+ // means "a typeid expression whose operand is potentially evaluated".
if (RD && RD->isPolymorphic()) {
Info.Diag(E, diag::note_constexpr_typeid_polymorphic)
<< E->getExprOperand()->getType()
@@ -3073,7 +2987,7 @@ public:
bool VisitUnaryAddrOf(const UnaryOperator *E);
bool VisitObjCStringLiteral(const ObjCStringLiteral *E)
{ return Success(E); }
- bool VisitObjCNumericLiteral(const ObjCNumericLiteral *E)
+ bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E)
{ return Success(E); }
bool VisitAddrLabelExpr(const AddrLabelExpr *E)
{ return Success(E); }
@@ -3373,6 +3287,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E,
Result = APValue(APValue::UninitStruct(), CD ? CD->getNumBases() : 0,
std::distance(RD->field_begin(), RD->field_end()));
+ if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
if (CD) {
@@ -3381,7 +3296,8 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E,
End = CD->bases_end(); I != End; ++I, ++Index) {
const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl();
LValue Subobject = This;
- HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout);
+ if (!HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout))
+ return false;
if (!HandleClassZeroInitialization(Info, E, Base, Subobject,
Result.getStructBase(Index)))
return false;
@@ -3391,15 +3307,16 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E,
for (RecordDecl::field_iterator I = RD->field_begin(), End = RD->field_end();
I != End; ++I) {
// -- if T is a reference type, no initialization is performed.
- if ((*I)->getType()->isReferenceType())
+ if (I->getType()->isReferenceType())
continue;
LValue Subobject = This;
- HandleLValueMember(Info, E, Subobject, *I, &Layout);
+ if (!HandleLValueMember(Info, E, Subobject, *I, &Layout))
+ return false;
- ImplicitValueInitExpr VIE((*I)->getType());
+ ImplicitValueInitExpr VIE(I->getType());
if (!EvaluateInPlace(
- Result.getStructField((*I)->getFieldIndex()), Info, Subobject, &VIE))
+ Result.getStructField(I->getFieldIndex()), Info, Subobject, &VIE))
return false;
}
@@ -3408,6 +3325,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E,
bool RecordExprEvaluator::ZeroInitialization(const Expr *E) {
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
+ if (RD->isInvalidDecl()) return false;
if (RD->isUnion()) {
// C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the
// object's first non-static named data member is zero-initialized
@@ -3418,9 +3336,10 @@ bool RecordExprEvaluator::ZeroInitialization(const Expr *E) {
}
LValue Subobject = This;
- HandleLValueMember(Info, E, Subobject, *I);
+ if (!HandleLValueMember(Info, E, Subobject, *I))
+ return false;
Result = APValue(*I);
- ImplicitValueInitExpr VIE((*I)->getType());
+ ImplicitValueInitExpr VIE(I->getType());
return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, &VIE);
}
@@ -3470,6 +3389,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
return false;
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
+ if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
if (RD->isUnion()) {
@@ -3484,7 +3404,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE;
LValue Subobject = This;
- HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout);
+ if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout))
+ return false;
return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr);
}
@@ -3507,15 +3428,16 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
// FIXME: Diagnostics here should point to the end of the initializer
// list, not the start.
- HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, Subobject,
- *Field, &Layout);
+ if (!HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E,
+ Subobject, *Field, &Layout))
+ return false;
// Perform an implicit value-initialization for members beyond the end of
// the initializer list.
ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType());
if (!EvaluateInPlace(
- Result.getStructField((*Field)->getFieldIndex()),
+ Result.getStructField(Field->getFieldIndex()),
Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) {
if (!Info.keepEvaluatingAfterFailure())
return false;
@@ -3528,6 +3450,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
const CXXConstructorDecl *FD = E->getConstructor();
+ if (FD->isInvalidDecl() || FD->getParent()->isInvalidDecl()) return false;
+
bool ZeroInit = E->requiresZeroInitialization();
if (CheckTrivialDefaultConstructor(Info, E->getExprLoc(), FD, ZeroInit)) {
// If we've already performed zero-initialization, we're already done.
@@ -3870,8 +3794,24 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
bool Success = true;
+ assert((!Result.isArray() || Result.getArrayInitializedElts() == 0) &&
+ "zero-initialized array shouldn't have any initialized elts");
+ APValue Filler;
+ if (Result.isArray() && Result.hasArrayFiller())
+ Filler = Result.getArrayFiller();
+
Result = APValue(APValue::UninitArray(), E->getNumInits(),
CAT->getSize().getZExtValue());
+
+ // If the array was previously zero-initialized, preserve the
+ // zero-initialized values.
+ if (!Filler.isUninit()) {
+ for (unsigned I = 0, E = Result.getArrayInitializedElts(); I != E; ++I)
+ Result.getArrayInitializedElt(I) = Filler;
+ if (Result.hasArrayFiller())
+ Result.getArrayFiller() = Filler;
+ }
+
LValue Subobject = This;
Subobject.addArray(Info, E, CAT);
unsigned Index = 0;
@@ -3898,15 +3838,29 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
}
bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
- const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType());
- if (!CAT)
- return Error(E);
+ // FIXME: The Subobject here isn't necessarily right. This rarely matters,
+ // but sometimes does:
+ // struct S { constexpr S() : p(&p) {} void *p; };
+ // S s[10];
+ LValue Subobject = This;
- bool HadZeroInit = !Result.isUninit();
- if (!HadZeroInit)
- Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue());
- if (!Result.hasArrayFiller())
- return true;
+ APValue *Value = &Result;
+ bool HadZeroInit = true;
+ QualType ElemTy = E->getType();
+ while (const ConstantArrayType *CAT =
+ Info.Ctx.getAsConstantArrayType(ElemTy)) {
+ Subobject.addArray(Info, E, CAT);
+ HadZeroInit &= !Value->isUninit();
+ if (!HadZeroInit)
+ *Value = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue());
+ if (!Value->hasArrayFiller())
+ return true;
+ Value = &Value->getArrayFiller();
+ ElemTy = CAT->getElementType();
+ }
+
+ if (!ElemTy->isRecordType())
+ return Error(E);
const CXXConstructorDecl *FD = E->getConstructor();
@@ -3916,17 +3870,15 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
return true;
if (ZeroInit) {
- LValue Subobject = This;
- Subobject.addArray(Info, E, CAT);
- ImplicitValueInitExpr VIE(CAT->getElementType());
- return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE);
+ ImplicitValueInitExpr VIE(ElemTy);
+ return EvaluateInPlace(*Value, Info, Subobject, &VIE);
}
const CXXRecordDecl *RD = FD->getParent();
if (RD->isUnion())
- Result.getArrayFiller() = APValue((FieldDecl*)0);
+ *Value = APValue((FieldDecl*)0);
else
- Result.getArrayFiller() =
+ *Value =
APValue(APValue::UninitStruct(), RD->getNumBases(),
std::distance(RD->field_begin(), RD->field_end()));
return true;
@@ -3938,23 +3890,16 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition))
return false;
- // FIXME: The Subobject here isn't necessarily right. This rarely matters,
- // but sometimes does:
- // struct S { constexpr S() : p(&p) {} void *p; };
- // S s[10];
- LValue Subobject = This;
- Subobject.addArray(Info, E, CAT);
-
if (ZeroInit && !HadZeroInit) {
- ImplicitValueInitExpr VIE(CAT->getElementType());
- if (!EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE))
+ ImplicitValueInitExpr VIE(ElemTy);
+ if (!EvaluateInPlace(*Value, Info, Subobject, &VIE))
return false;
}
llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
return HandleConstructorCall(E->getExprLoc(), Subobject, Args,
cast<CXXConstructorDecl>(Definition),
- Info, Result.getArrayFiller());
+ Info, *Value);
}
//===----------------------------------------------------------------------===//
@@ -4288,10 +4233,16 @@ QualType IntExprEvaluator::GetObjectType(APValue::LValueBase B) {
}
bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E) {
- // TODO: Perhaps we should let LLVM lower this?
LValue Base;
- if (!EvaluatePointer(E->getArg(0), Base, Info))
- return false;
+
+ {
+ // The operand of __builtin_object_size is never evaluated for side-effects.
+ // If there are any, but we can determine the pointed-to object anyway, then
+ // ignore the side-effects.
+ SpeculativeEvaluationRAII SpeculativeEval(Info);
+ if (!EvaluatePointer(E->getArg(0), Base, Info))
+ return false;
+ }
// If we can prove the base is null, lower to zero now.
if (!Base.getLValueBase()) return Success(0, E);
@@ -4323,14 +4274,17 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
if (TryEvaluateBuiltinObjectSize(E))
return true;
- // If evaluating the argument has side-effects we can't determine
- // the size of the object and lower it to unknown now.
+ // If evaluating the argument has side-effects, we can't determine the size
+ // of the object, and so we lower it to unknown now. CodeGen relies on us to
+ // handle all cases where the expression has side-effects.
if (E->getArg(0)->HasSideEffects(Info.Ctx)) {
if (E->getArg(1)->EvaluateKnownConstInt(Info.Ctx).getZExtValue() <= 1)
return Success(-1ULL, E);
return Success(0, E);
}
+ // Expression had no side effects, but we couldn't statically determine the
+ // size of the referenced object.
return Error(E);
}
@@ -5280,6 +5234,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) {
if (!RT)
return Error(OOE);
RecordDecl *RD = RT->getDecl();
+ if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD);
unsigned i = MemberDecl->getFieldIndex();
assert(i < RL.getFieldCount() && "offsetof field in wrong type");
@@ -5301,6 +5256,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) {
if (!RT)
return Error(OOE);
RecordDecl *RD = RT->getDecl();
+ if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD);
// Find the base class itself.
@@ -6385,10 +6341,6 @@ bool Expr::isEvaluatable(const ASTContext &Ctx) const {
return EvaluateAsRValue(Result, Ctx) && !Result.HasSideEffects;
}
-bool Expr::HasSideEffects(const ASTContext &Ctx) const {
- return HasSideEffect(Ctx).Visit(this);
-}
-
APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx) const {
EvalResult EvalResult;
bool Result = EvaluateAsRValue(EvalResult, Ctx);
@@ -6501,7 +6453,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
case Expr::CXXDependentScopeMemberExprClass:
case Expr::UnresolvedMemberExprClass:
case Expr::ObjCStringLiteralClass:
- case Expr::ObjCNumericLiteralClass:
+ case Expr::ObjCBoxedExprClass:
case Expr::ObjCArrayLiteralClass:
case Expr::ObjCDictionaryLiteralClass:
case Expr::ObjCEncodeExprClass:
diff --git a/lib/AST/ItaniumCXXABI.cpp b/lib/AST/ItaniumCXXABI.cpp
index 0027dbf..ce1244c 100644
--- a/lib/AST/ItaniumCXXABI.cpp
+++ b/lib/AST/ItaniumCXXABI.cpp
@@ -39,7 +39,7 @@ public:
return 1;
}
- CallingConv getDefaultMethodCallConv() const {
+ CallingConv getDefaultMethodCallConv(bool isVariadic) const {
return CC_C;
}
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index 0d405f1..7c7a5e5 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -657,7 +657,7 @@ void CXXNameMangler::mangleFloat(const llvm::APFloat &f) {
// mistake; see the discussion on cxx-abi-dev beginning on
// 2012-01-16.
- // Our requirements here are just barely wierd enough to justify
+ // Our requirements here are just barely weird enough to justify
// using a custom algorithm instead of post-processing APInt::toString().
llvm::APInt valueBits = f.bitcastToAPInt();
@@ -1032,17 +1032,14 @@ static const FieldDecl *FindFirstNamedDataMember(const RecordDecl *RD) {
for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I) {
- const FieldDecl *FD = *I;
+ if (I->getIdentifier())
+ return *I;
- if (FD->getIdentifier())
- return FD;
-
- if (const RecordType *RT = FD->getType()->getAs<RecordType>()) {
+ if (const RecordType *RT = I->getType()->getAs<RecordType>())
if (const FieldDecl *NamedDataMember =
FindFirstNamedDataMember(RT->getDecl()))
return NamedDataMember;
}
- }
// We didn't find a named data member.
return 0;
@@ -1892,12 +1889,23 @@ void CXXNameMangler::mangleType(const BuiltinType *T) {
}
// <type> ::= <function-type>
-// <function-type> ::= F [Y] <bare-function-type> E
+// <function-type> ::= [<CV-qualifiers>] F [Y]
+// <bare-function-type> [<ref-qualifier>] E
+// (Proposal to cxx-abi-dev, 2012-05-11)
void CXXNameMangler::mangleType(const FunctionProtoType *T) {
+ // Mangle CV-qualifiers, if present. These are 'this' qualifiers,
+ // e.g. "const" in "int (A::*)() const".
+ mangleQualifiers(Qualifiers::fromCVRMask(T->getTypeQuals()));
+
Out << 'F';
+
// FIXME: We don't have enough information in the AST to produce the 'Y'
// encoding for extern "C" function types.
mangleBareFunctionType(T, /*MangleReturnType=*/true);
+
+ // Mangle the ref-qualifier, if present.
+ mangleRefQualifier(T->getRefQualifier());
+
Out << 'E';
}
void CXXNameMangler::mangleType(const FunctionNoProtoType *T) {
@@ -1990,8 +1998,6 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) {
mangleType(QualType(T->getClass(), 0));
QualType PointeeType = T->getPointeeType();
if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(PointeeType)) {
- mangleQualifiers(Qualifiers::fromCVRMask(FPT->getTypeQuals()));
- mangleRefQualifier(FPT->getRefQualifier());
mangleType(FPT);
// Itanium C++ ABI 5.1.8:
@@ -2005,9 +2011,11 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) {
// which the function is a member is considered part of the type of
// function.
+ // Given that we already substitute member function pointers as a
+ // whole, the net effect of this rule is just to unconditionally
+ // suppress substitution on the function type in a member pointer.
// We increment the SeqID here to emulate adding an entry to the
- // substitution table. We can't actually add it because we don't want this
- // particular function type to be substituted.
+ // substitution table.
++SeqID;
} else
mangleType(PointeeType);
@@ -2390,7 +2398,7 @@ recurse:
case Expr::ObjCProtocolExprClass:
case Expr::ObjCSelectorExprClass:
case Expr::ObjCStringLiteralClass:
- case Expr::ObjCNumericLiteralClass:
+ case Expr::ObjCBoxedExprClass:
case Expr::ObjCArrayLiteralClass:
case Expr::ObjCDictionaryLiteralClass:
case Expr::ObjCSubscriptRefExprClass:
@@ -2981,7 +2989,7 @@ void CXXNameMangler::mangleFunctionParam(const ParmVarDecl *parm) {
// Top-level qualifiers. We don't have to worry about arrays here,
// because parameters declared as arrays should already have been
- // tranformed to have pointer type. FIXME: apparently these don't
+ // transformed to have pointer type. FIXME: apparently these don't
// get mangled if used as an rvalue of a known non-class type?
assert(!parm->getType()->isArrayType()
&& "parameter's type is still an array type?");
@@ -3124,7 +3132,7 @@ void CXXNameMangler::mangleTemplateArg(const NamedDecl *P,
break;
}
case TemplateArgument::Integral:
- mangleIntegerLiteral(A.getIntegralType(), *A.getAsIntegral());
+ mangleIntegerLiteral(A.getIntegralType(), A.getAsIntegral());
break;
case TemplateArgument::Declaration: {
assert(P && "Missing template parameter for declaration argument");
diff --git a/lib/AST/LambdaMangleContext.cpp b/lib/AST/LambdaMangleContext.cpp
index f5272a7..6f4fe2d 100644
--- a/lib/AST/LambdaMangleContext.cpp
+++ b/lib/AST/LambdaMangleContext.cpp
@@ -11,7 +11,9 @@
// the Itanium C++ ABI mangling numbers for lambda expressions.
//
//===----------------------------------------------------------------------===//
+
#include "clang/AST/LambdaMangleContext.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
using namespace clang;
diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp
index 73c9f57..d5f8371 100644
--- a/lib/AST/Mangle.cpp
+++ b/lib/AST/Mangle.cpp
@@ -40,7 +40,11 @@ static void mangleFunctionBlock(MangleContext &Context,
StringRef Outer,
const BlockDecl *BD,
raw_ostream &Out) {
- Out << "__" << Outer << "_block_invoke_" << Context.getBlockId(BD, true);
+ unsigned discriminator = Context.getBlockId(BD, true);
+ if (discriminator == 0)
+ Out << "__" << Outer << "_block_invoke";
+ else
+ Out << "__" << Outer << "_block_invoke_" << discriminator+1;
}
static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) {
@@ -62,8 +66,20 @@ static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) {
void MangleContext::anchor() { }
void MangleContext::mangleGlobalBlock(const BlockDecl *BD,
+ const NamedDecl *ID,
raw_ostream &Out) {
- Out << "__block_global_" << getBlockId(BD, false);
+ unsigned discriminator = getBlockId(BD, false);
+ if (ID) {
+ if (shouldMangleDeclName(ID))
+ mangleName(ID, Out);
+ else {
+ Out << ID->getIdentifier()->getName();
+ }
+ }
+ if (discriminator == 0)
+ Out << "_block_invoke";
+ else
+ Out << "_block_invoke_" << discriminator+1;
}
void MangleContext::mangleCtorBlock(const CXXConstructorDecl *CD,
@@ -99,8 +115,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD,
mangleObjCMethodName(Method, Stream);
} else {
const NamedDecl *ND = cast<NamedDecl>(DC);
- if (IdentifierInfo *II = ND->getIdentifier())
- Stream << II->getName();
+ if (!shouldMangleDeclName(ND) && ND->getIdentifier())
+ Stream << ND->getIdentifier()->getName();
else {
// FIXME: We were doing a mangleUnqualifiedName() before, but that's
// a private member of a class that will soon itself be private to the
@@ -131,12 +147,13 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD,
}
void MangleContext::mangleBlock(const BlockDecl *BD,
- raw_ostream &Out) {
+ raw_ostream &Out,
+ const NamedDecl *ID) {
const DeclContext *DC = BD->getDeclContext();
while (isa<BlockDecl>(DC) || isa<EnumDecl>(DC))
DC = DC->getParent();
if (DC->isFunctionOrMethod())
mangleBlock(DC, BD, Out);
else
- mangleGlobalBlock(BD, Out);
+ mangleGlobalBlock(BD, ID, Out);
}
diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp
index f33d6fe..51308ea 100644
--- a/lib/AST/MicrosoftCXXABI.cpp
+++ b/lib/AST/MicrosoftCXXABI.cpp
@@ -29,8 +29,8 @@ public:
unsigned getMemberPointerSize(const MemberPointerType *MPT) const;
- CallingConv getDefaultMethodCallConv() const {
- if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
+ CallingConv getDefaultMethodCallConv(bool isVariadic) const {
+ if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
return CC_X86ThisCall;
else
return CC_C;
diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp
index ba9856a..e2cee7f 100644
--- a/lib/AST/MicrosoftMangle.cpp
+++ b/lib/AST/MicrosoftMangle.cpp
@@ -21,6 +21,8 @@
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/ABI.h"
+#include <map>
+
using namespace clang;
namespace {
@@ -31,36 +33,59 @@ class MicrosoftCXXNameMangler {
MangleContext &Context;
raw_ostream &Out;
+ // FIXME: audit the performance of BackRefMap as it might do way too many
+ // copying of strings.
+ typedef std::map<std::string, unsigned> BackRefMap;
+ BackRefMap NameBackReferences;
+ bool UseNameBackReferences;
+
+ typedef llvm::DenseMap<void*, unsigned> ArgBackRefMap;
+ ArgBackRefMap TypeBackReferences;
+
ASTContext &getASTContext() const { return Context.getASTContext(); }
public:
MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_)
- : Context(C), Out(Out_) { }
+ : Context(C), Out(Out_), UseNameBackReferences(true) { }
+
+ raw_ostream &getStream() const { return Out; }
- void mangle(const NamedDecl *D, StringRef Prefix = "?");
+ void mangle(const NamedDecl *D, StringRef Prefix = "\01?");
void mangleName(const NamedDecl *ND);
void mangleFunctionEncoding(const FunctionDecl *FD);
void mangleVariableEncoding(const VarDecl *VD);
void mangleNumber(int64_t Number);
- void mangleType(QualType T);
+ void mangleNumber(const llvm::APSInt &Value);
+ void mangleType(QualType T, SourceRange Range);
private:
+ void disableBackReferences() { UseNameBackReferences = false; }
void mangleUnqualifiedName(const NamedDecl *ND) {
mangleUnqualifiedName(ND, ND->getDeclName());
}
void mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name);
void mangleSourceName(const IdentifierInfo *II);
void manglePostfix(const DeclContext *DC, bool NoFunction=false);
- void mangleOperatorName(OverloadedOperatorKind OO);
+ void mangleOperatorName(OverloadedOperatorKind OO, SourceLocation Loc);
void mangleQualifiers(Qualifiers Quals, bool IsMember);
+ void mangleUnscopedTemplateName(const TemplateDecl *ND);
+ void mangleTemplateInstantiationName(const TemplateDecl *TD,
+ const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs);
void mangleObjCMethodName(const ObjCMethodDecl *MD);
+ void mangleLocalName(const FunctionDecl *FD);
+
+ void mangleTypeRepeated(QualType T, SourceRange Range);
// Declare manglers for every type class.
#define ABSTRACT_TYPE(CLASS, PARENT)
#define NON_CANONICAL_TYPE(CLASS, PARENT)
-#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T);
+#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T, \
+ SourceRange Range);
#include "clang/AST/TypeNodes.def"
+#undef ABSTRACT_TYPE
+#undef NON_CANONICAL_TYPE
+#undef TYPE
void mangleType(const TagType*);
void mangleType(const FunctionType *T, const FunctionDecl *D,
@@ -69,8 +94,12 @@ private:
void mangleExtraDimensions(QualType T);
void mangleFunctionClass(const FunctionDecl *FD);
void mangleCallingConvention(const FunctionType *T, bool IsInstMethod = false);
+ void mangleIntegerLiteral(QualType T, const llvm::APSInt &Number);
void mangleThrowSpecification(const FunctionProtoType *T);
+ void mangleTemplateArgs(
+ const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs);
+
};
/// MicrosoftMangleContext - Overrides the default MangleContext for the
@@ -157,15 +186,15 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D,
StringRef Prefix) {
// MSVC doesn't mangle C++ names the same way it mangles extern "C" names.
// Therefore it's really important that we don't decorate the
- // name with leading underscores or leading/trailing at signs. So, emit a
- // asm marker at the start so we get the name right.
- Out << '\01'; // LLVM IR Marker for __asm("foo")
+ // name with leading underscores or leading/trailing at signs. So, by
+ // default, we emit an asm marker at the start so we get the name right.
+ // Callers can override this with a custom prefix.
// Any decl can be declared with __asm("foo") on it, and this takes precedence
// over all other naming in the .o file.
if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
// If we have an asm name, then we use it as the mangling.
- Out << ALA->getLabel();
+ Out << '\01' << ALA->getLabel();
return;
}
@@ -176,7 +205,15 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D,
mangleFunctionEncoding(FD);
else if (const VarDecl *VD = dyn_cast<VarDecl>(D))
mangleVariableEncoding(VD);
- // TODO: Fields? Can MSVC even mangle them?
+ else {
+ // TODO: Fields? Can MSVC even mangle them?
+ // Issue a diagnostic for now.
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this declaration yet");
+ Diags.Report(D->getLocation(), DiagID)
+ << D->getSourceRange();
+ }
}
void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) {
@@ -188,7 +225,7 @@ void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) {
// We should never ever see a FunctionNoProtoType at this point.
// We don't even know how to mangle their types anyway :).
- const FunctionProtoType *FT = cast<FunctionProtoType>(FD->getType());
+ const FunctionProtoType *FT = FD->getType()->castAs<FunctionProtoType>();
bool InStructor = false, InInstMethod = false;
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
@@ -232,16 +269,17 @@ void MicrosoftCXXNameMangler::mangleVariableEncoding(const VarDecl *VD) {
// ::= <type> A # pointers, references, arrays
// Pointers and references are odd. The type of 'int * const foo;' gets
// mangled as 'QAHA' instead of 'PAHB', for example.
- QualType Ty = VD->getType();
+ TypeLoc TL = VD->getTypeSourceInfo()->getTypeLoc();
+ QualType Ty = TL.getType();
if (Ty->isPointerType() || Ty->isReferenceType()) {
- mangleType(Ty);
+ mangleType(Ty, TL.getSourceRange());
Out << 'A';
- } else if (Ty->isArrayType()) {
+ } else if (const ArrayType *AT = getASTContext().getAsArrayType(Ty)) {
// Global arrays are funny, too.
- mangleType(cast<ArrayType>(Ty.getTypePtr()), true);
+ mangleType(AT, true);
Out << 'A';
} else {
- mangleType(Ty.getLocalUnqualifiedType());
+ mangleType(Ty.getLocalUnqualifiedType(), TL.getSourceRange());
mangleQualifiers(Ty.getLocalQualifiers(), false);
}
}
@@ -266,35 +304,156 @@ void MicrosoftCXXNameMangler::mangleName(const NamedDecl *ND) {
}
void MicrosoftCXXNameMangler::mangleNumber(int64_t Number) {
- // <number> ::= [?] <decimal digit> # <= 9
- // ::= [?] <hex digit>+ @ # > 9; A = 0, B = 1, etc...
+ // <number> ::= [?] <decimal digit> # 1 <= Number <= 10
+ // ::= [?] <hex digit>+ @ # 0 or > 9; A = 0, B = 1, etc...
+ // ::= [?] @ # 0 (alternate mangling, not emitted by VC)
if (Number < 0) {
Out << '?';
Number = -Number;
}
- if (Number >= 1 && Number <= 10) {
+ // There's a special shorter mangling for 0, but Microsoft
+ // chose not to use it. Instead, 0 gets mangled as "A@". Oh well...
+ if (Number >= 1 && Number <= 10)
Out << Number-1;
- } else {
+ else {
// We have to build up the encoding in reverse order, so it will come
// out right when we write it out.
char Encoding[16];
char *EndPtr = Encoding+sizeof(Encoding);
char *CurPtr = EndPtr;
- while (Number) {
+ do {
*--CurPtr = 'A' + (Number % 16);
Number /= 16;
+ } while (Number);
+ Out.write(CurPtr, EndPtr-CurPtr);
+ Out << '@';
+ }
+}
+
+void MicrosoftCXXNameMangler::mangleNumber(const llvm::APSInt &Value) {
+ if (Value.isSigned() && Value.isNegative()) {
+ Out << '?';
+ mangleNumber(llvm::APSInt(Value.abs()));
+ return;
+ }
+ llvm::APSInt Temp(Value);
+ if (Value.uge(1) && Value.ule(10)) {
+ --Temp;
+ Temp.print(Out, false);
+ } else {
+ // We have to build up the encoding in reverse order, so it will come
+ // out right when we write it out.
+ char Encoding[64];
+ char *EndPtr = Encoding+sizeof(Encoding);
+ char *CurPtr = EndPtr;
+ llvm::APSInt NibbleMask(Value.getBitWidth(), Value.isUnsigned());
+ NibbleMask = 0xf;
+ for (int i = 0, e = Value.getActiveBits() / 4; i != e; ++i) {
+ *--CurPtr = 'A' + Temp.And(NibbleMask).getLimitedValue(0xf);
+ Temp = Temp.lshr(4);
}
Out.write(CurPtr, EndPtr-CurPtr);
Out << '@';
}
}
+static const TemplateDecl *
+isTemplate(const NamedDecl *ND,
+ SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) {
+ // Check if we have a function template.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)){
+ if (const TemplateDecl *TD = FD->getPrimaryTemplate()) {
+ if (FD->getTemplateSpecializationArgsAsWritten()) {
+ const ASTTemplateArgumentListInfo *ArgList =
+ FD->getTemplateSpecializationArgsAsWritten();
+ TemplateArgs.append(ArgList->getTemplateArgs(),
+ ArgList->getTemplateArgs() +
+ ArgList->NumTemplateArgs);
+ } else {
+ const TemplateArgumentList *ArgList =
+ FD->getTemplateSpecializationArgs();
+ TemplateArgumentListInfo LI;
+ for (unsigned i = 0, e = ArgList->size(); i != e; ++i)
+ TemplateArgs.push_back(TemplateArgumentLoc(ArgList->get(i),
+ FD->getTypeSourceInfo()));
+ }
+ return TD;
+ }
+ }
+
+ // Check if we have a class template.
+ if (const ClassTemplateSpecializationDecl *Spec =
+ dyn_cast<ClassTemplateSpecializationDecl>(ND)) {
+ TypeSourceInfo *TSI = Spec->getTypeAsWritten();
+ if (TSI) {
+ TemplateSpecializationTypeLoc &TSTL =
+ cast<TemplateSpecializationTypeLoc>(TSI->getTypeLoc());
+ TemplateArgumentListInfo LI(TSTL.getLAngleLoc(), TSTL.getRAngleLoc());
+ for (unsigned i = 0, e = TSTL.getNumArgs(); i != e; ++i)
+ TemplateArgs.push_back(TSTL.getArgLoc(i));
+ } else {
+ TemplateArgumentListInfo LI;
+ const TemplateArgumentList &ArgList =
+ Spec->getTemplateArgs();
+ for (unsigned i = 0, e = ArgList.size(); i != e; ++i)
+ TemplateArgs.push_back(TemplateArgumentLoc(ArgList[i],
+ TemplateArgumentLocInfo()));
+ }
+ return Spec->getSpecializedTemplate();
+ }
+
+ return 0;
+}
+
void
MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
DeclarationName Name) {
// <unqualified-name> ::= <operator-name>
// ::= <ctor-dtor-name>
// ::= <source-name>
+ // ::= <template-name>
+ SmallVector<TemplateArgumentLoc, 2> TemplateArgs;
+ // Check if we have a template.
+ if (const TemplateDecl *TD = isTemplate(ND, TemplateArgs)) {
+ // We have a template.
+ // Here comes the tricky thing: if we need to mangle something like
+ // void foo(A::X<Y>, B::X<Y>),
+ // the X<Y> part is aliased. However, if you need to mangle
+ // void foo(A::X<A::Y>, A::X<B::Y>),
+ // the A::X<> part is not aliased.
+ // That said, from the mangler's perspective we have a structure like this:
+ // namespace[s] -> type[ -> template-parameters]
+ // but from the Clang perspective we have
+ // type [ -> template-parameters]
+ // \-> namespace[s]
+ // What we do is we create a new mangler, mangle the same type (without
+ // a namespace suffix) using the extra mangler with back references
+ // disabled (to avoid infinite recursion) and then use the mangled type
+ // name as a key to check the mangling of different types for aliasing.
+
+ std::string BackReferenceKey;
+ BackRefMap::iterator Found;
+ if (UseNameBackReferences) {
+ llvm::raw_string_ostream Stream(BackReferenceKey);
+ MicrosoftCXXNameMangler Extra(Context, Stream);
+ Extra.disableBackReferences();
+ Extra.mangleUnqualifiedName(ND, Name);
+ Stream.flush();
+
+ Found = NameBackReferences.find(BackReferenceKey);
+ }
+ if (!UseNameBackReferences || Found == NameBackReferences.end()) {
+ mangleTemplateInstantiationName(TD, TemplateArgs);
+ if (UseNameBackReferences && NameBackReferences.size() < 10) {
+ size_t Size = NameBackReferences.size();
+ NameBackReferences[BackReferenceKey] = Size;
+ }
+ } else {
+ Out << Found->second;
+ }
+ return;
+ }
+
switch (Name.getNameKind()) {
case DeclarationName::Identifier: {
if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) {
@@ -349,12 +508,17 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
break;
case DeclarationName::CXXOperatorName:
- mangleOperatorName(Name.getCXXOverloadedOperator());
+ mangleOperatorName(Name.getCXXOverloadedOperator(), ND->getLocation());
break;
- case DeclarationName::CXXLiteralOperatorName:
+ case DeclarationName::CXXLiteralOperatorName: {
// FIXME: Was this added in VS2010? Does MS even know how to mangle this?
- llvm_unreachable("Don't know how to mangle literal operators yet!");
+ DiagnosticsEngine Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this literal operator yet");
+ Diags.Report(ND->getLocation(), DiagID);
+ break;
+ }
case DeclarationName::CXXUsingDirective:
llvm_unreachable("Can't mangle a using directive name!");
@@ -364,8 +528,6 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC,
bool NoFunction) {
// <postfix> ::= <unqualified-name> [<postfix>]
- // ::= <template-postfix> <template-args> [<postfix>]
- // ::= <template-param>
// ::= <substitution> [<postfix>]
if (!DC) return;
@@ -386,13 +548,16 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC,
return;
else if (const ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(DC))
mangleObjCMethodName(Method);
+ else if (const FunctionDecl *Func = dyn_cast<FunctionDecl>(DC))
+ mangleLocalName(Func);
else {
mangleUnqualifiedName(cast<NamedDecl>(DC));
manglePostfix(DC->getParent(), NoFunction);
}
}
-void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) {
+void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO,
+ SourceLocation Loc) {
switch (OO) {
// ?0 # constructor
// ?1 # destructor
@@ -509,8 +674,13 @@ void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) {
// <operator-name> ::= ?_V # delete[]
case OO_Array_Delete: Out << "?_V"; break;
- case OO_Conditional:
- llvm_unreachable("Don't know how to mangle ?:");
+ case OO_Conditional: {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this conditional operator yet");
+ Diags.Report(Loc, DiagID);
+ break;
+ }
case OO_None:
case NUM_OVERLOADED_OPERATORS:
@@ -520,13 +690,141 @@ void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) {
void MicrosoftCXXNameMangler::mangleSourceName(const IdentifierInfo *II) {
// <source name> ::= <identifier> @
- Out << II->getName() << '@';
+ std::string key = II->getNameStart();
+ BackRefMap::iterator Found;
+ if (UseNameBackReferences)
+ Found = NameBackReferences.find(key);
+ if (!UseNameBackReferences || Found == NameBackReferences.end()) {
+ Out << II->getName() << '@';
+ if (UseNameBackReferences && NameBackReferences.size() < 10) {
+ size_t Size = NameBackReferences.size();
+ NameBackReferences[key] = Size;
+ }
+ } else {
+ Out << Found->second;
+ }
}
void MicrosoftCXXNameMangler::mangleObjCMethodName(const ObjCMethodDecl *MD) {
Context.mangleObjCMethodName(MD, Out);
}
+// Find out how many function decls live above this one and return an integer
+// suitable for use as the number in a numbered anonymous scope.
+// TODO: Memoize.
+static unsigned getLocalNestingLevel(const FunctionDecl *FD) {
+ const DeclContext *DC = FD->getParent();
+ int level = 1;
+
+ while (DC && !DC->isTranslationUnit()) {
+ if (isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC)) level++;
+ DC = DC->getParent();
+ }
+
+ return 2*level;
+}
+
+void MicrosoftCXXNameMangler::mangleLocalName(const FunctionDecl *FD) {
+ // <nested-name> ::= <numbered-anonymous-scope> ? <mangled-name>
+ // <numbered-anonymous-scope> ::= ? <number>
+ // Even though the name is rendered in reverse order (e.g.
+ // A::B::C is rendered as C@B@A), VC numbers the scopes from outermost to
+ // innermost. So a method bar in class C local to function foo gets mangled
+ // as something like:
+ // ?bar@C@?1??foo@@YAXXZ@QAEXXZ
+ // This is more apparent when you have a type nested inside a method of a
+ // type nested inside a function. A method baz in class D local to method
+ // bar of class C local to function foo gets mangled as:
+ // ?baz@D@?3??bar@C@?1??foo@@YAXXZ@QAEXXZ@QAEXXZ
+ // This scheme is general enough to support GCC-style nested
+ // functions. You could have a method baz of class C inside a function bar
+ // inside a function foo, like so:
+ // ?baz@C@?3??bar@?1??foo@@YAXXZ@YAXXZ@QAEXXZ
+ int NestLevel = getLocalNestingLevel(FD);
+ Out << '?';
+ mangleNumber(NestLevel);
+ Out << '?';
+ mangle(FD, "?");
+}
+
+void MicrosoftCXXNameMangler::mangleTemplateInstantiationName(
+ const TemplateDecl *TD,
+ const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) {
+ // <template-name> ::= <unscoped-template-name> <template-args>
+ // ::= <substitution>
+ // Always start with the unqualified name.
+
+ // Templates have their own context for back references.
+ BackRefMap TemplateContext;
+ NameBackReferences.swap(TemplateContext);
+
+ mangleUnscopedTemplateName(TD);
+ mangleTemplateArgs(TemplateArgs);
+
+ NameBackReferences.swap(TemplateContext);
+}
+
+void
+MicrosoftCXXNameMangler::mangleUnscopedTemplateName(const TemplateDecl *TD) {
+ // <unscoped-template-name> ::= ?$ <unqualified-name>
+ Out << "?$";
+ mangleUnqualifiedName(TD);
+}
+
+void
+MicrosoftCXXNameMangler::mangleIntegerLiteral(QualType T,
+ const llvm::APSInt &Value) {
+ // <integer-literal> ::= $0 <number>
+ Out << "$0";
+ // Make sure booleans are encoded as 0/1.
+ if (T->isBooleanType())
+ Out << (Value.getBoolValue() ? "0" : "A@");
+ else
+ mangleNumber(Value);
+}
+
+void
+MicrosoftCXXNameMangler::mangleTemplateArgs(
+ const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) {
+ // <template-args> ::= {<type> | <integer-literal>}+ @
+ unsigned NumTemplateArgs = TemplateArgs.size();
+ for (unsigned i = 0; i < NumTemplateArgs; ++i) {
+ const TemplateArgumentLoc &TAL = TemplateArgs[i];
+ const TemplateArgument &TA = TAL.getArgument();
+ switch (TA.getKind()) {
+ case TemplateArgument::Null:
+ llvm_unreachable("Can't mangle null template arguments!");
+ case TemplateArgument::Type:
+ mangleType(TA.getAsType(), TAL.getSourceRange());
+ break;
+ case TemplateArgument::Integral:
+ mangleIntegerLiteral(TA.getIntegralType(), TA.getAsIntegral());
+ break;
+ case TemplateArgument::Expression: {
+ // See if this is a constant expression.
+ Expr *TAE = TA.getAsExpr();
+ llvm::APSInt Value;
+ if (TAE->isIntegerConstantExpr(Value, Context.getASTContext())) {
+ mangleIntegerLiteral(TAE->getType(), Value);
+ break;
+ }
+ /* fallthrough */
+ } default: {
+ // Issue a diagnostic.
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this %select{ERROR|ERROR|pointer/reference|ERROR|"
+ "template|template pack expansion|expression|parameter pack}0 "
+ "template argument yet");
+ Diags.Report(TAL.getLocation(), DiagID)
+ << TA.getKind()
+ << TAL.getSourceRange();
+ }
+ }
+ }
+ Out << '@';
+}
+
void MicrosoftCXXNameMangler::mangleQualifiers(Qualifiers Quals,
bool IsMember) {
// <cvr-qualifiers> ::= [E] [F] [I] <base-cvr-qualifiers>
@@ -610,7 +908,29 @@ void MicrosoftCXXNameMangler::mangleQualifiers(Qualifiers Quals,
// FIXME: For now, just drop all extension qualifiers on the floor.
}
-void MicrosoftCXXNameMangler::mangleType(QualType T) {
+void MicrosoftCXXNameMangler::mangleTypeRepeated(QualType T, SourceRange Range) {
+ void *TypePtr = getASTContext().getCanonicalType(T).getAsOpaquePtr();
+ ArgBackRefMap::iterator Found = TypeBackReferences.find(TypePtr);
+
+ if (Found == TypeBackReferences.end()) {
+ size_t OutSizeBefore = Out.GetNumBytesInBuffer();
+
+ mangleType(T,Range);
+
+ // See if it's worth creating a back reference.
+ // Only types longer than 1 character are considered
+ // and only 10 back references slots are available:
+ bool LongerThanOneChar = (Out.GetNumBytesInBuffer() - OutSizeBefore > 1);
+ if (LongerThanOneChar && TypeBackReferences.size() < 10) {
+ size_t Size = TypeBackReferences.size();
+ TypeBackReferences[TypePtr] = Size;
+ }
+ } else {
+ Out << Found->second;
+ }
+}
+
+void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range) {
// Only operate on the canonical type!
T = getASTContext().getCanonicalType(T);
@@ -644,18 +964,22 @@ void MicrosoftCXXNameMangler::mangleType(QualType T) {
switch (T->getTypeClass()) {
#define ABSTRACT_TYPE(CLASS, PARENT)
#define NON_CANONICAL_TYPE(CLASS, PARENT) \
-case Type::CLASS: \
-llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \
-return;
+ case Type::CLASS: \
+ llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \
+ return;
#define TYPE(CLASS, PARENT) \
-case Type::CLASS: \
-mangleType(static_cast<const CLASS##Type*>(T.getTypePtr())); \
-break;
+ case Type::CLASS: \
+ mangleType(static_cast<const CLASS##Type*>(T.getTypePtr()), Range); \
+ break;
#include "clang/AST/TypeNodes.def"
+#undef ABSTRACT_TYPE
+#undef NON_CANONICAL_TYPE
+#undef TYPE
}
}
-void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T) {
+void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T,
+ SourceRange Range) {
// <type> ::= <builtin-type>
// <builtin-type> ::= X # void
// ::= C # signed char
@@ -713,24 +1037,32 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T) {
case BuiltinType::ObjCId: Out << "PAUobjc_object@@"; break;
case BuiltinType::ObjCClass: Out << "PAUobjc_class@@"; break;
case BuiltinType::ObjCSel: Out << "PAUobjc_selector@@"; break;
+
+ case BuiltinType::NullPtr: Out << "$$T"; break;
case BuiltinType::Char16:
case BuiltinType::Char32:
- case BuiltinType::Half:
- case BuiltinType::NullPtr:
- assert(0 && "Don't know how to mangle this type yet");
+ case BuiltinType::Half: {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this built-in %0 type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << T->getName(Context.getASTContext().getPrintingPolicy())
+ << Range;
+ break;
+ }
}
}
// <type> ::= <function-type>
-void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T) {
+void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T,
+ SourceRange) {
// Structors only appear in decls, so at this point we know it's not a
// structor type.
- // I'll probably have mangleType(MemberPointerType) call the mangleType()
- // method directly.
mangleType(T, NULL, false, false);
}
-void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T) {
+void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T,
+ SourceRange) {
llvm_unreachable("Can't mangle K&R function prototypes");
}
@@ -753,8 +1085,23 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T,
// ::= @ # structors (they have no declared return type)
if (IsStructor)
Out << '@';
- else
- mangleType(Proto->getResultType());
+ else {
+ QualType Result = Proto->getResultType();
+ const Type* RT = Result.getTypePtr();
+ if (!RT->isAnyPointerType() && !RT->isReferenceType()) {
+ if (Result.hasQualifiers() || !RT->isBuiltinType())
+ Out << '?';
+ if (!RT->isBuiltinType() && !Result.hasQualifiers()) {
+ // Lack of qualifiers for user types is mangled as 'A'.
+ Out << 'A';
+ }
+ }
+
+ // FIXME: Get the source range for the result type. Or, better yet,
+ // implement the unimplemented stuff so we don't need accurate source
+ // location info anymore :).
+ mangleType(Result, SourceRange());
+ }
// <argument-list> ::= X # void
// ::= <type>+ @
@@ -763,17 +1110,21 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T,
Out << 'X';
} else {
if (D) {
- // If we got a decl, use the "types-as-written" to make sure arrays
- // get mangled right.
+ // If we got a decl, use the type-as-written to make sure arrays
+ // get mangled right. Note that we can't rely on the TSI
+ // existing if (for example) the parameter was synthesized.
for (FunctionDecl::param_const_iterator Parm = D->param_begin(),
- ParmEnd = D->param_end();
- Parm != ParmEnd; ++Parm)
- mangleType((*Parm)->getTypeSourceInfo()->getType());
+ ParmEnd = D->param_end(); Parm != ParmEnd; ++Parm) {
+ TypeSourceInfo *TSI = (*Parm)->getTypeSourceInfo();
+ QualType Type = TSI ? TSI->getType() : (*Parm)->getType();
+ mangleTypeRepeated(Type, (*Parm)->getSourceRange());
+ }
} else {
+ // Happens for function pointer type arguments for example.
for (FunctionProtoType::arg_type_iterator Arg = Proto->arg_type_begin(),
ArgEnd = Proto->arg_type_end();
Arg != ArgEnd; ++Arg)
- mangleType(*Arg);
+ mangleTypeRepeated(*Arg, SourceRange());
}
// <builtin-type> ::= Z # ellipsis
if (Proto->isVariadic())
@@ -860,8 +1211,16 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(const FunctionType *T,
// that they could be in a DLL and somebody from another module could call
// them.)
CallingConv CC = T->getCallConv();
- if (CC == CC_Default)
- CC = IsInstMethod ? getASTContext().getDefaultMethodCallConv() : CC_C;
+ if (CC == CC_Default) {
+ if (IsInstMethod) {
+ const FunctionProtoType *FPT =
+ T->getCanonicalTypeUnqualified().getAs<FunctionProtoType>();
+ bool isVariadic = FPT->isVariadic();
+ CC = getASTContext().getDefaultCXXMethodCallConv(isVariadic);
+ } else {
+ CC = CC_C;
+ }
+ }
switch (CC) {
default:
llvm_unreachable("Unsupported CC for mangling");
@@ -884,8 +1243,15 @@ void MicrosoftCXXNameMangler::mangleThrowSpecification(
Out << 'Z';
}
-void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T) {
- llvm_unreachable("Don't know how to mangle UnresolvedUsingTypes yet!");
+void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T,
+ SourceRange Range) {
+ // Probably should be mangled as a template instantiation; need to see what
+ // VC does first.
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this unresolved dependent type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
// <type> ::= <union-type> | <struct-type> | <class-type> | <enum-type>
@@ -893,10 +1259,10 @@ void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T) {
// <struct-type> ::= U <name>
// <class-type> ::= V <name>
// <enum-type> ::= W <size> <name>
-void MicrosoftCXXNameMangler::mangleType(const EnumType *T) {
+void MicrosoftCXXNameMangler::mangleType(const EnumType *T, SourceRange) {
mangleType(static_cast<const TagType*>(T));
}
-void MicrosoftCXXNameMangler::mangleType(const RecordType *T) {
+void MicrosoftCXXNameMangler::mangleType(const RecordType *T, SourceRange) {
mangleType(static_cast<const TagType*>(T));
}
void MicrosoftCXXNameMangler::mangleType(const TagType *T) {
@@ -936,31 +1302,48 @@ void MicrosoftCXXNameMangler::mangleType(const ArrayType *T, bool IsGlobal) {
Out << 'Q';
mangleExtraDimensions(T->getElementType());
}
-void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T) {
+void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T,
+ SourceRange) {
mangleType(static_cast<const ArrayType *>(T), false);
}
-void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T) {
+void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T,
+ SourceRange) {
mangleType(static_cast<const ArrayType *>(T), false);
}
-void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T) {
+void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T,
+ SourceRange) {
mangleType(static_cast<const ArrayType *>(T), false);
}
-void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T) {
+void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T,
+ SourceRange) {
mangleType(static_cast<const ArrayType *>(T), false);
}
void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) {
SmallVector<llvm::APInt, 3> Dimensions;
for (;;) {
- if (ElementTy->isConstantArrayType()) {
- const ConstantArrayType *CAT =
- static_cast<const ConstantArrayType *>(ElementTy.getTypePtr());
+ if (const ConstantArrayType *CAT =
+ getASTContext().getAsConstantArrayType(ElementTy)) {
Dimensions.push_back(CAT->getSize());
ElementTy = CAT->getElementType();
} else if (ElementTy->isVariableArrayType()) {
- llvm_unreachable("Don't know how to mangle VLAs!");
+ const VariableArrayType *VAT =
+ getASTContext().getAsVariableArrayType(ElementTy);
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this variable-length array yet");
+ Diags.Report(VAT->getSizeExpr()->getExprLoc(), DiagID)
+ << VAT->getBracketsRange();
+ return;
} else if (ElementTy->isDependentSizedArrayType()) {
// The dependent expression has to be folded into a constant (TODO).
- llvm_unreachable("Don't know how to mangle dependent-sized arrays!");
+ const DependentSizedArrayType *DSAT =
+ getASTContext().getAsDependentSizedArrayType(ElementTy);
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this dependent-length array yet");
+ Diags.Report(DSAT->getSizeExpr()->getExprLoc(), DiagID)
+ << DSAT->getBracketsRange();
+ return;
} else if (ElementTy->isIncompleteArrayType()) continue;
else break;
}
@@ -974,151 +1357,246 @@ void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) {
mangleNumber(Dimensions[Dim].getLimitedValue());
}
}
- mangleType(ElementTy.getLocalUnqualifiedType());
+ mangleType(ElementTy.getLocalUnqualifiedType(), SourceRange());
}
// <type> ::= <pointer-to-member-type>
// <pointer-to-member-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers>
// <class name> <type>
-void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T) {
+void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T,
+ SourceRange Range) {
QualType PointeeType = T->getPointeeType();
- if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(PointeeType)) {
+ if (const FunctionProtoType *FPT = PointeeType->getAs<FunctionProtoType>()) {
Out << '8';
- mangleName(cast<RecordType>(T->getClass())->getDecl());
+ mangleName(T->getClass()->castAs<RecordType>()->getDecl());
mangleType(FPT, NULL, false, true);
} else {
mangleQualifiers(PointeeType.getQualifiers(), true);
- mangleName(cast<RecordType>(T->getClass())->getDecl());
- mangleType(PointeeType.getLocalUnqualifiedType());
+ mangleName(T->getClass()->castAs<RecordType>()->getDecl());
+ mangleType(PointeeType.getLocalUnqualifiedType(), Range);
}
}
-void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T) {
- llvm_unreachable("Don't know how to mangle TemplateTypeParmTypes yet!");
+void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this template type parameter type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
void MicrosoftCXXNameMangler::mangleType(
- const SubstTemplateTypeParmPackType *T) {
- llvm_unreachable(
- "Don't know how to mangle SubstTemplateTypeParmPackTypes yet!");
+ const SubstTemplateTypeParmPackType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this substituted parameter pack yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
// <type> ::= <pointer-type>
// <pointer-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers> <type>
-void MicrosoftCXXNameMangler::mangleType(const PointerType *T) {
+void MicrosoftCXXNameMangler::mangleType(const PointerType *T,
+ SourceRange Range) {
QualType PointeeTy = T->getPointeeType();
if (PointeeTy->isArrayType()) {
// Pointers to arrays are mangled like arrays.
- mangleExtraDimensions(T->getPointeeType());
- } else if (PointeeTy->isFunctionType()) {
+ mangleExtraDimensions(PointeeTy);
+ } else if (const FunctionType *FT = PointeeTy->getAs<FunctionType>()) {
// Function pointers are special.
Out << '6';
- mangleType(static_cast<const FunctionType *>(PointeeTy.getTypePtr()),
- NULL, false, false);
+ mangleType(FT, NULL, false, false);
} else {
if (!PointeeTy.hasQualifiers())
// Lack of qualifiers is mangled as 'A'.
Out << 'A';
- mangleType(PointeeTy);
+ mangleType(PointeeTy, Range);
}
}
-void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T) {
+void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T,
+ SourceRange Range) {
// Object pointers never have qualifiers.
Out << 'A';
- mangleType(T->getPointeeType());
+ mangleType(T->getPointeeType(), Range);
}
// <type> ::= <reference-type>
// <reference-type> ::= A <cvr-qualifiers> <type>
-void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T) {
+void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T,
+ SourceRange Range) {
Out << 'A';
QualType PointeeTy = T->getPointeeType();
if (!PointeeTy.hasQualifiers())
// Lack of qualifiers is mangled as 'A'.
Out << 'A';
- mangleType(PointeeTy);
-}
-
-void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T) {
- llvm_unreachable("Don't know how to mangle RValueReferenceTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const ComplexType *T) {
- llvm_unreachable("Don't know how to mangle ComplexTypes yet!");
+ mangleType(PointeeTy, Range);
}
-void MicrosoftCXXNameMangler::mangleType(const VectorType *T) {
- llvm_unreachable("Don't know how to mangle VectorTypes yet!");
-}
-void MicrosoftCXXNameMangler::mangleType(const ExtVectorType *T) {
- llvm_unreachable("Don't know how to mangle ExtVectorTypes yet!");
-}
-void MicrosoftCXXNameMangler::mangleType(const DependentSizedExtVectorType *T) {
- llvm_unreachable(
- "Don't know how to mangle DependentSizedExtVectorTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const ObjCInterfaceType *T) {
+// <type> ::= <r-value-reference-type>
+// <r-value-reference-type> ::= $$Q <cvr-qualifiers> <type>
+void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T,
+ SourceRange Range) {
+ Out << "$$Q";
+ QualType PointeeTy = T->getPointeeType();
+ if (!PointeeTy.hasQualifiers())
+ // Lack of qualifiers is mangled as 'A'.
+ Out << 'A';
+ mangleType(PointeeTy, Range);
+}
+
+void MicrosoftCXXNameMangler::mangleType(const ComplexType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this complex number type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const VectorType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this vector type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+void MicrosoftCXXNameMangler::mangleType(const ExtVectorType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this extended vector type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+void MicrosoftCXXNameMangler::mangleType(const DependentSizedExtVectorType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this dependent-sized extended vector type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const ObjCInterfaceType *T,
+ SourceRange) {
// ObjC interfaces have structs underlying them.
Out << 'U';
mangleName(T->getDecl());
}
-void MicrosoftCXXNameMangler::mangleType(const ObjCObjectType *T) {
+void MicrosoftCXXNameMangler::mangleType(const ObjCObjectType *T,
+ SourceRange Range) {
// We don't allow overloading by different protocol qualification,
// so mangling them isn't necessary.
- mangleType(T->getBaseType());
+ mangleType(T->getBaseType(), Range);
}
-void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T) {
+void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T,
+ SourceRange Range) {
Out << "_E";
- mangleType(T->getPointeeType());
+ mangleType(T->getPointeeType(), Range);
}
-void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T) {
- llvm_unreachable("Don't know how to mangle InjectedClassNameTypes yet!");
+void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this injected class name type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
-void MicrosoftCXXNameMangler::mangleType(const TemplateSpecializationType *T) {
- llvm_unreachable("Don't know how to mangle TemplateSpecializationTypes yet!");
+void MicrosoftCXXNameMangler::mangleType(const TemplateSpecializationType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this template specialization type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
-void MicrosoftCXXNameMangler::mangleType(const DependentNameType *T) {
- llvm_unreachable("Don't know how to mangle DependentNameTypes yet!");
+void MicrosoftCXXNameMangler::mangleType(const DependentNameType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this dependent name type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
void MicrosoftCXXNameMangler::mangleType(
- const DependentTemplateSpecializationType *T) {
- llvm_unreachable(
- "Don't know how to mangle DependentTemplateSpecializationTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T) {
- llvm_unreachable("Don't know how to mangle PackExpansionTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T) {
- llvm_unreachable("Don't know how to mangle TypeOfTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const TypeOfExprType *T) {
- llvm_unreachable("Don't know how to mangle TypeOfExprTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T) {
- llvm_unreachable("Don't know how to mangle DecltypeTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T) {
- llvm_unreachable("Don't know how to mangle UnaryTransformationTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const AutoType *T) {
- llvm_unreachable("Don't know how to mangle AutoTypes yet!");
-}
-
-void MicrosoftCXXNameMangler::mangleType(const AtomicType *T) {
- llvm_unreachable("Don't know how to mangle AtomicTypes yet!");
+ const DependentTemplateSpecializationType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this dependent template specialization type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this pack expansion yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this typeof(type) yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const TypeOfExprType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this typeof(expression) yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this decltype() yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this unary transform type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const AutoType *T, SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this 'auto' type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
+}
+
+void MicrosoftCXXNameMangler::mangleType(const AtomicType *T,
+ SourceRange Range) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this C11 atomic type yet");
+ Diags.Report(Range.getBegin(), DiagID)
+ << Range;
}
void MicrosoftMangleContext::mangleName(const NamedDecl *D,
@@ -1138,17 +1616,35 @@ void MicrosoftMangleContext::mangleName(const NamedDecl *D,
void MicrosoftMangleContext::mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &) {
- llvm_unreachable("Can't yet mangle thunks!");
+ unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle thunk for this method yet");
+ getDiags().Report(MD->getLocation(), DiagID);
}
void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
CXXDtorType Type,
const ThisAdjustment &,
raw_ostream &) {
- llvm_unreachable("Can't yet mangle destructor thunks!");
+ unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle thunk for this destructor yet");
+ getDiags().Report(DD->getLocation(), DiagID);
}
void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD,
- raw_ostream &) {
- llvm_unreachable("Can't yet mangle virtual tables!");
+ raw_ostream &Out) {
+ // <mangled-name> ::= ? <operator-name> <class-name> <storage-class>
+ // <cvr-qualifiers> [<name>] @
+ // <operator-name> ::= _7 # vftable
+ // ::= _8 # vbtable
+ // NOTE: <cvr-qualifiers> here is always 'B' (const). <storage-class>
+ // is always '6' for vftables and '7' for vbtables. (The difference is
+ // beyond me.)
+ // TODO: vbtables.
+ MicrosoftCXXNameMangler Mangler(*this, Out);
+ Mangler.getStream() << "\01??_7";
+ Mangler.mangleName(RD);
+ Mangler.getStream() << "6B";
+ // TODO: If the class has more than one vtable, mangle in the class it came
+ // from.
+ Mangler.getStream() << '@';
}
void MicrosoftMangleContext::mangleCXXVTT(const CXXRecordDecl *RD,
raw_ostream &) {
@@ -1162,11 +1658,19 @@ void MicrosoftMangleContext::mangleCXXCtorVTable(const CXXRecordDecl *RD,
}
void MicrosoftMangleContext::mangleCXXRTTI(QualType T,
raw_ostream &) {
- llvm_unreachable("Can't yet mangle RTTI!");
+ // FIXME: Give a location...
+ unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle RTTI descriptors for type %0 yet");
+ getDiags().Report(DiagID)
+ << T.getBaseTypeIdentifier();
}
void MicrosoftMangleContext::mangleCXXRTTIName(QualType T,
raw_ostream &) {
- llvm_unreachable("Can't yet mangle RTTI names!");
+ // FIXME: Give a location...
+ unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle the name of type %0 into RTTI descriptors yet");
+ getDiags().Report(DiagID)
+ << T.getBaseTypeIdentifier();
}
void MicrosoftMangleContext::mangleCXXCtor(const CXXConstructorDecl *D,
CXXCtorType Type,
@@ -1180,9 +1684,11 @@ void MicrosoftMangleContext::mangleCXXDtor(const CXXDestructorDecl *D,
MicrosoftCXXNameMangler mangler(*this, Out);
mangler.mangle(D);
}
-void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *,
+void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *VD,
raw_ostream &) {
- llvm_unreachable("Can't yet mangle reference temporaries!");
+ unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle this reference temporary yet");
+ getDiags().Report(VD->getLocation(), DiagID);
}
MangleContext *clang::createMicrosoftMangleContext(ASTContext &Context,
diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp
index f5ea2c5..39077d1 100644
--- a/lib/AST/NSAPI.cpp
+++ b/lib/AST/NSAPI.cpp
@@ -9,11 +9,13 @@
#include "clang/AST/NSAPI.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
using namespace clang;
NSAPI::NSAPI(ASTContext &ctx)
- : Ctx(ctx), ClassIds() {
+ : Ctx(ctx), ClassIds(), BOOLId(0), NSIntegerId(0), NSUIntegerId(0),
+ NSASCIIStringEncodingId(0), NSUTF8StringEncodingId(0) {
}
IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const {
@@ -40,6 +42,21 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const {
case NSStr_stringWithString:
Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithString"));
break;
+ case NSStr_stringWithUTF8String:
+ Sel = Ctx.Selectors.getUnarySelector(
+ &Ctx.Idents.get("stringWithUTF8String"));
+ break;
+ case NSStr_stringWithCStringEncoding: {
+ IdentifierInfo *KeyIdents[] = {
+ &Ctx.Idents.get("stringWithCString"),
+ &Ctx.Idents.get("encoding")
+ };
+ Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+ break;
+ }
+ case NSStr_stringWithCString:
+ Sel= Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithCString"));
+ break;
case NSStr_initWithString:
Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithString"));
break;
@@ -50,6 +67,17 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const {
return NSStringSelectors[MK];
}
+llvm::Optional<NSAPI::NSStringMethodKind>
+NSAPI::getNSStringMethodKind(Selector Sel) const {
+ for (unsigned i = 0; i != NumNSStringMethods; ++i) {
+ NSStringMethodKind MK = NSStringMethodKind(i);
+ if (Sel == getNSStringSelector(MK))
+ return MK;
+ }
+
+ return llvm::Optional<NSStringMethodKind>();
+}
+
Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const {
if (NSArraySelectors[MK].isNull()) {
Selector Sel;
@@ -251,11 +279,22 @@ NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const {
}
llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
-NSAPI::getNSNumberFactoryMethodKind(QualType T) {
+NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
const BuiltinType *BT = T->getAs<BuiltinType>();
if (!BT)
return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>();
-
+
+ const TypedefType *TDT = T->getAs<TypedefType>();
+ if (TDT) {
+ QualType TDTTy = QualType(TDT, 0);
+ if (isObjCBOOLType(TDTTy))
+ return NSAPI::NSNumberWithBool;
+ if (isObjCNSIntegerType(TDTTy))
+ return NSAPI::NSNumberWithInteger;
+ if (isObjCNSUIntegerType(TDTTy))
+ return NSAPI::NSNumberWithUnsignedInteger;
+ }
+
switch (BT->getKind()) {
case BuiltinType::Char_S:
case BuiltinType::SChar:
@@ -310,3 +349,65 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) {
return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>();
}
+
+/// \brief Returns true if \param T is a typedef of "BOOL" in objective-c.
+bool NSAPI::isObjCBOOLType(QualType T) const {
+ return isObjCTypedef(T, "BOOL", BOOLId);
+}
+/// \brief Returns true if \param T is a typedef of "NSInteger" in objective-c.
+bool NSAPI::isObjCNSIntegerType(QualType T) const {
+ return isObjCTypedef(T, "NSInteger", NSIntegerId);
+}
+/// \brief Returns true if \param T is a typedef of "NSUInteger" in objective-c.
+bool NSAPI::isObjCNSUIntegerType(QualType T) const {
+ return isObjCTypedef(T, "NSUInteger", NSUIntegerId);
+}
+
+bool NSAPI::isObjCTypedef(QualType T,
+ StringRef name, IdentifierInfo *&II) const {
+ if (!Ctx.getLangOpts().ObjC1)
+ return false;
+ if (T.isNull())
+ return false;
+
+ if (!II)
+ II = &Ctx.Idents.get(name);
+
+ while (const TypedefType *TDT = T->getAs<TypedefType>()) {
+ if (TDT->getDecl()->getDeclName().getAsIdentifierInfo() == II)
+ return true;
+ T = TDT->desugar();
+ }
+
+ return false;
+}
+
+bool NSAPI::isObjCEnumerator(const Expr *E,
+ StringRef name, IdentifierInfo *&II) const {
+ if (!Ctx.getLangOpts().ObjC1)
+ return false;
+ if (!E)
+ return false;
+
+ if (!II)
+ II = &Ctx.Idents.get(name);
+
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
+ if (const EnumConstantDecl *
+ EnumD = dyn_cast_or_null<EnumConstantDecl>(DRE->getDecl()))
+ return EnumD->getIdentifier() == II;
+
+ return false;
+}
+
+Selector NSAPI::getOrInitSelector(ArrayRef<StringRef> Ids,
+ Selector &Sel) const {
+ if (Sel.isNull()) {
+ SmallVector<IdentifierInfo *, 4> Idents;
+ for (ArrayRef<StringRef>::const_iterator
+ I = Ids.begin(), E = Ids.end(); I != E; ++I)
+ Idents.push_back(&Ctx.Idents.get(*I));
+ Sel = Ctx.Selectors.getSelector(Idents.size(), Idents.data());
+ }
+ return Sel;
+}
diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp
index 64016d9..fa87afd 100644
--- a/lib/AST/ParentMap.cpp
+++ b/lib/AST/ParentMap.cpp
@@ -23,13 +23,20 @@ typedef llvm::DenseMap<Stmt*, Stmt*> MapTy;
static void BuildParentMap(MapTy& M, Stmt* S) {
for (Stmt::child_range I = S->children(); I; ++I)
if (*I) {
- M[*I] = S;
- BuildParentMap(M, *I);
+ // Prefer the first time we see this statement in the traversal.
+ // This is important for PseudoObjectExprs.
+ Stmt *&Parent = M[*I];
+ if (!Parent) {
+ Parent = S;
+ BuildParentMap(M, *I);
+ }
}
// Also include the source expr tree of an OpaqueValueExpr in the map.
- if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S))
+ if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) {
+ M[OVE->getSourceExpr()] = S;
BuildParentMap(M, OVE->getSourceExpr());
+ }
}
ParentMap::ParentMap(Stmt* S) : Impl(0) {
diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp
new file mode 100644
index 0000000..c704cab
--- /dev/null
+++ b/lib/AST/RawCommentList.cpp
@@ -0,0 +1,271 @@
+//===--- RawCommentList.cpp - Processing raw comments -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/RawCommentList.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentBriefParser.h"
+#include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentParser.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+
+namespace {
+/// Get comment kind and bool describing if it is a trailing comment.
+std::pair<RawComment::CommentKind, bool> getCommentKind(StringRef Comment) {
+ if (Comment.size() < 3 || Comment[0] != '/')
+ return std::make_pair(RawComment::RCK_Invalid, false);
+
+ RawComment::CommentKind K;
+ if (Comment[1] == '/') {
+ if (Comment.size() < 3)
+ return std::make_pair(RawComment::RCK_OrdinaryBCPL, false);
+
+ if (Comment[2] == '/')
+ K = RawComment::RCK_BCPLSlash;
+ else if (Comment[2] == '!')
+ K = RawComment::RCK_BCPLExcl;
+ else
+ return std::make_pair(RawComment::RCK_OrdinaryBCPL, false);
+ } else {
+ assert(Comment.size() >= 4);
+
+ // Comment lexer does not understand escapes in comment markers, so pretend
+ // that this is not a comment.
+ if (Comment[1] != '*' ||
+ Comment[Comment.size() - 2] != '*' ||
+ Comment[Comment.size() - 1] != '/')
+ return std::make_pair(RawComment::RCK_Invalid, false);
+
+ if (Comment[2] == '*')
+ K = RawComment::RCK_JavaDoc;
+ else if (Comment[2] == '!')
+ K = RawComment::RCK_Qt;
+ else
+ return std::make_pair(RawComment::RCK_OrdinaryC, false);
+ }
+ const bool TrailingComment = (Comment.size() > 3) && (Comment[3] == '<');
+ return std::make_pair(K, TrailingComment);
+}
+
+bool mergedCommentIsTrailingComment(StringRef Comment) {
+ return (Comment.size() > 3) && (Comment[3] == '<');
+}
+} // unnamed namespace
+
+RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR,
+ bool Merged) :
+ Range(SR), RawTextValid(false), BriefTextValid(false),
+ IsAlmostTrailingComment(false),
+ BeginLineValid(false), EndLineValid(false) {
+ // Extract raw comment text, if possible.
+ if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) {
+ Kind = RCK_Invalid;
+ return;
+ }
+
+ if (!Merged) {
+ // Guess comment kind.
+ std::pair<CommentKind, bool> K = getCommentKind(RawText);
+ Kind = K.first;
+ IsTrailingComment = K.second;
+
+ IsAlmostTrailingComment = RawText.startswith("//<") ||
+ RawText.startswith("/*<");
+ } else {
+ Kind = RCK_Merged;
+ IsTrailingComment = mergedCommentIsTrailingComment(RawText);
+ }
+}
+
+const Decl *RawComment::getDecl() const {
+ if (DeclOrParsedComment.isNull())
+ return NULL;
+
+ if (const Decl *D = DeclOrParsedComment.dyn_cast<const Decl *>())
+ return D;
+
+ return DeclOrParsedComment.get<comments::FullComment *>()->getDecl();
+}
+
+unsigned RawComment::getBeginLine(const SourceManager &SM) const {
+ if (BeginLineValid)
+ return BeginLine;
+
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin());
+ BeginLine = SM.getLineNumber(LocInfo.first, LocInfo.second);
+ BeginLineValid = true;
+ return BeginLine;
+}
+
+unsigned RawComment::getEndLine(const SourceManager &SM) const {
+ if (EndLineValid)
+ return EndLine;
+
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getEnd());
+ EndLine = SM.getLineNumber(LocInfo.first, LocInfo.second);
+ EndLineValid = true;
+ return EndLine;
+}
+
+StringRef RawComment::getRawTextSlow(const SourceManager &SourceMgr) const {
+ FileID BeginFileID;
+ FileID EndFileID;
+ unsigned BeginOffset;
+ unsigned EndOffset;
+
+ llvm::tie(BeginFileID, BeginOffset) =
+ SourceMgr.getDecomposedLoc(Range.getBegin());
+ llvm::tie(EndFileID, EndOffset) =
+ SourceMgr.getDecomposedLoc(Range.getEnd());
+
+ const unsigned Length = EndOffset - BeginOffset;
+ if (Length < 2)
+ return StringRef();
+
+ // The comment can't begin in one file and end in another.
+ assert(BeginFileID == EndFileID);
+
+ bool Invalid = false;
+ const char *BufferStart = SourceMgr.getBufferData(BeginFileID,
+ &Invalid).data();
+ if (Invalid)
+ return StringRef();
+
+ return StringRef(BufferStart + BeginOffset, Length);
+}
+
+const char *RawComment::extractBriefText(const ASTContext &Context) const {
+ // Make sure that RawText is valid.
+ getRawText(Context.getSourceManager());
+
+ // Since we will be copying the resulting text, all allocations made during
+ // parsing are garbage after resulting string is formed. Thus we can use
+ // a separate allocator for all temporary stuff.
+ llvm::BumpPtrAllocator Allocator;
+
+ comments::CommandTraits Traits;
+ comments::Lexer L(Allocator, Traits,
+ Range.getBegin(), comments::CommentOptions(),
+ RawText.begin(), RawText.end());
+ comments::BriefParser P(L, Traits);
+
+ const std::string Result = P.Parse();
+ const unsigned BriefTextLength = Result.size();
+ char *BriefTextPtr = new (Context) char[BriefTextLength + 1];
+ memcpy(BriefTextPtr, Result.c_str(), BriefTextLength + 1);
+ BriefText = BriefTextPtr;
+ BriefTextValid = true;
+
+ return BriefTextPtr;
+}
+
+comments::FullComment *RawComment::parse(const ASTContext &Context) const {
+ // Make sure that RawText is valid.
+ getRawText(Context.getSourceManager());
+
+ comments::CommandTraits Traits;
+ comments::Lexer L(Context.getAllocator(), Traits,
+ getSourceRange().getBegin(), comments::CommentOptions(),
+ RawText.begin(), RawText.end());
+ comments::Sema S(Context.getAllocator(), Context.getSourceManager(),
+ Context.getDiagnostics(), Traits);
+ S.setDecl(getDecl());
+ comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(),
+ Context.getDiagnostics(), Traits);
+
+ comments::FullComment *FC = P.parseFullComment();
+ DeclOrParsedComment = FC;
+ return FC;
+}
+
+namespace {
+bool containsOnlyWhitespace(StringRef Str) {
+ return Str.find_first_not_of(" \t\f\v\r\n") == StringRef::npos;
+}
+
+bool onlyWhitespaceBetweenComments(SourceManager &SM,
+ const RawComment &C1, const RawComment &C2) {
+ std::pair<FileID, unsigned> C1EndLocInfo = SM.getDecomposedLoc(
+ C1.getSourceRange().getEnd());
+ std::pair<FileID, unsigned> C2BeginLocInfo = SM.getDecomposedLoc(
+ C2.getSourceRange().getBegin());
+
+ // Question does not make sense if comments are located in different files.
+ if (C1EndLocInfo.first != C2BeginLocInfo.first)
+ return false;
+
+ bool Invalid = false;
+ const char *Buffer = SM.getBufferData(C1EndLocInfo.first, &Invalid).data();
+ if (Invalid)
+ return false;
+
+ StringRef TextBetweenComments(Buffer + C1EndLocInfo.second,
+ C2BeginLocInfo.second - C1EndLocInfo.second);
+
+ return containsOnlyWhitespace(TextBetweenComments);
+}
+} // unnamed namespace
+
+void RawCommentList::addComment(const RawComment &RC,
+ llvm::BumpPtrAllocator &Allocator) {
+ if (RC.isInvalid())
+ return;
+
+ // Check if the comments are not in source order.
+ while (!Comments.empty() &&
+ !SourceMgr.isBeforeInTranslationUnit(
+ Comments.back()->getSourceRange().getBegin(),
+ RC.getSourceRange().getBegin())) {
+ // If they are, just pop a few last comments that don't fit.
+ // This happens if an \#include directive contains comments.
+ Comments.pop_back();
+ }
+
+ if (OnlyWhitespaceSeen) {
+ if (!onlyWhitespaceBetweenComments(SourceMgr, LastComment, RC))
+ OnlyWhitespaceSeen = false;
+ }
+
+ LastComment = RC;
+
+ // Ordinary comments are not interesting for us.
+ if (RC.isOrdinary())
+ return;
+
+ // If this is the first Doxygen comment, save it (because there isn't
+ // anything to merge it with).
+ if (Comments.empty()) {
+ Comments.push_back(new (Allocator) RawComment(RC));
+ OnlyWhitespaceSeen = true;
+ return;
+ }
+
+ const RawComment &C1 = *Comments.back();
+ const RawComment &C2 = RC;
+
+ // Merge comments only if there is only whitespace between them.
+ // Can't merge trailing and non-trailing comments.
+ // Merge trailing comments if they are on same or consecutive lines.
+ if (OnlyWhitespaceSeen &&
+ (C1.isTrailingComment() == C2.isTrailingComment()) &&
+ (!C1.isTrailingComment() ||
+ C1.getEndLine(SourceMgr) + 1 >= C2.getBeginLine(SourceMgr))) {
+ SourceRange MergedRange(C1.getSourceRange().getBegin(),
+ C2.getSourceRange().getEnd());
+ *Comments.back() = RawComment(SourceMgr, MergedRange, true);
+ } else
+ Comments.push_back(new (Allocator) RawComment(RC));
+
+ OnlyWhitespaceSeen = true;
+}
+
diff --git a/lib/AST/RecordLayout.cpp b/lib/AST/RecordLayout.cpp
index 0114eba..2ae0aab 100644
--- a/lib/AST/RecordLayout.cpp
+++ b/lib/AST/RecordLayout.cpp
@@ -32,7 +32,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
CharUnits alignment, CharUnits datasize,
const uint64_t *fieldoffsets,
unsigned fieldcount)
- : Size(size), DataSize(datasize), FieldOffsets(0), Alignment(alignment),
+ : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0),
FieldCount(fieldcount), CXXInfo(0) {
if (FieldCount > 0) {
FieldOffsets = new (Ctx) uint64_t[FieldCount];
@@ -43,7 +43,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
// Constructor for C++ records.
ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
CharUnits size, CharUnits alignment,
- CharUnits vfptroffset, CharUnits vbptroffset,
+ bool hasOwnVFPtr, CharUnits vbptroffset,
CharUnits datasize,
const uint64_t *fieldoffsets,
unsigned fieldcount,
@@ -53,8 +53,8 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
const CXXRecordDecl *PrimaryBase,
bool IsPrimaryBaseVirtual,
const BaseOffsetsMapTy& BaseOffsets,
- const BaseOffsetsMapTy& VBaseOffsets)
- : Size(size), DataSize(datasize), FieldOffsets(0), Alignment(alignment),
+ const VBaseOffsetsMapTy& VBaseOffsets)
+ : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0),
FieldCount(fieldcount), CXXInfo(new (Ctx) CXXRecordLayoutInfo)
{
if (FieldCount > 0) {
@@ -69,7 +69,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject;
CXXInfo->BaseOffsets = BaseOffsets;
CXXInfo->VBaseOffsets = VBaseOffsets;
- CXXInfo->VFPtrOffset = vfptroffset;
+ CXXInfo->HasOwnVFPtr = hasOwnVFPtr;
CXXInfo->VBPtrOffset = vbptroffset;
#ifndef NDEBUG
@@ -81,7 +81,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
"Primary virtual base must be at offset 0!");
}
} else {
- assert(getBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(getBaseClassOffset(PrimaryBase).isZero() &&
"Primary base must be at offset 0!");
}
}
diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp
index c2d9294..d5df63f 100644
--- a/lib/AST/RecordLayoutBuilder.cpp
+++ b/lib/AST/RecordLayoutBuilder.cpp
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
@@ -161,10 +162,9 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() {
// Check the fields.
for (CXXRecordDecl::field_iterator I = Class->field_begin(),
E = Class->field_end(); I != E; ++I) {
- const FieldDecl *FD = *I;
const RecordType *RT =
- Context.getBaseElementType(FD->getType())->getAs<RecordType>();
+ Context.getBaseElementType(I->getType())->getAs<RecordType>();
// We only care about record types.
if (!RT)
@@ -261,12 +261,11 @@ EmptySubobjectMap::CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info,
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(),
E = Info->Class->field_end(); I != E; ++I, ++FieldNo) {
- const FieldDecl *FD = *I;
- if (FD->isBitField())
+ if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
- if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset))
+ if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset))
return false;
}
@@ -310,12 +309,11 @@ void EmptySubobjectMap::UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info,
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(),
E = Info->Class->field_end(); I != E; ++I, ++FieldNo) {
- const FieldDecl *FD = *I;
- if (FD->isBitField())
+ if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
- UpdateEmptyFieldSubobjects(FD, FieldOffset);
+ UpdateEmptyFieldSubobjects(*I, FieldOffset);
}
}
@@ -380,13 +378,12 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD,
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I, ++FieldNo) {
- const FieldDecl *FD = *I;
- if (FD->isBitField())
+ if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
- if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset))
+ if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset))
return false;
}
@@ -491,13 +488,12 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD,
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I, ++FieldNo) {
- const FieldDecl *FD = *I;
- if (FD->isBitField())
+ if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
- UpdateEmptyFieldSubobjects(FD, FieldOffset);
+ UpdateEmptyFieldSubobjects(*I, FieldOffset);
}
}
@@ -538,6 +534,8 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const FieldDecl *FD,
}
}
+typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy;
+
class RecordLayoutBuilder {
protected:
// FIXME: Remove this and make the appropriate fields public.
@@ -600,8 +598,9 @@ protected:
/// out is virtual.
bool PrimaryBaseIsVirtual;
- /// VFPtrOffset - Virtual function table offset. Only for MS layout.
- CharUnits VFPtrOffset;
+ /// HasOwnVFPtr - Whether the class provides its own vtable/vftbl
+ /// pointer, as opposed to inheriting one from a primary base class.
+ bool HasOwnVFPtr;
/// VBPtrOffset - Virtual base table offset. Only for MS layout.
CharUnits VBPtrOffset;
@@ -612,7 +611,7 @@ protected:
BaseOffsetsMapTy Bases;
// VBases - virtual base classes and their offsets in the record.
- BaseOffsetsMapTy VBases;
+ ASTRecordLayout::VBaseOffsetsMapTy VBases;
/// IndirectPrimaryBases - Virtual base classes, direct or indirect, that are
/// primary base classes for some other direct or indirect base class.
@@ -652,7 +651,7 @@ protected:
NonVirtualAlignment(CharUnits::One()),
ZeroLengthBitfield(0), PrimaryBase(0),
PrimaryBaseIsVirtual(false),
- VFPtrOffset(CharUnits::fromQuantity(-1)),
+ HasOwnVFPtr(false),
VBPtrOffset(CharUnits::fromQuantity(-1)),
FirstNearlyEmptyVBase(0) { }
@@ -725,15 +724,20 @@ protected:
CharUnits Offset);
bool needsVFTable(const CXXRecordDecl *RD) const;
- bool hasNewVirtualFunction(const CXXRecordDecl *RD) const;
+ bool hasNewVirtualFunction(const CXXRecordDecl *RD,
+ bool IgnoreDestructor = false) const;
bool isPossiblePrimaryBase(const CXXRecordDecl *Base) const;
+ void computeVtordisps(const CXXRecordDecl *RD,
+ ClassSetTy &VtordispVBases);
+
/// LayoutVirtualBases - Lays out all the virtual bases.
void LayoutVirtualBases(const CXXRecordDecl *RD,
const CXXRecordDecl *MostDerivedClass);
/// LayoutVirtualBase - Lays out a single virtual base.
- void LayoutVirtualBase(const BaseSubobjectInfo *Base);
+ void LayoutVirtualBase(const BaseSubobjectInfo *Base,
+ bool IsVtordispNeed = false);
/// LayoutBase - Will lay out a base and return the offset where it was
/// placed, in chars.
@@ -1044,8 +1048,7 @@ RecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD) {
CharUnits PtrAlign =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0));
EnsureVTablePointerAlignment(PtrAlign);
- if (isMicrosoftCXXABI())
- VFPtrOffset = getSize();
+ HasOwnVFPtr = true;
setSize(getSize() + PtrWidth);
setDataSize(getSize());
}
@@ -1142,7 +1145,7 @@ RecordLayoutBuilder::AddPrimaryVirtualBaseOffsets(const BaseSubobjectInfo *Info,
assert(!VBases.count(Info->PrimaryVirtualBaseInfo->Class) &&
"primary vbase offset already exists!");
VBases.insert(std::make_pair(Info->PrimaryVirtualBaseInfo->Class,
- Offset));
+ ASTRecordLayout::VBaseInfo(Offset, false)));
// Traverse the primary virtual base.
AddPrimaryVirtualBaseOffsets(Info->PrimaryVirtualBaseInfo, Offset);
@@ -1193,19 +1196,177 @@ bool RecordLayoutBuilder::needsVFTable(const CXXRecordDecl *RD) const {
return hasNewVirtualFunction(RD);
}
+/// Does the given class inherit non-virtually from any of the classes
+/// in the given set?
+static bool hasNonVirtualBaseInSet(const CXXRecordDecl *RD,
+ const ClassSetTy &set) {
+ for (CXXRecordDecl::base_class_const_iterator
+ I = RD->bases_begin(), E = RD->bases_end(); I != E; ++I) {
+ // Ignore virtual links.
+ if (I->isVirtual()) continue;
+
+ // Check whether the set contains the base.
+ const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl();
+ if (set.count(base))
+ return true;
+
+ // Otherwise, recurse and propagate.
+ if (hasNonVirtualBaseInSet(base, set))
+ return true;
+ }
+
+ return false;
+}
+
+/// Does the given method (B::foo()) already override a method (A::foo())
+/// such that A requires a vtordisp in B? If so, we don't need to add a
+/// new vtordisp for B in a yet-more-derived class C providing C::foo().
+static bool overridesMethodRequiringVtorDisp(const ASTContext &Context,
+ const CXXMethodDecl *M) {
+ CXXMethodDecl::method_iterator
+ I = M->begin_overridden_methods(), E = M->end_overridden_methods();
+ if (I == E) return false;
+
+ const ASTRecordLayout::VBaseOffsetsMapTy &offsets =
+ Context.getASTRecordLayout(M->getParent()).getVBaseOffsetsMap();
+ do {
+ const CXXMethodDecl *overridden = *I;
+
+ // If the overridden method's class isn't recognized as a virtual
+ // base in the derived class, ignore it.
+ ASTRecordLayout::VBaseOffsetsMapTy::const_iterator
+ it = offsets.find(overridden->getParent());
+ if (it == offsets.end()) continue;
+
+ // Otherwise, check if the overridden method's class needs a vtordisp.
+ if (it->second.hasVtorDisp()) return true;
+
+ } while (++I != E);
+ return false;
+}
+
+/// In the Microsoft ABI, decide which of the virtual bases require a
+/// vtordisp field.
+void RecordLayoutBuilder::computeVtordisps(const CXXRecordDecl *RD,
+ ClassSetTy &vtordispVBases) {
+ // Bail out if we have no virtual bases.
+ assert(RD->getNumVBases());
+
+ // Build up the set of virtual bases that we haven't decided yet.
+ ClassSetTy undecidedVBases;
+ for (CXXRecordDecl::base_class_const_iterator
+ I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) {
+ const CXXRecordDecl *vbase = I->getType()->getAsCXXRecordDecl();
+ undecidedVBases.insert(vbase);
+ }
+ assert(!undecidedVBases.empty());
+
+ // A virtual base requires a vtordisp field in a derived class if it
+ // requires a vtordisp field in a base class. Walk all the direct
+ // bases and collect this information.
+ for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
+ E = RD->bases_end(); I != E; ++I) {
+ const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl();
+ const ASTRecordLayout &baseLayout = Context.getASTRecordLayout(base);
+
+ // Iterate over the set of virtual bases provided by this class.
+ for (ASTRecordLayout::VBaseOffsetsMapTy::const_iterator
+ VI = baseLayout.getVBaseOffsetsMap().begin(),
+ VE = baseLayout.getVBaseOffsetsMap().end(); VI != VE; ++VI) {
+ // If it doesn't need a vtordisp in this base, ignore it.
+ if (!VI->second.hasVtorDisp()) continue;
+
+ // If we've already seen it and decided it needs a vtordisp, ignore it.
+ if (!undecidedVBases.erase(VI->first))
+ continue;
+
+ // Add it.
+ vtordispVBases.insert(VI->first);
+
+ // Quit as soon as we've decided everything.
+ if (undecidedVBases.empty())
+ return;
+ }
+ }
+
+ // Okay, we have virtual bases that we haven't yet decided about. A
+ // virtual base requires a vtordisp if any the non-destructor
+ // virtual methods declared in this class directly override a method
+ // provided by that virtual base. (If so, we need to emit a thunk
+ // for that method, to be used in the construction vftable, which
+ // applies an additional 'vtordisp' this-adjustment.)
+
+ // Collect the set of bases directly overridden by any method in this class.
+ // It's possible that some of these classes won't be virtual bases, or won't be
+ // provided by virtual bases, or won't be virtual bases in the overridden
+ // instance but are virtual bases elsewhere. Only the last matters for what
+ // we're doing, and we can ignore those: if we don't directly override
+ // a method provided by a virtual copy of a base class, but we do directly
+ // override a method provided by a non-virtual copy of that base class,
+ // then we must indirectly override the method provided by the virtual base,
+ // and so we should already have collected it in the loop above.
+ ClassSetTy overriddenBases;
+ for (CXXRecordDecl::method_iterator
+ M = RD->method_begin(), E = RD->method_end(); M != E; ++M) {
+ // Ignore non-virtual methods and destructors.
+ if (isa<CXXDestructorDecl>(*M) || !M->isVirtual())
+ continue;
+
+ for (CXXMethodDecl::method_iterator I = M->begin_overridden_methods(),
+ E = M->end_overridden_methods(); I != E; ++I) {
+ const CXXMethodDecl *overriddenMethod = (*I);
+
+ // Ignore methods that override methods from vbases that require
+ // require vtordisps.
+ if (overridesMethodRequiringVtorDisp(Context, overriddenMethod))
+ continue;
+
+ // As an optimization, check immediately whether we're overriding
+ // something from the undecided set.
+ const CXXRecordDecl *overriddenBase = overriddenMethod->getParent();
+ if (undecidedVBases.erase(overriddenBase)) {
+ vtordispVBases.insert(overriddenBase);
+ if (undecidedVBases.empty()) return;
+
+ // We can't 'continue;' here because one of our undecided
+ // vbases might non-virtually inherit from this base.
+ // Consider:
+ // struct A { virtual void foo(); };
+ // struct B : A {};
+ // struct C : virtual A, virtual B { virtual void foo(); };
+ // We need a vtordisp for B here.
+ }
+
+ // Otherwise, just collect it.
+ overriddenBases.insert(overriddenBase);
+ }
+ }
+
+ // Walk the undecided v-bases and check whether they (non-virtually)
+ // provide any of the overridden bases. We don't need to consider
+ // virtual links because the vtordisp inheres to the layout
+ // subobject containing the base.
+ for (ClassSetTy::const_iterator
+ I = undecidedVBases.begin(), E = undecidedVBases.end(); I != E; ++I) {
+ if (hasNonVirtualBaseInSet(*I, overriddenBases))
+ vtordispVBases.insert(*I);
+ }
+}
+
/// hasNewVirtualFunction - Does the given polymorphic class declare a
/// virtual function that does not override a method from any of its
/// base classes?
bool
-RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const {
- assert(RD->isPolymorphic());
+RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD,
+ bool IgnoreDestructor) const {
if (!RD->getNumBases())
return true;
for (CXXRecordDecl::method_iterator method = RD->method_begin();
method != RD->method_end();
++method) {
- if (method->isVirtual() && !method->size_overridden_methods()) {
+ if (method->isVirtual() && !method->size_overridden_methods() &&
+ !(IgnoreDestructor && method->getKind() == Decl::CXXDestructor)) {
return true;
}
}
@@ -1215,11 +1376,11 @@ RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const {
/// isPossiblePrimaryBase - Is the given base class an acceptable
/// primary base class?
bool
-RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const {
+RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *base) const {
// In the Itanium ABI, a class can be a primary base class if it has
// a vtable for any reason.
if (!isMicrosoftCXXABI())
- return Base->isDynamicClass();
+ return base->isDynamicClass();
// In the MS ABI, a class can only be a primary base class if it
// provides a vf-table at a static offset. That means it has to be
@@ -1228,14 +1389,22 @@ RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const {
// base, which we have to guard against.
// First off, it has to have virtual functions.
- if (!Base->isPolymorphic()) return false;
+ if (!base->isPolymorphic()) return false;
+
+ // If it has no virtual bases, then the vfptr must be at a static offset.
+ if (!base->getNumVBases()) return true;
+
+ // Otherwise, the necessary information is cached in the layout.
+ const ASTRecordLayout &layout = Context.getASTRecordLayout(base);
+
+ // If the base has its own vfptr, it can be a primary base.
+ if (layout.hasOwnVFPtr()) return true;
- // If it has no virtual bases, then everything is at a static offset.
- if (!Base->getNumVBases()) return true;
+ // If the base has a primary base class, then it can be a primary base.
+ if (layout.getPrimaryBase()) return true;
- // Okay, just ask the base class's layout.
- return (Context.getASTRecordLayout(Base).getVFPtrOffset()
- != CharUnits::fromQuantity(-1));
+ // Otherwise it can't.
+ return false;
}
void
@@ -1288,10 +1457,12 @@ RecordLayoutBuilder::LayoutVirtualBases(const CXXRecordDecl *RD,
}
void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) {
-
if (!RD->getNumVBases())
return;
+ ClassSetTy VtordispVBases;
+ computeVtordisps(RD, VtordispVBases);
+
// This is substantially simplified because there are no virtual
// primary bases.
for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(),
@@ -1299,12 +1470,25 @@ void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) {
const CXXRecordDecl *BaseDecl = I->getType()->getAsCXXRecordDecl();
const BaseSubobjectInfo *BaseInfo = VirtualBaseInfo.lookup(BaseDecl);
assert(BaseInfo && "Did not find virtual base info!");
-
- LayoutVirtualBase(BaseInfo);
+
+ // If this base requires a vtordisp, add enough space for an int field.
+ // This is apparently always 32-bits, even on x64.
+ bool vtordispNeeded = false;
+ if (VtordispVBases.count(BaseDecl)) {
+ CharUnits IntSize =
+ CharUnits::fromQuantity(Context.getTargetInfo().getIntWidth() / 8);
+
+ setSize(getSize() + IntSize);
+ setDataSize(getSize());
+ vtordispNeeded = true;
+ }
+
+ LayoutVirtualBase(BaseInfo, vtordispNeeded);
}
}
-void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) {
+void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base,
+ bool IsVtordispNeed) {
assert(!Base->Derived && "Trying to lay out a primary virtual base!");
// Layout the base.
@@ -1312,9 +1496,11 @@ void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) {
// Add its base class offset.
assert(!VBases.count(Base->Class) && "vbase offset already exists!");
- VBases.insert(std::make_pair(Base->Class, Offset));
-
- AddPrimaryVirtualBaseOffsets(Base, Offset);
+ VBases.insert(std::make_pair(Base->Class,
+ ASTRecordLayout::VBaseInfo(Offset, IsVtordispNeed)));
+
+ if (!isMicrosoftCXXABI())
+ AddPrimaryVirtualBaseOffsets(Base, Offset);
}
CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) {
@@ -1461,8 +1647,8 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) {
Context.getTargetInfo().getCharAlign()));
NonVirtualAlignment = Alignment;
- if (isMicrosoftCXXABI() &&
- NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) {
+ if (isMicrosoftCXXABI()) {
+ if (NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) {
CharUnits AlignMember =
NonVirtualSize.RoundUpToAlignment(Alignment) - NonVirtualSize;
@@ -1472,9 +1658,9 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) {
NonVirtualSize = Context.toCharUnitsFromBits(
llvm::RoundUpToAlignment(getSizeInBits(),
Context.getTargetInfo().getCharAlign()));
+ }
MSLayoutVirtualBases(RD);
-
} else {
// Lay out the virtual bases and add the primary virtual base offsets.
LayoutVirtualBases(RD, RD);
@@ -1540,7 +1726,7 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
for (RecordDecl::field_iterator Field = D->field_begin(),
FieldEnd = D->field_end(); Field != FieldEnd; ++Field) {
if (IsMsStruct) {
- FieldDecl *FD = (*Field);
+ FieldDecl *FD = *Field;
if (Context.ZeroBitfieldFollowsBitfield(FD, LastFD))
ZeroLengthBitfield = FD;
// Zero-length bitfields following non-bitfield members are
@@ -1635,9 +1821,8 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
}
else if (!Context.getTargetInfo().useBitFieldTypeAlignment() &&
Context.getTargetInfo().useZeroLengthBitfieldAlignment()) {
- FieldDecl *FD = (*Field);
- if (FD->isBitField() && FD->getBitWidthValue(Context) == 0)
- ZeroLengthBitfield = FD;
+ if (Field->isBitField() && Field->getBitWidthValue(Context) == 0)
+ ZeroLengthBitfield = *Field;
}
LayoutField(*Field);
}
@@ -2166,6 +2351,10 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) {
if (MD->hasInlineBody())
continue;
+ // Ignore inline deleted or defaulted functions.
+ if (!MD->isUserProvided())
+ continue;
+
// We found it.
return MD;
}
@@ -2238,7 +2427,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
NewEntry =
new (*this) ASTRecordLayout(*this, Builder.getSize(),
Builder.Alignment,
- Builder.VFPtrOffset,
+ Builder.HasOwnVFPtr,
Builder.VBPtrOffset,
DataSize,
Builder.FieldOffsets.data(),
@@ -2375,7 +2564,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS,
IndentLevel++;
const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase();
- bool HasVfptr = Layout.getVFPtrOffset() != CharUnits::fromQuantity(-1);
+ bool HasVfptr = Layout.hasOwnVFPtr();
bool HasVbptr = Layout.getVBPtrOffset() != CharUnits::fromQuantity(-1);
// Vtable pointer.
@@ -2405,7 +2594,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS,
// vfptr and vbptr (for Microsoft C++ ABI)
if (HasVfptr) {
- PrintOffset(OS, Offset + Layout.getVFPtrOffset(), IndentLevel);
+ PrintOffset(OS, Offset, IndentLevel);
OS << '(' << *RD << " vftable pointer)\n";
}
if (HasVbptr) {
@@ -2417,27 +2606,29 @@ static void DumpCXXRecordLayout(raw_ostream &OS,
uint64_t FieldNo = 0;
for (CXXRecordDecl::field_iterator I = RD->field_begin(),
E = RD->field_end(); I != E; ++I, ++FieldNo) {
- const FieldDecl *Field = *I;
+ const FieldDecl &Field = **I;
CharUnits FieldOffset = Offset +
C.toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
- if (const RecordType *RT = Field->getType()->getAs<RecordType>()) {
+ if (const RecordType *RT = Field.getType()->getAs<RecordType>()) {
if (const CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
DumpCXXRecordLayout(OS, D, C, FieldOffset, IndentLevel,
- Field->getName().data(),
+ Field.getName().data(),
/*IncludeVirtualBases=*/true);
continue;
}
}
PrintOffset(OS, FieldOffset, IndentLevel);
- OS << Field->getType().getAsString() << ' ' << *Field << '\n';
+ OS << Field.getType().getAsString() << ' ' << Field << '\n';
}
if (!IncludeVirtualBases)
return;
// Dump virtual bases.
+ const ASTRecordLayout::VBaseOffsetsMapTy &vtordisps =
+ Layout.getVBaseOffsetsMap();
for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(),
E = RD->vbases_end(); I != E; ++I) {
assert(I->isVirtual() && "Found non-virtual class!");
@@ -2445,6 +2636,12 @@ static void DumpCXXRecordLayout(raw_ostream &OS,
cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl());
CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBase);
+
+ if (vtordisps.find(VBase)->second.hasVtorDisp()) {
+ PrintOffset(OS, VBaseOffset - CharUnits::fromQuantity(4), IndentLevel);
+ OS << "(vtordisp for vbase " << *VBase << ")\n";
+ }
+
DumpCXXRecordLayout(OS, VBase, C, VBaseOffset, IndentLevel,
VBase == PrimaryBase ?
"(primary virtual base)" : "(virtual base)",
diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp
index e4d9f0a..d877c3f 100644
--- a/lib/AST/Stmt.cpp
+++ b/lib/AST/Stmt.cpp
@@ -244,6 +244,22 @@ SourceLocation Stmt::getLocEnd() const {
llvm_unreachable("unknown statement kind");
}
+CompoundStmt::CompoundStmt(ASTContext &C, Stmt **StmtStart, unsigned NumStmts,
+ SourceLocation LB, SourceLocation RB)
+ : Stmt(CompoundStmtClass), LBracLoc(LB), RBracLoc(RB) {
+ CompoundStmtBits.NumStmts = NumStmts;
+ assert(CompoundStmtBits.NumStmts == NumStmts &&
+ "NumStmts doesn't fit in bits of CompoundStmtBits.NumStmts!");
+
+ if (NumStmts == 0) {
+ Body = 0;
+ return;
+ }
+
+ Body = new (C) Stmt*[NumStmts];
+ memcpy(Body, StmtStart, NumStmts * sizeof(*Body));
+}
+
void CompoundStmt::setStmts(ASTContext &C, Stmt **Stmts, unsigned NumStmts) {
if (this->Body)
C.Deallocate(Body);
@@ -257,6 +273,23 @@ const char *LabelStmt::getName() const {
return getDecl()->getIdentifier()->getNameStart();
}
+AttributedStmt *AttributedStmt::Create(ASTContext &C, SourceLocation Loc,
+ ArrayRef<const Attr*> Attrs,
+ Stmt *SubStmt) {
+ void *Mem = C.Allocate(sizeof(AttributedStmt) +
+ sizeof(Attr*) * (Attrs.size() - 1),
+ llvm::alignOf<AttributedStmt>());
+ return new (Mem) AttributedStmt(Loc, Attrs, SubStmt);
+}
+
+AttributedStmt *AttributedStmt::CreateEmpty(ASTContext &C, unsigned NumAttrs) {
+ assert(NumAttrs > 0 && "NumAttrs should be greater than zero");
+ void *Mem = C.Allocate(sizeof(AttributedStmt) +
+ sizeof(Attr*) * (NumAttrs - 1),
+ llvm::alignOf<AttributedStmt>());
+ return new (Mem) AttributedStmt(EmptyShell(), NumAttrs);
+}
+
// This is defined here to avoid polluting Stmt.h with importing Expr.h
SourceRange ReturnStmt::getSourceRange() const {
if (RetExpr)
@@ -328,7 +361,7 @@ void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C,
StringLiteral **Constraints,
Stmt **Exprs,
unsigned NumOutputs,
- unsigned NumInputs,
+ unsigned NumInputs,
StringLiteral **Clobbers,
unsigned NumClobbers) {
this->NumOutputs = NumOutputs;
@@ -336,19 +369,19 @@ void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C,
this->NumClobbers = NumClobbers;
unsigned NumExprs = NumOutputs + NumInputs;
-
+
C.Deallocate(this->Names);
this->Names = new (C) IdentifierInfo*[NumExprs];
std::copy(Names, Names + NumExprs, this->Names);
-
+
C.Deallocate(this->Exprs);
this->Exprs = new (C) Stmt*[NumExprs];
std::copy(Exprs, Exprs + NumExprs, this->Exprs);
-
+
C.Deallocate(this->Constraints);
this->Constraints = new (C) StringLiteral*[NumExprs];
std::copy(Constraints, Constraints + NumExprs, this->Constraints);
-
+
C.Deallocate(this->Clobbers);
this->Clobbers = new (C) StringLiteral*[NumClobbers];
std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers);
@@ -407,7 +440,7 @@ unsigned AsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
std::string CurStringPiece;
bool HasVariants = !C.getTargetInfo().hasNoAsmVariants();
-
+
while (1) {
// Done with the string?
if (CurPtr == StrEnd) {
@@ -428,7 +461,7 @@ unsigned AsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces,
CurStringPiece += CurChar;
continue;
}
-
+
// Escaped "%" character in asm string.
if (CurPtr == StrEnd) {
// % at end of string is invalid (no escape).
@@ -525,8 +558,8 @@ QualType CXXCatchStmt::getCaughtType() const {
// Constructors
//===----------------------------------------------------------------------===//
-AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple,
- bool isvolatile, bool msasm,
+AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple,
+ bool isvolatile, bool msasm,
unsigned numoutputs, unsigned numinputs,
IdentifierInfo **names, StringLiteral **constraints,
Expr **exprs, StringLiteral *asmstr, unsigned numclobbers,
@@ -535,8 +568,8 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple,
, IsSimple(issimple), IsVolatile(isvolatile), MSAsm(msasm)
, NumOutputs(numoutputs), NumInputs(numinputs), NumClobbers(numclobbers) {
- unsigned NumExprs = NumOutputs +NumInputs;
-
+ unsigned NumExprs = NumOutputs + NumInputs;
+
Names = new (C) IdentifierInfo*[NumExprs];
std::copy(names, names + NumExprs, Names);
@@ -550,6 +583,33 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple,
std::copy(clobbers, clobbers + NumClobbers, Clobbers);
}
+MSAsmStmt::MSAsmStmt(ASTContext &C, SourceLocation asmloc,
+ bool issimple, bool isvolatile, ArrayRef<Token> asmtoks,
+ ArrayRef<unsigned> lineends, StringRef asmstr,
+ ArrayRef<StringRef> clobbers, SourceLocation endloc)
+ : Stmt(MSAsmStmtClass), AsmLoc(asmloc), EndLoc(endloc),
+ AsmStr(asmstr.str()), IsSimple(issimple), IsVolatile(isvolatile),
+ NumAsmToks(asmtoks.size()), NumLineEnds(lineends.size()),
+ NumClobbers(clobbers.size()) {
+
+ AsmToks = new (C) Token[NumAsmToks];
+ for (unsigned i = 0, e = NumAsmToks; i != e; ++i)
+ AsmToks[i] = asmtoks[i];
+
+ LineEnds = new (C) unsigned[NumLineEnds];
+ for (unsigned i = 0, e = NumLineEnds; i != e; ++i)
+ LineEnds[i] = lineends[i];
+
+ Clobbers = new (C) StringRef[NumClobbers];
+ for (unsigned i = 0, e = NumClobbers; i != e; ++i) {
+ // FIXME: Avoid the allocation/copy if at all possible.
+ size_t size = clobbers[i].size();
+ char *dest = new (C) char[size];
+ std::strncpy(dest, clobbers[i].data(), size);
+ Clobbers[i] = StringRef(dest, size);
+ }
+}
+
ObjCForCollectionStmt::ObjCForCollectionStmt(Stmt *Elem, Expr *Collect,
Stmt *Body, SourceLocation FCL,
SourceLocation RPL)
@@ -571,31 +631,31 @@ ObjCAtTryStmt::ObjCAtTryStmt(SourceLocation atTryLoc, Stmt *atTryStmt,
Stmts[0] = atTryStmt;
for (unsigned I = 0; I != NumCatchStmts; ++I)
Stmts[I + 1] = CatchStmts[I];
-
+
if (HasFinally)
Stmts[NumCatchStmts + 1] = atFinallyStmt;
}
-ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context,
- SourceLocation atTryLoc,
+ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context,
+ SourceLocation atTryLoc,
Stmt *atTryStmt,
- Stmt **CatchStmts,
+ Stmt **CatchStmts,
unsigned NumCatchStmts,
Stmt *atFinallyStmt) {
- unsigned Size = sizeof(ObjCAtTryStmt) +
+ unsigned Size = sizeof(ObjCAtTryStmt) +
(1 + NumCatchStmts + (atFinallyStmt != 0)) * sizeof(Stmt *);
void *Mem = Context.Allocate(Size, llvm::alignOf<ObjCAtTryStmt>());
return new (Mem) ObjCAtTryStmt(atTryLoc, atTryStmt, CatchStmts, NumCatchStmts,
atFinallyStmt);
}
-ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context,
+ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context,
unsigned NumCatchStmts,
bool HasFinally) {
- unsigned Size = sizeof(ObjCAtTryStmt) +
+ unsigned Size = sizeof(ObjCAtTryStmt) +
(1 + NumCatchStmts + HasFinally) * sizeof(Stmt *);
void *Mem = Context.Allocate(Size, llvm::alignOf<ObjCAtTryStmt>());
- return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally);
+ return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally);
}
SourceRange ObjCAtTryStmt::getSourceRange() const {
@@ -606,12 +666,12 @@ SourceRange ObjCAtTryStmt::getSourceRange() const {
EndLoc = getCatchStmt(NumCatchStmts - 1)->getLocEnd();
else
EndLoc = getTryBody()->getLocEnd();
-
+
return SourceRange(AtTryLoc, EndLoc);
}
CXXTryStmt *CXXTryStmt::Create(ASTContext &C, SourceLocation tryLoc,
- Stmt *tryBlock, Stmt **handlers,
+ Stmt *tryBlock, Stmt **handlers,
unsigned numHandlers) {
std::size_t Size = sizeof(CXXTryStmt);
Size += ((numHandlers + 1) * sizeof(Stmt));
@@ -671,20 +731,20 @@ const VarDecl *CXXForRangeStmt::getLoopVariable() const {
return const_cast<CXXForRangeStmt*>(this)->getLoopVariable();
}
-IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond,
+IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond,
Stmt *then, SourceLocation EL, Stmt *elsev)
: Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL)
{
setConditionVariable(C, var);
SubExprs[COND] = reinterpret_cast<Stmt*>(cond);
SubExprs[THEN] = then;
- SubExprs[ELSE] = elsev;
+ SubExprs[ELSE] = elsev;
}
VarDecl *IfStmt::getConditionVariable() const {
if (!SubExprs[VAR])
return 0;
-
+
DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]);
return cast<VarDecl>(DS->getSingleDecl());
}
@@ -694,16 +754,16 @@ void IfStmt::setConditionVariable(ASTContext &C, VarDecl *V) {
SubExprs[VAR] = 0;
return;
}
-
+
SourceRange VarRange = V->getSourceRange();
SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(),
VarRange.getEnd());
}
-ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar,
- Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP,
+ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar,
+ Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP,
SourceLocation RP)
- : Stmt(ForStmtClass), ForLoc(FL), LParenLoc(LP), RParenLoc(RP)
+ : Stmt(ForStmtClass), ForLoc(FL), LParenLoc(LP), RParenLoc(RP)
{
SubExprs[INIT] = Init;
setConditionVariable(C, condVar);
@@ -715,7 +775,7 @@ ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar,
VarDecl *ForStmt::getConditionVariable() const {
if (!SubExprs[CONDVAR])
return 0;
-
+
DeclStmt *DS = cast<DeclStmt>(SubExprs[CONDVAR]);
return cast<VarDecl>(DS->getSingleDecl());
}
@@ -725,14 +785,14 @@ void ForStmt::setConditionVariable(ASTContext &C, VarDecl *V) {
SubExprs[CONDVAR] = 0;
return;
}
-
+
SourceRange VarRange = V->getSourceRange();
SubExprs[CONDVAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(),
VarRange.getEnd());
}
-SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond)
- : Stmt(SwitchStmtClass), FirstCase(0), AllEnumCasesCovered(0)
+SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond)
+ : Stmt(SwitchStmtClass), FirstCase(0), AllEnumCasesCovered(0)
{
setConditionVariable(C, Var);
SubExprs[COND] = reinterpret_cast<Stmt*>(cond);
@@ -742,7 +802,7 @@ SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond)
VarDecl *SwitchStmt::getConditionVariable() const {
if (!SubExprs[VAR])
return 0;
-
+
DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]);
return cast<VarDecl>(DS->getSingleDecl());
}
@@ -752,7 +812,7 @@ void SwitchStmt::setConditionVariable(ASTContext &C, VarDecl *V) {
SubExprs[VAR] = 0;
return;
}
-
+
SourceRange VarRange = V->getSourceRange();
SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(),
VarRange.getEnd());
@@ -764,7 +824,7 @@ Stmt *SwitchCase::getSubStmt() {
return cast<DefaultStmt>(this)->getSubStmt();
}
-WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body,
+WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body,
SourceLocation WL)
: Stmt(WhileStmtClass) {
setConditionVariable(C, Var);
@@ -776,7 +836,7 @@ WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body,
VarDecl *WhileStmt::getConditionVariable() const {
if (!SubExprs[VAR])
return 0;
-
+
DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]);
return cast<VarDecl>(DS->getSingleDecl());
}
diff --git a/lib/AST/StmtDumper.cpp b/lib/AST/StmtDumper.cpp
index b5e298c..962e352 100644
--- a/lib/AST/StmtDumper.cpp
+++ b/lib/AST/StmtDumper.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/PrettyPrinter.h"
@@ -166,6 +167,7 @@ namespace {
void VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node);
void VisitObjCEncodeExpr(ObjCEncodeExpr *Node);
void VisitObjCMessageExpr(ObjCMessageExpr* Node);
+ void VisitObjCBoxedExpr(ObjCBoxedExpr* Node);
void VisitObjCSelectorExpr(ObjCSelectorExpr *Node);
void VisitObjCProtocolExpr(ObjCProtocolExpr *Node);
void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node);
@@ -423,6 +425,7 @@ void StmtDumper::VisitPredefinedExpr(PredefinedExpr *Node) {
default: llvm_unreachable("unknown case");
case PredefinedExpr::Func: OS << " __func__"; break;
case PredefinedExpr::Function: OS << " __FUNCTION__"; break;
+ case PredefinedExpr::LFunction: OS << " L__FUNCTION__"; break;
case PredefinedExpr::PrettyFunction: OS << " __PRETTY_FUNCTION__";break;
}
}
@@ -445,18 +448,8 @@ void StmtDumper::VisitFloatingLiteral(FloatingLiteral *Node) {
void StmtDumper::VisitStringLiteral(StringLiteral *Str) {
DumpExpr(Str);
- // FIXME: this doesn't print wstrings right.
OS << " ";
- switch (Str->getKind()) {
- case StringLiteral::Ascii: break; // No prefix
- case StringLiteral::Wide: OS << 'L'; break;
- case StringLiteral::UTF8: OS << "u8"; break;
- case StringLiteral::UTF16: OS << 'u'; break;
- case StringLiteral::UTF32: OS << 'U'; break;
- }
- OS << '"';
- OS.write_escaped(Str->getString());
- OS << '"';
+ Str->outputString(OS);
}
void StmtDumper::VisitUnaryOperator(UnaryOperator *Node) {
@@ -471,7 +464,7 @@ void StmtDumper::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) {
OS << " sizeof ";
break;
case UETT_AlignOf:
- OS << " __alignof ";
+ OS << " alignof ";
break;
case UETT_VecStep:
OS << " vec_step ";
@@ -637,6 +630,11 @@ void StmtDumper::VisitObjCMessageExpr(ObjCMessageExpr* Node) {
}
}
+void StmtDumper::VisitObjCBoxedExpr(ObjCBoxedExpr* Node) {
+ DumpExpr(Node);
+ OS << " selector=" << Node->getBoxingMethod()->getSelector().getAsString();
+}
+
void StmtDumper::VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node) {
DumpStmt(Node);
if (VarDecl *CatchParam = Node->getCatchParamDecl()) {
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 0d1066b..2f7cb55 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
@@ -172,9 +173,9 @@ void StmtPrinter::VisitLabelStmt(LabelStmt *Node) {
void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) {
OS << "[[";
bool first = true;
- for (AttrVec::const_iterator it = Node->getAttrs().begin(),
- end = Node->getAttrs().end();
- it != end; ++it) {
+ for (ArrayRef<const Attr*>::iterator it = Node->getAttrs().begin(),
+ end = Node->getAttrs().end();
+ it != end; ++it) {
if (!first) {
OS << ", ";
first = false;
@@ -429,6 +430,11 @@ void StmtPrinter::VisitAsmStmt(AsmStmt *Node) {
OS << ");\n";
}
+void StmtPrinter::VisitMSAsmStmt(MSAsmStmt *Node) {
+ // FIXME: Implement MS style inline asm statement printer.
+ Indent() << "asm ()";
+}
+
void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) {
Indent() << "@try";
if (CompoundStmt *TS = dyn_cast<CompoundStmt>(Node->getTryBody())) {
@@ -638,6 +644,9 @@ void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
case PredefinedExpr::Function:
OS << "__FUNCTION__";
break;
+ case PredefinedExpr::LFunction:
+ OS << "L__FUNCTION__";
+ break;
case PredefinedExpr::PrettyFunction:
OS << "__PRETTY_FUNCTION__";
break;
@@ -734,93 +743,7 @@ void StmtPrinter::VisitImaginaryLiteral(ImaginaryLiteral *Node) {
}
void StmtPrinter::VisitStringLiteral(StringLiteral *Str) {
- switch (Str->getKind()) {
- case StringLiteral::Ascii: break; // no prefix.
- case StringLiteral::Wide: OS << 'L'; break;
- case StringLiteral::UTF8: OS << "u8"; break;
- case StringLiteral::UTF16: OS << 'u'; break;
- case StringLiteral::UTF32: OS << 'U'; break;
- }
- OS << '"';
- static char Hex[] = "0123456789ABCDEF";
-
- unsigned LastSlashX = Str->getLength();
- for (unsigned I = 0, N = Str->getLength(); I != N; ++I) {
- switch (uint32_t Char = Str->getCodeUnit(I)) {
- default:
- // FIXME: Convert UTF-8 back to codepoints before rendering.
-
- // Convert UTF-16 surrogate pairs back to codepoints before rendering.
- // Leave invalid surrogates alone; we'll use \x for those.
- if (Str->getKind() == StringLiteral::UTF16 && I != N - 1 &&
- Char >= 0xd800 && Char <= 0xdbff) {
- uint32_t Trail = Str->getCodeUnit(I + 1);
- if (Trail >= 0xdc00 && Trail <= 0xdfff) {
- Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00);
- ++I;
- }
- }
-
- if (Char > 0xff) {
- // If this is a wide string, output characters over 0xff using \x
- // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a
- // codepoint: use \x escapes for invalid codepoints.
- if (Str->getKind() == StringLiteral::Wide ||
- (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) {
- // FIXME: Is this the best way to print wchar_t?
- OS << "\\x";
- int Shift = 28;
- while ((Char >> Shift) == 0)
- Shift -= 4;
- for (/**/; Shift >= 0; Shift -= 4)
- OS << Hex[(Char >> Shift) & 15];
- LastSlashX = I;
- break;
- }
-
- if (Char > 0xffff)
- OS << "\\U00"
- << Hex[(Char >> 20) & 15]
- << Hex[(Char >> 16) & 15];
- else
- OS << "\\u";
- OS << Hex[(Char >> 12) & 15]
- << Hex[(Char >> 8) & 15]
- << Hex[(Char >> 4) & 15]
- << Hex[(Char >> 0) & 15];
- break;
- }
-
- // If we used \x... for the previous character, and this character is a
- // hexadecimal digit, prevent it being slurped as part of the \x.
- if (LastSlashX + 1 == I) {
- switch (Char) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- OS << "\"\"";
- }
- }
-
- if (Char <= 0xff && isprint(Char))
- OS << (char)Char;
- else // Output anything hard as an octal escape.
- OS << '\\'
- << (char)('0' + ((Char >> 6) & 7))
- << (char)('0' + ((Char >> 3) & 7))
- << (char)('0' + ((Char >> 0) & 7));
- break;
- // Handle some common non-printable cases to make dumps prettier.
- case '\\': OS << "\\\\"; break;
- case '"': OS << "\\\""; break;
- case '\n': OS << "\\n"; break;
- case '\t': OS << "\\t"; break;
- case '\a': OS << "\\a"; break;
- case '\b': OS << "\\b"; break;
- }
- }
- OS << '"';
+ Str->outputString(OS);
}
void StmtPrinter::VisitParenExpr(ParenExpr *Node) {
OS << "(";
@@ -892,7 +815,12 @@ void StmtPrinter::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node){
OS << "sizeof";
break;
case UETT_AlignOf:
- OS << "__alignof";
+ if (Policy.LangOpts.CPlusPlus)
+ OS << "alignof";
+ else if (Policy.LangOpts.C11)
+ OS << "_Alignof";
+ else
+ OS << "__alignof";
break;
case UETT_VecStep:
OS << "vec_step";
@@ -1275,7 +1203,7 @@ void StmtPrinter::VisitUserDefinedLiteral(UserDefinedLiteral *Node) {
const TemplateArgument &Pack = Args->get(0);
for (TemplateArgument::pack_iterator I = Pack.pack_begin(),
E = Pack.pack_end(); I != E; ++I) {
- char C = (char)I->getAsIntegral()->getZExtValue();
+ char C = (char)I->getAsIntegral().getZExtValue();
OS << C;
}
break;
@@ -1727,9 +1655,9 @@ void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) {
VisitStringLiteral(Node->getString());
}
-void StmtPrinter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) {
+void StmtPrinter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) {
OS << "@";
- Visit(E->getNumber());
+ Visit(E->getSubExpr());
}
void StmtPrinter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) {
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index e50523a..2168b64 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -178,6 +178,11 @@ void StmtProfiler::VisitAsmStmt(const AsmStmt *S) {
VisitStringLiteral(S->getClobber(I));
}
+void StmtProfiler::VisitMSAsmStmt(const MSAsmStmt *S) {
+ // FIXME: Implement MS style inline asm statement profiler.
+ VisitStmt(S);
+}
+
void StmtProfiler::VisitCXXCatchStmt(const CXXCatchStmt *S) {
VisitStmt(S);
VisitType(S->getCaughtType());
@@ -981,7 +986,7 @@ void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) {
VisitExpr(S);
}
-void StmtProfiler::VisitObjCNumericLiteral(const ObjCNumericLiteral *E) {
+void StmtProfiler::VisitObjCBoxedExpr(const ObjCBoxedExpr *E) {
VisitExpr(E);
}
@@ -1161,7 +1166,7 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) {
break;
case TemplateArgument::Integral:
- Arg.getAsIntegral()->Profile(ID);
+ Arg.getAsIntegral().Profile(ID);
VisitType(Arg.getIntegralType());
break;
diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp
index 531e03e..f8dd396 100644
--- a/lib/AST/TemplateBase.cpp
+++ b/lib/AST/TemplateBase.cpp
@@ -36,17 +36,17 @@ using namespace clang;
static void printIntegral(const TemplateArgument &TemplArg,
raw_ostream &Out) {
const ::clang::Type *T = TemplArg.getIntegralType().getTypePtr();
- const llvm::APSInt *Val = TemplArg.getAsIntegral();
+ const llvm::APSInt &Val = TemplArg.getAsIntegral();
if (T->isBooleanType()) {
- Out << (Val->getBoolValue() ? "true" : "false");
+ Out << (Val.getBoolValue() ? "true" : "false");
} else if (T->isCharType()) {
- const char Ch = Val->getZExtValue();
+ const char Ch = Val.getZExtValue();
Out << ((Ch == '\'') ? "'\\" : "'");
Out.write_escaped(StringRef(&Ch, 1), /*UseHexEscapes=*/ true);
Out << "'";
} else {
- Out << Val->toString(10);
+ Out << Val;
}
}
@@ -54,6 +54,25 @@ static void printIntegral(const TemplateArgument &TemplArg,
// TemplateArgument Implementation
//===----------------------------------------------------------------------===//
+TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value,
+ QualType Type)
+ : Kind(Integral) {
+ // Copy the APSInt value into our decomposed form.
+ Integer.BitWidth = Value.getBitWidth();
+ Integer.IsUnsigned = Value.isUnsigned();
+ // If the value is large, we have to get additional memory from the ASTContext
+ unsigned NumWords = Value.getNumWords();
+ if (NumWords > 1) {
+ void *Mem = Ctx.Allocate(NumWords * sizeof(uint64_t));
+ std::memcpy(Mem, Value.getRawData(), NumWords * sizeof(uint64_t));
+ Integer.pVal = static_cast<uint64_t *>(Mem);
+ } else {
+ Integer.VAL = Value.getZExtValue();
+ }
+
+ Integer.Type = Type.getAsOpaquePtr();
+}
+
TemplateArgument TemplateArgument::CreatePackCopy(ASTContext &Context,
const TemplateArgument *Args,
unsigned NumArgs) {
@@ -246,7 +265,7 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
}
case Integral:
- getAsIntegral()->Profile(ID);
+ getAsIntegral().Profile(ID);
getIntegralType().Profile(ID);
break;
@@ -275,7 +294,7 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const {
case Integral:
return getIntegralType() == Other.getIntegralType() &&
- *getAsIntegral() == *Other.getAsIntegral();
+ getAsIntegral() == Other.getAsIntegral();
case Pack:
if (Args.NumArgs != Other.Args.NumArgs) return false;
@@ -498,7 +517,7 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB,
return DB << "nullptr";
case TemplateArgument::Integral:
- return DB << Arg.getAsIntegral()->toString(10);
+ return DB << Arg.getAsIntegral().toString(10);
case TemplateArgument::Template:
return DB << Arg.getAsTemplate();
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 3f6a094..abefae4 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -288,6 +288,28 @@ QualType QualType::IgnoreParens(QualType T) {
return T;
}
+/// \brief This will check for a TypedefType by removing any existing sugar
+/// until it reaches a TypedefType or a non-sugared type.
+template <> const TypedefType *Type::getAs() const {
+ const Type *Cur = this;
+
+ while (true) {
+ if (const TypedefType *TDT = dyn_cast<TypedefType>(Cur))
+ return TDT;
+ switch (Cur->getTypeClass()) {
+#define ABSTRACT_TYPE(Class, Parent)
+#define TYPE(Class, Parent) \
+ case Class: { \
+ const Class##Type *Ty = cast<Class##Type>(Cur); \
+ if (!Ty->isSugared()) return 0; \
+ Cur = Ty->desugar().getTypePtr(); \
+ break; \
+ }
+#include "clang/AST/TypeNodes.def"
+ }
+ }
+}
+
/// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic
/// sugar off the given type. This should produce an object of the
/// same dynamic type as the canonical type.
@@ -895,6 +917,14 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
}
bool QualType::isPODType(ASTContext &Context) const {
+ // C++11 has a more relaxed definition of POD.
+ if (Context.getLangOpts().CPlusPlus0x)
+ return isCXX11PODType(Context);
+
+ return isCXX98PODType(Context);
+}
+
+bool QualType::isCXX98PODType(ASTContext &Context) const {
// The compiler shouldn't query this for incomplete types, but the user might.
// We return false for that case. Except for incomplete arrays of PODs, which
// are PODs according to the standard.
@@ -902,7 +932,7 @@ bool QualType::isPODType(ASTContext &Context) const {
return 0;
if ((*this)->isIncompleteArrayType())
- return Context.getBaseElementType(*this).isPODType(Context);
+ return Context.getBaseElementType(*this).isCXX98PODType(Context);
if ((*this)->isIncompleteType())
return false;
@@ -929,7 +959,7 @@ bool QualType::isPODType(ASTContext &Context) const {
case Type::VariableArray:
case Type::ConstantArray:
// IncompleteArray is handled above.
- return Context.getBaseElementType(*this).isPODType(Context);
+ return Context.getBaseElementType(*this).isCXX98PODType(Context);
case Type::ObjCObjectPointer:
case Type::BlockPointer:
@@ -1417,7 +1447,7 @@ const char *Type::getTypeClassName() const {
llvm_unreachable("Invalid type class.");
}
-const char *BuiltinType::getName(const PrintingPolicy &Policy) const {
+StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
switch (getKind()) {
case Void: return "void";
case Bool: return Policy.Bool ? "bool" : "_Bool";
@@ -1554,6 +1584,11 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
slot[1] = epi.ExceptionSpecTemplate;
// This exception specification doesn't make the type dependent, because
// it's not instantiated as part of instantiating the type.
+ } else if (getExceptionSpecType() == EST_Unevaluated) {
+ // Store the function decl from which we will resolve our
+ // exception specification.
+ FunctionDecl **slot = reinterpret_cast<FunctionDecl**>(argSlot + numArgs);
+ slot[0] = epi.ExceptionSpecDecl;
}
if (epi.ConsumedArguments) {
@@ -1637,7 +1672,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr());
} else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){
epi.NoexceptExpr->Profile(ID, Context, false);
- } else if (epi.ExceptionSpecType == EST_Uninstantiated) {
+ } else if (epi.ExceptionSpecType == EST_Uninstantiated ||
+ epi.ExceptionSpecType == EST_Unevaluated) {
ID.AddPointer(epi.ExceptionSpecDecl->getCanonicalDecl());
}
if (epi.ConsumedArguments) {
@@ -1832,8 +1868,7 @@ TemplateSpecializationType(TemplateName T,
Canon.isNull()? T.isDependent()
: Canon->isInstantiationDependentType(),
false,
- Canon.isNull()? T.containsUnexpandedParameterPack()
- : Canon->containsUnexpandedParameterPack()),
+ T.containsUnexpandedParameterPack()),
Template(T), NumArgs(NumArgs), TypeAlias(!AliasedType.isNull()) {
assert(!T.getAsDependentTemplateName() &&
"Use DependentTemplateSpecializationType for dependent template-name");
@@ -1858,6 +1893,8 @@ TemplateSpecializationType(TemplateName T,
// arguments is. Given:
// template<typename T> using U = int;
// U<T> is always non-dependent, irrespective of the type T.
+ // However, U<Ts> contains an unexpanded parameter pack, even though
+ // its expansion (and thus its desugared type) doesn't.
if (Canon.isNull() && Args[Arg].isDependent())
setDependent();
else if (Args[Arg].isInstantiationDependent())
@@ -1866,7 +1903,7 @@ TemplateSpecializationType(TemplateName T,
if (Args[Arg].getKind() == TemplateArgument::Type &&
Args[Arg].getAsType()->isVariablyModifiedType())
setVariablyModified();
- if (Canon.isNull() && Args[Arg].containsUnexpandedParameterPack())
+ if (Args[Arg].containsUnexpandedParameterPack())
setContainsUnexpandedParameterPack();
new (&TemplateArgs[Arg]) TemplateArgument(Args[Arg]);
diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp
index caa19b1..c7bb7da 100644
--- a/lib/AST/TypeLoc.cpp
+++ b/lib/AST/TypeLoc.cpp
@@ -13,6 +13,7 @@
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/TypeLocVisitor.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang;
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index 3bf80e7..c42117c 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@@ -19,8 +20,10 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
namespace {
@@ -40,62 +43,124 @@ namespace {
Policy.SuppressStrongLifetime = Old;
}
};
+
+ class ParamPolicyRAII {
+ PrintingPolicy &Policy;
+ bool Old;
+
+ public:
+ explicit ParamPolicyRAII(PrintingPolicy &Policy)
+ : Policy(Policy), Old(Policy.SuppressSpecifiers) {
+ Policy.SuppressSpecifiers = false;
+ }
+
+ ~ParamPolicyRAII() {
+ Policy.SuppressSpecifiers = Old;
+ }
+ };
+
+ class ElaboratedTypePolicyRAII {
+ PrintingPolicy &Policy;
+ bool SuppressTagKeyword;
+ bool SuppressScope;
+
+ public:
+ explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) {
+ SuppressTagKeyword = Policy.SuppressTagKeyword;
+ SuppressScope = Policy.SuppressScope;
+ Policy.SuppressTagKeyword = true;
+ Policy.SuppressScope = true;
+ }
+
+ ~ElaboratedTypePolicyRAII() {
+ Policy.SuppressTagKeyword = SuppressTagKeyword;
+ Policy.SuppressScope = SuppressScope;
+ }
+ };
class TypePrinter {
PrintingPolicy Policy;
+ bool HasEmptyPlaceHolder;
public:
- explicit TypePrinter(const PrintingPolicy &Policy) : Policy(Policy) { }
-
- void print(const Type *ty, Qualifiers qs, std::string &buffer);
- void print(QualType T, std::string &S);
- void AppendScope(DeclContext *DC, std::string &S);
- void printTag(TagDecl *T, std::string &S);
+ explicit TypePrinter(const PrintingPolicy &Policy)
+ : Policy(Policy), HasEmptyPlaceHolder(false) { }
+
+ void print(const Type *ty, Qualifiers qs, raw_ostream &OS,
+ StringRef PlaceHolder);
+ void print(QualType T, raw_ostream &OS, StringRef PlaceHolder);
+
+ static bool canPrefixQualifiers(const Type *T, bool &NeedARCStrongQualifier);
+ void spaceBeforePlaceHolder(raw_ostream &OS);
+ void printTypeSpec(const NamedDecl *D, raw_ostream &OS);
+
+ void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS);
+ void printBefore(QualType T, raw_ostream &OS);
+ void printAfter(const Type *ty, Qualifiers qs, raw_ostream &OS);
+ void printAfter(QualType T, raw_ostream &OS);
+ void AppendScope(DeclContext *DC, raw_ostream &OS);
+ void printTag(TagDecl *T, raw_ostream &OS);
#define ABSTRACT_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT) \
- void print##CLASS(const CLASS##Type *T, std::string &S);
+ void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \
+ void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS);
#include "clang/AST/TypeNodes.def"
};
}
-static void AppendTypeQualList(std::string &S, unsigned TypeQuals) {
+static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals) {
+ bool appendSpace = false;
if (TypeQuals & Qualifiers::Const) {
- if (!S.empty()) S += ' ';
- S += "const";
+ OS << "const";
+ appendSpace = true;
}
if (TypeQuals & Qualifiers::Volatile) {
- if (!S.empty()) S += ' ';
- S += "volatile";
+ if (appendSpace) OS << ' ';
+ OS << "volatile";
+ appendSpace = true;
}
if (TypeQuals & Qualifiers::Restrict) {
- if (!S.empty()) S += ' ';
- S += "restrict";
+ if (appendSpace) OS << ' ';
+ OS << "restrict";
}
}
-void TypePrinter::print(QualType t, std::string &buffer) {
+void TypePrinter::spaceBeforePlaceHolder(raw_ostream &OS) {
+ if (!HasEmptyPlaceHolder)
+ OS << ' ';
+}
+
+void TypePrinter::print(QualType t, raw_ostream &OS, StringRef PlaceHolder) {
SplitQualType split = t.split();
- print(split.Ty, split.Quals, buffer);
+ print(split.Ty, split.Quals, OS, PlaceHolder);
}
-void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) {
+void TypePrinter::print(const Type *T, Qualifiers Quals, raw_ostream &OS,
+ StringRef PlaceHolder) {
if (!T) {
- buffer += "NULL TYPE";
+ OS << "NULL TYPE";
return;
}
if (Policy.SuppressSpecifiers && T->isSpecifierType())
return;
-
- // Print qualifiers as appropriate.
-
+
+ SaveAndRestore<bool> PHVal(HasEmptyPlaceHolder, PlaceHolder.empty());
+
+ printBefore(T, Quals, OS);
+ OS << PlaceHolder;
+ printAfter(T, Quals, OS);
+}
+
+bool TypePrinter::canPrefixQualifiers(const Type *T,
+ bool &NeedARCStrongQualifier) {
// CanPrefixQualifiers - We prefer to print type qualifiers before the type,
// so that we get "const int" instead of "int const", but we can't do this if
// the type is complex. For example if the type is "int*", we *must* print
// "int * const", printing "const int *" is different. Only do this when the
// type expands to a simple string.
bool CanPrefixQualifiers = false;
- bool NeedARCStrongQualifier = false;
+ NeedARCStrongQualifier = false;
Type::TypeClass TC = T->getTypeClass();
if (const AutoType *AT = dyn_cast<AutoType>(T))
TC = AT->desugar()->getTypeClass();
@@ -157,493 +222,616 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) {
CanPrefixQualifiers = false;
break;
}
-
- if (!CanPrefixQualifiers && !Quals.empty()) {
- std::string qualsBuffer;
+
+ return CanPrefixQualifiers;
+}
+
+void TypePrinter::printBefore(QualType T, raw_ostream &OS) {
+ SplitQualType Split = T.split();
+
+ // If we have cv1 T, where T is substituted for cv2 U, only print cv1 - cv2
+ // at this level.
+ Qualifiers Quals = Split.Quals;
+ if (const SubstTemplateTypeParmType *Subst =
+ dyn_cast<SubstTemplateTypeParmType>(Split.Ty))
+ Quals -= QualType(Subst, 0).getQualifiers();
+
+ printBefore(Split.Ty, Quals, OS);
+}
+
+/// \brief Prints the part of the type string before an identifier, e.g. for
+/// "int foo[10]" it prints "int ".
+void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) {
+ if (Policy.SuppressSpecifiers && T->isSpecifierType())
+ return;
+
+ SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder);
+
+ // Print qualifiers as appropriate.
+
+ bool CanPrefixQualifiers = false;
+ bool NeedARCStrongQualifier = false;
+ CanPrefixQualifiers = canPrefixQualifiers(T, NeedARCStrongQualifier);
+
+ if (CanPrefixQualifiers && !Quals.empty()) {
if (NeedARCStrongQualifier) {
IncludeStrongLifetimeRAII Strong(Policy);
- Quals.getAsStringInternal(qualsBuffer, Policy);
+ Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/true);
} else {
- Quals.getAsStringInternal(qualsBuffer, Policy);
- }
-
- if (!qualsBuffer.empty()) {
- if (!buffer.empty()) {
- qualsBuffer += ' ';
- qualsBuffer += buffer;
- }
- std::swap(buffer, qualsBuffer);
+ Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/true);
}
}
-
+
+ bool hasAfterQuals = false;
+ if (!CanPrefixQualifiers && !Quals.empty()) {
+ hasAfterQuals = !Quals.isEmptyWhenPrinted(Policy);
+ if (hasAfterQuals)
+ HasEmptyPlaceHolder = false;
+ }
+
switch (T->getTypeClass()) {
#define ABSTRACT_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT) case Type::CLASS: \
- print##CLASS(cast<CLASS##Type>(T), buffer); \
+ print##CLASS##Before(cast<CLASS##Type>(T), OS); \
break;
#include "clang/AST/TypeNodes.def"
}
-
- // If we're adding the qualifiers as a prefix, do it now.
- if (CanPrefixQualifiers && !Quals.empty()) {
- std::string qualsBuffer;
+
+ if (hasAfterQuals) {
if (NeedARCStrongQualifier) {
IncludeStrongLifetimeRAII Strong(Policy);
- Quals.getAsStringInternal(qualsBuffer, Policy);
+ Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/!PrevPHIsEmpty.get());
} else {
- Quals.getAsStringInternal(qualsBuffer, Policy);
- }
-
- if (!qualsBuffer.empty()) {
- if (!buffer.empty()) {
- qualsBuffer += ' ';
- qualsBuffer += buffer;
- }
- std::swap(buffer, qualsBuffer);
+ Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/!PrevPHIsEmpty.get());
}
}
}
-void TypePrinter::printBuiltin(const BuiltinType *T, std::string &S) {
- if (S.empty()) {
- S = T->getName(Policy);
- } else {
- // Prefix the basic type, e.g. 'int X'.
- S = ' ' + S;
- S = T->getName(Policy) + S;
+void TypePrinter::printAfter(QualType t, raw_ostream &OS) {
+ SplitQualType split = t.split();
+ printAfter(split.Ty, split.Quals, OS);
+}
+
+/// \brief Prints the part of the type string after an identifier, e.g. for
+/// "int foo[10]" it prints "[10]".
+void TypePrinter::printAfter(const Type *T, Qualifiers Quals, raw_ostream &OS) {
+ switch (T->getTypeClass()) {
+#define ABSTRACT_TYPE(CLASS, PARENT)
+#define TYPE(CLASS, PARENT) case Type::CLASS: \
+ print##CLASS##After(cast<CLASS##Type>(T), OS); \
+ break;
+#include "clang/AST/TypeNodes.def"
}
}
-void TypePrinter::printComplex(const ComplexType *T, std::string &S) {
- print(T->getElementType(), S);
- S = "_Complex " + S;
+void TypePrinter::printBuiltinBefore(const BuiltinType *T, raw_ostream &OS) {
+ OS << T->getName(Policy);
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printBuiltinAfter(const BuiltinType *T, raw_ostream &OS) { }
-void TypePrinter::printPointer(const PointerType *T, std::string &S) {
- S = '*' + S;
-
+void TypePrinter::printComplexBefore(const ComplexType *T, raw_ostream &OS) {
+ OS << "_Complex ";
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printComplexAfter(const ComplexType *T, raw_ostream &OS) {
+ printAfter(T->getElementType(), OS);
+}
+
+void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getPointeeType(), OS);
// Handle things like 'int (*A)[4];' correctly.
// FIXME: this should include vectors, but vectors use attributes I guess.
if (isa<ArrayType>(T->getPointeeType()))
- S = '(' + S + ')';
-
+ OS << '(';
+ OS << '*';
+}
+void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getPointeeType(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ // Handle things like 'int (*A)[4];' correctly.
+ // FIXME: this should include vectors, but vectors use attributes I guess.
+ if (isa<ArrayType>(T->getPointeeType()))
+ OS << ')';
+ printAfter(T->getPointeeType(), OS);
}
-void TypePrinter::printBlockPointer(const BlockPointerType *T, std::string &S) {
- S = '^' + S;
- print(T->getPointeeType(), S);
+void TypePrinter::printBlockPointerBefore(const BlockPointerType *T,
+ raw_ostream &OS) {
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getPointeeType(), OS);
+ OS << '^';
+}
+void TypePrinter::printBlockPointerAfter(const BlockPointerType *T,
+ raw_ostream &OS) {
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printAfter(T->getPointeeType(), OS);
}
-void TypePrinter::printLValueReference(const LValueReferenceType *T,
- std::string &S) {
- S = '&' + S;
-
+void TypePrinter::printLValueReferenceBefore(const LValueReferenceType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getPointeeTypeAsWritten(), OS);
// Handle things like 'int (&A)[4];' correctly.
// FIXME: this should include vectors, but vectors use attributes I guess.
if (isa<ArrayType>(T->getPointeeTypeAsWritten()))
- S = '(' + S + ')';
-
+ OS << '(';
+ OS << '&';
+}
+void TypePrinter::printLValueReferenceAfter(const LValueReferenceType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getPointeeTypeAsWritten(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ // Handle things like 'int (&A)[4];' correctly.
+ // FIXME: this should include vectors, but vectors use attributes I guess.
+ if (isa<ArrayType>(T->getPointeeTypeAsWritten()))
+ OS << ')';
+ printAfter(T->getPointeeTypeAsWritten(), OS);
}
-void TypePrinter::printRValueReference(const RValueReferenceType *T,
- std::string &S) {
- S = "&&" + S;
-
+void TypePrinter::printRValueReferenceBefore(const RValueReferenceType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getPointeeTypeAsWritten(), OS);
// Handle things like 'int (&&A)[4];' correctly.
// FIXME: this should include vectors, but vectors use attributes I guess.
if (isa<ArrayType>(T->getPointeeTypeAsWritten()))
- S = '(' + S + ')';
-
+ OS << '(';
+ OS << "&&";
+}
+void TypePrinter::printRValueReferenceAfter(const RValueReferenceType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getPointeeTypeAsWritten(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ // Handle things like 'int (&&A)[4];' correctly.
+ // FIXME: this should include vectors, but vectors use attributes I guess.
+ if (isa<ArrayType>(T->getPointeeTypeAsWritten()))
+ OS << ')';
+ printAfter(T->getPointeeTypeAsWritten(), OS);
}
-void TypePrinter::printMemberPointer(const MemberPointerType *T,
- std::string &S) {
- PrintingPolicy InnerPolicy(Policy);
- Policy.SuppressTag = true;
- std::string C = QualType(T->getClass(), 0).getAsString(InnerPolicy);
- C += "::*";
- S = C + S;
-
+void TypePrinter::printMemberPointerBefore(const MemberPointerType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getPointeeType(), OS);
// Handle things like 'int (Cls::*A)[4];' correctly.
// FIXME: this should include vectors, but vectors use attributes I guess.
if (isa<ArrayType>(T->getPointeeType()))
- S = '(' + S + ')';
-
+ OS << '(';
+
+ PrintingPolicy InnerPolicy(Policy);
+ InnerPolicy.SuppressTag = false;
+ TypePrinter(InnerPolicy).print(QualType(T->getClass(), 0), OS, StringRef());
+
+ OS << "::*";
+}
+void TypePrinter::printMemberPointerAfter(const MemberPointerType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getPointeeType(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ // Handle things like 'int (Cls::*A)[4];' correctly.
+ // FIXME: this should include vectors, but vectors use attributes I guess.
+ if (isa<ArrayType>(T->getPointeeType()))
+ OS << ')';
+ printAfter(T->getPointeeType(), OS);
}
-void TypePrinter::printConstantArray(const ConstantArrayType *T,
- std::string &S) {
- S += '[';
- S += llvm::utostr(T->getSize().getZExtValue());
- S += ']';
-
+void TypePrinter::printConstantArrayBefore(const ConstantArrayType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getElementType(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printConstantArrayAfter(const ConstantArrayType *T,
+ raw_ostream &OS) {
+ OS << '[' << T->getSize().getZExtValue() << ']';
+ printAfter(T->getElementType(), OS);
}
-void TypePrinter::printIncompleteArray(const IncompleteArrayType *T,
- std::string &S) {
- S += "[]";
+void TypePrinter::printIncompleteArrayBefore(const IncompleteArrayType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getElementType(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printIncompleteArrayAfter(const IncompleteArrayType *T,
+ raw_ostream &OS) {
+ OS << "[]";
+ printAfter(T->getElementType(), OS);
}
-void TypePrinter::printVariableArray(const VariableArrayType *T,
- std::string &S) {
- S += '[';
-
+void TypePrinter::printVariableArrayBefore(const VariableArrayType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printVariableArrayAfter(const VariableArrayType *T,
+ raw_ostream &OS) {
+ OS << '[';
if (T->getIndexTypeQualifiers().hasQualifiers()) {
- AppendTypeQualList(S, T->getIndexTypeCVRQualifiers());
- S += ' ';
+ AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers());
+ OS << ' ';
}
-
+
if (T->getSizeModifier() == VariableArrayType::Static)
- S += "static";
+ OS << "static";
else if (T->getSizeModifier() == VariableArrayType::Star)
- S += '*';
-
- if (T->getSizeExpr()) {
- std::string SStr;
- llvm::raw_string_ostream s(SStr);
- T->getSizeExpr()->printPretty(s, 0, Policy);
- S += s.str();
- }
- S += ']';
-
- IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getElementType(), S);
+ OS << '*';
+
+ if (T->getSizeExpr())
+ T->getSizeExpr()->printPretty(OS, 0, Policy);
+ OS << ']';
+
+ printAfter(T->getElementType(), OS);
}
-void TypePrinter::printDependentSizedArray(const DependentSizedArrayType *T,
- std::string &S) {
- S += '[';
-
- if (T->getSizeExpr()) {
- std::string SStr;
- llvm::raw_string_ostream s(SStr);
- T->getSizeExpr()->printPretty(s, 0, Policy);
- S += s.str();
- }
- S += ']';
-
+void TypePrinter::printDependentSizedArrayBefore(
+ const DependentSizedArrayType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getElementType(), S);
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printDependentSizedArrayAfter(
+ const DependentSizedArrayType *T,
+ raw_ostream &OS) {
+ OS << '[';
+ if (T->getSizeExpr())
+ T->getSizeExpr()->printPretty(OS, 0, Policy);
+ OS << ']';
+ printAfter(T->getElementType(), OS);
}
-void TypePrinter::printDependentSizedExtVector(
+void TypePrinter::printDependentSizedExtVectorBefore(
const DependentSizedExtVectorType *T,
- std::string &S) {
- print(T->getElementType(), S);
-
- S += " __attribute__((ext_vector_type(";
- if (T->getSizeExpr()) {
- std::string SStr;
- llvm::raw_string_ostream s(SStr);
- T->getSizeExpr()->printPretty(s, 0, Policy);
- S += s.str();
- }
- S += ")))";
+ raw_ostream &OS) {
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printDependentSizedExtVectorAfter(
+ const DependentSizedExtVectorType *T,
+ raw_ostream &OS) {
+ OS << " __attribute__((ext_vector_type(";
+ if (T->getSizeExpr())
+ T->getSizeExpr()->printPretty(OS, 0, Policy);
+ OS << ")))";
+ printAfter(T->getElementType(), OS);
}
-void TypePrinter::printVector(const VectorType *T, std::string &S) {
+void TypePrinter::printVectorBefore(const VectorType *T, raw_ostream &OS) {
switch (T->getVectorKind()) {
case VectorType::AltiVecPixel:
- S = "__vector __pixel " + S;
+ OS << "__vector __pixel ";
break;
case VectorType::AltiVecBool:
- print(T->getElementType(), S);
- S = "__vector __bool " + S;
+ OS << "__vector __bool ";
+ printBefore(T->getElementType(), OS);
break;
case VectorType::AltiVecVector:
- print(T->getElementType(), S);
- S = "__vector " + S;
+ OS << "__vector ";
+ printBefore(T->getElementType(), OS);
break;
case VectorType::NeonVector:
- print(T->getElementType(), S);
- S = ("__attribute__((neon_vector_type(" +
- llvm::utostr_32(T->getNumElements()) + "))) " + S);
+ OS << "__attribute__((neon_vector_type("
+ << T->getNumElements() << "))) ";
+ printBefore(T->getElementType(), OS);
break;
case VectorType::NeonPolyVector:
- print(T->getElementType(), S);
- S = ("__attribute__((neon_polyvector_type(" +
- llvm::utostr_32(T->getNumElements()) + "))) " + S);
+ OS << "__attribute__((neon_polyvector_type(" <<
+ T->getNumElements() << "))) ";
+ printBefore(T->getElementType(), OS);
break;
case VectorType::GenericVector: {
// FIXME: We prefer to print the size directly here, but have no way
// to get the size of the type.
- print(T->getElementType(), S);
- std::string V = "__attribute__((__vector_size__(";
- V += llvm::utostr_32(T->getNumElements()); // convert back to bytes.
- std::string ET;
- print(T->getElementType(), ET);
- V += " * sizeof(" + ET + ")))) ";
- S = V + S;
+ OS << "__attribute__((__vector_size__("
+ << T->getNumElements()
+ << " * sizeof(";
+ print(T->getElementType(), OS, StringRef());
+ OS << ")))) ";
+ printBefore(T->getElementType(), OS);
break;
}
}
}
+void TypePrinter::printVectorAfter(const VectorType *T, raw_ostream &OS) {
+ printAfter(T->getElementType(), OS);
+}
-void TypePrinter::printExtVector(const ExtVectorType *T, std::string &S) {
- S += " __attribute__((ext_vector_type(";
- S += llvm::utostr_32(T->getNumElements());
- S += ")))";
- print(T->getElementType(), S);
+void TypePrinter::printExtVectorBefore(const ExtVectorType *T,
+ raw_ostream &OS) {
+ printBefore(T->getElementType(), OS);
+}
+void TypePrinter::printExtVectorAfter(const ExtVectorType *T, raw_ostream &OS) {
+ printAfter(T->getElementType(), OS);
+ OS << " __attribute__((ext_vector_type(";
+ OS << T->getNumElements();
+ OS << ")))";
}
void
-FunctionProtoType::printExceptionSpecification(std::string &S,
+FunctionProtoType::printExceptionSpecification(raw_ostream &OS,
PrintingPolicy Policy) const {
if (hasDynamicExceptionSpec()) {
- S += " throw(";
+ OS << " throw(";
if (getExceptionSpecType() == EST_MSAny)
- S += "...";
+ OS << "...";
else
for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) {
if (I)
- S += ", ";
+ OS << ", ";
- S += getExceptionType(I).getAsString(Policy);
+ OS << getExceptionType(I).stream(Policy);
}
- S += ")";
+ OS << ')';
} else if (isNoexceptExceptionSpec(getExceptionSpecType())) {
- S += " noexcept";
+ OS << " noexcept";
if (getExceptionSpecType() == EST_ComputedNoexcept) {
- S += "(";
- llvm::raw_string_ostream EOut(S);
- getNoexceptExpr()->printPretty(EOut, 0, Policy);
- EOut.flush();
- S += EOut.str();
- S += ")";
+ OS << '(';
+ getNoexceptExpr()->printPretty(OS, 0, Policy);
+ OS << ')';
}
}
}
-void TypePrinter::printFunctionProto(const FunctionProtoType *T,
- std::string &S) {
+void TypePrinter::printFunctionProtoBefore(const FunctionProtoType *T,
+ raw_ostream &OS) {
+ if (T->hasTrailingReturn()) {
+ OS << "auto ";
+ if (!HasEmptyPlaceHolder)
+ OS << '(';
+ } else {
+ // If needed for precedence reasons, wrap the inner part in grouping parens.
+ SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false);
+ printBefore(T->getResultType(), OS);
+ if (!PrevPHIsEmpty.get())
+ OS << '(';
+ }
+}
+
+void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
+ raw_ostream &OS) {
// If needed for precedence reasons, wrap the inner part in grouping parens.
- if (!S.empty())
- S = "(" + S + ")";
-
- S += "(";
- std::string Tmp;
- PrintingPolicy ParamPolicy(Policy);
- ParamPolicy.SuppressSpecifiers = false;
- for (unsigned i = 0, e = T->getNumArgs(); i != e; ++i) {
- if (i) S += ", ";
- print(T->getArgType(i), Tmp);
- S += Tmp;
- Tmp.clear();
+ if (!HasEmptyPlaceHolder)
+ OS << ')';
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
+
+ OS << '(';
+ {
+ ParamPolicyRAII ParamPolicy(Policy);
+ for (unsigned i = 0, e = T->getNumArgs(); i != e; ++i) {
+ if (i) OS << ", ";
+ print(T->getArgType(i), OS, StringRef());
+ }
}
if (T->isVariadic()) {
if (T->getNumArgs())
- S += ", ";
- S += "...";
+ OS << ", ";
+ OS << "...";
} else if (T->getNumArgs() == 0 && !Policy.LangOpts.CPlusPlus) {
// Do not emit int() if we have a proto, emit 'int(void)'.
- S += "void";
+ OS << "void";
}
- S += ")";
+ OS << ')';
FunctionType::ExtInfo Info = T->getExtInfo();
switch(Info.getCC()) {
case CC_Default: break;
case CC_C:
- S += " __attribute__((cdecl))";
+ OS << " __attribute__((cdecl))";
break;
case CC_X86StdCall:
- S += " __attribute__((stdcall))";
+ OS << " __attribute__((stdcall))";
break;
case CC_X86FastCall:
- S += " __attribute__((fastcall))";
+ OS << " __attribute__((fastcall))";
break;
case CC_X86ThisCall:
- S += " __attribute__((thiscall))";
+ OS << " __attribute__((thiscall))";
break;
case CC_X86Pascal:
- S += " __attribute__((pascal))";
+ OS << " __attribute__((pascal))";
break;
case CC_AAPCS:
- S += " __attribute__((pcs(\"aapcs\")))";
+ OS << " __attribute__((pcs(\"aapcs\")))";
break;
case CC_AAPCS_VFP:
- S += " __attribute__((pcs(\"aapcs-vfp\")))";
+ OS << " __attribute__((pcs(\"aapcs-vfp\")))";
break;
}
if (Info.getNoReturn())
- S += " __attribute__((noreturn))";
+ OS << " __attribute__((noreturn))";
if (Info.getRegParm())
- S += " __attribute__((regparm (" +
- llvm::utostr_32(Info.getRegParm()) + ")))";
-
- AppendTypeQualList(S, T->getTypeQuals());
+ OS << " __attribute__((regparm ("
+ << Info.getRegParm() << ")))";
+
+ if (unsigned quals = T->getTypeQuals()) {
+ OS << ' ';
+ AppendTypeQualList(OS, quals);
+ }
switch (T->getRefQualifier()) {
case RQ_None:
break;
case RQ_LValue:
- S += " &";
+ OS << " &";
break;
case RQ_RValue:
- S += " &&";
+ OS << " &&";
break;
}
- T->printExceptionSpecification(S, Policy);
+ T->printExceptionSpecification(OS, Policy);
+
if (T->hasTrailingReturn()) {
- std::string ResultS;
- print(T->getResultType(), ResultS);
- S = "auto " + S + " -> " + ResultS;
+ OS << " -> ";
+ print(T->getResultType(), OS, StringRef());
} else
- print(T->getResultType(), S);
+ printAfter(T->getResultType(), OS);
}
-void TypePrinter::printFunctionNoProto(const FunctionNoProtoType *T,
- std::string &S) {
+void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T,
+ raw_ostream &OS) {
+ // If needed for precedence reasons, wrap the inner part in grouping parens.
+ SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false);
+ printBefore(T->getResultType(), OS);
+ if (!PrevPHIsEmpty.get())
+ OS << '(';
+}
+void TypePrinter::printFunctionNoProtoAfter(const FunctionNoProtoType *T,
+ raw_ostream &OS) {
// If needed for precedence reasons, wrap the inner part in grouping parens.
- if (!S.empty())
- S = "(" + S + ")";
+ if (!HasEmptyPlaceHolder)
+ OS << ')';
+ SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
- S += "()";
+ OS << "()";
if (T->getNoReturnAttr())
- S += " __attribute__((noreturn))";
- print(T->getResultType(), S);
+ OS << " __attribute__((noreturn))";
+ printAfter(T->getResultType(), OS);
}
-static void printTypeSpec(const NamedDecl *D, std::string &S) {
+void TypePrinter::printTypeSpec(const NamedDecl *D, raw_ostream &OS) {
IdentifierInfo *II = D->getIdentifier();
- if (S.empty())
- S = II->getName().str();
- else
- S = II->getName().str() + ' ' + S;
+ OS << II->getName();
+ spaceBeforePlaceHolder(OS);
}
-void TypePrinter::printUnresolvedUsing(const UnresolvedUsingType *T,
- std::string &S) {
- printTypeSpec(T->getDecl(), S);
+void TypePrinter::printUnresolvedUsingBefore(const UnresolvedUsingType *T,
+ raw_ostream &OS) {
+ printTypeSpec(T->getDecl(), OS);
}
+void TypePrinter::printUnresolvedUsingAfter(const UnresolvedUsingType *T,
+ raw_ostream &OS) { }
-void TypePrinter::printTypedef(const TypedefType *T, std::string &S) {
- printTypeSpec(T->getDecl(), S);
+void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) {
+ printTypeSpec(T->getDecl(), OS);
}
+void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) { }
-void TypePrinter::printTypeOfExpr(const TypeOfExprType *T, std::string &S) {
- if (!S.empty()) // Prefix the basic type, e.g. 'typeof(e) X'.
- S = ' ' + S;
- std::string Str;
- llvm::raw_string_ostream s(Str);
- T->getUnderlyingExpr()->printPretty(s, 0, Policy);
- S = "typeof " + s.str() + S;
+void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T,
+ raw_ostream &OS) {
+ OS << "typeof ";
+ T->getUnderlyingExpr()->printPretty(OS, 0, Policy);
+ spaceBeforePlaceHolder(OS);
}
-
-void TypePrinter::printTypeOf(const TypeOfType *T, std::string &S) {
- if (!S.empty()) // Prefix the basic type, e.g. 'typeof(t) X'.
- S = ' ' + S;
- std::string Tmp;
- print(T->getUnderlyingType(), Tmp);
- S = "typeof(" + Tmp + ")" + S;
+void TypePrinter::printTypeOfExprAfter(const TypeOfExprType *T,
+ raw_ostream &OS) { }
+
+void TypePrinter::printTypeOfBefore(const TypeOfType *T, raw_ostream &OS) {
+ OS << "typeof(";
+ print(T->getUnderlyingType(), OS, StringRef());
+ OS << ')';
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printTypeOfAfter(const TypeOfType *T, raw_ostream &OS) { }
-void TypePrinter::printDecltype(const DecltypeType *T, std::string &S) {
- if (!S.empty()) // Prefix the basic type, e.g. 'decltype(t) X'.
- S = ' ' + S;
- std::string Str;
- llvm::raw_string_ostream s(Str);
- T->getUnderlyingExpr()->printPretty(s, 0, Policy);
- S = "decltype(" + s.str() + ")" + S;
+void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) {
+ OS << "decltype(";
+ T->getUnderlyingExpr()->printPretty(OS, 0, Policy);
+ OS << ')';
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printDecltypeAfter(const DecltypeType *T, raw_ostream &OS) { }
-void TypePrinter::printUnaryTransform(const UnaryTransformType *T,
- std::string &S) {
- if (!S.empty())
- S = ' ' + S;
- std::string Str;
+void TypePrinter::printUnaryTransformBefore(const UnaryTransformType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getBaseType(), Str);
switch (T->getUTTKind()) {
case UnaryTransformType::EnumUnderlyingType:
- S = "__underlying_type(" + Str + ")" + S;
- break;
+ OS << "__underlying_type(";
+ print(T->getBaseType(), OS, StringRef());
+ OS << ')';
+ spaceBeforePlaceHolder(OS);
+ return;
}
+
+ printBefore(T->getBaseType(), OS);
+}
+void TypePrinter::printUnaryTransformAfter(const UnaryTransformType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+
+ switch (T->getUTTKind()) {
+ case UnaryTransformType::EnumUnderlyingType:
+ return;
+ }
+
+ printAfter(T->getBaseType(), OS);
}
-void TypePrinter::printAuto(const AutoType *T, std::string &S) {
+void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) {
// If the type has been deduced, do not print 'auto'.
if (T->isDeduced()) {
- print(T->getDeducedType(), S);
+ printBefore(T->getDeducedType(), OS);
} else {
- if (!S.empty()) // Prefix the basic type, e.g. 'auto X'.
- S = ' ' + S;
- S = "auto" + S;
+ OS << "auto";
+ spaceBeforePlaceHolder(OS);
}
}
+void TypePrinter::printAutoAfter(const AutoType *T, raw_ostream &OS) {
+ // If the type has been deduced, do not print 'auto'.
+ if (T->isDeduced())
+ printAfter(T->getDeducedType(), OS);
+}
-void TypePrinter::printAtomic(const AtomicType *T, std::string &S) {
- if (!S.empty())
- S = ' ' + S;
- std::string Str;
+void TypePrinter::printAtomicBefore(const AtomicType *T, raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getValueType(), Str);
- S = "_Atomic(" + Str + ")" + S;
+ OS << "_Atomic(";
+ print(T->getValueType(), OS, StringRef());
+ OS << ')';
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printAtomicAfter(const AtomicType *T, raw_ostream &OS) { }
/// Appends the given scope to the end of a string.
-void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) {
+void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) {
if (DC->isTranslationUnit()) return;
- AppendScope(DC->getParent(), Buffer);
-
- unsigned OldSize = Buffer.size();
+ AppendScope(DC->getParent(), OS);
if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) {
if (Policy.SuppressUnwrittenScope &&
(NS->isAnonymousNamespace() || NS->isInline()))
return;
if (NS->getIdentifier())
- Buffer += NS->getNameAsString();
+ OS << NS->getName() << "::";
else
- Buffer += "<anonymous>";
+ OS << "<anonymous>::";
} else if (ClassTemplateSpecializationDecl *Spec
= dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
IncludeStrongLifetimeRAII Strong(Policy);
+ OS << Spec->getIdentifier()->getName();
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
- std::string TemplateArgsStr
- = TemplateSpecializationType::PrintTemplateArgumentList(
+ TemplateSpecializationType::PrintTemplateArgumentList(OS,
TemplateArgs.data(),
TemplateArgs.size(),
Policy);
- Buffer += Spec->getIdentifier()->getName();
- Buffer += TemplateArgsStr;
+ OS << "::";
} else if (TagDecl *Tag = dyn_cast<TagDecl>(DC)) {
if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl())
- Buffer += Typedef->getIdentifier()->getName();
+ OS << Typedef->getIdentifier()->getName() << "::";
else if (Tag->getIdentifier())
- Buffer += Tag->getIdentifier()->getName();
+ OS << Tag->getIdentifier()->getName() << "::";
else
return;
}
-
- if (Buffer.size() != OldSize)
- Buffer += "::";
}
-void TypePrinter::printTag(TagDecl *D, std::string &InnerString) {
+void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
if (Policy.SuppressTag)
return;
- std::string Buffer;
bool HasKindDecoration = false;
// bool SuppressTagKeyword
@@ -654,25 +842,24 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) {
if (!(Policy.LangOpts.CPlusPlus || Policy.SuppressTagKeyword ||
D->getTypedefNameForAnonDecl())) {
HasKindDecoration = true;
- Buffer += D->getKindName();
- Buffer += ' ';
+ OS << D->getKindName();
+ OS << ' ';
}
// Compute the full nested-name-specifier for this type.
// In C, this will always be empty except when the type
// being printed is anonymous within other Record.
if (!Policy.SuppressScope)
- AppendScope(D->getDeclContext(), Buffer);
+ AppendScope(D->getDeclContext(), OS);
if (const IdentifierInfo *II = D->getIdentifier())
- Buffer += II->getNameStart();
+ OS << II->getName();
else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) {
assert(Typedef->getIdentifier() && "Typedef without identifier?");
- Buffer += Typedef->getIdentifier()->getNameStart();
+ OS << Typedef->getIdentifier()->getName();
} else {
// Make an unambiguous representation for anonymous types, e.g.
// <anonymous enum at /usr/include/string.h:120:9>
- llvm::raw_string_ostream OS(Buffer);
if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) {
OS << "<lambda";
@@ -717,219 +904,223 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) {
NumArgs = TemplateArgs.size();
}
IncludeStrongLifetimeRAII Strong(Policy);
- Buffer += TemplateSpecializationType::PrintTemplateArgumentList(Args,
- NumArgs,
- Policy);
- }
-
- if (!InnerString.empty()) {
- Buffer += ' ';
- Buffer += InnerString;
+ TemplateSpecializationType::PrintTemplateArgumentList(OS,
+ Args, NumArgs,
+ Policy);
}
- std::swap(Buffer, InnerString);
+ spaceBeforePlaceHolder(OS);
}
-void TypePrinter::printRecord(const RecordType *T, std::string &S) {
- printTag(T->getDecl(), S);
+void TypePrinter::printRecordBefore(const RecordType *T, raw_ostream &OS) {
+ printTag(T->getDecl(), OS);
}
+void TypePrinter::printRecordAfter(const RecordType *T, raw_ostream &OS) { }
-void TypePrinter::printEnum(const EnumType *T, std::string &S) {
- printTag(T->getDecl(), S);
+void TypePrinter::printEnumBefore(const EnumType *T, raw_ostream &OS) {
+ printTag(T->getDecl(), OS);
}
+void TypePrinter::printEnumAfter(const EnumType *T, raw_ostream &OS) { }
-void TypePrinter::printTemplateTypeParm(const TemplateTypeParmType *T,
- std::string &S) {
- if (!S.empty()) // Prefix the basic type, e.g. 'parmname X'.
- S = ' ' + S;
-
+void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T,
+ raw_ostream &OS) {
if (IdentifierInfo *Id = T->getIdentifier())
- S = Id->getName().str() + S;
+ OS << Id->getName();
else
- S = "type-parameter-" + llvm::utostr_32(T->getDepth()) + '-' +
- llvm::utostr_32(T->getIndex()) + S;
+ OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex();
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printTemplateTypeParmAfter(const TemplateTypeParmType *T,
+ raw_ostream &OS) { }
-void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T,
- std::string &S) {
+void TypePrinter::printSubstTemplateTypeParmBefore(
+ const SubstTemplateTypeParmType *T,
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- print(T->getReplacementType(), S);
+ printBefore(T->getReplacementType(), OS);
+}
+void TypePrinter::printSubstTemplateTypeParmAfter(
+ const SubstTemplateTypeParmType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ printAfter(T->getReplacementType(), OS);
}
-void TypePrinter::printSubstTemplateTypeParmPack(
+void TypePrinter::printSubstTemplateTypeParmPackBefore(
const SubstTemplateTypeParmPackType *T,
- std::string &S) {
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- printTemplateTypeParm(T->getReplacedParameter(), S);
+ printTemplateTypeParmBefore(T->getReplacedParameter(), OS);
+}
+void TypePrinter::printSubstTemplateTypeParmPackAfter(
+ const SubstTemplateTypeParmPackType *T,
+ raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ printTemplateTypeParmAfter(T->getReplacedParameter(), OS);
}
-void TypePrinter::printTemplateSpecialization(
+void TypePrinter::printTemplateSpecializationBefore(
const TemplateSpecializationType *T,
- std::string &S) {
+ raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- std::string SpecString;
+ T->getTemplateName().print(OS, Policy);
- {
- llvm::raw_string_ostream OS(SpecString);
- T->getTemplateName().print(OS, Policy);
- }
-
- SpecString += TemplateSpecializationType::PrintTemplateArgumentList(
- T->getArgs(),
- T->getNumArgs(),
- Policy);
- if (S.empty())
- S.swap(SpecString);
- else
- S = SpecString + ' ' + S;
+ TemplateSpecializationType::PrintTemplateArgumentList(OS,
+ T->getArgs(),
+ T->getNumArgs(),
+ Policy);
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printTemplateSpecializationAfter(
+ const TemplateSpecializationType *T,
+ raw_ostream &OS) { }
-void TypePrinter::printInjectedClassName(const InjectedClassNameType *T,
- std::string &S) {
- printTemplateSpecialization(T->getInjectedTST(), S);
+void TypePrinter::printInjectedClassNameBefore(const InjectedClassNameType *T,
+ raw_ostream &OS) {
+ printTemplateSpecializationBefore(T->getInjectedTST(), OS);
}
-
-void TypePrinter::printElaborated(const ElaboratedType *T, std::string &S) {
- std::string MyString;
-
- {
- llvm::raw_string_ostream OS(MyString);
- OS << TypeWithKeyword::getKeywordName(T->getKeyword());
- if (T->getKeyword() != ETK_None)
- OS << " ";
- NestedNameSpecifier* Qualifier = T->getQualifier();
- if (Qualifier)
- Qualifier->print(OS, Policy);
- }
-
- std::string TypeStr;
- PrintingPolicy InnerPolicy(Policy);
- InnerPolicy.SuppressTagKeyword = true;
- InnerPolicy.SuppressScope = true;
- TypePrinter(InnerPolicy).print(T->getNamedType(), TypeStr);
+void TypePrinter::printInjectedClassNameAfter(const InjectedClassNameType *T,
+ raw_ostream &OS) { }
+
+void TypePrinter::printElaboratedBefore(const ElaboratedType *T,
+ raw_ostream &OS) {
+ OS << TypeWithKeyword::getKeywordName(T->getKeyword());
+ if (T->getKeyword() != ETK_None)
+ OS << " ";
+ NestedNameSpecifier* Qualifier = T->getQualifier();
+ if (Qualifier)
+ Qualifier->print(OS, Policy);
- MyString += TypeStr;
- if (S.empty())
- S.swap(MyString);
- else
- S = MyString + ' ' + S;
+ ElaboratedTypePolicyRAII PolicyRAII(Policy);
+ printBefore(T->getNamedType(), OS);
+}
+void TypePrinter::printElaboratedAfter(const ElaboratedType *T,
+ raw_ostream &OS) {
+ ElaboratedTypePolicyRAII PolicyRAII(Policy);
+ printAfter(T->getNamedType(), OS);
}
-void TypePrinter::printParen(const ParenType *T, std::string &S) {
- if (!S.empty() && !isa<FunctionType>(T->getInnerType()))
- S = '(' + S + ')';
- print(T->getInnerType(), S);
+void TypePrinter::printParenBefore(const ParenType *T, raw_ostream &OS) {
+ if (!HasEmptyPlaceHolder && !isa<FunctionType>(T->getInnerType())) {
+ printBefore(T->getInnerType(), OS);
+ OS << '(';
+ } else
+ printBefore(T->getInnerType(), OS);
+}
+void TypePrinter::printParenAfter(const ParenType *T, raw_ostream &OS) {
+ if (!HasEmptyPlaceHolder && !isa<FunctionType>(T->getInnerType())) {
+ OS << ')';
+ printAfter(T->getInnerType(), OS);
+ } else
+ printAfter(T->getInnerType(), OS);
}
-void TypePrinter::printDependentName(const DependentNameType *T, std::string &S) {
- std::string MyString;
+void TypePrinter::printDependentNameBefore(const DependentNameType *T,
+ raw_ostream &OS) {
+ OS << TypeWithKeyword::getKeywordName(T->getKeyword());
+ if (T->getKeyword() != ETK_None)
+ OS << " ";
- {
- llvm::raw_string_ostream OS(MyString);
- OS << TypeWithKeyword::getKeywordName(T->getKeyword());
- if (T->getKeyword() != ETK_None)
- OS << " ";
-
- T->getQualifier()->print(OS, Policy);
-
- OS << T->getIdentifier()->getName();
- }
+ T->getQualifier()->print(OS, Policy);
- if (S.empty())
- S.swap(MyString);
- else
- S = MyString + ' ' + S;
+ OS << T->getIdentifier()->getName();
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printDependentNameAfter(const DependentNameType *T,
+ raw_ostream &OS) { }
-void TypePrinter::printDependentTemplateSpecialization(
- const DependentTemplateSpecializationType *T, std::string &S) {
+void TypePrinter::printDependentTemplateSpecializationBefore(
+ const DependentTemplateSpecializationType *T, raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
- std::string MyString;
- {
- llvm::raw_string_ostream OS(MyString);
- OS << TypeWithKeyword::getKeywordName(T->getKeyword());
- if (T->getKeyword() != ETK_None)
- OS << " ";
-
- if (T->getQualifier())
- T->getQualifier()->print(OS, Policy);
- OS << T->getIdentifier()->getName();
- OS << TemplateSpecializationType::PrintTemplateArgumentList(
- T->getArgs(),
- T->getNumArgs(),
- Policy);
- }
+ OS << TypeWithKeyword::getKeywordName(T->getKeyword());
+ if (T->getKeyword() != ETK_None)
+ OS << " ";
- if (S.empty())
- S.swap(MyString);
- else
- S = MyString + ' ' + S;
+ if (T->getQualifier())
+ T->getQualifier()->print(OS, Policy);
+ OS << T->getIdentifier()->getName();
+ TemplateSpecializationType::PrintTemplateArgumentList(OS,
+ T->getArgs(),
+ T->getNumArgs(),
+ Policy);
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printDependentTemplateSpecializationAfter(
+ const DependentTemplateSpecializationType *T, raw_ostream &OS) { }
-void TypePrinter::printPackExpansion(const PackExpansionType *T,
- std::string &S) {
- print(T->getPattern(), S);
- S += "...";
+void TypePrinter::printPackExpansionBefore(const PackExpansionType *T,
+ raw_ostream &OS) {
+ printBefore(T->getPattern(), OS);
+}
+void TypePrinter::printPackExpansionAfter(const PackExpansionType *T,
+ raw_ostream &OS) {
+ printAfter(T->getPattern(), OS);
+ OS << "...";
}
-void TypePrinter::printAttributed(const AttributedType *T,
- std::string &S) {
+void TypePrinter::printAttributedBefore(const AttributedType *T,
+ raw_ostream &OS) {
// Prefer the macro forms of the GC and ownership qualifiers.
if (T->getAttrKind() == AttributedType::attr_objc_gc ||
T->getAttrKind() == AttributedType::attr_objc_ownership)
- return print(T->getEquivalentType(), S);
+ return printBefore(T->getEquivalentType(), OS);
+
+ printBefore(T->getModifiedType(), OS);
+}
- print(T->getModifiedType(), S);
+void TypePrinter::printAttributedAfter(const AttributedType *T,
+ raw_ostream &OS) {
+ // Prefer the macro forms of the GC and ownership qualifiers.
+ if (T->getAttrKind() == AttributedType::attr_objc_gc ||
+ T->getAttrKind() == AttributedType::attr_objc_ownership)
+ return printAfter(T->getEquivalentType(), OS);
// TODO: not all attributes are GCC-style attributes.
- S += " __attribute__((";
+ OS << " __attribute__((";
switch (T->getAttrKind()) {
case AttributedType::attr_address_space:
- S += "address_space(";
- S += T->getEquivalentType().getAddressSpace();
- S += ")";
+ OS << "address_space(";
+ OS << T->getEquivalentType().getAddressSpace();
+ OS << ')';
break;
case AttributedType::attr_vector_size: {
- S += "__vector_size__(";
+ OS << "__vector_size__(";
if (const VectorType *vector =T->getEquivalentType()->getAs<VectorType>()) {
- S += vector->getNumElements();
- S += " * sizeof(";
-
- std::string tmp;
- print(vector->getElementType(), tmp);
- S += tmp;
- S += ")";
+ OS << vector->getNumElements();
+ OS << " * sizeof(";
+ print(vector->getElementType(), OS, StringRef());
+ OS << ')';
}
- S += ")";
+ OS << ')';
break;
}
case AttributedType::attr_neon_vector_type:
case AttributedType::attr_neon_polyvector_type: {
if (T->getAttrKind() == AttributedType::attr_neon_vector_type)
- S += "neon_vector_type(";
+ OS << "neon_vector_type(";
else
- S += "neon_polyvector_type(";
+ OS << "neon_polyvector_type(";
const VectorType *vector = T->getEquivalentType()->getAs<VectorType>();
- S += llvm::utostr_32(vector->getNumElements());
- S += ")";
+ OS << vector->getNumElements();
+ OS << ')';
break;
}
case AttributedType::attr_regparm: {
- S += "regparm(";
+ OS << "regparm(";
QualType t = T->getEquivalentType();
while (!t->isFunctionType())
t = t->getPointeeType();
- S += t->getAs<FunctionType>()->getRegParmType();
- S += ")";
+ OS << t->getAs<FunctionType>()->getRegParmType();
+ OS << ')';
break;
}
case AttributedType::attr_objc_gc: {
- S += "objc_gc(";
+ OS << "objc_gc(";
QualType tmp = T->getEquivalentType();
while (tmp.getObjCGCAttr() == Qualifiers::GCNone) {
@@ -939,116 +1130,244 @@ void TypePrinter::printAttributed(const AttributedType *T,
}
if (tmp.isObjCGCWeak())
- S += "weak";
+ OS << "weak";
else
- S += "strong";
- S += ")";
+ OS << "strong";
+ OS << ')';
break;
}
case AttributedType::attr_objc_ownership:
- S += "objc_ownership(";
+ OS << "objc_ownership(";
switch (T->getEquivalentType().getObjCLifetime()) {
case Qualifiers::OCL_None: llvm_unreachable("no ownership!");
- case Qualifiers::OCL_ExplicitNone: S += "none"; break;
- case Qualifiers::OCL_Strong: S += "strong"; break;
- case Qualifiers::OCL_Weak: S += "weak"; break;
- case Qualifiers::OCL_Autoreleasing: S += "autoreleasing"; break;
+ case Qualifiers::OCL_ExplicitNone: OS << "none"; break;
+ case Qualifiers::OCL_Strong: OS << "strong"; break;
+ case Qualifiers::OCL_Weak: OS << "weak"; break;
+ case Qualifiers::OCL_Autoreleasing: OS << "autoreleasing"; break;
}
- S += ")";
+ OS << ')';
break;
- case AttributedType::attr_noreturn: S += "noreturn"; break;
- case AttributedType::attr_cdecl: S += "cdecl"; break;
- case AttributedType::attr_fastcall: S += "fastcall"; break;
- case AttributedType::attr_stdcall: S += "stdcall"; break;
- case AttributedType::attr_thiscall: S += "thiscall"; break;
- case AttributedType::attr_pascal: S += "pascal"; break;
+ case AttributedType::attr_noreturn: OS << "noreturn"; break;
+ case AttributedType::attr_cdecl: OS << "cdecl"; break;
+ case AttributedType::attr_fastcall: OS << "fastcall"; break;
+ case AttributedType::attr_stdcall: OS << "stdcall"; break;
+ case AttributedType::attr_thiscall: OS << "thiscall"; break;
+ case AttributedType::attr_pascal: OS << "pascal"; break;
case AttributedType::attr_pcs: {
- S += "pcs(";
+ OS << "pcs(";
QualType t = T->getEquivalentType();
while (!t->isFunctionType())
t = t->getPointeeType();
- S += (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ?
+ OS << (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ?
"\"aapcs\"" : "\"aapcs-vfp\"");
- S += ")";
+ OS << ')';
break;
}
}
- S += "))";
+ OS << "))";
}
-void TypePrinter::printObjCInterface(const ObjCInterfaceType *T,
- std::string &S) {
- if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'.
- S = ' ' + S;
-
- std::string ObjCQIString = T->getDecl()->getNameAsString();
- S = ObjCQIString + S;
+void TypePrinter::printObjCInterfaceBefore(const ObjCInterfaceType *T,
+ raw_ostream &OS) {
+ OS << T->getDecl()->getName();
+ spaceBeforePlaceHolder(OS);
}
+void TypePrinter::printObjCInterfaceAfter(const ObjCInterfaceType *T,
+ raw_ostream &OS) { }
-void TypePrinter::printObjCObject(const ObjCObjectType *T,
- std::string &S) {
+void TypePrinter::printObjCObjectBefore(const ObjCObjectType *T,
+ raw_ostream &OS) {
if (T->qual_empty())
- return print(T->getBaseType(), S);
+ return printBefore(T->getBaseType(), OS);
- std::string tmp;
- print(T->getBaseType(), tmp);
- tmp += '<';
+ print(T->getBaseType(), OS, StringRef());
+ OS << '<';
bool isFirst = true;
for (ObjCObjectType::qual_iterator
I = T->qual_begin(), E = T->qual_end(); I != E; ++I) {
if (isFirst)
isFirst = false;
else
- tmp += ',';
- tmp += (*I)->getNameAsString();
+ OS << ',';
+ OS << (*I)->getName();
}
- tmp += '>';
-
- if (!S.empty()) {
- tmp += ' ';
- tmp += S;
- }
- std::swap(tmp, S);
+ OS << '>';
+ spaceBeforePlaceHolder(OS);
+}
+void TypePrinter::printObjCObjectAfter(const ObjCObjectType *T,
+ raw_ostream &OS) {
+ if (T->qual_empty())
+ return printAfter(T->getBaseType(), OS);
}
-void TypePrinter::printObjCObjectPointer(const ObjCObjectPointerType *T,
- std::string &S) {
- std::string ObjCQIString;
-
- T->getPointeeType().getLocalQualifiers().getAsStringInternal(ObjCQIString,
- Policy);
- if (!ObjCQIString.empty())
- ObjCQIString += ' ';
-
+void TypePrinter::printObjCObjectPointerBefore(const ObjCObjectPointerType *T,
+ raw_ostream &OS) {
+ T->getPointeeType().getLocalQualifiers().print(OS, Policy,
+ /*appendSpaceIfNonEmpty=*/true);
+
if (T->isObjCIdType() || T->isObjCQualifiedIdType())
- ObjCQIString += "id";
+ OS << "id";
else if (T->isObjCClassType() || T->isObjCQualifiedClassType())
- ObjCQIString += "Class";
+ OS << "Class";
else if (T->isObjCSelType())
- ObjCQIString += "SEL";
+ OS << "SEL";
else
- ObjCQIString += T->getInterfaceDecl()->getNameAsString();
+ OS << T->getInterfaceDecl()->getName();
if (!T->qual_empty()) {
- ObjCQIString += '<';
+ OS << '<';
for (ObjCObjectPointerType::qual_iterator I = T->qual_begin(),
E = T->qual_end();
I != E; ++I) {
- ObjCQIString += (*I)->getNameAsString();
+ OS << (*I)->getName();
if (I+1 != E)
- ObjCQIString += ',';
+ OS << ',';
+ }
+ OS << '>';
+ }
+
+ if (!T->isObjCIdType() && !T->isObjCQualifiedIdType()) {
+ OS << " *"; // Don't forget the implicit pointer.
+ } else {
+ spaceBeforePlaceHolder(OS);
+ }
+}
+void TypePrinter::printObjCObjectPointerAfter(const ObjCObjectPointerType *T,
+ raw_ostream &OS) { }
+
+void TemplateSpecializationType::
+ PrintTemplateArgumentList(raw_ostream &OS,
+ const TemplateArgumentListInfo &Args,
+ const PrintingPolicy &Policy) {
+ return PrintTemplateArgumentList(OS,
+ Args.getArgumentArray(),
+ Args.size(),
+ Policy);
+}
+
+void
+TemplateSpecializationType::PrintTemplateArgumentList(
+ raw_ostream &OS,
+ const TemplateArgument *Args,
+ unsigned NumArgs,
+ const PrintingPolicy &Policy,
+ bool SkipBrackets) {
+ if (!SkipBrackets)
+ OS << '<';
+
+ bool needSpace = false;
+ for (unsigned Arg = 0; Arg < NumArgs; ++Arg) {
+ if (Arg > 0)
+ OS << ", ";
+
+ // Print the argument into a string.
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream ArgOS(Buf);
+ if (Args[Arg].getKind() == TemplateArgument::Pack) {
+ PrintTemplateArgumentList(ArgOS,
+ Args[Arg].pack_begin(),
+ Args[Arg].pack_size(),
+ Policy, true);
+ } else {
+ Args[Arg].print(Policy, ArgOS);
+ }
+ StringRef ArgString = ArgOS.str();
+
+ // If this is the first argument and its string representation
+ // begins with the global scope specifier ('::foo'), add a space
+ // to avoid printing the diagraph '<:'.
+ if (!Arg && !ArgString.empty() && ArgString[0] == ':')
+ OS << ' ';
+
+ OS << ArgString;
+
+ needSpace = (!ArgString.empty() && ArgString.back() == '>');
+ }
+
+ // If the last character of our string is '>', add another space to
+ // keep the two '>''s separate tokens. We don't *have* to do this in
+ // C++0x, but it's still good hygiene.
+ if (needSpace)
+ OS << ' ';
+
+ if (!SkipBrackets)
+ OS << '>';
+}
+
+// Sadly, repeat all that with TemplateArgLoc.
+void TemplateSpecializationType::
+PrintTemplateArgumentList(raw_ostream &OS,
+ const TemplateArgumentLoc *Args, unsigned NumArgs,
+ const PrintingPolicy &Policy) {
+ OS << '<';
+
+ bool needSpace = false;
+ for (unsigned Arg = 0; Arg < NumArgs; ++Arg) {
+ if (Arg > 0)
+ OS << ", ";
+
+ // Print the argument into a string.
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream ArgOS(Buf);
+ if (Args[Arg].getArgument().getKind() == TemplateArgument::Pack) {
+ PrintTemplateArgumentList(ArgOS,
+ Args[Arg].getArgument().pack_begin(),
+ Args[Arg].getArgument().pack_size(),
+ Policy, true);
+ } else {
+ Args[Arg].getArgument().print(Policy, ArgOS);
}
- ObjCQIString += '>';
+ StringRef ArgString = ArgOS.str();
+
+ // If this is the first argument and its string representation
+ // begins with the global scope specifier ('::foo'), add a space
+ // to avoid printing the diagraph '<:'.
+ if (!Arg && !ArgString.empty() && ArgString[0] == ':')
+ OS << ' ';
+
+ OS << ArgString;
+
+ needSpace = (!ArgString.empty() && ArgString.back() == '>');
}
- if (!T->isObjCIdType() && !T->isObjCQualifiedIdType())
- ObjCQIString += " *"; // Don't forget the implicit pointer.
- else if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'.
- S = ' ' + S;
+ // If the last character of our string is '>', add another space to
+ // keep the two '>''s separate tokens. We don't *have* to do this in
+ // C++0x, but it's still good hygiene.
+ if (needSpace)
+ OS << ' ';
+
+ OS << '>';
+}
+
+void
+FunctionProtoType::printExceptionSpecification(std::string &S,
+ PrintingPolicy Policy) const {
- S = ObjCQIString + S;
+ if (hasDynamicExceptionSpec()) {
+ S += " throw(";
+ if (getExceptionSpecType() == EST_MSAny)
+ S += "...";
+ else
+ for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) {
+ if (I)
+ S += ", ";
+
+ S += getExceptionType(I).getAsString(Policy);
+ }
+ S += ")";
+ } else if (isNoexceptExceptionSpec(getExceptionSpecType())) {
+ S += " noexcept";
+ if (getExceptionSpecType() == EST_ComputedNoexcept) {
+ S += "(";
+ llvm::raw_string_ostream EOut(S);
+ getNoexceptExpr()->printPretty(EOut, 0, Policy);
+ EOut.flush();
+ S += EOut.str();
+ S += ")";
+ }
+ }
}
std::string TemplateSpecializationType::
@@ -1148,15 +1467,14 @@ PrintTemplateArgumentList(const TemplateArgumentLoc *Args, unsigned NumArgs,
}
void QualType::dump(const char *msg) const {
- std::string R = "identifier";
- LangOptions LO;
- getAsStringInternal(R, PrintingPolicy(LO));
if (msg)
llvm::errs() << msg << ": ";
- llvm::errs() << R << "\n";
+ LangOptions LO;
+ print(llvm::errs(), PrintingPolicy(LO), "identifier");
+ llvm::errs() << '\n';
}
void QualType::dump() const {
- dump("");
+ dump(0);
}
void Type::dump() const {
@@ -1171,51 +1489,99 @@ std::string Qualifiers::getAsString() const {
// Appends qualifiers to the given string, separated by spaces. Will
// prefix a space if the string is non-empty. Will not append a final
// space.
-void Qualifiers::getAsStringInternal(std::string &S,
- const PrintingPolicy& Policy) const {
- AppendTypeQualList(S, getCVRQualifiers());
+std::string Qualifiers::getAsString(const PrintingPolicy &Policy) const {
+ SmallString<64> Buf;
+ llvm::raw_svector_ostream StrOS(Buf);
+ print(StrOS, Policy);
+ return StrOS.str();
+}
+
+bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const {
+ if (getCVRQualifiers())
+ return false;
+
+ if (getAddressSpace())
+ return false;
+
+ if (getObjCGCAttr())
+ return false;
+
+ if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime())
+ if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime))
+ return false;
+
+ return true;
+}
+
+// Appends qualifiers to the given string, separated by spaces. Will
+// prefix a space if the string is non-empty. Will not append a final
+// space.
+void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy,
+ bool appendSpaceIfNonEmpty) const {
+ bool addSpace = false;
+
+ unsigned quals = getCVRQualifiers();
+ if (quals) {
+ AppendTypeQualList(OS, quals);
+ addSpace = true;
+ }
if (unsigned addrspace = getAddressSpace()) {
- if (!S.empty()) S += ' ';
+ if (addSpace)
+ OS << ' ';
+ addSpace = true;
switch (addrspace) {
case LangAS::opencl_global:
- S += "__global";
+ OS << "__global";
break;
case LangAS::opencl_local:
- S += "__local";
+ OS << "__local";
break;
case LangAS::opencl_constant:
- S += "__constant";
+ OS << "__constant";
break;
default:
- S += "__attribute__((address_space(";
- S += llvm::utostr_32(addrspace);
- S += ")))";
+ OS << "__attribute__((address_space(";
+ OS << addrspace;
+ OS << ")))";
}
}
if (Qualifiers::GC gc = getObjCGCAttr()) {
- if (!S.empty()) S += ' ';
+ if (addSpace)
+ OS << ' ';
+ addSpace = true;
if (gc == Qualifiers::Weak)
- S += "__weak";
+ OS << "__weak";
else
- S += "__strong";
+ OS << "__strong";
}
if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) {
- if (!S.empty() &&
- !(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime))
- S += ' ';
-
+ if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)){
+ if (addSpace)
+ OS << ' ';
+ addSpace = true;
+ }
+
switch (lifetime) {
case Qualifiers::OCL_None: llvm_unreachable("none but true");
- case Qualifiers::OCL_ExplicitNone: S += "__unsafe_unretained"; break;
+ case Qualifiers::OCL_ExplicitNone: OS << "__unsafe_unretained"; break;
case Qualifiers::OCL_Strong:
if (!Policy.SuppressStrongLifetime)
- S += "__strong";
+ OS << "__strong";
break;
- case Qualifiers::OCL_Weak: S += "__weak"; break;
- case Qualifiers::OCL_Autoreleasing: S += "__autoreleasing"; break;
+ case Qualifiers::OCL_Weak: OS << "__weak"; break;
+ case Qualifiers::OCL_Autoreleasing: OS << "__autoreleasing"; break;
}
}
+
+ if (appendSpaceIfNonEmpty && addSpace)
+ OS << ' ';
+}
+
+std::string QualType::getAsString(const PrintingPolicy &Policy) const {
+ std::string S;
+ getAsStringInternal(S, Policy);
+ return S;
}
std::string QualType::getAsString(const Type *ty, Qualifiers qs) {
@@ -1225,8 +1591,25 @@ std::string QualType::getAsString(const Type *ty, Qualifiers qs) {
return buffer;
}
+void QualType::print(const Type *ty, Qualifiers qs,
+ raw_ostream &OS, const PrintingPolicy &policy,
+ const Twine &PlaceHolder) {
+ SmallString<128> PHBuf;
+ StringRef PH;
+ if (PlaceHolder.isSingleStringRef())
+ PH = PlaceHolder.getSingleStringRef();
+ else
+ PH = PlaceHolder.toStringRef(PHBuf);
+
+ TypePrinter(policy).print(ty, qs, OS, PH);
+}
+
void QualType::getAsStringInternal(const Type *ty, Qualifiers qs,
std::string &buffer,
const PrintingPolicy &policy) {
- TypePrinter(policy).print(ty, qs, buffer);
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream StrOS(Buf);
+ TypePrinter(policy).print(ty, qs, StrOS, buffer);
+ std::string str = StrOS.str();
+ buffer.swap(str);
}
diff --git a/lib/AST/VTTBuilder.cpp b/lib/AST/VTTBuilder.cpp
index f5ff624..5ca4e86 100644
--- a/lib/AST/VTTBuilder.cpp
+++ b/lib/AST/VTTBuilder.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/VTTBuilder.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/TargetInfo.h"
diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp
index 107d9fb..104530f 100644
--- a/lib/AST/VTableBuilder.cpp
+++ b/lib/AST/VTableBuilder.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/VTableBuilder.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/TargetInfo.h"
@@ -164,7 +165,7 @@ FinalOverriders::FinalOverriders(const CXXRecordDecl *MostDerivedClass,
SubobjectOffsets, SubobjectLayoutClassOffsets,
SubobjectCounts);
- // Get the the final overriders.
+ // Get the final overriders.
CXXFinalOverriderMap FinalOverriders;
MostDerivedClass->getFinalOverriders(FinalOverriders);
@@ -630,7 +631,7 @@ VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base,
// Get the base offset of the primary base.
if (PrimaryBaseIsVirtual) {
- assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() &&
"Primary vbase should have a zero offset!");
const ASTRecordLayout &MostDerivedClassLayout =
@@ -639,7 +640,7 @@ VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base,
PrimaryBaseOffset =
MostDerivedClassLayout.getVBaseClassOffset(PrimaryBase);
} else {
- assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getBaseClassOffset(PrimaryBase).isZero() &&
"Primary base should have a zero offset!");
PrimaryBaseOffset = Base.getBaseOffset();
@@ -682,7 +683,7 @@ void VCallAndVBaseOffsetBuilder::AddVCallOffsets(BaseSubobject Base,
// primary base will have its vcall and vbase offsets emitted already.
if (PrimaryBase && !Layout.isPrimaryBaseVirtual()) {
// Get the base offset of the primary base.
- assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getBaseClassOffset(PrimaryBase).isZero() &&
"Primary base should have a zero offset!");
AddVCallOffsets(BaseSubobject(PrimaryBase, Base.getBaseOffset()),
@@ -1370,7 +1371,7 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider,
break;
if (Layout.isPrimaryBaseVirtual()) {
- assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() &&
"Primary base should always be at offset 0!");
const ASTRecordLayout &LayoutClassLayout =
@@ -1384,7 +1385,7 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider,
break;
}
} else {
- assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getBaseClassOffset(PrimaryBase).isZero() &&
"Primary base should always be at offset 0!");
}
@@ -1436,7 +1437,7 @@ VTableBuilder::AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass,
CharUnits PrimaryBaseOffset;
CharUnits PrimaryBaseOffsetInLayoutClass;
if (Layout.isPrimaryBaseVirtual()) {
- assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() &&
"Primary vbase should have a zero offset!");
const ASTRecordLayout &MostDerivedClassLayout =
@@ -1451,7 +1452,7 @@ VTableBuilder::AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass,
PrimaryBaseOffsetInLayoutClass =
LayoutClassLayout.getVBaseClassOffset(PrimaryBase);
} else {
- assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 &&
+ assert(Layout.getBaseClassOffset(PrimaryBase).isZero() &&
"Primary base should have a zero offset!");
PrimaryBaseOffset = Base.getBaseOffset();
diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp
new file mode 100644
index 0000000..085049d
--- /dev/null
+++ b/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -0,0 +1,547 @@
+//===--- ASTMatchFinder.cpp - Structural query framework ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements an algorithm to efficiently search for matches on AST nodes.
+// Uses memoization to support recursive matches like HasDescendant.
+//
+// The general idea is to visit all AST nodes with a RecursiveASTVisitor,
+// calling the Matches(...) method of each matcher we are running on each
+// AST node. The matcher can recurse via the ASTMatchFinder interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include <set>
+
+namespace clang {
+namespace ast_matchers {
+namespace internal {
+namespace {
+
+// We use memoization to avoid running the same matcher on the same
+// AST node twice. This pair is the key for looking up match
+// result. It consists of an ID of the MatcherInterface (for
+// identifying the matcher) and a pointer to the AST node.
+typedef std::pair<uint64_t, const void*> UntypedMatchInput;
+
+// Used to store the result of a match and possibly bound nodes.
+struct MemoizedMatchResult {
+ bool ResultOfMatch;
+ BoundNodesTree Nodes;
+};
+
+// A RecursiveASTVisitor that traverses all children or all descendants of
+// a node.
+class MatchChildASTVisitor
+ : public RecursiveASTVisitor<MatchChildASTVisitor> {
+public:
+ typedef RecursiveASTVisitor<MatchChildASTVisitor> VisitorBase;
+
+ // Creates an AST visitor that matches 'matcher' on all children or
+ // descendants of a traversed node. max_depth is the maximum depth
+ // to traverse: use 1 for matching the children and INT_MAX for
+ // matching the descendants.
+ MatchChildASTVisitor(const UntypedBaseMatcher *BaseMatcher,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder,
+ int MaxDepth,
+ ASTMatchFinder::TraversalKind Traversal,
+ ASTMatchFinder::BindKind Bind)
+ : BaseMatcher(BaseMatcher),
+ Finder(Finder),
+ Builder(Builder),
+ CurrentDepth(-1),
+ MaxDepth(MaxDepth),
+ Traversal(Traversal),
+ Bind(Bind),
+ Matches(false) {}
+
+ // Returns true if a match is found in the subtree rooted at the
+ // given AST node. This is done via a set of mutually recursive
+ // functions. Here's how the recursion is done (the *wildcard can
+ // actually be Decl, Stmt, or Type):
+ //
+ // - Traverse(node) calls BaseTraverse(node) when it needs
+ // to visit the descendants of node.
+ // - BaseTraverse(node) then calls (via VisitorBase::Traverse*(node))
+ // Traverse*(c) for each child c of 'node'.
+ // - Traverse*(c) in turn calls Traverse(c), completing the
+ // recursion.
+ template <typename T>
+ bool findMatch(const T &Node) {
+ reset();
+ traverse(Node);
+ return Matches;
+ }
+
+ // The following are overriding methods from the base visitor class.
+ // They are public only to allow CRTP to work. They are *not *part
+ // of the public API of this class.
+ bool TraverseDecl(Decl *DeclNode) {
+ return (DeclNode == NULL) || traverse(*DeclNode);
+ }
+ bool TraverseStmt(Stmt *StmtNode) {
+ const Stmt *StmtToTraverse = StmtNode;
+ if (Traversal ==
+ ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses) {
+ const Expr *ExprNode = dyn_cast_or_null<Expr>(StmtNode);
+ if (ExprNode != NULL) {
+ StmtToTraverse = ExprNode->IgnoreParenImpCasts();
+ }
+ }
+ return (StmtToTraverse == NULL) || traverse(*StmtToTraverse);
+ }
+ bool TraverseType(QualType TypeNode) {
+ return traverse(TypeNode);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return true; }
+
+private:
+ // Used for updating the depth during traversal.
+ struct ScopedIncrement {
+ explicit ScopedIncrement(int *Depth) : Depth(Depth) { ++(*Depth); }
+ ~ScopedIncrement() { --(*Depth); }
+
+ private:
+ int *Depth;
+ };
+
+ // Resets the state of this object.
+ void reset() {
+ Matches = false;
+ CurrentDepth = -1;
+ }
+
+ // Forwards the call to the corresponding Traverse*() method in the
+ // base visitor class.
+ bool baseTraverse(const Decl &DeclNode) {
+ return VisitorBase::TraverseDecl(const_cast<Decl*>(&DeclNode));
+ }
+ bool baseTraverse(const Stmt &StmtNode) {
+ return VisitorBase::TraverseStmt(const_cast<Stmt*>(&StmtNode));
+ }
+ bool baseTraverse(QualType TypeNode) {
+ return VisitorBase::TraverseType(TypeNode);
+ }
+
+ // Traverses the subtree rooted at 'node'; returns true if the
+ // traversal should continue after this function returns; also sets
+ // matched_ to true if a match is found during the traversal.
+ template <typename T>
+ bool traverse(const T &Node) {
+ TOOLING_COMPILE_ASSERT(IsBaseType<T>::value,
+ traverse_can_only_be_instantiated_with_base_type);
+ ScopedIncrement ScopedDepth(&CurrentDepth);
+ if (CurrentDepth == 0) {
+ // We don't want to match the root node, so just recurse.
+ return baseTraverse(Node);
+ }
+ if (Bind != ASTMatchFinder::BK_All) {
+ if (BaseMatcher->matches(Node, Finder, Builder)) {
+ Matches = true;
+ return false; // Abort as soon as a match is found.
+ }
+ if (CurrentDepth < MaxDepth) {
+ // The current node doesn't match, and we haven't reached the
+ // maximum depth yet, so recurse.
+ return baseTraverse(Node);
+ }
+ // The current node doesn't match, and we have reached the
+ // maximum depth, so don't recurse (but continue the traversal
+ // such that other nodes at the current level can be visited).
+ return true;
+ } else {
+ BoundNodesTreeBuilder RecursiveBuilder;
+ if (BaseMatcher->matches(Node, Finder, &RecursiveBuilder)) {
+ // After the first match the matcher succeeds.
+ Matches = true;
+ Builder->addMatch(RecursiveBuilder.build());
+ }
+ if (CurrentDepth < MaxDepth) {
+ baseTraverse(Node);
+ }
+ // In kBindAll mode we always search for more matches.
+ return true;
+ }
+ }
+
+ const UntypedBaseMatcher *const BaseMatcher;
+ ASTMatchFinder *const Finder;
+ BoundNodesTreeBuilder *const Builder;
+ int CurrentDepth;
+ const int MaxDepth;
+ const ASTMatchFinder::TraversalKind Traversal;
+ const ASTMatchFinder::BindKind Bind;
+ bool Matches;
+};
+
+// Controls the outermost traversal of the AST and allows to match multiple
+// matchers.
+class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
+ public ASTMatchFinder {
+public:
+ MatchASTVisitor(std::vector< std::pair<const UntypedBaseMatcher*,
+ MatchFinder::MatchCallback*> > *Triggers)
+ : Triggers(Triggers),
+ ActiveASTContext(NULL) {
+ }
+
+ void set_active_ast_context(ASTContext *NewActiveASTContext) {
+ ActiveASTContext = NewActiveASTContext;
+ }
+
+ // The following Visit*() and Traverse*() functions "override"
+ // methods in RecursiveASTVisitor.
+
+ bool VisitTypedefDecl(TypedefDecl *DeclNode) {
+ // When we see 'typedef A B', we add name 'B' to the set of names
+ // A's canonical type maps to. This is necessary for implementing
+ // IsDerivedFrom(x) properly, where x can be the name of the base
+ // class or any of its aliases.
+ //
+ // In general, the is-alias-of (as defined by typedefs) relation
+ // is tree-shaped, as you can typedef a type more than once. For
+ // example,
+ //
+ // typedef A B;
+ // typedef A C;
+ // typedef C D;
+ // typedef C E;
+ //
+ // gives you
+ //
+ // A
+ // |- B
+ // `- C
+ // |- D
+ // `- E
+ //
+ // It is wrong to assume that the relation is a chain. A correct
+ // implementation of IsDerivedFrom() needs to recognize that B and
+ // E are aliases, even though neither is a typedef of the other.
+ // Therefore, we cannot simply walk through one typedef chain to
+ // find out whether the type name matches.
+ const Type *TypeNode = DeclNode->getUnderlyingType().getTypePtr();
+ const Type *CanonicalType = // root of the typedef tree
+ ActiveASTContext->getCanonicalType(TypeNode);
+ TypeAliases[CanonicalType].insert(DeclNode);
+ return true;
+ }
+
+ bool TraverseDecl(Decl *DeclNode);
+ bool TraverseStmt(Stmt *StmtNode);
+ bool TraverseType(QualType TypeNode);
+ bool TraverseTypeLoc(TypeLoc TypeNode);
+
+ // Matches children or descendants of 'Node' with 'BaseMatcher'.
+ template <typename T>
+ bool memoizedMatchesRecursively(const T &Node,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder, int MaxDepth,
+ TraversalKind Traversal, BindKind Bind) {
+ TOOLING_COMPILE_ASSERT((llvm::is_same<T, Decl>::value) ||
+ (llvm::is_same<T, Stmt>::value),
+ type_does_not_support_memoization);
+ const UntypedMatchInput input(BaseMatcher.getID(), &Node);
+ std::pair<MemoizationMap::iterator, bool> InsertResult
+ = ResultCache.insert(std::make_pair(input, MemoizedMatchResult()));
+ if (InsertResult.second) {
+ BoundNodesTreeBuilder DescendantBoundNodesBuilder;
+ InsertResult.first->second.ResultOfMatch =
+ matchesRecursively(Node, BaseMatcher, &DescendantBoundNodesBuilder,
+ MaxDepth, Traversal, Bind);
+ InsertResult.first->second.Nodes =
+ DescendantBoundNodesBuilder.build();
+ }
+ InsertResult.first->second.Nodes.copyTo(Builder);
+ return InsertResult.first->second.ResultOfMatch;
+ }
+
+ // Matches children or descendants of 'Node' with 'BaseMatcher'.
+ template <typename T>
+ bool matchesRecursively(const T &Node, const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder, int MaxDepth,
+ TraversalKind Traversal, BindKind Bind) {
+ MatchChildASTVisitor Visitor(
+ &BaseMatcher, this, Builder, MaxDepth, Traversal, Bind);
+ return Visitor.findMatch(Node);
+ }
+
+ virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration,
+ const Matcher<NamedDecl> &Base,
+ BoundNodesTreeBuilder *Builder);
+
+ // Implements ASTMatchFinder::MatchesChildOf.
+ virtual bool matchesChildOf(const Decl &DeclNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ TraversalKind Traversal,
+ BindKind Bind) {
+ return matchesRecursively(DeclNode, BaseMatcher, Builder, 1, Traversal,
+ Bind);
+ }
+ virtual bool matchesChildOf(const Stmt &StmtNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ TraversalKind Traversal,
+ BindKind Bind) {
+ return matchesRecursively(StmtNode, BaseMatcher, Builder, 1, Traversal,
+ Bind);
+ }
+
+ // Implements ASTMatchFinder::MatchesDescendantOf.
+ virtual bool matchesDescendantOf(const Decl &DeclNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ BindKind Bind) {
+ return memoizedMatchesRecursively(DeclNode, BaseMatcher, Builder, INT_MAX,
+ TK_AsIs, Bind);
+ }
+ virtual bool matchesDescendantOf(const Stmt &StmtNode,
+ const UntypedBaseMatcher &BaseMatcher,
+ BoundNodesTreeBuilder *Builder,
+ BindKind Bind) {
+ return memoizedMatchesRecursively(StmtNode, BaseMatcher, Builder, INT_MAX,
+ TK_AsIs, Bind);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return true; }
+
+private:
+ // Implements a BoundNodesTree::Visitor that calls a MatchCallback with
+ // the aggregated bound nodes for each match.
+ class MatchVisitor : public BoundNodesTree::Visitor {
+ public:
+ MatchVisitor(ASTContext* Context,
+ MatchFinder::MatchCallback* Callback)
+ : Context(Context),
+ Callback(Callback) {}
+
+ virtual void visitMatch(const BoundNodes& BoundNodesView) {
+ Callback->run(MatchFinder::MatchResult(BoundNodesView, Context));
+ }
+
+ private:
+ ASTContext* Context;
+ MatchFinder::MatchCallback* Callback;
+ };
+
+ // Returns true if 'TypeNode' has an alias that matches the given matcher.
+ bool typeHasMatchingAlias(const Type *TypeNode,
+ const Matcher<NamedDecl> Matcher,
+ BoundNodesTreeBuilder *Builder) {
+ const Type *const CanonicalType =
+ ActiveASTContext->getCanonicalType(TypeNode);
+ const std::set<const TypedefDecl*> &Aliases = TypeAliases[CanonicalType];
+ for (std::set<const TypedefDecl*>::const_iterator
+ It = Aliases.begin(), End = Aliases.end();
+ It != End; ++It) {
+ if (Matcher.matches(**It, this, Builder))
+ return true;
+ }
+ return false;
+ }
+
+ // Matches all registered matchers on the given node and calls the
+ // result callback for every node that matches.
+ template <typename T>
+ void match(const T &node) {
+ for (std::vector< std::pair<const UntypedBaseMatcher*,
+ MatchFinder::MatchCallback*> >::const_iterator
+ It = Triggers->begin(), End = Triggers->end();
+ It != End; ++It) {
+ BoundNodesTreeBuilder Builder;
+ if (It->first->matches(node, this, &Builder)) {
+ BoundNodesTree BoundNodes = Builder.build();
+ MatchVisitor Visitor(ActiveASTContext, It->second);
+ BoundNodes.visitMatches(&Visitor);
+ }
+ }
+ }
+
+ std::vector< std::pair<const UntypedBaseMatcher*,
+ MatchFinder::MatchCallback*> > *const Triggers;
+ ASTContext *ActiveASTContext;
+
+ // Maps a canonical type to its TypedefDecls.
+ llvm::DenseMap<const Type*, std::set<const TypedefDecl*> > TypeAliases;
+
+ // Maps (matcher, node) -> the match result for memoization.
+ typedef llvm::DenseMap<UntypedMatchInput, MemoizedMatchResult> MemoizationMap;
+ MemoizationMap ResultCache;
+};
+
+// Returns true if the given class is directly or indirectly derived
+// from a base type with the given name. A class is considered to be
+// also derived from itself.
+bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration,
+ const Matcher<NamedDecl> &Base,
+ BoundNodesTreeBuilder *Builder) {
+ if (Base.matches(*Declaration, this, Builder))
+ return true;
+ if (!Declaration->hasDefinition())
+ return false;
+ typedef CXXRecordDecl::base_class_const_iterator BaseIterator;
+ for (BaseIterator It = Declaration->bases_begin(),
+ End = Declaration->bases_end(); It != End; ++It) {
+ const Type *TypeNode = It->getType().getTypePtr();
+
+ if (typeHasMatchingAlias(TypeNode, Base, Builder))
+ return true;
+
+ // Type::getAs<...>() drills through typedefs.
+ if (TypeNode->getAs<DependentNameType>() != NULL ||
+ TypeNode->getAs<TemplateTypeParmType>() != NULL)
+ // Dependent names and template TypeNode parameters will be matched when
+ // the template is instantiated.
+ continue;
+ CXXRecordDecl *ClassDecl = NULL;
+ TemplateSpecializationType const *TemplateType =
+ TypeNode->getAs<TemplateSpecializationType>();
+ if (TemplateType != NULL) {
+ if (TemplateType->getTemplateName().isDependent())
+ // Dependent template specializations will be matched when the
+ // template is instantiated.
+ continue;
+
+ // For template specialization types which are specializing a template
+ // declaration which is an explicit or partial specialization of another
+ // template declaration, getAsCXXRecordDecl() returns the corresponding
+ // ClassTemplateSpecializationDecl.
+ //
+ // For template specialization types which are specializing a template
+ // declaration which is neither an explicit nor partial specialization of
+ // another template declaration, getAsCXXRecordDecl() returns NULL and
+ // we get the CXXRecordDecl of the templated declaration.
+ CXXRecordDecl *SpecializationDecl =
+ TemplateType->getAsCXXRecordDecl();
+ if (SpecializationDecl != NULL) {
+ ClassDecl = SpecializationDecl;
+ } else {
+ ClassDecl = llvm::dyn_cast<CXXRecordDecl>(
+ TemplateType->getTemplateName()
+ .getAsTemplateDecl()->getTemplatedDecl());
+ }
+ } else {
+ ClassDecl = TypeNode->getAsCXXRecordDecl();
+ }
+ assert(ClassDecl != NULL);
+ assert(ClassDecl != Declaration);
+ if (classIsDerivedFrom(ClassDecl, Base, Builder))
+ return true;
+ }
+ return false;
+}
+
+bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
+ if (DeclNode == NULL) {
+ return true;
+ }
+ match(*DeclNode);
+ return RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode);
+}
+
+bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode) {
+ if (StmtNode == NULL) {
+ return true;
+ }
+ match(*StmtNode);
+ return RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode);
+}
+
+bool MatchASTVisitor::TraverseType(QualType TypeNode) {
+ match(TypeNode);
+ return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode);
+}
+
+bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) {
+ match(TypeLoc.getType());
+ return RecursiveASTVisitor<MatchASTVisitor>::
+ TraverseTypeLoc(TypeLoc);
+}
+
+class MatchASTConsumer : public ASTConsumer {
+public:
+ MatchASTConsumer(std::vector< std::pair<const UntypedBaseMatcher*,
+ MatchFinder::MatchCallback*> > *Triggers,
+ MatchFinder::ParsingDoneTestCallback *ParsingDone)
+ : Visitor(Triggers),
+ ParsingDone(ParsingDone) {}
+
+private:
+ virtual void HandleTranslationUnit(ASTContext &Context) {
+ if (ParsingDone != NULL) {
+ ParsingDone->run();
+ }
+ Visitor.set_active_ast_context(&Context);
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+ Visitor.set_active_ast_context(NULL);
+ }
+
+ MatchASTVisitor Visitor;
+ MatchFinder::ParsingDoneTestCallback *ParsingDone;
+};
+
+} // end namespace
+} // end namespace internal
+
+MatchFinder::MatchResult::MatchResult(const BoundNodes &Nodes,
+ ASTContext *Context)
+ : Nodes(Nodes), Context(Context),
+ SourceManager(&Context->getSourceManager()) {}
+
+MatchFinder::MatchCallback::~MatchCallback() {}
+MatchFinder::ParsingDoneTestCallback::~ParsingDoneTestCallback() {}
+
+MatchFinder::MatchFinder() : ParsingDone(NULL) {}
+
+MatchFinder::~MatchFinder() {
+ for (std::vector< std::pair<const internal::UntypedBaseMatcher*,
+ MatchFinder::MatchCallback*> >::const_iterator
+ It = Triggers.begin(), End = Triggers.end();
+ It != End; ++It) {
+ delete It->first;
+ }
+}
+
+void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch,
+ MatchCallback *Action) {
+ Triggers.push_back(std::make_pair(
+ new internal::TypedBaseMatcher<Decl>(NodeMatch), Action));
+}
+
+void MatchFinder::addMatcher(const TypeMatcher &NodeMatch,
+ MatchCallback *Action) {
+ Triggers.push_back(std::make_pair(
+ new internal::TypedBaseMatcher<QualType>(NodeMatch), Action));
+}
+
+void MatchFinder::addMatcher(const StatementMatcher &NodeMatch,
+ MatchCallback *Action) {
+ Triggers.push_back(std::make_pair(
+ new internal::TypedBaseMatcher<Stmt>(NodeMatch), Action));
+}
+
+ASTConsumer *MatchFinder::newASTConsumer() {
+ return new internal::MatchASTConsumer(&Triggers, ParsingDone);
+}
+
+void MatchFinder::registerTestCallbackAfterParsing(
+ MatchFinder::ParsingDoneTestCallback *NewParsingDone) {
+ ParsingDone = NewParsingDone;
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/lib/ASTMatchers/ASTMatchersInternal.cpp b/lib/ASTMatchers/ASTMatchersInternal.cpp
new file mode 100644
index 0000000..69c5190
--- /dev/null
+++ b/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -0,0 +1,102 @@
+//===--- ASTMatchersInternal.cpp - Structural query framework -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the base layer of the matcher framework.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace internal {
+
+BoundNodesTree::BoundNodesTree() {}
+
+BoundNodesTree::BoundNodesTree(
+ const std::map<std::string, const Decl*>& DeclBindings,
+ const std::map<std::string, const Stmt*>& StmtBindings,
+ const std::vector<BoundNodesTree> RecursiveBindings)
+ : DeclBindings(DeclBindings), StmtBindings(StmtBindings),
+ RecursiveBindings(RecursiveBindings) {}
+
+void BoundNodesTree::copyTo(BoundNodesTreeBuilder* Builder) const {
+ copyBindingsTo(DeclBindings, Builder);
+ copyBindingsTo(StmtBindings, Builder);
+ for (std::vector<BoundNodesTree>::const_iterator
+ I = RecursiveBindings.begin(),
+ E = RecursiveBindings.end();
+ I != E; ++I) {
+ Builder->addMatch(*I);
+ }
+}
+
+template <typename T>
+void BoundNodesTree::copyBindingsTo(
+ const T& Bindings, BoundNodesTreeBuilder* Builder) const {
+ for (typename T::const_iterator I = Bindings.begin(),
+ E = Bindings.end();
+ I != E; ++I) {
+ Builder->setBinding(I->first, I->second);
+ }
+}
+
+void BoundNodesTree::visitMatches(Visitor* ResultVisitor) {
+ std::map<std::string, const Decl*> AggregatedDeclBindings;
+ std::map<std::string, const Stmt*> AggregatedStmtBindings;
+ visitMatchesRecursively(ResultVisitor, AggregatedDeclBindings,
+ AggregatedStmtBindings);
+}
+
+void BoundNodesTree::
+visitMatchesRecursively(Visitor* ResultVisitor,
+ std::map<std::string, const Decl*>
+ AggregatedDeclBindings,
+ std::map<std::string, const Stmt*>
+ AggregatedStmtBindings) {
+ copy(DeclBindings.begin(), DeclBindings.end(),
+ inserter(AggregatedDeclBindings, AggregatedDeclBindings.begin()));
+ copy(StmtBindings.begin(), StmtBindings.end(),
+ inserter(AggregatedStmtBindings, AggregatedStmtBindings.begin()));
+ if (RecursiveBindings.empty()) {
+ ResultVisitor->visitMatch(BoundNodes(AggregatedDeclBindings,
+ AggregatedStmtBindings));
+ } else {
+ for (unsigned I = 0; I < RecursiveBindings.size(); ++I) {
+ RecursiveBindings[I].visitMatchesRecursively(ResultVisitor,
+ AggregatedDeclBindings,
+ AggregatedStmtBindings);
+ }
+ }
+}
+
+BoundNodesTreeBuilder::BoundNodesTreeBuilder() {}
+
+void BoundNodesTreeBuilder::setBinding(const std::string &Id,
+ const Decl *Node) {
+ DeclBindings[Id] = Node;
+}
+
+void BoundNodesTreeBuilder::setBinding(const std::string &Id,
+ const Stmt *Node) {
+ StmtBindings[Id] = Node;
+}
+
+void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) {
+ RecursiveBindings.push_back(Bindings);
+}
+
+BoundNodesTree BoundNodesTreeBuilder::build() const {
+ return BoundNodesTree(DeclBindings, StmtBindings, RecursiveBindings);
+}
+
+} // end namespace internal
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/lib/ASTMatchers/CMakeLists.txt b/lib/ASTMatchers/CMakeLists.txt
new file mode 100644
index 0000000..8fc7d4b
--- /dev/null
+++ b/lib/ASTMatchers/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+set(LLVM_USED_LIBS clangBasic clangAST)
+
+add_clang_library(clangASTMatchers
+ ASTMatchFinder.cpp
+ ASTMatchersInternal.cpp
+ )
+
+add_dependencies(clangASTMatchers
+ ClangAttrClasses
+ ClangAttrImpl
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangStmtNodes
+ )
diff --git a/lib/ASTMatchers/Makefile b/lib/ASTMatchers/Makefile
new file mode 100644
index 0000000..76d8271
--- /dev/null
+++ b/lib/ASTMatchers/Makefile
@@ -0,0 +1,13 @@
+##===- clang/lib/ASTMatchers/Makefile ----------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+LIBRARYNAME := clangASTMatchers
+
+include $(CLANG_LEVEL)/Makefile
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp
index 659cc6d..7de7f39 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/lib/Analysis/AnalysisDeclContext.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@@ -34,11 +35,9 @@ typedef llvm::DenseMap<const void *, ManagedAnalysis *> ManagedAnalysisMap;
AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
const Decl *d,
- idx::TranslationUnit *tu,
const CFG::BuildOptions &buildOptions)
: Manager(Mgr),
D(d),
- TU(tu),
cfgBuildOptions(buildOptions),
forcedBlkExprs(0),
builtCFG(false),
@@ -50,11 +49,9 @@ AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
}
AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
- const Decl *d,
- idx::TranslationUnit *tu)
+ const Decl *d)
: Manager(Mgr),
D(d),
- TU(tu),
forcedBlkExprs(0),
builtCFG(false),
builtCompleteCFG(false),
@@ -184,8 +181,16 @@ void AnalysisDeclContext::dumpCFG(bool ShowColors) {
}
ParentMap &AnalysisDeclContext::getParentMap() {
- if (!PM)
+ if (!PM) {
PM.reset(new ParentMap(getBody()));
+ if (const CXXConstructorDecl *C = dyn_cast<CXXConstructorDecl>(getDecl())) {
+ for (CXXConstructorDecl::init_const_iterator I = C->init_begin(),
+ E = C->init_end();
+ I != E; ++I) {
+ PM->addStmt((*I)->getInit());
+ }
+ }
+ }
return *PM;
}
@@ -195,11 +200,10 @@ PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() {
return PCA.get();
}
-AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D,
- idx::TranslationUnit *TU) {
+AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) {
AnalysisDeclContext *&AC = Contexts[D];
if (!AC)
- AC = new AnalysisDeclContext(this, D, TU, cfgBuildOptions);
+ AC = new AnalysisDeclContext(this, D, cfgBuildOptions);
return AC;
}
@@ -209,6 +213,14 @@ AnalysisDeclContext::getStackFrame(LocationContext const *Parent, const Stmt *S,
return getLocationContextManager().getStackFrame(this, Parent, S, Blk, Idx);
}
+const BlockInvocationContext *
+AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent,
+ const clang::BlockDecl *BD,
+ const void *ContextData) {
+ return getLocationContextManager().getBlockInvocationContext(this, parent,
+ BD, ContextData);
+}
+
LocationContextManager & AnalysisDeclContext::getLocationContextManager() {
assert(Manager &&
"Cannot create LocationContexts without an AnalysisDeclContextManager!");
@@ -239,7 +251,7 @@ void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) {
}
void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, getAnalysisDeclContext(), getParent(), BD);
+ Profile(ID, getAnalysisDeclContext(), getParent(), BD, ContextData);
}
//===----------------------------------------------------------------------===//
@@ -288,6 +300,24 @@ LocationContextManager::getScope(AnalysisDeclContext *ctx,
return getLocationContext<ScopeContext, Stmt>(ctx, parent, s);
}
+const BlockInvocationContext *
+LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx,
+ const LocationContext *parent,
+ const BlockDecl *BD,
+ const void *ContextData) {
+ llvm::FoldingSetNodeID ID;
+ BlockInvocationContext::Profile(ID, ctx, parent, BD, ContextData);
+ void *InsertPos;
+ BlockInvocationContext *L =
+ cast_or_null<BlockInvocationContext>(Contexts.FindNodeOrInsertPos(ID,
+ InsertPos));
+ if (!L) {
+ L = new BlockInvocationContext(ctx, parent, BD, ContextData);
+ Contexts.InsertNode(L, InsertPos);
+ }
+ return L;
+}
+
//===----------------------------------------------------------------------===//
// LocationContext methods.
//===----------------------------------------------------------------------===//
@@ -302,19 +332,6 @@ const StackFrameContext *LocationContext::getCurrentStackFrame() const {
return NULL;
}
-const StackFrameContext *
-LocationContext::getStackFrameForDeclContext(const DeclContext *DC) const {
- const LocationContext *LC = this;
- while (LC) {
- if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) {
- if (cast<DeclContext>(SFC->getDecl()) == DC)
- return SFC;
- }
- LC = LC->getParent();
- }
- return NULL;
-}
-
bool LocationContext::isParentOf(const LocationContext *LC) const {
do {
const LocationContext *Parent = LC->getParent();
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 2f1f1cb..05c5385 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -1,4 +1,4 @@
-//===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===//
+ //===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,6 +14,7 @@
#include "llvm/Support/SaveAndRestore.h"
#include "clang/Analysis/CFG.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/PrettyPrinter.h"
@@ -312,19 +313,6 @@ private:
CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc);
CFGBlock *VisitBinaryOperator(BinaryOperator *B, AddStmtChoice asc);
CFGBlock *VisitBreakStmt(BreakStmt *B);
- CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S);
- CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E,
- AddStmtChoice asc);
- CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T);
- CFGBlock *VisitCXXTryStmt(CXXTryStmt *S);
- CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S);
- CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E,
- AddStmtChoice asc);
- CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc);
- CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
- AddStmtChoice asc);
- CFGBlock *VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C,
- AddStmtChoice asc);
CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc);
CFGBlock *VisitCaseStmt(CaseStmt *C);
CFGBlock *VisitChooseExpr(ChooseExpr *C, AddStmtChoice asc);
@@ -332,31 +320,47 @@ private:
CFGBlock *VisitConditionalOperator(AbstractConditionalOperator *C,
AddStmtChoice asc);
CFGBlock *VisitContinueStmt(ContinueStmt *C);
+ CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E,
+ AddStmtChoice asc);
+ CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S);
+ CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc);
+ CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S);
+ CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
+ AddStmtChoice asc);
+ CFGBlock *VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C,
+ AddStmtChoice asc);
+ CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T);
+ CFGBlock *VisitCXXTryStmt(CXXTryStmt *S);
CFGBlock *VisitDeclStmt(DeclStmt *DS);
CFGBlock *VisitDeclSubExpr(DeclStmt *DS);
CFGBlock *VisitDefaultStmt(DefaultStmt *D);
CFGBlock *VisitDoStmt(DoStmt *D);
- CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc);
+ CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
CFGBlock *VisitForStmt(ForStmt *F);
CFGBlock *VisitGotoStmt(GotoStmt *G);
CFGBlock *VisitIfStmt(IfStmt *I);
CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc);
CFGBlock *VisitIndirectGotoStmt(IndirectGotoStmt *I);
CFGBlock *VisitLabelStmt(LabelStmt *L);
- CFGBlock *VisitLambdaExpr(LambdaExpr *L);
+ CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc);
+ CFGBlock *VisitLogicalOperator(BinaryOperator *B);
+ std::pair<CFGBlock *, CFGBlock *> VisitLogicalOperator(BinaryOperator *B,
+ Stmt *Term,
+ CFGBlock *TrueBlock,
+ CFGBlock *FalseBlock);
CFGBlock *VisitMemberExpr(MemberExpr *M, AddStmtChoice asc);
CFGBlock *VisitObjCAtCatchStmt(ObjCAtCatchStmt *S);
- CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S);
CFGBlock *VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S);
CFGBlock *VisitObjCAtThrowStmt(ObjCAtThrowStmt *S);
CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S);
+ CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S);
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
- CFGBlock *VisitReturnStmt(ReturnStmt *R);
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
- CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
- AddStmtChoice asc);
+ CFGBlock *VisitReturnStmt(ReturnStmt *R);
CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc);
CFGBlock *VisitSwitchStmt(SwitchStmt *S);
+ CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
+ AddStmtChoice asc);
CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc);
CFGBlock *VisitWhileStmt(WhileStmt *W);
@@ -772,13 +776,12 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B,
// If this destructor is marked as a no-return destructor, we need to
// create a new block for the destructor which does not have as a successor
// anything built thus far: control won't flow out of this block.
- QualType Ty;
- if ((*I)->getType()->isReferenceType()) {
+ QualType Ty = (*I)->getType();
+ if (Ty->isReferenceType()) {
Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit());
- } else {
- Ty = Context->getBaseElementType((*I)->getType());
}
-
+ Ty = Context->getBaseElementType(Ty);
+
const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
if (cast<FunctionType>(Dtor->getType())->getNoReturnAttr())
Block = createNoReturnBlock();
@@ -1070,9 +1073,6 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::LambdaExprClass:
return VisitLambdaExpr(cast<LambdaExpr>(S), asc);
- case Stmt::AttributedStmtClass:
- return Visit(cast<AttributedStmt>(S)->getSubStmt(), asc);
-
case Stmt::MemberExprClass:
return VisitMemberExpr(cast<MemberExpr>(S), asc);
@@ -1166,55 +1166,111 @@ CFGBlock *CFGBuilder::VisitUnaryOperator(UnaryOperator *U,
return Visit(U->getSubExpr(), AddStmtChoice());
}
-CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B,
- AddStmtChoice asc) {
- if (B->isLogicalOp()) { // && or ||
- CFGBlock *ConfluenceBlock = Block ? Block : createBlock();
- appendStmt(ConfluenceBlock, B);
+CFGBlock *CFGBuilder::VisitLogicalOperator(BinaryOperator *B) {
+ CFGBlock *ConfluenceBlock = Block ? Block : createBlock();
+ appendStmt(ConfluenceBlock, B);
- if (badCFG)
- return 0;
+ if (badCFG)
+ return 0;
- // create the block evaluating the LHS
- CFGBlock *LHSBlock = createBlock(false);
- LHSBlock->setTerminator(B);
+ return VisitLogicalOperator(B, 0, ConfluenceBlock, ConfluenceBlock).first;
+}
- // create the block evaluating the RHS
- Succ = ConfluenceBlock;
- Block = NULL;
- CFGBlock *RHSBlock = addStmt(B->getRHS());
+std::pair<CFGBlock*, CFGBlock*>
+CFGBuilder::VisitLogicalOperator(BinaryOperator *B,
+ Stmt *Term,
+ CFGBlock *TrueBlock,
+ CFGBlock *FalseBlock) {
- if (RHSBlock) {
- if (badCFG)
- return 0;
- } else {
- // Create an empty block for cases where the RHS doesn't require
- // any explicit statements in the CFG.
- RHSBlock = createBlock();
+ // Introspect the RHS. If it is a nested logical operation, we recursively
+ // build the CFG using this function. Otherwise, resort to default
+ // CFG construction behavior.
+ Expr *RHS = B->getRHS()->IgnoreParens();
+ CFGBlock *RHSBlock, *ExitBlock;
+
+ do {
+ if (BinaryOperator *B_RHS = dyn_cast<BinaryOperator>(RHS))
+ if (B_RHS->isLogicalOp()) {
+ llvm::tie(RHSBlock, ExitBlock) =
+ VisitLogicalOperator(B_RHS, Term, TrueBlock, FalseBlock);
+ break;
+ }
+
+ // The RHS is not a nested logical operation. Don't push the terminator
+ // down further, but instead visit RHS and construct the respective
+ // pieces of the CFG, and link up the RHSBlock with the terminator
+ // we have been provided.
+ ExitBlock = RHSBlock = createBlock(false);
+
+ if (!Term) {
+ assert(TrueBlock == FalseBlock);
+ addSuccessor(RHSBlock, TrueBlock);
+ }
+ else {
+ RHSBlock->setTerminator(Term);
+ TryResult KnownVal = tryEvaluateBool(RHS);
+ addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
+ addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
}
- // Generate the blocks for evaluating the LHS.
- Block = LHSBlock;
- CFGBlock *EntryLHSBlock = addStmt(B->getLHS());
+ Block = RHSBlock;
+ RHSBlock = addStmt(RHS);
+ }
+ while (false);
- // See if this is a known constant.
- TryResult KnownVal = tryEvaluateBool(B->getLHS());
- if (KnownVal.isKnown() && (B->getOpcode() == BO_LOr))
- KnownVal.negate();
+ if (badCFG)
+ return std::make_pair((CFGBlock*)0, (CFGBlock*)0);
+
+ // Generate the blocks for evaluating the LHS.
+ Expr *LHS = B->getLHS()->IgnoreParens();
+
+ if (BinaryOperator *B_LHS = dyn_cast<BinaryOperator>(LHS))
+ if (B_LHS->isLogicalOp()) {
+ if (B->getOpcode() == BO_LOr)
+ FalseBlock = RHSBlock;
+ else
+ TrueBlock = RHSBlock;
- // Now link the LHSBlock with RHSBlock.
- if (B->getOpcode() == BO_LOr) {
- addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock);
- addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock);
- } else {
- assert(B->getOpcode() == BO_LAnd);
- addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock);
- addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock);
+ // For the LHS, treat 'B' as the terminator that we want to sink
+ // into the nested branch. The RHS always gets the top-most
+ // terminator.
+ return VisitLogicalOperator(B_LHS, B, TrueBlock, FalseBlock);
}
- return EntryLHSBlock;
+ // Create the block evaluating the LHS.
+ // This contains the '&&' or '||' as the terminator.
+ CFGBlock *LHSBlock = createBlock(false);
+ LHSBlock->setTerminator(B);
+
+ Block = LHSBlock;
+ CFGBlock *EntryLHSBlock = addStmt(LHS);
+
+ if (badCFG)
+ return std::make_pair((CFGBlock*)0, (CFGBlock*)0);
+
+ // See if this is a known constant.
+ TryResult KnownVal = tryEvaluateBool(LHS);
+
+ // Now link the LHSBlock with RHSBlock.
+ if (B->getOpcode() == BO_LOr) {
+ addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
+ addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : RHSBlock);
+ } else {
+ assert(B->getOpcode() == BO_LAnd);
+ addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock);
+ addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
}
+ return std::make_pair(EntryLHSBlock, ExitBlock);
+}
+
+
+CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B,
+ AddStmtChoice asc) {
+ // && or ||
+ if (B->isLogicalOp())
+ return VisitLogicalOperator(B);
+
if (B->getOpcode() == BO_Comma) { // ,
autoCreateBlock();
appendStmt(Block, B);
@@ -1284,7 +1340,7 @@ static bool CanThrow(Expr *E, ASTContext &Ctx) {
const FunctionType *FT = Ty->getAs<FunctionType>();
if (FT) {
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
- if (Proto->getExceptionSpecType() != EST_Uninstantiated &&
+ if (!isUnresolvedExceptionSpec(Proto->getExceptionSpecType()) &&
Proto->isNothrow(Ctx))
return false;
}
@@ -1435,6 +1491,12 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C,
if (badCFG)
return 0;
+ // If the condition is a logical '&&' or '||', build a more accurate CFG.
+ if (BinaryOperator *Cond =
+ dyn_cast<BinaryOperator>(C->getCond()->IgnoreParens()))
+ if (Cond->isLogicalOp())
+ return VisitLogicalOperator(Cond, C, LHSBlock, RHSBlock).first;
+
// Create the block that will contain the condition.
Block = createBlock(false);
@@ -1471,11 +1533,10 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) {
CFGBlock *B = 0;
- // FIXME: Add a reverse iterator for DeclStmt to avoid this extra copy.
- typedef SmallVector<Decl*,10> BufTy;
- BufTy Buf(DS->decl_begin(), DS->decl_end());
-
- for (BufTy::reverse_iterator I = Buf.rbegin(), E = Buf.rend(); I != E; ++I) {
+ // Build an individual DeclStmt for each decl.
+ for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(),
+ E = DS->decl_rend();
+ I != E; ++I) {
// Get the alignment of the new DeclStmt, padding out to >=8 bytes.
unsigned A = llvm::AlignOf<DeclStmt>::Alignment < 8
? 8 : llvm::AlignOf<DeclStmt>::Alignment;
@@ -1645,6 +1706,19 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
}
}
+ // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by
+ // having these handle the actual control-flow jump. Note that
+ // if we introduce a condition variable, e.g. "if (int x = exp1 || exp2)"
+ // we resort to the old control-flow behavior. This special handling
+ // removes infeasible paths from the control-flow graph by having the
+ // control-flow transfer of '&&' or '||' go directly into the then/else
+ // blocks directly.
+ if (!I->getConditionVariable())
+ if (BinaryOperator *Cond =
+ dyn_cast<BinaryOperator>(I->getCond()->IgnoreParens()))
+ if (Cond->isLogicalOp())
+ return VisitLogicalOperator(Cond, I, ThenBlock, ElseBlock).first;
+
// Now create a new block containing the if statement.
Block = createBlock(false);
@@ -1795,75 +1869,26 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);
- // Because of short-circuit evaluation, the condition of the loop can span
- // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
- // evaluate the condition.
- CFGBlock *ExitConditionBlock = createBlock(false);
- CFGBlock *EntryConditionBlock = ExitConditionBlock;
-
- // Set the terminator for the "exit" condition block.
- ExitConditionBlock->setTerminator(F);
-
- // Now add the actual condition to the condition block. Because the condition
- // itself may contain control-flow, new blocks may be created.
- if (Stmt *C = F->getCond()) {
- Block = ExitConditionBlock;
- EntryConditionBlock = addStmt(C);
- if (badCFG)
- return 0;
- assert(Block == EntryConditionBlock ||
- (Block == 0 && EntryConditionBlock == Succ));
-
- // If this block contains a condition variable, add both the condition
- // variable and initializer to the CFG.
- if (VarDecl *VD = F->getConditionVariable()) {
- if (Expr *Init = VD->getInit()) {
- autoCreateBlock();
- appendStmt(Block, F->getConditionVariableDeclStmt());
- EntryConditionBlock = addStmt(Init);
- assert(Block == EntryConditionBlock);
- }
- }
-
- if (Block) {
- if (badCFG)
- return 0;
- }
- }
-
- // The condition block is the implicit successor for the loop body as well as
- // any code above the loop.
- Succ = EntryConditionBlock;
-
- // See if this is a known constant.
- TryResult KnownVal(true);
-
- if (F->getCond())
- KnownVal = tryEvaluateBool(F->getCond());
+ CFGBlock *BodyBlock = 0, *TransitionBlock = 0;
// Now create the loop body.
{
assert(F->getBody());
- // Save the current values for Block, Succ, and continue targets.
- SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
- SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget);
+ // Save the current values for Block, Succ, continue and break targets.
+ SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
+ SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget);
- // Create a new block to contain the (bottom) of the loop body.
- Block = NULL;
-
- // Loop body should end with destructor of Condition variable (if any).
- addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F);
+ // Create an empty block to represent the transition block for looping back
+ // to the head of the loop. If we have increment code, it will
+ // go in this block as well.
+ Block = Succ = TransitionBlock = createBlock(false);
+ TransitionBlock->setLoopTarget(F);
if (Stmt *I = F->getInc()) {
// Generate increment code in its own basic block. This is the target of
// continue statements.
Succ = addStmt(I);
- } else {
- // No increment code. Create a special, empty, block that is used as the
- // target block for "looping back" to the start of the loop.
- assert(Succ == EntryConditionBlock);
- Succ = Block ? Block : createBlock();
}
// Finish up the increment (or empty) block if it hasn't been already.
@@ -1874,11 +1899,13 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
Block = 0;
}
- ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos);
+ // The starting block for the loop increment is the block that should
+ // represent the 'loop target' for looping back to the start of the loop.
+ ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos);
+ ContinueJumpTarget.block->setLoopTarget(F);
- // The starting block for the loop increment is the block that should
- // represent the 'loop target' for looping back to the start of the loop.
- ContinueJumpTarget.block->setLoopTarget(F);
+ // Loop body should end with destructor of Condition variable (if any).
+ addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F);
// If body is not a compound statement create implicit scope
// and add destructors.
@@ -1887,20 +1914,79 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
// Now populate the body block, and in the process create new blocks as we
// walk the body of the loop.
- CFGBlock *BodyBlock = addStmt(F->getBody());
+ BodyBlock = addStmt(F->getBody());
- if (!BodyBlock)
- BodyBlock = ContinueJumpTarget.block;//can happen for "for (...;...;...);"
+ if (!BodyBlock) {
+ // In the case of "for (...;...;...);" we can have a null BodyBlock.
+ // Use the continue jump target as the proxy for the body.
+ BodyBlock = ContinueJumpTarget.block;
+ }
else if (badCFG)
return 0;
+ }
+
+ // Because of short-circuit evaluation, the condition of the loop can span
+ // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
+ // evaluate the condition.
+ CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0;
+
+ do {
+ Expr *C = F->getCond();
+
+ // Specially handle logical operators, which have a slightly
+ // more optimal CFG representation.
+ if (BinaryOperator *Cond =
+ dyn_cast_or_null<BinaryOperator>(C ? C->IgnoreParens() : 0))
+ if (Cond->isLogicalOp()) {
+ llvm::tie(EntryConditionBlock, ExitConditionBlock) =
+ VisitLogicalOperator(Cond, F, BodyBlock, LoopSuccessor);
+ break;
+ }
- // This new body block is a successor to our "exit" condition block.
+ // The default case when not handling logical operators.
+ EntryConditionBlock = ExitConditionBlock = createBlock(false);
+ ExitConditionBlock->setTerminator(F);
+
+ // See if this is a known constant.
+ TryResult KnownVal(true);
+
+ if (C) {
+ // Now add the actual condition to the condition block.
+ // Because the condition itself may contain control-flow, new blocks may
+ // be created. Thus we update "Succ" after adding the condition.
+ Block = ExitConditionBlock;
+ EntryConditionBlock = addStmt(C);
+
+ // If this block contains a condition variable, add both the condition
+ // variable and initializer to the CFG.
+ if (VarDecl *VD = F->getConditionVariable()) {
+ if (Expr *Init = VD->getInit()) {
+ autoCreateBlock();
+ appendStmt(Block, F->getConditionVariableDeclStmt());
+ EntryConditionBlock = addStmt(Init);
+ assert(Block == EntryConditionBlock);
+ }
+ }
+
+ if (Block && badCFG)
+ return 0;
+
+ KnownVal = tryEvaluateBool(C);
+ }
+
+ // Add the loop body entry as a successor to the condition.
addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock);
- }
+ // Link up the condition block with the code that follows the loop. (the
+ // false branch).
+ addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor);
- // Link up the condition block with the code that follows the loop. (the
- // false branch).
- addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor);
+ } while (false);
+
+ // Link up the loop-back block to the entry condition block.
+ addSuccessor(TransitionBlock, EntryConditionBlock);
+
+ // The condition block is the implicit successor for any code above the loop.
+ Succ = EntryConditionBlock;
// If the loop contains initialization, create a new block for those
// statements. This block can also contain statements that precede the loop.
@@ -2108,74 +2194,30 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
return 0;
LoopSuccessor = Block;
Block = 0;
- } else
+ } else {
LoopSuccessor = Succ;
-
- // Because of short-circuit evaluation, the condition of the loop can span
- // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
- // evaluate the condition.
- CFGBlock *ExitConditionBlock = createBlock(false);
- CFGBlock *EntryConditionBlock = ExitConditionBlock;
-
- // Set the terminator for the "exit" condition block.
- ExitConditionBlock->setTerminator(W);
-
- // Now add the actual condition to the condition block. Because the condition
- // itself may contain control-flow, new blocks may be created. Thus we update
- // "Succ" after adding the condition.
- if (Stmt *C = W->getCond()) {
- Block = ExitConditionBlock;
- EntryConditionBlock = addStmt(C);
- // The condition might finish the current 'Block'.
- Block = EntryConditionBlock;
-
- // If this block contains a condition variable, add both the condition
- // variable and initializer to the CFG.
- if (VarDecl *VD = W->getConditionVariable()) {
- if (Expr *Init = VD->getInit()) {
- autoCreateBlock();
- appendStmt(Block, W->getConditionVariableDeclStmt());
- EntryConditionBlock = addStmt(Init);
- assert(Block == EntryConditionBlock);
- }
- }
-
- if (Block) {
- if (badCFG)
- return 0;
- }
}
- // The condition block is the implicit successor for the loop body as well as
- // any code above the loop.
- Succ = EntryConditionBlock;
-
- // See if this is a known constant.
- const TryResult& KnownVal = tryEvaluateBool(W->getCond());
+ CFGBlock *BodyBlock = 0, *TransitionBlock = 0;
// Process the loop body.
{
assert(W->getBody());
- // Save the current values for Block, Succ, and continue and break targets
+ // Save the current values for Block, Succ, continue and break targets.
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget),
- save_break(BreakJumpTarget);
+ save_break(BreakJumpTarget);
// Create an empty block to represent the transition block for looping back
// to the head of the loop.
- Block = 0;
- assert(Succ == EntryConditionBlock);
- Succ = createBlock();
- Succ->setLoopTarget(W);
+ Succ = TransitionBlock = createBlock(false);
+ TransitionBlock->setLoopTarget(W);
ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos);
// All breaks should go to the code following the loop.
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);
- // NULL out Block to force lazy instantiation of blocks for the body.
- Block = NULL;
-
// Loop body should end with destructor of Condition variable (if any).
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
@@ -2185,22 +2227,69 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
addLocalScopeAndDtors(W->getBody());
// Create the body. The returned block is the entry to the loop body.
- CFGBlock *BodyBlock = addStmt(W->getBody());
+ BodyBlock = addStmt(W->getBody());
if (!BodyBlock)
BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;"
- else if (Block) {
- if (badCFG)
- return 0;
+ else if (Block && badCFG)
+ return 0;
+ }
+
+ // Because of short-circuit evaluation, the condition of the loop can span
+ // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
+ // evaluate the condition.
+ CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0;
+
+ do {
+ Expr *C = W->getCond();
+
+ // Specially handle logical operators, which have a slightly
+ // more optimal CFG representation.
+ if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(C->IgnoreParens()))
+ if (Cond->isLogicalOp()) {
+ llvm::tie(EntryConditionBlock, ExitConditionBlock) =
+ VisitLogicalOperator(Cond, W, BodyBlock,
+ LoopSuccessor);
+ break;
+ }
+
+ // The default case when not handling logical operators.
+ EntryConditionBlock = ExitConditionBlock = createBlock(false);
+ ExitConditionBlock->setTerminator(W);
+
+ // Now add the actual condition to the condition block.
+ // Because the condition itself may contain control-flow, new blocks may
+ // be created. Thus we update "Succ" after adding the condition.
+ Block = ExitConditionBlock;
+ Block = EntryConditionBlock = addStmt(C);
+
+ // If this block contains a condition variable, add both the condition
+ // variable and initializer to the CFG.
+ if (VarDecl *VD = W->getConditionVariable()) {
+ if (Expr *Init = VD->getInit()) {
+ autoCreateBlock();
+ appendStmt(Block, W->getConditionVariableDeclStmt());
+ EntryConditionBlock = addStmt(Init);
+ assert(Block == EntryConditionBlock);
+ }
}
+ if (Block && badCFG)
+ return 0;
+
+ // See if this is a known constant.
+ const TryResult& KnownVal = tryEvaluateBool(C);
+
// Add the loop body entry as a successor to the condition.
addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock);
- }
+ // Link up the condition block with the code that follows the loop. (the
+ // false branch).
+ addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor);
- // Link up the condition block with the code that follows the loop. (the
- // false branch).
- addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor);
+ } while(false);
+
+ // Link up the loop-back block to the entry condition block.
+ addSuccessor(TransitionBlock, EntryConditionBlock);
// There can be no more statements in the condition block since we loop back
// to this block. NULL out Block to force lazy creation of another block.
@@ -3203,8 +3292,8 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
}
bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const {
- if (const CXXDestructorDecl *cdecl = getDestructorDecl(astContext)) {
- QualType ty = cdecl->getType();
+ if (const CXXDestructorDecl *decl = getDestructorDecl(astContext)) {
+ QualType ty = decl->getType();
return cast<FunctionType>(ty)->getNoReturnAttr();
}
return false;
@@ -3631,8 +3720,7 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper,
const Type* T = VD->getType().getTypePtr();
if (const ReferenceType* RT = T->getAs<ReferenceType>())
T = RT->getPointeeType().getTypePtr();
- else if (const Type *ET = T->getArrayElementTypeNoTypeQual())
- T = ET;
+ T = T->getBaseElementTypeUnsafe();
OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
OS << " (Implicit destructor)\n";
@@ -3644,11 +3732,7 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper,
} else if (const CFGMemberDtor *ME = E.getAs<CFGMemberDtor>()) {
const FieldDecl *FD = ME->getFieldDecl();
-
- const Type *T = FD->getType().getTypePtr();
- if (const Type *ET = T->getArrayElementTypeNoTypeQual())
- T = ET;
-
+ const Type *T = FD->getType()->getBaseElementTypeUnsafe();
OS << "this->" << FD->getName();
OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()";
OS << " (Member object destructor)\n";
diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt
index ca2392b..d57e481 100644
--- a/lib/Analysis/CMakeLists.txt
+++ b/lib/Analysis/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(LLVM_USED_LIBS clangBasic clangAST)
-
add_clang_library(clangAnalysis
AnalysisDeclContext.cpp
CallGraph.cpp
@@ -20,5 +18,17 @@ add_clang_library(clangAnalysis
UninitializedValues.cpp
)
-add_dependencies(clangAnalysis ClangAttrClasses ClangAttrList
- ClangDiagnosticAnalysis ClangDeclNodes ClangStmtNodes)
+add_dependencies(clangAnalysis
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDiagnosticCommon
+ ClangDeclNodes
+ ClangDiagnosticAnalysis
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangAnalysis
+ clangBasic
+ clangAST
+ )
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index 96a16c3..6b75956 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/lib/Analysis/CallGraph.cpp
@@ -25,12 +25,11 @@ namespace {
/// given function body.
class CGBuilder : public StmtVisitor<CGBuilder> {
CallGraph *G;
- const Decl *FD;
CallGraphNode *CallerNode;
public:
- CGBuilder(CallGraph *g, const Decl *D, CallGraphNode *N)
- : G(g), FD(D), CallerNode(N) {}
+ CGBuilder(CallGraph *g, CallGraphNode *N)
+ : G(g), CallerNode(N) {}
void VisitStmt(Stmt *S) { VisitChildren(S); }
@@ -99,7 +98,7 @@ void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) {
Root->addCallee(Node, this);
// Process all the calls by this function as well.
- CGBuilder builder(this, D, Node);
+ CGBuilder builder(this, Node);
if (Stmt *Body = D->getBody())
builder.Visit(Body);
}
diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp
index 7e9e38f..ce973af 100644
--- a/lib/Analysis/CocoaConventions.cpp
+++ b/lib/Analysis/CocoaConventions.cpp
@@ -17,6 +17,8 @@
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cctype>
+
using namespace clang;
using namespace ento;
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index ba45865..e7ea486 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/lib/Analysis/FormatString.cpp
@@ -15,7 +15,7 @@
#include "FormatStringParsing.h"
#include "clang/Basic/LangOptions.h"
-using clang::analyze_format_string::ArgTypeResult;
+using clang::analyze_format_string::ArgType;
using clang::analyze_format_string::FormatStringHandler;
using clang::analyze_format_string::FormatSpecifier;
using clang::analyze_format_string::LengthModifier;
@@ -229,18 +229,34 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
}
//===----------------------------------------------------------------------===//
-// Methods on ArgTypeResult.
+// Methods on ArgType.
//===----------------------------------------------------------------------===//
-bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
+bool ArgType::matchesType(ASTContext &C, QualType argTy) const {
+ if (Ptr) {
+ // It has to be a pointer.
+ const PointerType *PT = argTy->getAs<PointerType>();
+ if (!PT)
+ return false;
+
+ // We cannot write through a const qualified pointer.
+ if (PT->getPointeeType().isConstQualified())
+ return false;
+
+ argTy = PT->getPointeeType();
+ }
+
switch (K) {
case InvalidTy:
- llvm_unreachable("ArgTypeResult must be valid");
+ llvm_unreachable("ArgType must be valid");
case UnknownTy:
return true;
case AnyCharTy: {
+ if (const EnumType *ETy = argTy->getAs<EnumType>())
+ argTy = ETy->getDecl()->getIntegerType();
+
if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
switch (BT->getKind()) {
default:
@@ -255,7 +271,10 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
}
case SpecificTy: {
+ if (const EnumType *ETy = argTy->getAs<EnumType>())
+ argTy = ETy->getDecl()->getIntegerType();
argTy = C.getCanonicalType(argTy).getUnqualifiedType();
+
if (T == argTy)
return true;
// Check for "compatible types".
@@ -265,10 +284,9 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
break;
case BuiltinType::Char_S:
case BuiltinType::SChar:
- return T == C.UnsignedCharTy;
case BuiltinType::Char_U:
case BuiltinType::UChar:
- return T == C.SignedCharTy;
+ return T == C.UnsignedCharTy || T == C.SignedCharTy;
case BuiltinType::Short:
return T == C.UnsignedShortTy;
case BuiltinType::UShort:
@@ -319,20 +337,21 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
}
case WIntTy: {
- // Instead of doing a lookup for the definition of 'wint_t' (which
- // is defined by the system headers) instead see if wchar_t and
- // the argument type promote to the same type.
- QualType PromoWChar =
- C.getWCharType()->isPromotableIntegerType()
- ? C.getPromotedIntegerType(C.getWCharType()) : C.getWCharType();
+
QualType PromoArg =
argTy->isPromotableIntegerType()
? C.getPromotedIntegerType(argTy) : argTy;
- PromoWChar = C.getCanonicalType(PromoWChar).getUnqualifiedType();
+ QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType();
PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType();
- return PromoWChar == PromoArg;
+ // If the promoted argument is the corresponding signed type of the
+ // wint_t type, then it should match.
+ if (PromoArg->hasSignedIntegerRepresentation() &&
+ C.getCorrespondingUnsignedType(PromoArg) == WInt)
+ return true;
+
+ return WInt == PromoArg;
}
case CPointerTy:
@@ -358,40 +377,63 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const {
}
}
- llvm_unreachable("Invalid ArgTypeResult Kind!");
+ llvm_unreachable("Invalid ArgType Kind!");
}
-QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const {
+QualType ArgType::getRepresentativeType(ASTContext &C) const {
+ QualType Res;
switch (K) {
case InvalidTy:
- llvm_unreachable("No representative type for Invalid ArgTypeResult");
+ llvm_unreachable("No representative type for Invalid ArgType");
case UnknownTy:
- return QualType();
+ llvm_unreachable("No representative type for Unknown ArgType");
case AnyCharTy:
- return C.CharTy;
+ Res = C.CharTy;
+ break;
case SpecificTy:
- return T;
+ Res = T;
+ break;
case CStrTy:
- return C.getPointerType(C.CharTy);
+ Res = C.getPointerType(C.CharTy);
+ break;
case WCStrTy:
- return C.getPointerType(C.getWCharType());
+ Res = C.getPointerType(C.getWCharType());
+ break;
case ObjCPointerTy:
- return C.ObjCBuiltinIdTy;
+ Res = C.ObjCBuiltinIdTy;
+ break;
case CPointerTy:
- return C.VoidPtrTy;
+ Res = C.VoidPtrTy;
+ break;
case WIntTy: {
- QualType WC = C.getWCharType();
- return WC->isPromotableIntegerType() ? C.getPromotedIntegerType(WC) : WC;
+ Res = C.getWIntType();
+ break;
}
}
- llvm_unreachable("Invalid ArgTypeResult Kind!");
+ if (Ptr)
+ Res = C.getPointerType(Res);
+ return Res;
}
-std::string ArgTypeResult::getRepresentativeTypeName(ASTContext &C) const {
+std::string ArgType::getRepresentativeTypeName(ASTContext &C) const {
std::string S = getRepresentativeType(C).getAsString();
- if (Name && S != Name)
- return std::string("'") + Name + "' (aka '" + S + "')";
+
+ std::string Alias;
+ if (Name) {
+ // Use a specific name for this type, e.g. "size_t".
+ Alias = Name;
+ if (Ptr) {
+ // If ArgType is actually a pointer to T, append an asterisk.
+ Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *";
+ }
+ // If Alias is the same as the underlying type, e.g. wchar_t, then drop it.
+ if (S == Alias)
+ Alias.clear();
+ }
+
+ if (!Alias.empty())
+ return std::string("'") + Alias + "' (aka '" + S + "')";
return std::string("'") + S + "'";
}
@@ -400,7 +442,7 @@ std::string ArgTypeResult::getRepresentativeTypeName(ASTContext &C) const {
// Methods on OptionalAmount.
//===----------------------------------------------------------------------===//
-ArgTypeResult
+ArgType
analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const {
return Ctx.IntTy;
}
@@ -676,3 +718,37 @@ bool FormatSpecifier::hasStandardLengthConversionCombination() const {
}
return true;
}
+
+bool FormatSpecifier::namedTypeToLengthModifier(QualType QT,
+ LengthModifier &LM) {
+ assert(isa<TypedefType>(QT) && "Expected a TypedefType");
+ const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl();
+
+ for (;;) {
+ const IdentifierInfo *Identifier = Typedef->getIdentifier();
+ if (Identifier->getName() == "size_t") {
+ LM.setKind(LengthModifier::AsSizeT);
+ return true;
+ } else if (Identifier->getName() == "ssize_t") {
+ // Not C99, but common in Unix.
+ LM.setKind(LengthModifier::AsSizeT);
+ return true;
+ } else if (Identifier->getName() == "intmax_t") {
+ LM.setKind(LengthModifier::AsIntMax);
+ return true;
+ } else if (Identifier->getName() == "uintmax_t") {
+ LM.setKind(LengthModifier::AsIntMax);
+ return true;
+ } else if (Identifier->getName() == "ptrdiff_t") {
+ LM.setKind(LengthModifier::AsPtrDiff);
+ return true;
+ }
+
+ QualType T = Typedef->getUnderlyingType();
+ if (!isa<TypedefType>(T))
+ break;
+
+ Typedef = cast<TypedefType>(T)->getDecl();
+ }
+ return false;
+}
diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp
index ff6607d..38f8199 100644
--- a/lib/Analysis/LiveVariables.cpp
+++ b/lib/Analysis/LiveVariables.cpp
@@ -284,6 +284,14 @@ void TransferFunctions::Visit(Stmt *S) {
}
break;
}
+ case Stmt::ObjCMessageExprClass: {
+ // In calls to super, include the implicit "self" pointer as being live.
+ ObjCMessageExpr *CE = cast<ObjCMessageExpr>(S);
+ if (CE->getReceiverKind() == ObjCMessageExpr::SuperInstance)
+ val.liveDecls = LV.DSetFact.add(val.liveDecls,
+ LV.analysisContext.getSelfDecl());
+ break;
+ }
case Stmt::DeclStmtClass: {
const DeclStmt *DS = cast<DeclStmt>(S);
if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) {
@@ -455,6 +463,12 @@ LiveVariablesImpl::runOnBlock(const CFGBlock *block,
for (CFGBlock::const_reverse_iterator it = block->rbegin(),
ei = block->rend(); it != ei; ++it) {
const CFGElement &elem = *it;
+
+ if (const CFGAutomaticObjDtor *Dtor = dyn_cast<CFGAutomaticObjDtor>(&elem)){
+ val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl());
+ continue;
+ }
+
if (!isa<CFGStmt>(elem))
continue;
@@ -486,6 +500,11 @@ LiveVariables::computeLiveness(AnalysisDeclContext &AC,
if (!cfg)
return 0;
+ // The analysis currently has scalability issues for very large CFGs.
+ // Bail out if it looks too large.
+ if (cfg->getNumBlockIDs() > 300000)
+ return 0;
+
LiveVariablesImpl *LV = new LiveVariablesImpl(AC, killAtAssign);
// Construct the dataflow worklist. Enqueue the exit block as the
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index e1049b3..9e4c0fe 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -15,7 +15,7 @@
#include "clang/Analysis/Analyses/FormatString.h"
#include "FormatStringParsing.h"
-using clang::analyze_format_string::ArgTypeResult;
+using clang::analyze_format_string::ArgType;
using clang::analyze_format_string::FormatStringHandler;
using clang::analyze_format_string::LengthModifier;
using clang::analyze_format_string::OptionalAmount;
@@ -241,20 +241,20 @@ bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
// Methods on PrintfSpecifier.
//===----------------------------------------------------------------------===//
-ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx,
- bool IsObjCLiteral) const {
+ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
+ bool IsObjCLiteral) const {
const PrintfConversionSpecifier &CS = getConversionSpecifier();
if (!CS.consumesDataArgument())
- return ArgTypeResult::Invalid();
+ return ArgType::Invalid();
if (CS.getKind() == ConversionSpecifier::cArg)
switch (LM.getKind()) {
case LengthModifier::None: return Ctx.IntTy;
case LengthModifier::AsLong:
- return ArgTypeResult(ArgTypeResult::WIntTy, "wint_t");
+ return ArgType(ArgType::WIntTy, "wint_t");
default:
- return ArgTypeResult::Invalid();
+ return ArgType::Invalid();
}
if (CS.isIntArg())
@@ -263,22 +263,22 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx,
// GNU extension.
return Ctx.LongLongTy;
case LengthModifier::None: return Ctx.IntTy;
- case LengthModifier::AsChar: return ArgTypeResult::AnyCharTy;
+ case LengthModifier::AsChar: return ArgType::AnyCharTy;
case LengthModifier::AsShort: return Ctx.ShortTy;
case LengthModifier::AsLong: return Ctx.LongTy;
case LengthModifier::AsLongLong:
case LengthModifier::AsQuad:
return Ctx.LongLongTy;
case LengthModifier::AsIntMax:
- return ArgTypeResult(Ctx.getIntMaxType(), "intmax_t");
+ return ArgType(Ctx.getIntMaxType(), "intmax_t");
case LengthModifier::AsSizeT:
// FIXME: How to get the corresponding signed version of size_t?
- return ArgTypeResult();
+ return ArgType();
case LengthModifier::AsPtrDiff:
- return ArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t");
+ return ArgType(Ctx.getPointerDiffType(), "ptrdiff_t");
case LengthModifier::AsAllocate:
case LengthModifier::AsMAllocate:
- return ArgTypeResult::Invalid();
+ return ArgType::Invalid();
}
if (CS.isUIntArg())
@@ -294,16 +294,16 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx,
case LengthModifier::AsQuad:
return Ctx.UnsignedLongLongTy;
case LengthModifier::AsIntMax:
- return ArgTypeResult(Ctx.getUIntMaxType(), "uintmax_t");
+ return ArgType(Ctx.getUIntMaxType(), "uintmax_t");
case LengthModifier::AsSizeT:
- return ArgTypeResult(Ctx.getSizeType(), "size_t");
+ return ArgType(Ctx.getSizeType(), "size_t");
case LengthModifier::AsPtrDiff:
// FIXME: How to get the corresponding unsigned
// version of ptrdiff_t?
- return ArgTypeResult();
+ return ArgType();
case LengthModifier::AsAllocate:
case LengthModifier::AsMAllocate:
- return ArgTypeResult::Invalid();
+ return ArgType::Invalid();
}
if (CS.isDoubleArg()) {
@@ -312,37 +312,90 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx,
return Ctx.DoubleTy;
}
+ if (CS.getKind() == ConversionSpecifier::nArg) {
+ switch (LM.getKind()) {
+ case LengthModifier::None:
+ return ArgType::PtrTo(Ctx.IntTy);
+ case LengthModifier::AsChar:
+ return ArgType::PtrTo(Ctx.SignedCharTy);
+ case LengthModifier::AsShort:
+ return ArgType::PtrTo(Ctx.ShortTy);
+ case LengthModifier::AsLong:
+ return ArgType::PtrTo(Ctx.LongTy);
+ case LengthModifier::AsLongLong:
+ case LengthModifier::AsQuad:
+ return ArgType::PtrTo(Ctx.LongLongTy);
+ case LengthModifier::AsIntMax:
+ return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
+ case LengthModifier::AsSizeT:
+ return ArgType(); // FIXME: ssize_t
+ case LengthModifier::AsPtrDiff:
+ return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
+ case LengthModifier::AsLongDouble:
+ return ArgType(); // FIXME: Is this a known extension?
+ case LengthModifier::AsAllocate:
+ case LengthModifier::AsMAllocate:
+ return ArgType::Invalid();
+ }
+ }
+
switch (CS.getKind()) {
case ConversionSpecifier::sArg:
if (LM.getKind() == LengthModifier::AsWideChar) {
if (IsObjCLiteral)
return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst());
- return ArgTypeResult(ArgTypeResult::WCStrTy, "wchar_t *");
+ return ArgType(ArgType::WCStrTy, "wchar_t *");
}
- return ArgTypeResult::CStrTy;
+ return ArgType::CStrTy;
case ConversionSpecifier::SArg:
if (IsObjCLiteral)
return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst());
- return ArgTypeResult(ArgTypeResult::WCStrTy, "wchar_t *");
+ return ArgType(ArgType::WCStrTy, "wchar_t *");
case ConversionSpecifier::CArg:
if (IsObjCLiteral)
return Ctx.UnsignedShortTy;
- return ArgTypeResult(Ctx.WCharTy, "wchar_t");
+ return ArgType(Ctx.WCharTy, "wchar_t");
case ConversionSpecifier::pArg:
- return ArgTypeResult::CPointerTy;
+ return ArgType::CPointerTy;
case ConversionSpecifier::ObjCObjArg:
- return ArgTypeResult::ObjCPointerTy;
+ return ArgType::ObjCPointerTy;
default:
break;
}
// FIXME: Handle other cases.
- return ArgTypeResult();
+ return ArgType();
}
bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
ASTContext &Ctx, bool IsObjCLiteral) {
- // Handle strings first (char *, wchar_t *)
+ // %n is different from other conversion specifiers; don't try to fix it.
+ if (CS.getKind() == ConversionSpecifier::nArg)
+ return false;
+
+ // Handle Objective-C objects first. Note that while the '%@' specifier will
+ // not warn for structure pointer or void pointer arguments (because that's
+ // how CoreFoundation objects are implemented), we only show a fixit for '%@'
+ // if we know it's an object (block, id, class, or __attribute__((NSObject))).
+ if (QT->isObjCRetainableType()) {
+ if (!IsObjCLiteral)
+ return false;
+
+ CS.setKind(ConversionSpecifier::ObjCObjArg);
+
+ // Disable irrelevant flags
+ HasThousandsGrouping = false;
+ HasPlusPrefix = false;
+ HasSpacePrefix = false;
+ HasAlternativeForm = false;
+ HasLeadingZeroes = false;
+ Precision.setHowSpecified(OptionalAmount::NotSpecified);
+ LM.setKind(LengthModifier::None);
+
+ return true;
+ }
+
+ // Handle strings next (char *, wchar_t *)
if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
CS.setKind(ConversionSpecifier::sArg);
@@ -359,6 +412,10 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
return true;
}
+ // If it's an enum, get its underlying type.
+ if (const EnumType *ETy = QT->getAs<EnumType>())
+ QT = ETy->getDecl()->getIntegerType();
+
// We can only work with builtin types.
const BuiltinType *BT = QT->getAs<BuiltinType>();
if (!BT)
@@ -421,24 +478,11 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
}
// Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
- if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) {
- const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier();
- if (Identifier->getName() == "size_t") {
- LM.setKind(LengthModifier::AsSizeT);
- } else if (Identifier->getName() == "ssize_t") {
- // Not C99, but common in Unix.
- LM.setKind(LengthModifier::AsSizeT);
- } else if (Identifier->getName() == "intmax_t") {
- LM.setKind(LengthModifier::AsIntMax);
- } else if (Identifier->getName() == "uintmax_t") {
- LM.setKind(LengthModifier::AsIntMax);
- } else if (Identifier->getName() == "ptrdiff_t") {
- LM.setKind(LengthModifier::AsPtrDiff);
- }
- }
+ if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x))
+ namedTypeToLengthModifier(QT, LM);
// If fixing the length modifier was enough, we are done.
- const analyze_printf::ArgTypeResult &ATR = getArgType(Ctx, IsObjCLiteral);
+ const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral);
if (hasValidLengthModifier() && ATR.isValid() && ATR.matchesType(Ctx, QT))
return true;
diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp
index 3f711b4..7d67e8a 100644
--- a/lib/Analysis/ProgramPoint.cpp
+++ b/lib/Analysis/ProgramPoint.cpp
@@ -36,8 +36,10 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
return PreStore(S, LC, tag);
case ProgramPoint::PostLValueKind:
return PostLValue(S, LC, tag);
- case ProgramPoint::PostPurgeDeadSymbolsKind:
- return PostPurgeDeadSymbols(S, LC, tag);
+ case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
+ return PostStmtPurgeDeadSymbols(S, LC, tag);
+ case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
+ return PreStmtPurgeDeadSymbols(S, LC, tag);
}
}
diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp
index c8b491a..5d659ce 100644
--- a/lib/Analysis/PseudoConstantAnalysis.cpp
+++ b/lib/Analysis/PseudoConstantAnalysis.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include <deque>
using namespace clang;
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index 6bc4adb..2942400 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -15,12 +15,11 @@
#include "clang/Analysis/Analyses/FormatString.h"
#include "FormatStringParsing.h"
-using clang::analyze_format_string::ArgTypeResult;
+using clang::analyze_format_string::ArgType;
using clang::analyze_format_string::FormatStringHandler;
using clang::analyze_format_string::LengthModifier;
using clang::analyze_format_string::OptionalAmount;
using clang::analyze_format_string::ConversionSpecifier;
-using clang::analyze_scanf::ScanfArgTypeResult;
using clang::analyze_scanf::ScanfConversionSpecifier;
using clang::analyze_scanf::ScanfSpecifier;
using clang::UpdateOnReturn;
@@ -194,37 +193,42 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
return ScanfSpecifierResult(Start, FS);
}
-ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
+ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
const ScanfConversionSpecifier &CS = getConversionSpecifier();
if (!CS.consumesDataArgument())
- return ScanfArgTypeResult::Invalid();
+ return ArgType::Invalid();
switch(CS.getKind()) {
// Signed int.
case ConversionSpecifier::dArg:
case ConversionSpecifier::iArg:
switch (LM.getKind()) {
- case LengthModifier::None: return ArgTypeResult(Ctx.IntTy);
+ case LengthModifier::None:
+ return ArgType::PtrTo(Ctx.IntTy);
case LengthModifier::AsChar:
- return ArgTypeResult(ArgTypeResult::AnyCharTy);
- case LengthModifier::AsShort: return ArgTypeResult(Ctx.ShortTy);
- case LengthModifier::AsLong: return ArgTypeResult(Ctx.LongTy);
+ return ArgType::PtrTo(ArgType::AnyCharTy);
+ case LengthModifier::AsShort:
+ return ArgType::PtrTo(Ctx.ShortTy);
+ case LengthModifier::AsLong:
+ return ArgType::PtrTo(Ctx.LongTy);
case LengthModifier::AsLongLong:
case LengthModifier::AsQuad:
- return ArgTypeResult(Ctx.LongLongTy);
+ return ArgType::PtrTo(Ctx.LongLongTy);
case LengthModifier::AsIntMax:
- return ScanfArgTypeResult(Ctx.getIntMaxType(), "intmax_t *");
+ return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
case LengthModifier::AsSizeT:
// FIXME: ssize_t.
- return ScanfArgTypeResult();
+ return ArgType();
case LengthModifier::AsPtrDiff:
- return ScanfArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t *");
+ return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
case LengthModifier::AsLongDouble:
// GNU extension.
- return ArgTypeResult(Ctx.LongLongTy);
- case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid();
- case LengthModifier::AsMAllocate: return ScanfArgTypeResult::Invalid();
+ return ArgType::PtrTo(Ctx.LongLongTy);
+ case LengthModifier::AsAllocate:
+ return ArgType::Invalid();
+ case LengthModifier::AsMAllocate:
+ return ArgType::Invalid();
}
// Unsigned int.
@@ -233,25 +237,31 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case ConversionSpecifier::xArg:
case ConversionSpecifier::XArg:
switch (LM.getKind()) {
- case LengthModifier::None: return ArgTypeResult(Ctx.UnsignedIntTy);
- case LengthModifier::AsChar: return ArgTypeResult(Ctx.UnsignedCharTy);
- case LengthModifier::AsShort: return ArgTypeResult(Ctx.UnsignedShortTy);
- case LengthModifier::AsLong: return ArgTypeResult(Ctx.UnsignedLongTy);
+ case LengthModifier::None:
+ return ArgType::PtrTo(Ctx.UnsignedIntTy);
+ case LengthModifier::AsChar:
+ return ArgType::PtrTo(Ctx.UnsignedCharTy);
+ case LengthModifier::AsShort:
+ return ArgType::PtrTo(Ctx.UnsignedShortTy);
+ case LengthModifier::AsLong:
+ return ArgType::PtrTo(Ctx.UnsignedLongTy);
case LengthModifier::AsLongLong:
case LengthModifier::AsQuad:
- return ArgTypeResult(Ctx.UnsignedLongLongTy);
+ return ArgType::PtrTo(Ctx.UnsignedLongLongTy);
case LengthModifier::AsIntMax:
- return ScanfArgTypeResult(Ctx.getUIntMaxType(), "uintmax_t *");
+ return ArgType::PtrTo(ArgType(Ctx.getUIntMaxType(), "uintmax_t"));
case LengthModifier::AsSizeT:
- return ScanfArgTypeResult(Ctx.getSizeType(), "size_t *");
+ return ArgType::PtrTo(ArgType(Ctx.getSizeType(), "size_t"));
case LengthModifier::AsPtrDiff:
// FIXME: Unsigned version of ptrdiff_t?
- return ScanfArgTypeResult();
+ return ArgType();
case LengthModifier::AsLongDouble:
// GNU extension.
- return ArgTypeResult(Ctx.UnsignedLongLongTy);
- case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid();
- case LengthModifier::AsMAllocate: return ScanfArgTypeResult::Invalid();
+ return ArgType::PtrTo(Ctx.UnsignedLongLongTy);
+ case LengthModifier::AsAllocate:
+ return ArgType::Invalid();
+ case LengthModifier::AsMAllocate:
+ return ArgType::Invalid();
}
// Float.
@@ -264,12 +274,14 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case ConversionSpecifier::gArg:
case ConversionSpecifier::GArg:
switch (LM.getKind()) {
- case LengthModifier::None: return ArgTypeResult(Ctx.FloatTy);
- case LengthModifier::AsLong: return ArgTypeResult(Ctx.DoubleTy);
+ case LengthModifier::None:
+ return ArgType::PtrTo(Ctx.FloatTy);
+ case LengthModifier::AsLong:
+ return ArgType::PtrTo(Ctx.DoubleTy);
case LengthModifier::AsLongDouble:
- return ArgTypeResult(Ctx.LongDoubleTy);
+ return ArgType::PtrTo(Ctx.LongDoubleTy);
default:
- return ScanfArgTypeResult::Invalid();
+ return ArgType::Invalid();
}
// Char, string and scanlist.
@@ -277,37 +289,65 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case ConversionSpecifier::sArg:
case ConversionSpecifier::ScanListArg:
switch (LM.getKind()) {
- case LengthModifier::None: return ScanfArgTypeResult::CStrTy;
+ case LengthModifier::None:
+ return ArgType::PtrTo(ArgType::AnyCharTy);
case LengthModifier::AsLong:
- return ScanfArgTypeResult(ScanfArgTypeResult::WCStrTy, "wchar_t *");
+ return ArgType::PtrTo(ArgType(Ctx.getWCharType(), "wchar_t"));
case LengthModifier::AsAllocate:
case LengthModifier::AsMAllocate:
- return ScanfArgTypeResult(ArgTypeResult::CStrTy);
+ return ArgType::PtrTo(ArgType::CStrTy);
default:
- return ScanfArgTypeResult::Invalid();
+ return ArgType::Invalid();
}
case ConversionSpecifier::CArg:
case ConversionSpecifier::SArg:
// FIXME: Mac OS X specific?
switch (LM.getKind()) {
case LengthModifier::None:
- return ScanfArgTypeResult(ScanfArgTypeResult::WCStrTy, "wchar_t *");
+ return ArgType::PtrTo(ArgType(Ctx.getWCharType(), "wchar_t"));
case LengthModifier::AsAllocate:
case LengthModifier::AsMAllocate:
- return ScanfArgTypeResult(ArgTypeResult::WCStrTy, "wchar_t **");
+ return ArgType::PtrTo(ArgType(ArgType::WCStrTy, "wchar_t *"));
default:
- return ScanfArgTypeResult::Invalid();
+ return ArgType::Invalid();
}
// Pointer.
case ConversionSpecifier::pArg:
- return ScanfArgTypeResult(ArgTypeResult(ArgTypeResult::CPointerTy));
+ return ArgType::PtrTo(ArgType::CPointerTy);
+
+ // Write-back.
+ case ConversionSpecifier::nArg:
+ switch (LM.getKind()) {
+ case LengthModifier::None:
+ return ArgType::PtrTo(Ctx.IntTy);
+ case LengthModifier::AsChar:
+ return ArgType::PtrTo(Ctx.SignedCharTy);
+ case LengthModifier::AsShort:
+ return ArgType::PtrTo(Ctx.ShortTy);
+ case LengthModifier::AsLong:
+ return ArgType::PtrTo(Ctx.LongTy);
+ case LengthModifier::AsLongLong:
+ case LengthModifier::AsQuad:
+ return ArgType::PtrTo(Ctx.LongLongTy);
+ case LengthModifier::AsIntMax:
+ return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
+ case LengthModifier::AsSizeT:
+ return ArgType(); // FIXME: ssize_t
+ case LengthModifier::AsPtrDiff:
+ return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
+ case LengthModifier::AsLongDouble:
+ return ArgType(); // FIXME: Is this a known extension?
+ case LengthModifier::AsAllocate:
+ case LengthModifier::AsMAllocate:
+ return ArgType::Invalid();
+ }
default:
break;
}
- return ScanfArgTypeResult();
+ return ArgType();
}
bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
@@ -315,7 +355,16 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
if (!QT->isPointerType())
return false;
+ // %n is different from other conversion specifiers; don't try to fix it.
+ if (CS.getKind() == ConversionSpecifier::nArg)
+ return false;
+
QualType PT = QT->getPointeeType();
+
+ // If it's an enum, get its underlying type.
+ if (const EnumType *ETy = QT->getAs<EnumType>())
+ QT = ETy->getDecl()->getIntegerType();
+
const BuiltinType *BT = PT->getAs<BuiltinType>();
if (!BT)
return false;
@@ -377,25 +426,12 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
}
// Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
- if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) {
- const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier();
- if (Identifier->getName() == "size_t") {
- LM.setKind(LengthModifier::AsSizeT);
- } else if (Identifier->getName() == "ssize_t") {
- // Not C99, but common in Unix.
- LM.setKind(LengthModifier::AsSizeT);
- } else if (Identifier->getName() == "intmax_t") {
- LM.setKind(LengthModifier::AsIntMax);
- } else if (Identifier->getName() == "uintmax_t") {
- LM.setKind(LengthModifier::AsIntMax);
- } else if (Identifier->getName() == "ptrdiff_t") {
- LM.setKind(LengthModifier::AsPtrDiff);
- }
- }
+ if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x))
+ namedTypeToLengthModifier(PT, LM);
// If fixing the length modifier was enough, we are done.
- const analyze_scanf::ScanfArgTypeResult &ATR = getArgType(Ctx);
- if (hasValidLengthModifier() && ATR.isValid() && ATR.matchesType(Ctx, QT))
+ const analyze_scanf::ArgType &AT = getArgType(Ctx);
+ if (hasValidLengthModifier() && AT.isValid() && AT.matchesType(Ctx, QT))
return true;
// Figure out the conversion specifier.
@@ -452,48 +488,3 @@ bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H,
assert(I == E && "Format string not exhausted");
return false;
}
-
-bool ScanfArgTypeResult::matchesType(ASTContext& C, QualType argTy) const {
- switch (K) {
- case InvalidTy:
- llvm_unreachable("ArgTypeResult must be valid");
- case UnknownTy:
- return true;
- case CStrTy:
- return ArgTypeResult(ArgTypeResult::CStrTy).matchesType(C, argTy);
- case WCStrTy:
- return ArgTypeResult(ArgTypeResult::WCStrTy).matchesType(C, argTy);
- case PtrToArgTypeResultTy: {
- const PointerType *PT = argTy->getAs<PointerType>();
- if (!PT)
- return false;
- return A.matchesType(C, PT->getPointeeType());
- }
- }
-
- llvm_unreachable("Invalid ScanfArgTypeResult Kind!");
-}
-
-QualType ScanfArgTypeResult::getRepresentativeType(ASTContext &C) const {
- switch (K) {
- case InvalidTy:
- llvm_unreachable("No representative type for Invalid ArgTypeResult");
- case UnknownTy:
- return QualType();
- case CStrTy:
- return C.getPointerType(C.CharTy);
- case WCStrTy:
- return C.getPointerType(C.getWCharType());
- case PtrToArgTypeResultTy:
- return C.getPointerType(A.getRepresentativeType(C));
- }
-
- llvm_unreachable("Invalid ScanfArgTypeResult Kind!");
-}
-
-std::string ScanfArgTypeResult::getRepresentativeTypeName(ASTContext& C) const {
- std::string S = getRepresentativeType(C).getAsString();
- if (!Name)
- return std::string("'") + S + "'";
- return std::string("'") + Name + "' (aka '" + S + "')";
-}
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 2f7e794..5954682 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -26,6 +26,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -45,8 +46,15 @@ ThreadSafetyHandler::~ThreadSafetyHandler() {}
namespace {
-/// \brief A MutexID object uniquely identifies a particular mutex, and
-/// is built from an Expr* (i.e. calling a lock function).
+/// SExpr implements a simple expression language that is used to store,
+/// compare, and pretty-print C++ expressions. Unlike a clang Expr, a SExpr
+/// does not capture surface syntax, and it does not distinguish between
+/// C++ concepts, like pointers and references, that have no real semantic
+/// differences. This simplicity allows SExprs to be meaningfully compared,
+/// e.g.
+/// (x) = x
+/// (*this).foo = this->foo
+/// *&a = a
///
/// Thread-safety analysis works by comparing lock expressions. Within the
/// body of a function, an expression such as "x->foo->bar.mu" will resolve to
@@ -59,41 +67,194 @@ namespace {
///
/// The current implementation assumes, but does not verify, that multiple uses
/// of the same lock expression satisfies these criteria.
-///
-/// Clang introduces an additional wrinkle, which is that it is difficult to
-/// derive canonical expressions, or compare expressions directly for equality.
-/// Thus, we identify a mutex not by an Expr, but by the list of named
-/// declarations that are referenced by the Expr. In other words,
-/// x->foo->bar.mu will be a four element vector with the Decls for
-/// mu, bar, and foo, and x. The vector will uniquely identify the expression
-/// for all practical purposes. Null is used to denote 'this'.
-///
-/// Note we will need to perform substitution on "this" and function parameter
-/// names when constructing a lock expression.
-///
-/// For example:
-/// class C { Mutex Mu; void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
-/// void myFunc(C *X) { ... X->lock() ... }
-/// The original expression for the mutex acquired by myFunc is "this->Mu", but
-/// "X" is substituted for "this" so we get X->Mu();
-///
-/// For another example:
-/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
-/// MyList *MyL;
-/// foo(MyL); // requires lock MyL->Mu to be held
-class MutexID {
- SmallVector<NamedDecl*, 2> DeclSeq;
-
- /// Build a Decl sequence representing the lock from the given expression.
+class SExpr {
+private:
+ enum ExprOp {
+ EOP_Nop, //< No-op
+ EOP_Wildcard, //< Matches anything.
+ EOP_This, //< This keyword.
+ EOP_NVar, //< Named variable.
+ EOP_LVar, //< Local variable.
+ EOP_Dot, //< Field access
+ EOP_Call, //< Function call
+ EOP_MCall, //< Method call
+ EOP_Index, //< Array index
+ EOP_Unary, //< Unary operation
+ EOP_Binary, //< Binary operation
+ EOP_Unknown //< Catchall for everything else
+ };
+
+
+ class SExprNode {
+ private:
+ unsigned char Op; //< Opcode of the root node
+ unsigned char Flags; //< Additional opcode-specific data
+ unsigned short Sz; //< Number of child nodes
+ const void* Data; //< Additional opcode-specific data
+
+ public:
+ SExprNode(ExprOp O, unsigned F, const void* D)
+ : Op(static_cast<unsigned char>(O)),
+ Flags(static_cast<unsigned char>(F)), Sz(1), Data(D)
+ { }
+
+ unsigned size() const { return Sz; }
+ void setSize(unsigned S) { Sz = S; }
+
+ ExprOp kind() const { return static_cast<ExprOp>(Op); }
+
+ const NamedDecl* getNamedDecl() const {
+ assert(Op == EOP_NVar || Op == EOP_LVar || Op == EOP_Dot);
+ return reinterpret_cast<const NamedDecl*>(Data);
+ }
+
+ const NamedDecl* getFunctionDecl() const {
+ assert(Op == EOP_Call || Op == EOP_MCall);
+ return reinterpret_cast<const NamedDecl*>(Data);
+ }
+
+ bool isArrow() const { return Op == EOP_Dot && Flags == 1; }
+ void setArrow(bool A) { Flags = A ? 1 : 0; }
+
+ unsigned arity() const {
+ switch (Op) {
+ case EOP_Nop: return 0;
+ case EOP_Wildcard: return 0;
+ case EOP_NVar: return 0;
+ case EOP_LVar: return 0;
+ case EOP_This: return 0;
+ case EOP_Dot: return 1;
+ case EOP_Call: return Flags+1; // First arg is function.
+ case EOP_MCall: return Flags+1; // First arg is implicit obj.
+ case EOP_Index: return 2;
+ case EOP_Unary: return 1;
+ case EOP_Binary: return 2;
+ case EOP_Unknown: return Flags;
+ }
+ return 0;
+ }
+
+ bool operator==(const SExprNode& Other) const {
+ // Ignore flags and size -- they don't matter.
+ return (Op == Other.Op &&
+ Data == Other.Data);
+ }
+
+ bool operator!=(const SExprNode& Other) const {
+ return !(*this == Other);
+ }
+
+ bool matches(const SExprNode& Other) const {
+ return (*this == Other) ||
+ (Op == EOP_Wildcard) ||
+ (Other.Op == EOP_Wildcard);
+ }
+ };
+
+
+ /// \brief Encapsulates the lexical context of a function call. The lexical
+ /// context includes the arguments to the call, including the implicit object
+ /// argument. When an attribute containing a mutex expression is attached to
+ /// a method, the expression may refer to formal parameters of the method.
+ /// Actual arguments must be substituted for formal parameters to derive
+ /// the appropriate mutex expression in the lexical context where the function
+ /// is called. PrevCtx holds the context in which the arguments themselves
+ /// should be evaluated; multiple calling contexts can be chained together
+ /// by the lock_returned attribute.
+ struct CallingContext {
+ const NamedDecl* AttrDecl; // The decl to which the attribute is attached.
+ Expr* SelfArg; // Implicit object argument -- e.g. 'this'
+ bool SelfArrow; // is Self referred to with -> or .?
+ unsigned NumArgs; // Number of funArgs
+ Expr** FunArgs; // Function arguments
+ CallingContext* PrevCtx; // The previous context; or 0 if none.
+
+ CallingContext(const NamedDecl *D = 0, Expr *S = 0,
+ unsigned N = 0, Expr **A = 0, CallingContext *P = 0)
+ : AttrDecl(D), SelfArg(S), SelfArrow(false),
+ NumArgs(N), FunArgs(A), PrevCtx(P)
+ { }
+ };
+
+ typedef SmallVector<SExprNode, 4> NodeVector;
+
+private:
+ // A SExpr is a list of SExprNodes in prefix order. The Size field allows
+ // the list to be traversed as a tree.
+ NodeVector NodeVec;
+
+private:
+ unsigned makeNop() {
+ NodeVec.push_back(SExprNode(EOP_Nop, 0, 0));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeWildcard() {
+ NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeNamedVar(const NamedDecl *D) {
+ NodeVec.push_back(SExprNode(EOP_NVar, 0, D));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeLocalVar(const NamedDecl *D) {
+ NodeVec.push_back(SExprNode(EOP_LVar, 0, D));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeThis() {
+ NodeVec.push_back(SExprNode(EOP_This, 0, 0));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeDot(const NamedDecl *D, bool Arrow) {
+ NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeCall(unsigned NumArgs, const NamedDecl *D) {
+ NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeMCall(unsigned NumArgs, const NamedDecl *D) {
+ NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, D));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeIndex() {
+ NodeVec.push_back(SExprNode(EOP_Index, 0, 0));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeUnary() {
+ NodeVec.push_back(SExprNode(EOP_Unary, 0, 0));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeBinary() {
+ NodeVec.push_back(SExprNode(EOP_Binary, 0, 0));
+ return NodeVec.size()-1;
+ }
+
+ unsigned makeUnknown(unsigned Arity) {
+ NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0));
+ return NodeVec.size()-1;
+ }
+
+ /// Build an SExpr from the given C++ expression.
/// Recursive function that terminates on DeclRefExpr.
- /// Note: this function merely creates a MutexID; it does not check to
+ /// Note: this function merely creates a SExpr; it does not check to
/// ensure that the original expression is a valid mutex expression.
- void buildMutexID(Expr *Exp, const NamedDecl *D, Expr *Parent,
- unsigned NumArgs, Expr **FunArgs) {
- if (!Exp) {
- DeclSeq.clear();
- return;
- }
+ ///
+ /// NDeref returns the number of Derefence and AddressOf operations
+ /// preceeding the Expr; this is used to decide whether to pretty-print
+ /// SExprs with . or ->.
+ unsigned buildSExpr(Expr *Exp, CallingContext* CallCtx, int* NDeref = 0) {
+ if (!Exp)
+ return 0;
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
@@ -103,144 +264,246 @@ class MutexID {
cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();
unsigned i = PV->getFunctionScopeIndex();
- if (FunArgs && FD == D->getCanonicalDecl()) {
+ if (CallCtx && CallCtx->FunArgs &&
+ FD == CallCtx->AttrDecl->getCanonicalDecl()) {
// Substitute call arguments for references to function parameters
- assert(i < NumArgs);
- buildMutexID(FunArgs[i], D, 0, 0, 0);
- return;
+ assert(i < CallCtx->NumArgs);
+ return buildSExpr(CallCtx->FunArgs[i], CallCtx->PrevCtx, NDeref);
}
// Map the param back to the param of the original function declaration.
- DeclSeq.push_back(FD->getParamDecl(i));
- return;
+ makeNamedVar(FD->getParamDecl(i));
+ return 1;
}
// Not a function parameter -- just store the reference.
- DeclSeq.push_back(ND);
- } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
- NamedDecl *ND = ME->getMemberDecl();
- DeclSeq.push_back(ND);
- buildMutexID(ME->getBase(), D, Parent, NumArgs, FunArgs);
+ makeNamedVar(ND);
+ return 1;
} else if (isa<CXXThisExpr>(Exp)) {
- if (Parent)
- buildMutexID(Parent, D, 0, 0, 0);
+ // Substitute parent for 'this'
+ if (CallCtx && CallCtx->SelfArg) {
+ if (!CallCtx->SelfArrow && NDeref)
+ // 'this' is a pointer, but self is not, so need to take address.
+ --(*NDeref);
+ return buildSExpr(CallCtx->SelfArg, CallCtx->PrevCtx, NDeref);
+ }
else {
- DeclSeq.push_back(0); // Use 0 to represent 'this'.
- return; // mutexID is still valid in this case
+ makeThis();
+ return 1;
}
+ } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ NamedDecl *ND = ME->getMemberDecl();
+ int ImplicitDeref = ME->isArrow() ? 1 : 0;
+ unsigned Root = makeDot(ND, false);
+ unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref);
+ NodeVec[Root].setArrow(ImplicitDeref > 0);
+ NodeVec[Root].setSize(Sz + 1);
+ return Sz + 1;
} else if (CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
- DeclSeq.push_back(CMCE->getMethodDecl()->getCanonicalDecl());
- buildMutexID(CMCE->getImplicitObjectArgument(),
- D, Parent, NumArgs, FunArgs);
+ // When calling a function with a lock_returned attribute, replace
+ // the function call with the expression in lock_returned.
+ if (LockReturnedAttr* At =
+ CMCE->getMethodDecl()->getAttr<LockReturnedAttr>()) {
+ CallingContext LRCallCtx(CMCE->getMethodDecl());
+ LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument();
+ LRCallCtx.SelfArrow =
+ dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow();
+ LRCallCtx.NumArgs = CMCE->getNumArgs();
+ LRCallCtx.FunArgs = CMCE->getArgs();
+ LRCallCtx.PrevCtx = CallCtx;
+ return buildSExpr(At->getArg(), &LRCallCtx);
+ }
+ // Hack to treat smart pointers and iterators as pointers;
+ // ignore any method named get().
+ if (CMCE->getMethodDecl()->getNameAsString() == "get" &&
+ CMCE->getNumArgs() == 0) {
+ if (NDeref && dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow())
+ ++(*NDeref);
+ return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref);
+ }
unsigned NumCallArgs = CMCE->getNumArgs();
+ unsigned Root =
+ makeMCall(NumCallArgs, CMCE->getMethodDecl()->getCanonicalDecl());
+ unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx);
Expr** CallArgs = CMCE->getArgs();
for (unsigned i = 0; i < NumCallArgs; ++i) {
- buildMutexID(CallArgs[i], D, Parent, NumArgs, FunArgs);
+ Sz += buildSExpr(CallArgs[i], CallCtx);
}
+ NodeVec[Root].setSize(Sz + 1);
+ return Sz + 1;
} else if (CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
- buildMutexID(CE->getCallee(), D, Parent, NumArgs, FunArgs);
+ if (LockReturnedAttr* At =
+ CE->getDirectCallee()->getAttr<LockReturnedAttr>()) {
+ CallingContext LRCallCtx(CE->getDirectCallee());
+ LRCallCtx.NumArgs = CE->getNumArgs();
+ LRCallCtx.FunArgs = CE->getArgs();
+ LRCallCtx.PrevCtx = CallCtx;
+ return buildSExpr(At->getArg(), &LRCallCtx);
+ }
+ // Treat smart pointers and iterators as pointers;
+ // ignore the * and -> operators.
+ if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ OverloadedOperatorKind k = OE->getOperator();
+ if (k == OO_Star) {
+ if (NDeref) ++(*NDeref);
+ return buildSExpr(OE->getArg(0), CallCtx, NDeref);
+ }
+ else if (k == OO_Arrow) {
+ return buildSExpr(OE->getArg(0), CallCtx, NDeref);
+ }
+ }
unsigned NumCallArgs = CE->getNumArgs();
+ unsigned Root = makeCall(NumCallArgs, 0);
+ unsigned Sz = buildSExpr(CE->getCallee(), CallCtx);
Expr** CallArgs = CE->getArgs();
for (unsigned i = 0; i < NumCallArgs; ++i) {
- buildMutexID(CallArgs[i], D, Parent, NumArgs, FunArgs);
+ Sz += buildSExpr(CallArgs[i], CallCtx);
}
+ NodeVec[Root].setSize(Sz+1);
+ return Sz+1;
} else if (BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {
- buildMutexID(BOE->getLHS(), D, Parent, NumArgs, FunArgs);
- buildMutexID(BOE->getRHS(), D, Parent, NumArgs, FunArgs);
+ unsigned Root = makeBinary();
+ unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx);
+ Sz += buildSExpr(BOE->getRHS(), CallCtx);
+ NodeVec[Root].setSize(Sz);
+ return Sz;
} else if (UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {
- buildMutexID(UOE->getSubExpr(), D, Parent, NumArgs, FunArgs);
+ // Ignore & and * operators -- they're no-ops.
+ // However, we try to figure out whether the expression is a pointer,
+ // so we can use . and -> appropriately in error messages.
+ if (UOE->getOpcode() == UO_Deref) {
+ if (NDeref) ++(*NDeref);
+ return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);
+ }
+ if (UOE->getOpcode() == UO_AddrOf) {
+ if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UOE->getSubExpr())) {
+ if (DRE->getDecl()->isCXXInstanceMember()) {
+ // This is a pointer-to-member expression, e.g. &MyClass::mu_.
+ // We interpret this syntax specially, as a wildcard.
+ unsigned Root = makeDot(DRE->getDecl(), false);
+ makeWildcard();
+ NodeVec[Root].setSize(2);
+ return 2;
+ }
+ }
+ if (NDeref) --(*NDeref);
+ return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);
+ }
+ unsigned Root = makeUnary();
+ unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx);
+ NodeVec[Root].setSize(Sz);
+ return Sz;
} else if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(Exp)) {
- buildMutexID(ASE->getBase(), D, Parent, NumArgs, FunArgs);
- buildMutexID(ASE->getIdx(), D, Parent, NumArgs, FunArgs);
+ unsigned Root = makeIndex();
+ unsigned Sz = buildSExpr(ASE->getBase(), CallCtx);
+ Sz += buildSExpr(ASE->getIdx(), CallCtx);
+ NodeVec[Root].setSize(Sz);
+ return Sz;
} else if (AbstractConditionalOperator *CE =
- dyn_cast<AbstractConditionalOperator>(Exp)) {
- buildMutexID(CE->getCond(), D, Parent, NumArgs, FunArgs);
- buildMutexID(CE->getTrueExpr(), D, Parent, NumArgs, FunArgs);
- buildMutexID(CE->getFalseExpr(), D, Parent, NumArgs, FunArgs);
+ dyn_cast<AbstractConditionalOperator>(Exp)) {
+ unsigned Root = makeUnknown(3);
+ unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
+ Sz += buildSExpr(CE->getTrueExpr(), CallCtx);
+ Sz += buildSExpr(CE->getFalseExpr(), CallCtx);
+ NodeVec[Root].setSize(Sz);
+ return Sz;
} else if (ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {
- buildMutexID(CE->getCond(), D, Parent, NumArgs, FunArgs);
- buildMutexID(CE->getLHS(), D, Parent, NumArgs, FunArgs);
- buildMutexID(CE->getRHS(), D, Parent, NumArgs, FunArgs);
+ unsigned Root = makeUnknown(3);
+ unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
+ Sz += buildSExpr(CE->getLHS(), CallCtx);
+ Sz += buildSExpr(CE->getRHS(), CallCtx);
+ NodeVec[Root].setSize(Sz);
+ return Sz;
} else if (CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
- buildMutexID(CE->getSubExpr(), D, Parent, NumArgs, FunArgs);
+ return buildSExpr(CE->getSubExpr(), CallCtx, NDeref);
} else if (ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
- buildMutexID(PE->getSubExpr(), D, Parent, NumArgs, FunArgs);
+ return buildSExpr(PE->getSubExpr(), CallCtx, NDeref);
+ } else if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) {
+ return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref);
+ } else if (CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) {
+ return buildSExpr(E->getSubExpr(), CallCtx, NDeref);
} else if (isa<CharacterLiteral>(Exp) ||
- isa<CXXNullPtrLiteralExpr>(Exp) ||
- isa<GNUNullExpr>(Exp) ||
- isa<CXXBoolLiteralExpr>(Exp) ||
- isa<FloatingLiteral>(Exp) ||
- isa<ImaginaryLiteral>(Exp) ||
- isa<IntegerLiteral>(Exp) ||
- isa<StringLiteral>(Exp) ||
- isa<ObjCStringLiteral>(Exp)) {
- return; // FIXME: Ignore literals for now
+ isa<CXXNullPtrLiteralExpr>(Exp) ||
+ isa<GNUNullExpr>(Exp) ||
+ isa<CXXBoolLiteralExpr>(Exp) ||
+ isa<FloatingLiteral>(Exp) ||
+ isa<ImaginaryLiteral>(Exp) ||
+ isa<IntegerLiteral>(Exp) ||
+ isa<StringLiteral>(Exp) ||
+ isa<ObjCStringLiteral>(Exp)) {
+ makeNop();
+ return 1; // FIXME: Ignore literals for now
} else {
- // Ignore. FIXME: mark as invalid expression?
+ makeNop();
+ return 1; // Ignore. FIXME: mark as invalid expression?
}
}
- /// \brief Construct a MutexID from an expression.
+ /// \brief Construct a SExpr from an expression.
/// \param MutexExp The original mutex expression within an attribute
/// \param DeclExp An expression involving the Decl on which the attribute
/// occurs.
/// \param D The declaration to which the lock/unlock attribute is attached.
- void buildMutexIDFromExp(Expr *MutexExp, Expr *DeclExp, const NamedDecl *D) {
- Expr *Parent = 0;
- unsigned NumArgs = 0;
- Expr **FunArgs = 0;
+ void buildSExprFromExpr(Expr *MutexExp, Expr *DeclExp, const NamedDecl *D) {
+ CallingContext CallCtx(D);
// If we are processing a raw attribute expression, with no substitutions.
if (DeclExp == 0) {
- buildMutexID(MutexExp, D, 0, 0, 0);
+ buildSExpr(MutexExp, 0);
return;
}
- // Examine DeclExp to find Parent and FunArgs, which are used to substitute
+ // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute
// for formal parameters when we call buildMutexID later.
if (MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
- Parent = ME->getBase();
+ CallCtx.SelfArg = ME->getBase();
+ CallCtx.SelfArrow = ME->isArrow();
} else if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(DeclExp)) {
- Parent = CE->getImplicitObjectArgument();
- NumArgs = CE->getNumArgs();
- FunArgs = CE->getArgs();
+ CallCtx.SelfArg = CE->getImplicitObjectArgument();
+ CallCtx.SelfArrow = dyn_cast<MemberExpr>(CE->getCallee())->isArrow();
+ CallCtx.NumArgs = CE->getNumArgs();
+ CallCtx.FunArgs = CE->getArgs();
} else if (CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
- NumArgs = CE->getNumArgs();
- FunArgs = CE->getArgs();
+ CallCtx.NumArgs = CE->getNumArgs();
+ CallCtx.FunArgs = CE->getArgs();
} else if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(DeclExp)) {
- Parent = 0; // FIXME -- get the parent from DeclStmt
- NumArgs = CE->getNumArgs();
- FunArgs = CE->getArgs();
+ CallCtx.SelfArg = 0; // FIXME -- get the parent from DeclStmt
+ CallCtx.NumArgs = CE->getNumArgs();
+ CallCtx.FunArgs = CE->getArgs();
} else if (D && isa<CXXDestructorDecl>(D)) {
// There's no such thing as a "destructor call" in the AST.
- Parent = DeclExp;
+ CallCtx.SelfArg = DeclExp;
}
// If the attribute has no arguments, then assume the argument is "this".
if (MutexExp == 0) {
- buildMutexID(Parent, D, 0, 0, 0);
+ buildSExpr(CallCtx.SelfArg, 0);
return;
}
- buildMutexID(MutexExp, D, Parent, NumArgs, FunArgs);
+ // For most attributes.
+ buildSExpr(MutexExp, &CallCtx);
}
-public:
- explicit MutexID(clang::Decl::EmptyShell e) {
- DeclSeq.clear();
+ /// \brief Get index of next sibling of node i.
+ unsigned getNextSibling(unsigned i) const {
+ return i + NodeVec[i].size();
}
+public:
+ explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); }
+
/// \param MutexExp The original mutex expression within an attribute
/// \param DeclExp An expression involving the Decl on which the attribute
/// occurs.
/// \param D The declaration to which the lock/unlock attribute is attached.
/// Caller must check isValid() after construction.
- MutexID(Expr* MutexExp, Expr *DeclExp, const NamedDecl* D) {
- buildMutexIDFromExp(MutexExp, DeclExp, D);
+ SExpr(Expr* MutexExp, Expr *DeclExp, const NamedDecl* D) {
+ buildSExprFromExpr(MutexExp, DeclExp, D);
}
/// Return true if this is a valid decl sequence.
/// Caller must call this by hand after construction to handle errors.
bool isValid() const {
- return !DeclSeq.empty();
+ return !NodeVec.empty();
}
/// Issue a warning about an invalid lock expression
@@ -255,44 +518,144 @@ public:
Handler.handleInvalidLockExp(Loc);
}
- bool operator==(const MutexID &other) const {
- return DeclSeq == other.DeclSeq;
+ bool operator==(const SExpr &other) const {
+ return NodeVec == other.NodeVec;
}
- bool operator!=(const MutexID &other) const {
+ bool operator!=(const SExpr &other) const {
return !(*this == other);
}
- // SmallVector overloads Operator< to do lexicographic ordering. Note that
- // we use pointer equality (and <) to compare NamedDecls. This means the order
- // of MutexIDs in a lockset is nondeterministic. In order to output
- // diagnostics in a deterministic ordering, we must order all diagnostics to
- // output by SourceLocation when iterating through this lockset.
- bool operator<(const MutexID &other) const {
- return DeclSeq < other.DeclSeq;
+ bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const {
+ if (NodeVec[i].matches(Other.NodeVec[j])) {
+ unsigned n = NodeVec[i].arity();
+ bool Result = true;
+ unsigned ci = i+1; // first child of i
+ unsigned cj = j+1; // first child of j
+ for (unsigned k = 0; k < n;
+ ++k, ci=getNextSibling(ci), cj = Other.getNextSibling(cj)) {
+ Result = Result && matches(Other, ci, cj);
+ }
+ return Result;
+ }
+ return false;
}
- /// \brief Returns the name of the first Decl in the list for a given MutexID;
- /// e.g. the lock expression foo.bar() has name "bar".
- /// The caret will point unambiguously to the lock expression, so using this
- /// name in diagnostics is a way to get simple, and consistent, mutex names.
- /// We do not want to output the entire expression text for security reasons.
- std::string getName() const {
+ /// \brief Pretty print a lock expression for use in error messages.
+ std::string toString(unsigned i = 0) const {
assert(isValid());
- if (!DeclSeq.front())
- return "this"; // Use 0 to represent 'this'.
- return DeclSeq.front()->getNameAsString();
+ if (i >= NodeVec.size())
+ return "";
+
+ const SExprNode* N = &NodeVec[i];
+ switch (N->kind()) {
+ case EOP_Nop:
+ return "_";
+ case EOP_Wildcard:
+ return "(?)";
+ case EOP_This:
+ return "this";
+ case EOP_NVar:
+ case EOP_LVar: {
+ return N->getNamedDecl()->getNameAsString();
+ }
+ case EOP_Dot: {
+ if (NodeVec[i+1].kind() == EOP_Wildcard) {
+ std::string S = "&";
+ S += N->getNamedDecl()->getQualifiedNameAsString();
+ return S;
+ }
+ std::string FieldName = N->getNamedDecl()->getNameAsString();
+ if (NodeVec[i+1].kind() == EOP_This)
+ return FieldName;
+
+ std::string S = toString(i+1);
+ if (N->isArrow())
+ return S + "->" + FieldName;
+ else
+ return S + "." + FieldName;
+ }
+ case EOP_Call: {
+ std::string S = toString(i+1) + "(";
+ unsigned NumArgs = N->arity()-1;
+ unsigned ci = getNextSibling(i+1);
+ for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
+ S += toString(ci);
+ if (k+1 < NumArgs) S += ",";
+ }
+ S += ")";
+ return S;
+ }
+ case EOP_MCall: {
+ std::string S = "";
+ if (NodeVec[i+1].kind() != EOP_This)
+ S = toString(i+1) + ".";
+ if (const NamedDecl *D = N->getFunctionDecl())
+ S += D->getNameAsString() + "(";
+ else
+ S += "#(";
+ unsigned NumArgs = N->arity()-1;
+ unsigned ci = getNextSibling(i+1);
+ for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
+ S += toString(ci);
+ if (k+1 < NumArgs) S += ",";
+ }
+ S += ")";
+ return S;
+ }
+ case EOP_Index: {
+ std::string S1 = toString(i+1);
+ std::string S2 = toString(i+1 + NodeVec[i+1].size());
+ return S1 + "[" + S2 + "]";
+ }
+ case EOP_Unary: {
+ std::string S = toString(i+1);
+ return "#" + S;
+ }
+ case EOP_Binary: {
+ std::string S1 = toString(i+1);
+ std::string S2 = toString(i+1 + NodeVec[i+1].size());
+ return "(" + S1 + "#" + S2 + ")";
+ }
+ case EOP_Unknown: {
+ unsigned NumChildren = N->arity();
+ if (NumChildren == 0)
+ return "(...)";
+ std::string S = "(";
+ unsigned ci = i+1;
+ for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) {
+ S += toString(ci);
+ if (j+1 < NumChildren) S += "#";
+ }
+ S += ")";
+ return S;
+ }
+ }
+ return "";
}
+};
- void Profile(llvm::FoldingSetNodeID &ID) const {
- for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
- E = DeclSeq.end(); I != E; ++I) {
- ID.AddPointer(*I);
- }
+
+
+/// \brief A short list of SExprs
+class MutexIDList : public SmallVector<SExpr, 3> {
+public:
+ /// \brief Return true if the list contains the specified SExpr
+ /// Performs a linear search, because these lists are almost always very small.
+ bool contains(const SExpr& M) {
+ for (iterator I=begin(),E=end(); I != E; ++I)
+ if ((*I) == M) return true;
+ return false;
+ }
+
+ /// \brief Push M onto list, bud discard duplicates
+ void push_back_nodup(const SExpr& M) {
+ if (!contains(M)) push_back(M);
}
};
+
/// \brief This is a helper class that stores info about the most recent
/// accquire of a Lock.
///
@@ -307,14 +670,18 @@ struct LockData {
///
/// FIXME: add support for re-entrant locking and lock up/downgrading
LockKind LKind;
- MutexID UnderlyingMutex; // for ScopedLockable objects
+ bool Managed; // for ScopedLockable objects
+ SExpr UnderlyingMutex; // for ScopedLockable objects
- LockData(SourceLocation AcquireLoc, LockKind LKind)
- : AcquireLoc(AcquireLoc), LKind(LKind), UnderlyingMutex(Decl::EmptyShell())
+ LockData(SourceLocation AcquireLoc, LockKind LKind, bool M = false)
+ : AcquireLoc(AcquireLoc), LKind(LKind), Managed(M),
+ UnderlyingMutex(Decl::EmptyShell())
{}
- LockData(SourceLocation AcquireLoc, LockKind LKind, const MutexID &Mu)
- : AcquireLoc(AcquireLoc), LKind(LKind), UnderlyingMutex(Mu) {}
+ LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu)
+ : AcquireLoc(AcquireLoc), LKind(LKind), Managed(false),
+ UnderlyingMutex(Mu)
+ {}
bool operator==(const LockData &other) const {
return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
@@ -331,10 +698,102 @@ struct LockData {
};
-/// A Lockset maps each MutexID (defined above) to information about how it has
+/// \brief A FactEntry stores a single fact that is known at a particular point
+/// in the program execution. Currently, this is information regarding a lock
+/// that is held at that point.
+struct FactEntry {
+ SExpr MutID;
+ LockData LDat;
+
+ FactEntry(const SExpr& M, const LockData& L)
+ : MutID(M), LDat(L)
+ { }
+};
+
+
+typedef unsigned short FactID;
+
+/// \brief FactManager manages the memory for all facts that are created during
+/// the analysis of a single routine.
+class FactManager {
+private:
+ std::vector<FactEntry> Facts;
+
+public:
+ FactID newLock(const SExpr& M, const LockData& L) {
+ Facts.push_back(FactEntry(M,L));
+ return static_cast<unsigned short>(Facts.size() - 1);
+ }
+
+ const FactEntry& operator[](FactID F) const { return Facts[F]; }
+ FactEntry& operator[](FactID F) { return Facts[F]; }
+};
+
+
+/// \brief A FactSet is the set of facts that are known to be true at a
+/// particular program point. FactSets must be small, because they are
+/// frequently copied, and are thus implemented as a set of indices into a
+/// table maintained by a FactManager. A typical FactSet only holds 1 or 2
+/// locks, so we can get away with doing a linear search for lookup. Note
+/// that a hashtable or map is inappropriate in this case, because lookups
+/// may involve partial pattern matches, rather than exact matches.
+class FactSet {
+private:
+ typedef SmallVector<FactID, 4> FactVec;
+
+ FactVec FactIDs;
+
+public:
+ typedef FactVec::iterator iterator;
+ typedef FactVec::const_iterator const_iterator;
+
+ iterator begin() { return FactIDs.begin(); }
+ const_iterator begin() const { return FactIDs.begin(); }
+
+ iterator end() { return FactIDs.end(); }
+ const_iterator end() const { return FactIDs.end(); }
+
+ bool isEmpty() const { return FactIDs.size() == 0; }
+
+ FactID addLock(FactManager& FM, const SExpr& M, const LockData& L) {
+ FactID F = FM.newLock(M, L);
+ FactIDs.push_back(F);
+ return F;
+ }
+
+ bool removeLock(FactManager& FM, const SExpr& M) {
+ unsigned n = FactIDs.size();
+ if (n == 0)
+ return false;
+
+ for (unsigned i = 0; i < n-1; ++i) {
+ if (FM[FactIDs[i]].MutID.matches(M)) {
+ FactIDs[i] = FactIDs[n-1];
+ FactIDs.pop_back();
+ return true;
+ }
+ }
+ if (FM[FactIDs[n-1]].MutID.matches(M)) {
+ FactIDs.pop_back();
+ return true;
+ }
+ return false;
+ }
+
+ LockData* findLock(FactManager& FM, const SExpr& M) const {
+ for (const_iterator I=begin(), E=end(); I != E; ++I) {
+ if (FM[*I].MutID.matches(M)) return &FM[*I].LDat;
+ }
+ return 0;
+ }
+};
+
+
+
+/// A Lockset maps each SExpr (defined above) to information about how it has
/// been locked.
-typedef llvm::ImmutableMap<MutexID, LockData> Lockset;
-typedef llvm::ImmutableMap<NamedDecl*, unsigned> LocalVarContext;
+typedef llvm::ImmutableMap<SExpr, LockData> Lockset;
+typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
class LocalVariableMap;
@@ -345,15 +804,15 @@ enum CFGBlockSide { CBS_Entry, CBS_Exit };
/// maintained for each block in the CFG. See LocalVariableMap for more
/// information about the contexts.
struct CFGBlockInfo {
- Lockset EntrySet; // Lockset held at entry to block
- Lockset ExitSet; // Lockset held at exit from block
+ FactSet EntrySet; // Lockset held at entry to block
+ FactSet ExitSet; // Lockset held at exit from block
LocalVarContext EntryContext; // Context held at entry to block
LocalVarContext ExitContext; // Context held at exit from block
SourceLocation EntryLoc; // Location of first statement in block
SourceLocation ExitLoc; // Location of last statement in block.
unsigned EntryIndex; // Used to replay contexts later
- const Lockset &getSet(CFGBlockSide Side) const {
+ const FactSet &getSet(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntrySet : ExitSet;
}
SourceLocation getLocation(CFGBlockSide Side) const {
@@ -361,14 +820,12 @@ struct CFGBlockInfo {
}
private:
- CFGBlockInfo(Lockset EmptySet, LocalVarContext EmptyCtx)
- : EntrySet(EmptySet), ExitSet(EmptySet),
- EntryContext(EmptyCtx), ExitContext(EmptyCtx)
+ CFGBlockInfo(LocalVarContext EmptyCtx)
+ : EntryContext(EmptyCtx), ExitContext(EmptyCtx)
{ }
public:
- static CFGBlockInfo getEmptyBlockInfo(Lockset::Factory &F,
- LocalVariableMap &M);
+ static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
};
@@ -398,21 +855,21 @@ public:
public:
friend class LocalVariableMap;
- NamedDecl *Dec; // The original declaration for this variable.
- Expr *Exp; // The expression for this variable, OR
- unsigned Ref; // Reference to another VarDefinition
- Context Ctx; // The map with which Exp should be interpreted.
+ const NamedDecl *Dec; // The original declaration for this variable.
+ const Expr *Exp; // The expression for this variable, OR
+ unsigned Ref; // Reference to another VarDefinition
+ Context Ctx; // The map with which Exp should be interpreted.
bool isReference() { return !Exp; }
private:
// Create ordinary variable definition
- VarDefinition(NamedDecl *D, Expr *E, Context C)
+ VarDefinition(const NamedDecl *D, const Expr *E, Context C)
: Dec(D), Exp(E), Ref(0), Ctx(C)
{ }
// Create reference to previous definition
- VarDefinition(NamedDecl *D, unsigned R, Context C)
+ VarDefinition(const NamedDecl *D, unsigned R, Context C)
: Dec(D), Exp(0), Ref(R), Ctx(C)
{ }
};
@@ -430,7 +887,7 @@ public:
}
/// Look up a definition, within the given context.
- const VarDefinition* lookup(NamedDecl *D, Context Ctx) {
+ const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {
const unsigned *i = Ctx.lookup(D);
if (!i)
return 0;
@@ -441,7 +898,7 @@ public:
/// Look up the definition for D within the given context. Returns
/// NULL if the expression is not statically known. If successful, also
/// modifies Ctx to hold the context of the return Expr.
- Expr* lookupExpr(NamedDecl *D, Context &Ctx) {
+ const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {
const unsigned *P = Ctx.lookup(D);
if (!P)
return 0;
@@ -476,7 +933,7 @@ public:
llvm::errs() << "Undefined";
return;
}
- NamedDecl *Dec = VarDefinitions[i].Dec;
+ const NamedDecl *Dec = VarDefinitions[i].Dec;
if (!Dec) {
llvm::errs() << "<<NULL>>";
return;
@@ -488,7 +945,7 @@ public:
/// Dumps an ASCII representation of the variable map to llvm::errs()
void dump() {
for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
- Expr *Exp = VarDefinitions[i].Exp;
+ const Expr *Exp = VarDefinitions[i].Exp;
unsigned Ref = VarDefinitions[i].Ref;
dumpVarDefinitionName(i);
@@ -504,7 +961,7 @@ public:
/// Dumps an ASCII representation of a Context to llvm::errs()
void dumpContext(Context C) {
for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
- NamedDecl *D = I.getKey();
+ const NamedDecl *D = I.getKey();
D->printName(llvm::errs());
const unsigned *i = C.lookup(D);
llvm::errs() << " -> ";
@@ -528,7 +985,7 @@ protected:
// Adds a new definition to the given context, and returns a new context.
// This method should be called when declaring a new variable.
- Context addDefinition(NamedDecl *D, Expr *Exp, Context Ctx) {
+ Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
assert(!Ctx.contains(D));
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.add(Ctx, D, newID);
@@ -537,7 +994,7 @@ protected:
}
// Add a new reference to an existing definition.
- Context addReference(NamedDecl *D, unsigned i, Context Ctx) {
+ Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.add(Ctx, D, newID);
VarDefinitions.push_back(VarDefinition(D, i, Ctx));
@@ -546,7 +1003,7 @@ protected:
// Updates a definition only if that definition is already in the map.
// This method should be called when assigning to an existing variable.
- Context updateDefinition(NamedDecl *D, Expr *Exp, Context Ctx) {
+ Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
if (Ctx.contains(D)) {
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.remove(Ctx, D);
@@ -559,7 +1016,7 @@ protected:
// Removes a definition from the context, but keeps the variable name
// as a valid variable. The index 0 is a placeholder for cleared definitions.
- Context clearDefinition(NamedDecl *D, Context Ctx) {
+ Context clearDefinition(const NamedDecl *D, Context Ctx) {
Context NewCtx = Ctx;
if (NewCtx.contains(D)) {
NewCtx = ContextFactory.remove(NewCtx, D);
@@ -569,7 +1026,7 @@ protected:
}
// Remove a definition entirely frmo the context.
- Context removeDefinition(NamedDecl *D, Context Ctx) {
+ Context removeDefinition(const NamedDecl *D, Context Ctx) {
Context NewCtx = Ctx;
if (NewCtx.contains(D)) {
NewCtx = ContextFactory.remove(NewCtx, D);
@@ -586,9 +1043,8 @@ protected:
// This has to be defined after LocalVariableMap.
-CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(Lockset::Factory &F,
- LocalVariableMap &M) {
- return CFGBlockInfo(F.getEmptyMap(), M.getEmptyContext());
+CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
+ return CFGBlockInfo(M.getEmptyContext());
}
@@ -655,7 +1111,7 @@ LocalVariableMap::Context
LocalVariableMap::intersectContexts(Context C1, Context C2) {
Context Result = C1;
for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
- NamedDecl *Dec = I.getKey();
+ const NamedDecl *Dec = I.getKey();
unsigned i1 = I.getData();
const unsigned *i2 = C2.lookup(Dec);
if (!i2) // variable doesn't exist on second path
@@ -672,7 +1128,7 @@ LocalVariableMap::intersectContexts(Context C1, Context C2) {
LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
Context Result = getEmptyContext();
for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
- NamedDecl *Dec = I.getKey();
+ const NamedDecl *Dec = I.getKey();
unsigned i = I.getData();
Result = addReference(Dec, i, Result);
}
@@ -684,7 +1140,7 @@ LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
// createReferenceContext.
void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
- NamedDecl *Dec = I.getKey();
+ const NamedDecl *Dec = I.getKey();
unsigned i1 = I.getData();
VarDefinition *VDef = &VarDefinitions[i1];
assert(VDef->isReference());
@@ -725,7 +1181,7 @@ void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
// incoming back edge, it duplicates the context, creating new definitions
// that refer back to the originals. (These correspond to places where SSA
// might have to insert a phi node.) On the second pass, these definitions are
-// set to NULL if the the variable has changed on the back-edge (i.e. a phi
+// set to NULL if the variable has changed on the back-edge (i.e. a phi
// node was actually required.) E.g.
//
// { Context | VarDefinitions }
@@ -869,24 +1325,294 @@ static void findBlockLocations(CFG *CFGraph,
class ThreadSafetyAnalyzer {
friend class BuildLockset;
- ThreadSafetyHandler &Handler;
- Lockset::Factory LocksetFactory;
- LocalVariableMap LocalVarMap;
+ ThreadSafetyHandler &Handler;
+ LocalVariableMap LocalVarMap;
+ FactManager FactMan;
+ std::vector<CFGBlockInfo> BlockInfo;
public:
ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
- Lockset intersectAndWarn(const CFGBlockInfo &Block1, CFGBlockSide Side1,
- const CFGBlockInfo &Block2, CFGBlockSide Side2,
- LockErrorKind LEK);
+ void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat);
+ void removeLock(FactSet &FSet, const SExpr &Mutex,
+ SourceLocation UnlockLoc, bool FullyRemove=false);
+
+ template <typename AttrType>
+ void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
+ const NamedDecl *D);
- Lockset addLock(Lockset &LSet, Expr *MutexExp, const NamedDecl *D,
- LockKind LK, SourceLocation Loc);
+ template <class AttrType>
+ void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
+ const NamedDecl *D,
+ const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
+ Expr *BrE, bool Neg);
+
+ const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
+ bool &Negate);
+
+ void getEdgeLockset(FactSet &Result, const FactSet &ExitSet,
+ const CFGBlock* PredBlock,
+ const CFGBlock *CurrBlock);
+
+ void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2,
+ SourceLocation JoinLoc,
+ LockErrorKind LEK1, LockErrorKind LEK2,
+ bool Modify=true);
+
+ void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2,
+ SourceLocation JoinLoc, LockErrorKind LEK1,
+ bool Modify=true) {
+ intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
+ }
void runAnalysis(AnalysisDeclContext &AC);
};
+/// \brief Add a new lock to the lockset, warning if the lock is already there.
+/// \param Mutex -- the Mutex expression for the lock
+/// \param LDat -- the LockData for the lock
+void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
+ const LockData &LDat) {
+ // FIXME: deal with acquired before/after annotations.
+ // FIXME: Don't always warn when we have support for reentrant locks.
+ if (FSet.findLock(FactMan, Mutex)) {
+ Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
+ } else {
+ FSet.addLock(FactMan, Mutex, LDat);
+ }
+}
+
+
+/// \brief Remove a lock from the lockset, warning if the lock is not there.
+/// \param LockExp The lock expression corresponding to the lock to be removed
+/// \param UnlockLoc The source location of the unlock (only used in error msg)
+void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
+ const SExpr &Mutex,
+ SourceLocation UnlockLoc,
+ bool FullyRemove) {
+ const LockData *LDat = FSet.findLock(FactMan, Mutex);
+ if (!LDat) {
+ Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc);
+ return;
+ }
+
+ if (LDat->UnderlyingMutex.isValid()) {
+ // This is scoped lockable object, which manages the real mutex.
+ if (FullyRemove) {
+ // We're destroying the managing object.
+ // Remove the underlying mutex if it exists; but don't warn.
+ if (FSet.findLock(FactMan, LDat->UnderlyingMutex))
+ FSet.removeLock(FactMan, LDat->UnderlyingMutex);
+ } else {
+ // We're releasing the underlying mutex, but not destroying the
+ // managing object. Warn on dual release.
+ if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
+ Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(),
+ UnlockLoc);
+ }
+ FSet.removeLock(FactMan, LDat->UnderlyingMutex);
+ return;
+ }
+ }
+ FSet.removeLock(FactMan, Mutex);
+}
+
+
+/// \brief Extract the list of mutexIDs from the attribute on an expression,
+/// and push them onto Mtxs, discarding any duplicates.
+template <typename AttrType>
+void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
+ Expr *Exp, const NamedDecl *D) {
+ typedef typename AttrType::args_iterator iterator_type;
+
+ if (Attr->args_size() == 0) {
+ // The mutex held is the "this" object.
+ SExpr Mu(0, Exp, D);
+ if (!Mu.isValid())
+ SExpr::warnInvalidLock(Handler, 0, Exp, D);
+ else
+ Mtxs.push_back_nodup(Mu);
+ return;
+ }
+
+ for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
+ SExpr Mu(*I, Exp, D);
+ if (!Mu.isValid())
+ SExpr::warnInvalidLock(Handler, *I, Exp, D);
+ else
+ Mtxs.push_back_nodup(Mu);
+ }
+}
+
+
+/// \brief Extract the list of mutexIDs from a trylock attribute. If the
+/// trylock applies to the given edge, then push them onto Mtxs, discarding
+/// any duplicates.
+template <class AttrType>
+void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
+ Expr *Exp, const NamedDecl *D,
+ const CFGBlock *PredBlock,
+ const CFGBlock *CurrBlock,
+ Expr *BrE, bool Neg) {
+ // Find out which branch has the lock
+ bool branch = 0;
+ if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) {
+ branch = BLE->getValue();
+ }
+ else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) {
+ branch = ILE->getValue().getBoolValue();
+ }
+ int branchnum = branch ? 0 : 1;
+ if (Neg) branchnum = !branchnum;
+
+ // If we've taken the trylock branch, then add the lock
+ int i = 0;
+ for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),
+ SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {
+ if (*SI == CurrBlock && i == branchnum) {
+ getMutexIDs(Mtxs, Attr, Exp, D);
+ }
+ }
+}
+
+
+bool getStaticBooleanValue(Expr* E, bool& TCond) {
+ if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
+ TCond = false;
+ return true;
+ } else if (CXXBoolLiteralExpr *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
+ TCond = BLE->getValue();
+ return true;
+ } else if (IntegerLiteral *ILE = dyn_cast<IntegerLiteral>(E)) {
+ TCond = ILE->getValue().getBoolValue();
+ return true;
+ } else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) {
+ return getStaticBooleanValue(CE->getSubExpr(), TCond);
+ }
+ return false;
+}
+
+
+// If Cond can be traced back to a function call, return the call expression.
+// The negate variable should be called with false, and will be set to true
+// if the function call is negated, e.g. if (!mu.tryLock(...))
+const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
+ LocalVarContext C,
+ bool &Negate) {
+ if (!Cond)
+ return 0;
+
+ if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
+ return CallExp;
+ }
+ else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Cond)) {
+ return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
+ }
+ else if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) {
+ return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
+ }
+ else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
+ const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
+ return getTrylockCallExpr(E, C, Negate);
+ }
+ else if (const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
+ if (UOP->getOpcode() == UO_LNot) {
+ Negate = !Negate;
+ return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
+ }
+ return 0;
+ }
+ else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) {
+ if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
+ if (BOP->getOpcode() == BO_NE)
+ Negate = !Negate;
+
+ bool TCond = false;
+ if (getStaticBooleanValue(BOP->getRHS(), TCond)) {
+ if (!TCond) Negate = !Negate;
+ return getTrylockCallExpr(BOP->getLHS(), C, Negate);
+ }
+ else if (getStaticBooleanValue(BOP->getLHS(), TCond)) {
+ if (!TCond) Negate = !Negate;
+ return getTrylockCallExpr(BOP->getRHS(), C, Negate);
+ }
+ return 0;
+ }
+ return 0;
+ }
+ // FIXME -- handle && and || as well.
+ return 0;
+}
+
+
+/// \brief Find the lockset that holds on the edge between PredBlock
+/// and CurrBlock. The edge set is the exit set of PredBlock (passed
+/// as the ExitSet parameter) plus any trylocks, which are conditionally held.
+void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
+ const FactSet &ExitSet,
+ const CFGBlock *PredBlock,
+ const CFGBlock *CurrBlock) {
+ Result = ExitSet;
+
+ if (!PredBlock->getTerminatorCondition())
+ return;
+
+ bool Negate = false;
+ const Stmt *Cond = PredBlock->getTerminatorCondition();
+ const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
+ const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
+
+ CallExpr *Exp =
+ const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate));
+ if (!Exp)
+ return;
+
+ NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
+ if(!FunDecl || !FunDecl->hasAttrs())
+ return;
+
+
+ MutexIDList ExclusiveLocksToAdd;
+ MutexIDList SharedLocksToAdd;
+
+ // If the condition is a call to a Trylock function, then grab the attributes
+ AttrVec &ArgAttrs = FunDecl->getAttrs();
+ for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
+ Attr *Attr = ArgAttrs[i];
+ switch (Attr->getKind()) {
+ case attr::ExclusiveTrylockFunction: {
+ ExclusiveTrylockFunctionAttr *A =
+ cast<ExclusiveTrylockFunctionAttr>(Attr);
+ getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
+ PredBlock, CurrBlock, A->getSuccessValue(), Negate);
+ break;
+ }
+ case attr::SharedTrylockFunction: {
+ SharedTrylockFunctionAttr *A =
+ cast<SharedTrylockFunctionAttr>(Attr);
+ getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
+ PredBlock, CurrBlock, A->getSuccessValue(), Negate);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Add and remove locks.
+ SourceLocation Loc = Exp->getExprLoc();
+ for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
+ addLock(Result, ExclusiveLocksToAdd[i],
+ LockData(Loc, LK_Exclusive));
+ }
+ for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
+ addLock(Result, SharedLocksToAdd[i],
+ LockData(Loc, LK_Shared));
+ }
+}
+
+
/// \brief We use this class to visit different types of expressions in
/// CFGBlocks, and build up the lockset.
/// An expression may cause us to add or remove locks from the lockset, or else
@@ -895,50 +1621,31 @@ public:
class BuildLockset : public StmtVisitor<BuildLockset> {
friend class ThreadSafetyAnalyzer;
- ThreadSafetyHandler &Handler;
- Lockset::Factory &LocksetFactory;
- LocalVariableMap &LocalVarMap;
-
- Lockset LSet;
+ ThreadSafetyAnalyzer *Analyzer;
+ FactSet FSet;
LocalVariableMap::Context LVarCtx;
unsigned CtxIndex;
// Helper functions
- void addLock(const MutexID &Mutex, const LockData &LDat);
- void removeLock(const MutexID &Mutex, SourceLocation UnlockLoc);
+ const ValueDecl *getValueDecl(Expr *Exp);
- template <class AttrType>
- void addLocksToSet(LockKind LK, AttrType *Attr,
- Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
- void removeLocksFromSet(UnlockFunctionAttr *Attr,
- Expr *Exp, NamedDecl* FunDecl);
+ void warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp, AccessKind AK,
+ Expr *MutexExp, ProtectedOperationKind POK);
- const ValueDecl *getValueDecl(Expr *Exp);
- void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
- Expr *MutexExp, ProtectedOperationKind POK);
void checkAccess(Expr *Exp, AccessKind AK);
void checkDereference(Expr *Exp, AccessKind AK);
- void handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
-
- template <class AttrType>
- void addTrylock(LockKind LK, AttrType *Attr, Expr *Exp, NamedDecl *FunDecl,
- const CFGBlock* PredBlock, const CFGBlock *CurrBlock,
- Expr *BrE, bool Neg);
- CallExpr* getTrylockCallExpr(Stmt *Cond, LocalVariableMap::Context C,
- bool &Negate);
- void handleTrylock(Stmt *Cond, const CFGBlock* PredBlock,
- const CFGBlock *CurrBlock);
+ void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0);
/// \brief Returns true if the lockset contains a lock, regardless of whether
/// the lock is held exclusively or shared.
- bool locksetContains(const MutexID &Lock) const {
- return LSet.lookup(Lock);
+ bool locksetContains(const SExpr &Mu) const {
+ return FSet.findLock(Analyzer->FactMan, Mu);
}
/// \brief Returns true if the lockset contains a lock with the passed in
/// locktype.
- bool locksetContains(const MutexID &Lock, LockKind KindRequested) const {
- const LockData *LockHeld = LSet.lookup(Lock);
+ bool locksetContains(const SExpr &Mu, LockKind KindRequested) const {
+ const LockData *LockHeld = FSet.findLock(Analyzer->FactMan, Mu);
return (LockHeld && KindRequested == LockHeld->LKind);
}
@@ -946,7 +1653,7 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
/// passed in locktype. So for example, if we pass in LK_Shared, this function
/// returns true if the lock is held LK_Shared or LK_Exclusive. If we pass in
/// LK_Exclusive, this function returns true if the lock is held LK_Exclusive.
- bool locksetContainsAtLeast(const MutexID &Lock,
+ bool locksetContainsAtLeast(const SExpr &Lock,
LockKind KindRequested) const {
switch (KindRequested) {
case LK_Shared:
@@ -958,12 +1665,10 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
}
public:
- BuildLockset(ThreadSafetyAnalyzer *analyzer, CFGBlockInfo &Info)
+ BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
: StmtVisitor<BuildLockset>(),
- Handler(analyzer->Handler),
- LocksetFactory(analyzer->LocksetFactory),
- LocalVarMap(analyzer->LocalVarMap),
- LSet(Info.EntrySet),
+ Analyzer(Anlzr),
+ FSet(Info.EntrySet),
LVarCtx(Info.EntryContext),
CtxIndex(Info.EntryIndex)
{}
@@ -976,104 +1681,6 @@ public:
void VisitDeclStmt(DeclStmt *S);
};
-/// \brief Add a new lock to the lockset, warning if the lock is already there.
-/// \param Mutex -- the Mutex expression for the lock
-/// \param LDat -- the LockData for the lock
-void BuildLockset::addLock(const MutexID &Mutex, const LockData& LDat) {
- // FIXME: deal with acquired before/after annotations.
- // FIXME: Don't always warn when we have support for reentrant locks.
- if (locksetContains(Mutex))
- Handler.handleDoubleLock(Mutex.getName(), LDat.AcquireLoc);
- else
- LSet = LocksetFactory.add(LSet, Mutex, LDat);
-}
-
-/// \brief Remove a lock from the lockset, warning if the lock is not there.
-/// \param LockExp The lock expression corresponding to the lock to be removed
-/// \param UnlockLoc The source location of the unlock (only used in error msg)
-void BuildLockset::removeLock(const MutexID &Mutex, SourceLocation UnlockLoc) {
- const LockData *LDat = LSet.lookup(Mutex);
- if (!LDat)
- Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
- else {
- // For scoped-lockable vars, remove the mutex associated with this var.
- if (LDat->UnderlyingMutex.isValid())
- removeLock(LDat->UnderlyingMutex, UnlockLoc);
- LSet = LocksetFactory.remove(LSet, Mutex);
- }
-}
-
-/// \brief This function, parameterized by an attribute type, is used to add a
-/// set of locks specified as attribute arguments to the lockset.
-template <typename AttrType>
-void BuildLockset::addLocksToSet(LockKind LK, AttrType *Attr,
- Expr *Exp, NamedDecl* FunDecl, VarDecl *VD) {
- typedef typename AttrType::args_iterator iterator_type;
-
- SourceLocation ExpLocation = Exp->getExprLoc();
-
- // Figure out if we're calling the constructor of scoped lockable class
- bool isScopedVar = false;
- if (VD) {
- if (CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FunDecl)) {
- CXXRecordDecl* PD = CD->getParent();
- if (PD && PD->getAttr<ScopedLockableAttr>())
- isScopedVar = true;
- }
- }
-
- if (Attr->args_size() == 0) {
- // The mutex held is the "this" object.
- MutexID Mutex(0, Exp, FunDecl);
- if (!Mutex.isValid())
- MutexID::warnInvalidLock(Handler, 0, Exp, FunDecl);
- else
- addLock(Mutex, LockData(ExpLocation, LK));
- return;
- }
-
- for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
- MutexID Mutex(*I, Exp, FunDecl);
- if (!Mutex.isValid())
- MutexID::warnInvalidLock(Handler, *I, Exp, FunDecl);
- else {
- addLock(Mutex, LockData(ExpLocation, LK));
- if (isScopedVar) {
- // For scoped lockable vars, map this var to its underlying mutex.
- DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
- MutexID SMutex(&DRE, 0, 0);
- addLock(SMutex, LockData(VD->getLocation(), LK, Mutex));
- }
- }
- }
-}
-
-/// \brief This function removes a set of locks specified as attribute
-/// arguments from the lockset.
-void BuildLockset::removeLocksFromSet(UnlockFunctionAttr *Attr,
- Expr *Exp, NamedDecl* FunDecl) {
- SourceLocation ExpLocation;
- if (Exp) ExpLocation = Exp->getExprLoc();
-
- if (Attr->args_size() == 0) {
- // The mutex held is the "this" object.
- MutexID Mu(0, Exp, FunDecl);
- if (!Mu.isValid())
- MutexID::warnInvalidLock(Handler, 0, Exp, FunDecl);
- else
- removeLock(Mu, ExpLocation);
- return;
- }
-
- for (UnlockFunctionAttr::args_iterator I = Attr->args_begin(),
- E = Attr->args_end(); I != E; ++I) {
- MutexID Mutex(*I, Exp, FunDecl);
- if (!Mutex.isValid())
- MutexID::warnInvalidLock(Handler, *I, Exp, FunDecl);
- else
- removeLock(Mutex, ExpLocation);
- }
-}
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
@@ -1093,11 +1700,12 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
ProtectedOperationKind POK) {
LockKind LK = getLockKindFromAccessKind(AK);
- MutexID Mutex(MutexExp, Exp, D);
+ SExpr Mutex(MutexExp, Exp, D);
if (!Mutex.isValid())
- MutexID::warnInvalidLock(Handler, MutexExp, Exp, D);
+ SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
else if (!locksetContainsAtLeast(Mutex, LK))
- Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK, Exp->getExprLoc());
+ Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
+ Exp->getExprLoc());
}
/// \brief This method identifies variable dereferences and checks pt_guarded_by
@@ -1116,8 +1724,9 @@ void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
if(!D || !D->hasAttrs())
return;
- if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
- Handler.handleNoMutexHeld(D, POK_VarDereference, AK, Exp->getExprLoc());
+ if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty())
+ Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
+ Exp->getExprLoc());
const AttrVec &ArgAttrs = D->getAttrs();
for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
@@ -1134,8 +1743,9 @@ void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
if(!D || !D->hasAttrs())
return;
- if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
- Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc());
+ if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty())
+ Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
+ Exp->getExprLoc());
const AttrVec &ArgAttrs = D->getAttrs();
for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
@@ -1153,68 +1763,68 @@ void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
/// and check that the appropriate locks are held. Non-const method calls with
/// the same signature as const method calls can be also treated as reads.
///
-/// FIXME: We need to also visit CallExprs to catch/check global functions.
-///
-/// FIXME: Do not flag an error for member variables accessed in constructors/
-/// destructors
-void BuildLockset::handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD) {
- AttrVec &ArgAttrs = D->getAttrs();
+void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
+ const AttrVec &ArgAttrs = D->getAttrs();
+ MutexIDList ExclusiveLocksToAdd;
+ MutexIDList SharedLocksToAdd;
+ MutexIDList LocksToRemove;
+
for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
- Attr *Attr = ArgAttrs[i];
- switch (Attr->getKind()) {
+ Attr *At = const_cast<Attr*>(ArgAttrs[i]);
+ switch (At->getKind()) {
// When we encounter an exclusive lock function, we need to add the lock
// to our lockset with kind exclusive.
case attr::ExclusiveLockFunction: {
- ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(Attr);
- addLocksToSet(LK_Exclusive, A, Exp, D, VD);
+ ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At);
+ Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D);
break;
}
// When we encounter a shared lock function, we need to add the lock
// to our lockset with kind shared.
case attr::SharedLockFunction: {
- SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(Attr);
- addLocksToSet(LK_Shared, A, Exp, D, VD);
+ SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At);
+ Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D);
break;
}
// When we encounter an unlock function, we need to remove unlocked
// mutexes from the lockset, and flag a warning if they are not there.
case attr::UnlockFunction: {
- UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
- removeLocksFromSet(UFAttr, Exp, D);
+ UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At);
+ Analyzer->getMutexIDs(LocksToRemove, A, Exp, D);
break;
}
case attr::ExclusiveLocksRequired: {
- ExclusiveLocksRequiredAttr *ELRAttr =
- cast<ExclusiveLocksRequiredAttr>(Attr);
+ ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At);
for (ExclusiveLocksRequiredAttr::args_iterator
- I = ELRAttr->args_begin(), E = ELRAttr->args_end(); I != E; ++I)
+ I = A->args_begin(), E = A->args_end(); I != E; ++I)
warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
break;
}
case attr::SharedLocksRequired: {
- SharedLocksRequiredAttr *SLRAttr = cast<SharedLocksRequiredAttr>(Attr);
+ SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At);
- for (SharedLocksRequiredAttr::args_iterator I = SLRAttr->args_begin(),
- E = SLRAttr->args_end(); I != E; ++I)
+ for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(),
+ E = A->args_end(); I != E; ++I)
warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
break;
}
case attr::LocksExcluded: {
- LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr);
- for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(),
- E = LEAttr->args_end(); I != E; ++I) {
- MutexID Mutex(*I, Exp, D);
+ LocksExcludedAttr *A = cast<LocksExcludedAttr>(At);
+ for (LocksExcludedAttr::args_iterator I = A->args_begin(),
+ E = A->args_end(); I != E; ++I) {
+ SExpr Mutex(*I, Exp, D);
if (!Mutex.isValid())
- MutexID::warnInvalidLock(Handler, *I, Exp, D);
+ SExpr::warnInvalidLock(Analyzer->Handler, *I, Exp, D);
else if (locksetContains(Mutex))
- Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
- Exp->getExprLoc());
+ Analyzer->Handler.handleFunExcludesLock(D->getName(),
+ Mutex.toString(),
+ Exp->getExprLoc());
}
break;
}
@@ -1224,102 +1834,50 @@ void BuildLockset::handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD) {
break;
}
}
-}
-
-
-/// \brief Add lock to set, if the current block is in the taken branch of a
-/// trylock.
-template <class AttrType>
-void BuildLockset::addTrylock(LockKind LK, AttrType *Attr, Expr *Exp,
- NamedDecl *FunDecl, const CFGBlock *PredBlock,
- const CFGBlock *CurrBlock, Expr *BrE, bool Neg) {
- // Find out which branch has the lock
- bool branch = 0;
- if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) {
- branch = BLE->getValue();
- }
- else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) {
- branch = ILE->getValue().getBoolValue();
- }
- int branchnum = branch ? 0 : 1;
- if (Neg) branchnum = !branchnum;
- // If we've taken the trylock branch, then add the lock
- int i = 0;
- for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),
- SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {
- if (*SI == CurrBlock && i == branchnum) {
- addLocksToSet(LK, Attr, Exp, FunDecl, 0);
+ // Figure out if we're calling the constructor of scoped lockable class
+ bool isScopedVar = false;
+ if (VD) {
+ if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) {
+ const CXXRecordDecl* PD = CD->getParent();
+ if (PD && PD->getAttr<ScopedLockableAttr>())
+ isScopedVar = true;
}
}
-}
-
-// If Cond can be traced back to a function call, return the call expression.
-// The negate variable should be called with false, and will be set to true
-// if the function call is negated, e.g. if (!mu.tryLock(...))
-CallExpr* BuildLockset::getTrylockCallExpr(Stmt *Cond,
- LocalVariableMap::Context C,
- bool &Negate) {
- if (!Cond)
- return 0;
-
- if (CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
- return CallExp;
- }
- else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) {
- return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
+ // Add locks.
+ SourceLocation Loc = Exp->getExprLoc();
+ for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, ExclusiveLocksToAdd[i],
+ LockData(Loc, LK_Exclusive, isScopedVar));
}
- else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
- Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
- return getTrylockCallExpr(E, C, Negate);
- }
- else if (UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
- if (UOP->getOpcode() == UO_LNot) {
- Negate = !Negate;
- return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
- }
+ for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, SharedLocksToAdd[i],
+ LockData(Loc, LK_Shared, isScopedVar));
}
- // FIXME -- handle && and || as well.
- return NULL;
-}
-
-
-/// \brief Process a conditional branch from a previous block to the current
-/// block, looking for trylock calls.
-void BuildLockset::handleTrylock(Stmt *Cond, const CFGBlock *PredBlock,
- const CFGBlock *CurrBlock) {
- bool Negate = false;
- CallExpr *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
- if (!Exp)
- return;
- NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
- if(!FunDecl || !FunDecl->hasAttrs())
- return;
+ // Add the managing object as a dummy mutex, mapped to the underlying mutex.
+ // FIXME -- this doesn't work if we acquire multiple locks.
+ if (isScopedVar) {
+ SourceLocation MLoc = VD->getLocation();
+ DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
+ SExpr SMutex(&DRE, 0, 0);
- // If the condition is a call to a Trylock function, then grab the attributes
- AttrVec &ArgAttrs = FunDecl->getAttrs();
- for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
- Attr *Attr = ArgAttrs[i];
- switch (Attr->getKind()) {
- case attr::ExclusiveTrylockFunction: {
- ExclusiveTrylockFunctionAttr *A =
- cast<ExclusiveTrylockFunctionAttr>(Attr);
- addTrylock(LK_Exclusive, A, Exp, FunDecl, PredBlock, CurrBlock,
- A->getSuccessValue(), Negate);
- break;
- }
- case attr::SharedTrylockFunction: {
- SharedTrylockFunctionAttr *A =
- cast<SharedTrylockFunctionAttr>(Attr);
- addTrylock(LK_Shared, A, Exp, FunDecl, PredBlock, CurrBlock,
- A->getSuccessValue(), Negate);
- break;
- }
- default:
- break;
+ for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive,
+ ExclusiveLocksToAdd[i]));
}
+ for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared,
+ SharedLocksToAdd[i]));
+ }
+ }
+
+ // Remove locks.
+ // FIXME -- should only fully remove if the attribute refers to 'this'.
+ bool Dtor = isa<CXXDestructorDecl>(D);
+ for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) {
+ Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor);
}
}
@@ -1351,7 +1909,7 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
return;
// adjust the context
- LVarCtx = LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
+ LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
checkAccess(LHSExp, AK_Written);
@@ -1383,13 +1941,17 @@ void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) {
void BuildLockset::VisitDeclStmt(DeclStmt *S) {
// adjust the context
- LVarCtx = LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
+ LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
DeclGroupRef DGrp = S->getDeclGroup();
for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) {
Decl *D = *I;
if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) {
Expr *E = VD->getInit();
+ // handle constructors that involve temporaries
+ if (ExprWithCleanups *EWC = dyn_cast_or_null<ExprWithCleanups>(E))
+ E = EWC->getSubExpr();
+
if (CXXConstructExpr *CE = dyn_cast_or_null<CXXConstructExpr>(E)) {
NamedDecl *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
if (!CtorD || !CtorD->hasAttrs())
@@ -1401,6 +1963,7 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) {
}
+
/// \brief Compute the intersection of two locksets and issue warnings for any
/// locks in the symmetric difference.
///
@@ -1409,58 +1972,80 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) {
/// A; if () then B; else C; D; we need to check that the lockset after B and C
/// are the same. In the event of a difference, we use the intersection of these
/// two locksets at the start of D.
-Lockset ThreadSafetyAnalyzer::intersectAndWarn(const CFGBlockInfo &Block1,
- CFGBlockSide Side1,
- const CFGBlockInfo &Block2,
- CFGBlockSide Side2,
- LockErrorKind LEK) {
- Lockset LSet1 = Block1.getSet(Side1);
- Lockset LSet2 = Block2.getSet(Side2);
-
- Lockset Intersection = LSet1;
- for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
- const MutexID &LSet2Mutex = I.getKey();
- const LockData &LSet2LockData = I.getData();
- if (const LockData *LD = LSet1.lookup(LSet2Mutex)) {
- if (LD->LKind != LSet2LockData.LKind) {
- Handler.handleExclusiveAndShared(LSet2Mutex.getName(),
- LSet2LockData.AcquireLoc,
- LD->AcquireLoc);
- if (LD->LKind != LK_Exclusive)
- Intersection = LocksetFactory.add(Intersection, LSet2Mutex,
- LSet2LockData);
+///
+/// \param LSet1 The first lockset.
+/// \param LSet2 The second lockset.
+/// \param JoinLoc The location of the join point for error reporting
+/// \param LEK1 The error message to report if a mutex is missing from LSet1
+/// \param LEK2 The error message to report if a mutex is missing from Lset2
+void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
+ const FactSet &FSet2,
+ SourceLocation JoinLoc,
+ LockErrorKind LEK1,
+ LockErrorKind LEK2,
+ bool Modify) {
+ FactSet FSet1Orig = FSet1;
+
+ for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end();
+ I != E; ++I) {
+ const SExpr &FSet2Mutex = FactMan[*I].MutID;
+ const LockData &LDat2 = FactMan[*I].LDat;
+
+ if (const LockData *LDat1 = FSet1.findLock(FactMan, FSet2Mutex)) {
+ if (LDat1->LKind != LDat2.LKind) {
+ Handler.handleExclusiveAndShared(FSet2Mutex.toString(),
+ LDat2.AcquireLoc,
+ LDat1->AcquireLoc);
+ if (Modify && LDat1->LKind != LK_Exclusive) {
+ FSet1.removeLock(FactMan, FSet2Mutex);
+ FSet1.addLock(FactMan, FSet2Mutex, LDat2);
+ }
}
} else {
- Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
- LSet2LockData.AcquireLoc,
- Block1.getLocation(Side1), LEK);
+ if (LDat2.UnderlyingMutex.isValid()) {
+ if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
+ // If this is a scoped lock that manages another mutex, and if the
+ // underlying mutex is still held, then warn about the underlying
+ // mutex.
+ Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(),
+ LDat2.AcquireLoc,
+ JoinLoc, LEK1);
+ }
+ }
+ else if (!LDat2.Managed)
+ Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(),
+ LDat2.AcquireLoc,
+ JoinLoc, LEK1);
}
}
- for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) {
- if (!LSet2.contains(I.getKey())) {
- const MutexID &Mutex = I.getKey();
- const LockData &MissingLock = I.getData();
- Handler.handleMutexHeldEndOfScope(Mutex.getName(),
- MissingLock.AcquireLoc,
- Block2.getLocation(Side2), LEK);
- Intersection = LocksetFactory.remove(Intersection, Mutex);
+ for (FactSet::const_iterator I = FSet1.begin(), E = FSet1.end();
+ I != E; ++I) {
+ const SExpr &FSet1Mutex = FactMan[*I].MutID;
+ const LockData &LDat1 = FactMan[*I].LDat;
+
+ if (!FSet2.findLock(FactMan, FSet1Mutex)) {
+ if (LDat1.UnderlyingMutex.isValid()) {
+ if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) {
+ // If this is a scoped lock that manages another mutex, and if the
+ // underlying mutex is still held, then warn about the underlying
+ // mutex.
+ Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(),
+ LDat1.AcquireLoc,
+ JoinLoc, LEK1);
+ }
+ }
+ else if (!LDat1.Managed)
+ Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(),
+ LDat1.AcquireLoc,
+ JoinLoc, LEK2);
+ if (Modify)
+ FSet1.removeLock(FactMan, FSet1Mutex);
}
}
- return Intersection;
}
-Lockset ThreadSafetyAnalyzer::addLock(Lockset &LSet, Expr *MutexExp,
- const NamedDecl *D,
- LockKind LK, SourceLocation Loc) {
- MutexID Mutex(MutexExp, 0, D);
- if (!Mutex.isValid()) {
- MutexID::warnInvalidLock(Handler, MutexExp, 0, D);
- return LSet;
- }
- LockData NewLock(Loc, LK);
- return LocksetFactory.add(LSet, Mutex, NewLock);
-}
+
/// \brief Check a function's CFG for thread-safety violations.
///
@@ -1472,6 +2057,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
if (!CFGraph) return;
const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl());
+ // AC.dumpCFG(true);
+
if (!D)
return; // Ignore anonymous functions for now.
if (D->getAttr<NoThreadSafetyAnalysisAttr>())
@@ -1485,8 +2072,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
if (isa<CXXDestructorDecl>(D))
return; // Don't check inside destructors.
- std::vector<CFGBlockInfo> BlockInfo(CFGraph->getNumBlockIDs(),
- CFGBlockInfo::getEmptyBlockInfo(LocksetFactory, LocalVarMap));
+ BlockInfo.resize(CFGraph->getNumBlockIDs(),
+ CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
// We need to explore the CFG via a "topological" ordering.
// That way, we will be guaranteed to have information about required
@@ -1505,27 +2092,22 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// FIXME: is there a more intelligent way to check lock/unlock functions?
if (!SortedGraph->empty() && D->hasAttrs()) {
const CFGBlock *FirstBlock = *SortedGraph->begin();
- Lockset &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
+ FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
const AttrVec &ArgAttrs = D->getAttrs();
+
+ MutexIDList ExclusiveLocksToAdd;
+ MutexIDList SharedLocksToAdd;
+
+ SourceLocation Loc = D->getLocation();
for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
Attr *Attr = ArgAttrs[i];
- SourceLocation AttrLoc = Attr->getLocation();
- if (SharedLocksRequiredAttr *SLRAttr
- = dyn_cast<SharedLocksRequiredAttr>(Attr)) {
- for (SharedLocksRequiredAttr::args_iterator
- SLRIter = SLRAttr->args_begin(),
- SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter)
- InitialLockset = addLock(InitialLockset,
- *SLRIter, D, LK_Shared,
- AttrLoc);
- } else if (ExclusiveLocksRequiredAttr *ELRAttr
- = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) {
- for (ExclusiveLocksRequiredAttr::args_iterator
- ELRIter = ELRAttr->args_begin(),
- ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter)
- InitialLockset = addLock(InitialLockset,
- *ELRIter, D, LK_Exclusive,
- AttrLoc);
+ Loc = Attr->getLocation();
+ if (ExclusiveLocksRequiredAttr *A
+ = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) {
+ getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D);
+ } else if (SharedLocksRequiredAttr *A
+ = dyn_cast<SharedLocksRequiredAttr>(Attr)) {
+ getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D);
} else if (isa<UnlockFunctionAttr>(Attr)) {
// Don't try to check unlock functions for now
return;
@@ -1535,8 +2117,24 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
} else if (isa<SharedLockFunctionAttr>(Attr)) {
// Don't try to check lock functions for now
return;
+ } else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {
+ // Don't try to check trylock functions for now
+ return;
+ } else if (isa<SharedTrylockFunctionAttr>(Attr)) {
+ // Don't try to check trylock functions for now
+ return;
}
}
+
+ // FIXME -- Loc can be wrong here.
+ for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
+ addLock(InitialLockset, ExclusiveLocksToAdd[i],
+ LockData(Loc, LK_Exclusive));
+ }
+ for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
+ addLock(InitialLockset, SharedLocksToAdd[i],
+ LockData(Loc, LK_Shared));
+ }
}
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
@@ -1587,15 +2185,16 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
int PrevBlockID = (*PI)->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
+ FactSet PrevLockset;
+ getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
if (!LocksetInitialized) {
- CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet;
+ CurrBlockInfo->EntrySet = PrevLockset;
LocksetInitialized = true;
} else {
- CurrBlockInfo->EntrySet =
- intersectAndWarn(*CurrBlockInfo, CBS_Entry,
- *PrevBlockInfo, CBS_Exit,
- LEK_LockedSomePredecessors);
+ intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
+ CurrBlockInfo->EntryLoc,
+ LEK_LockedSomePredecessors);
}
}
@@ -1619,23 +2218,20 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
const Stmt *Terminator = PrevBlock->getTerminator();
bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
+ FactSet PrevLockset;
+ getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet,
+ PrevBlock, CurrBlock);
+
// Do not update EntrySet.
- intersectAndWarn(*CurrBlockInfo, CBS_Entry, *PrevBlockInfo, CBS_Exit,
+ intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
+ PrevBlockInfo->ExitLoc,
IsLoop ? LEK_LockedSomeLoopIterations
- : LEK_LockedSomePredecessors);
+ : LEK_LockedSomePredecessors,
+ false);
}
}
BuildLockset LocksetBuilder(this, *CurrBlockInfo);
- CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
- PE = CurrBlock->pred_end();
- if (PI != PE) {
- // If the predecessor ended in a branch, then process any trylocks.
- // FIXME -- check to make sure there's only one predecessor.
- if (Stmt *TCE = (*PI)->getTerminatorCondition()) {
- LocksetBuilder.handleTrylock(TCE, *PI, CurrBlock);
- }
- }
// Visit all the statements in the basic block.
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
@@ -1665,7 +2261,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
break;
}
}
- CurrBlockInfo->ExitSet = LocksetBuilder.LSet;
+ CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
// For every back edge from CurrBlock (the end of the loop) to another block
// (FirstLoopBlock) we need to check that the Lockset of Block is equal to
@@ -1679,19 +2275,24 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
continue;
CFGBlock *FirstLoopBlock = *SI;
- CFGBlockInfo &PreLoop = BlockInfo[FirstLoopBlock->getBlockID()];
- CFGBlockInfo &LoopEnd = BlockInfo[CurrBlockID];
- intersectAndWarn(LoopEnd, CBS_Exit, PreLoop, CBS_Entry,
- LEK_LockedSomeLoopIterations);
+ CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->getBlockID()];
+ CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
+ intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet,
+ PreLoop->EntryLoc,
+ LEK_LockedSomeLoopIterations,
+ false);
}
}
- CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry().getBlockID()];
- CFGBlockInfo &Final = BlockInfo[CFGraph->getExit().getBlockID()];
+ CFGBlockInfo *Initial = &BlockInfo[CFGraph->getEntry().getBlockID()];
+ CFGBlockInfo *Final = &BlockInfo[CFGraph->getExit().getBlockID()];
// FIXME: Should we call this function for all blocks which exit the function?
- intersectAndWarn(Initial, CBS_Entry, Final, CBS_Exit,
- LEK_LockedAtEndOfFunction);
+ intersectAndWarn(Initial->EntrySet, Final->ExitSet,
+ Final->ExitLoc,
+ LEK_LockedAtEndOfFunction,
+ LEK_NotLockedAtEndOfFunction,
+ false);
}
} // end anonymous namespace
diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp
index 1c7e6b6..858be45 100644
--- a/lib/Analysis/UninitializedValues.cpp
+++ b/lib/Analysis/UninitializedValues.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/PackedVector.h"
#include "llvm/ADT/DenseMap.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/AnalysisContext.h"
@@ -25,6 +26,8 @@
using namespace clang;
+#define DEBUG_LOGGING 0
+
static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&
!vd->isExceptionVariable() &&
@@ -95,143 +98,79 @@ static bool isAlwaysUninit(const Value v) {
namespace {
typedef llvm::PackedVector<Value, 2> ValueVector;
-typedef std::pair<ValueVector *, ValueVector *> BVPair;
class CFGBlockValues {
const CFG &cfg;
- BVPair *vals;
+ std::vector<ValueVector*> vals;
ValueVector scratch;
DeclToIndex declToIndex;
-
- ValueVector &lazyCreate(ValueVector *&bv);
public:
CFGBlockValues(const CFG &cfg);
~CFGBlockValues();
-
+
unsigned getNumEntries() const { return declToIndex.size(); }
void computeSetOfDeclarations(const DeclContext &dc);
- ValueVector &getValueVector(const CFGBlock *block,
- const CFGBlock *dstBlock);
-
- BVPair &getValueVectors(const CFGBlock *block, bool shouldLazyCreate);
+ ValueVector &getValueVector(const CFGBlock *block) {
+ return *vals[block->getBlockID()];
+ }
+ void setAllScratchValues(Value V);
void mergeIntoScratch(ValueVector const &source, bool isFirst);
bool updateValueVectorWithScratch(const CFGBlock *block);
- bool updateValueVectors(const CFGBlock *block, const BVPair &newVals);
bool hasNoDeclarations() const {
return declToIndex.size() == 0;
}
void resetScratch();
- ValueVector &getScratch() { return scratch; }
ValueVector::reference operator[](const VarDecl *vd);
+
+ Value getValue(const CFGBlock *block, const CFGBlock *dstBlock,
+ const VarDecl *vd) {
+ const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
+ assert(idx.hasValue());
+ return getValueVector(block)[idx.getValue()];
+ }
};
} // end anonymous namespace
-CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {
- unsigned n = cfg.getNumBlockIDs();
- if (!n)
- return;
- vals = new std::pair<ValueVector*, ValueVector*>[n];
- memset((void*)vals, 0, sizeof(*vals) * n);
-}
+CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {}
CFGBlockValues::~CFGBlockValues() {
- unsigned n = cfg.getNumBlockIDs();
- if (n == 0)
- return;
- for (unsigned i = 0; i < n; ++i) {
- delete vals[i].first;
- delete vals[i].second;
- }
- delete [] vals;
+ for (std::vector<ValueVector*>::iterator I = vals.begin(), E = vals.end();
+ I != E; ++I)
+ delete *I;
}
void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {
declToIndex.computeMap(dc);
- scratch.resize(declToIndex.size());
-}
-
-ValueVector &CFGBlockValues::lazyCreate(ValueVector *&bv) {
- if (!bv)
- bv = new ValueVector(declToIndex.size());
- return *bv;
-}
-
-/// This function pattern matches for a '&&' or '||' that appears at
-/// the beginning of a CFGBlock that also (1) has a terminator and
-/// (2) has no other elements. If such an expression is found, it is returned.
-static const BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) {
- if (block->empty())
- return 0;
-
- CFGElement front = block->front();
- const CFGStmt *cstmt = front.getAs<CFGStmt>();
- if (!cstmt)
- return 0;
-
- const BinaryOperator *b = dyn_cast_or_null<BinaryOperator>(cstmt->getStmt());
-
- if (!b || !b->isLogicalOp())
- return 0;
-
- if (block->pred_size() == 2) {
- if (block->getTerminatorCondition() == b) {
- if (block->succ_size() == 2)
- return b;
- }
- else if (block->size() == 1)
- return b;
- }
-
- return 0;
-}
-
-ValueVector &CFGBlockValues::getValueVector(const CFGBlock *block,
- const CFGBlock *dstBlock) {
- unsigned idx = block->getBlockID();
- if (dstBlock && getLogicalOperatorInChain(block)) {
- if (*block->succ_begin() == dstBlock)
- return lazyCreate(vals[idx].first);
- assert(*(block->succ_begin()+1) == dstBlock);
- return lazyCreate(vals[idx].second);
- }
-
- assert(vals[idx].second == 0);
- return lazyCreate(vals[idx].first);
-}
-
-BVPair &CFGBlockValues::getValueVectors(const clang::CFGBlock *block,
- bool shouldLazyCreate) {
- unsigned idx = block->getBlockID();
- lazyCreate(vals[idx].first);
- if (shouldLazyCreate)
- lazyCreate(vals[idx].second);
- return vals[idx];
+ unsigned decls = declToIndex.size();
+ scratch.resize(decls);
+ unsigned n = cfg.getNumBlockIDs();
+ if (!n)
+ return;
+ vals.resize(n);
+ for (unsigned i = 0; i < n; ++i)
+ vals[i] = new ValueVector(decls);
}
-#if 0
+#if DEBUG_LOGGING
static void printVector(const CFGBlock *block, ValueVector &bv,
unsigned num) {
-
llvm::errs() << block->getBlockID() << " :";
for (unsigned i = 0; i < bv.size(); ++i) {
llvm::errs() << ' ' << bv[i];
}
llvm::errs() << " : " << num << '\n';
}
+#endif
-static void printVector(const char *name, ValueVector const &bv) {
- llvm::errs() << name << " : ";
- for (unsigned i = 0; i < bv.size(); ++i) {
- llvm::errs() << ' ' << bv[i];
- }
- llvm::errs() << "\n";
+void CFGBlockValues::setAllScratchValues(Value V) {
+ for (unsigned I = 0, E = scratch.size(); I != E; ++I)
+ scratch[I] = V;
}
-#endif
void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
bool isFirst) {
@@ -242,30 +181,16 @@ void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
}
bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {
- ValueVector &dst = getValueVector(block, 0);
+ ValueVector &dst = getValueVector(block);
bool changed = (dst != scratch);
if (changed)
dst = scratch;
-#if 0
+#if DEBUG_LOGGING
printVector(block, scratch, 0);
#endif
return changed;
}
-bool CFGBlockValues::updateValueVectors(const CFGBlock *block,
- const BVPair &newVals) {
- BVPair &vals = getValueVectors(block, true);
- bool changed = *newVals.first != *vals.first ||
- *newVals.second != *vals.second;
- *vals.first = *newVals.first;
- *vals.second = *newVals.second;
-#if 0
- printVector(block, *vals.first, 1);
- printVector(block, *vals.second, 2);
-#endif
- return changed;
-}
-
void CFGBlockValues::resetScratch() {
scratch.reset();
}
@@ -321,7 +246,7 @@ const CFGBlock *DataflowWorklist::dequeue() {
}
//------------------------------------------------------------------------====//
-// Transfer function for uninitialized values analysis.
+// Classification of DeclRefExprs as use or initialization.
//====------------------------------------------------------------------------//
namespace {
@@ -329,106 +254,339 @@ class FindVarResult {
const VarDecl *vd;
const DeclRefExpr *dr;
public:
- FindVarResult(VarDecl *vd, DeclRefExpr *dr) : vd(vd), dr(dr) {}
-
+ FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {}
+
const DeclRefExpr *getDeclRefExpr() const { return dr; }
const VarDecl *getDecl() const { return vd; }
};
-
+
+static const Expr *stripCasts(ASTContext &C, const Expr *Ex) {
+ while (Ex) {
+ Ex = Ex->IgnoreParenNoopCasts(C);
+ if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
+ if (CE->getCastKind() == CK_LValueBitCast) {
+ Ex = CE->getSubExpr();
+ continue;
+ }
+ }
+ break;
+ }
+ return Ex;
+}
+
+/// If E is an expression comprising a reference to a single variable, find that
+/// variable.
+static FindVarResult findVar(const Expr *E, const DeclContext *DC) {
+ if (const DeclRefExpr *DRE =
+ dyn_cast<DeclRefExpr>(stripCasts(DC->getParentASTContext(), E)))
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()))
+ if (isTrackedVar(VD, DC))
+ return FindVarResult(VD, DRE);
+ return FindVarResult(0, 0);
+}
+
+/// \brief Classify each DeclRefExpr as an initialization or a use. Any
+/// DeclRefExpr which isn't explicitly classified will be assumed to have
+/// escaped the analysis and will be treated as an initialization.
+class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
+public:
+ enum Class {
+ Init,
+ Use,
+ SelfInit,
+ Ignore
+ };
+
+private:
+ const DeclContext *DC;
+ llvm::DenseMap<const DeclRefExpr*, Class> Classification;
+
+ bool isTrackedVar(const VarDecl *VD) const {
+ return ::isTrackedVar(VD, DC);
+ }
+
+ void classify(const Expr *E, Class C);
+
+public:
+ ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {}
+
+ void VisitDeclStmt(DeclStmt *DS);
+ void VisitUnaryOperator(UnaryOperator *UO);
+ void VisitBinaryOperator(BinaryOperator *BO);
+ void VisitCallExpr(CallExpr *CE);
+ void VisitCastExpr(CastExpr *CE);
+
+ void operator()(Stmt *S) { Visit(S); }
+
+ Class get(const DeclRefExpr *DRE) const {
+ llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I
+ = Classification.find(DRE);
+ if (I != Classification.end())
+ return I->second;
+
+ const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (!VD || !isTrackedVar(VD))
+ return Ignore;
+
+ return Init;
+ }
+};
+}
+
+static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) {
+ if (Expr *Init = VD->getInit()) {
+ const DeclRefExpr *DRE
+ = dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init));
+ if (DRE && DRE->getDecl() == VD)
+ return DRE;
+ }
+ return 0;
+}
+
+void ClassifyRefs::classify(const Expr *E, Class C) {
+ FindVarResult Var = findVar(E, DC);
+ if (const DeclRefExpr *DRE = Var.getDeclRefExpr())
+ Classification[DRE] = std::max(Classification[DRE], C);
+}
+
+void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {
+ for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end();
+ DI != DE; ++DI) {
+ VarDecl *VD = dyn_cast<VarDecl>(*DI);
+ if (VD && isTrackedVar(VD))
+ if (const DeclRefExpr *DRE = getSelfInitExpr(VD))
+ Classification[DRE] = SelfInit;
+ }
+}
+
+void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {
+ // Ignore the evaluation of a DeclRefExpr on the LHS of an assignment. If this
+ // is not a compound-assignment, we will treat it as initializing the variable
+ // when TransferFunctions visits it. A compound-assignment does not affect
+ // whether a variable is uninitialized, and there's no point counting it as a
+ // use.
+ if (BO->isCompoundAssignmentOp())
+ classify(BO->getLHS(), Use);
+ else if (BO->getOpcode() == BO_Assign)
+ classify(BO->getLHS(), Ignore);
+}
+
+void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {
+ // Increment and decrement are uses despite there being no lvalue-to-rvalue
+ // conversion.
+ if (UO->isIncrementDecrementOp())
+ classify(UO->getSubExpr(), Use);
+}
+
+void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
+ // If a value is passed by const reference to a function, we should not assume
+ // that it is initialized by the call, and we conservatively do not assume
+ // that it is used.
+ for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
+ I != E; ++I)
+ if ((*I)->getType().isConstQualified() && (*I)->isGLValue())
+ classify(*I, Ignore);
+}
+
+void ClassifyRefs::VisitCastExpr(CastExpr *CE) {
+ if (CE->getCastKind() == CK_LValueToRValue)
+ classify(CE->getSubExpr(), Use);
+ else if (CStyleCastExpr *CSE = dyn_cast<CStyleCastExpr>(CE)) {
+ if (CSE->getType()->isVoidType()) {
+ // Squelch any detected load of an uninitialized value if
+ // we cast it to void.
+ // e.g. (void) x;
+ classify(CSE->getSubExpr(), Ignore);
+ }
+ }
+}
+
+//------------------------------------------------------------------------====//
+// Transfer function for uninitialized values analysis.
+//====------------------------------------------------------------------------//
+
+namespace {
class TransferFunctions : public StmtVisitor<TransferFunctions> {
CFGBlockValues &vals;
const CFG &cfg;
+ const CFGBlock *block;
AnalysisDeclContext &ac;
+ const ClassifyRefs &classification;
UninitVariablesHandler *handler;
-
- /// The last DeclRefExpr seen when analyzing a block. Used to
- /// cheat when detecting cases when the address of a variable is taken.
- DeclRefExpr *lastDR;
-
- /// The last lvalue-to-rvalue conversion of a variable whose value
- /// was uninitialized. Normally this results in a warning, but it is
- /// possible to either silence the warning in some cases, or we
- /// propagate the uninitialized value.
- CastExpr *lastLoad;
-
- /// For some expressions, we want to ignore any post-processing after
- /// visitation.
- bool skipProcessUses;
-
+
public:
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
- AnalysisDeclContext &ac,
+ const CFGBlock *block, AnalysisDeclContext &ac,
+ const ClassifyRefs &classification,
UninitVariablesHandler *handler)
- : vals(vals), cfg(cfg), ac(ac), handler(handler),
- lastDR(0), lastLoad(0),
- skipProcessUses(false) {}
-
- void reportUninit(const DeclRefExpr *ex, const VarDecl *vd,
- bool isAlwaysUninit);
+ : vals(vals), cfg(cfg), block(block), ac(ac),
+ classification(classification), handler(handler) {}
+ void reportUse(const Expr *ex, const VarDecl *vd);
+
+ void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS);
void VisitBlockExpr(BlockExpr *be);
+ void VisitCallExpr(CallExpr *ce);
void VisitDeclStmt(DeclStmt *ds);
void VisitDeclRefExpr(DeclRefExpr *dr);
- void VisitUnaryOperator(UnaryOperator *uo);
void VisitBinaryOperator(BinaryOperator *bo);
- void VisitCastExpr(CastExpr *ce);
- void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs);
- void Visit(Stmt *s);
-
+
bool isTrackedVar(const VarDecl *vd) {
return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));
}
-
- FindVarResult findBlockVarDecl(Expr *ex);
-
- void ProcessUses(Stmt *s = 0);
-};
-}
-static const Expr *stripCasts(ASTContext &C, const Expr *Ex) {
- while (Ex) {
- Ex = Ex->IgnoreParenNoopCasts(C);
- if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
- if (CE->getCastKind() == CK_LValueBitCast) {
- Ex = CE->getSubExpr();
- continue;
+ FindVarResult findVar(const Expr *ex) {
+ return ::findVar(ex, cast<DeclContext>(ac.getDecl()));
+ }
+
+ UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) {
+ UninitUse Use(ex, isAlwaysUninit(v));
+
+ assert(isUninitialized(v));
+ if (Use.getKind() == UninitUse::Always)
+ return Use;
+
+ // If an edge which leads unconditionally to this use did not initialize
+ // the variable, we can say something stronger than 'may be uninitialized':
+ // we can say 'either it's used uninitialized or you have dead code'.
+ //
+ // We track the number of successors of a node which have been visited, and
+ // visit a node once we have visited all of its successors. Only edges where
+ // the variable might still be uninitialized are followed. Since a variable
+ // can't transfer from being initialized to being uninitialized, this will
+ // trace out the subgraph which inevitably leads to the use and does not
+ // initialize the variable. We do not want to skip past loops, since their
+ // non-termination might be correlated with the initialization condition.
+ //
+ // For example:
+ //
+ // void f(bool a, bool b) {
+ // block1: int n;
+ // if (a) {
+ // block2: if (b)
+ // block3: n = 1;
+ // block4: } else if (b) {
+ // block5: while (!a) {
+ // block6: do_work(&a);
+ // n = 2;
+ // }
+ // }
+ // block7: if (a)
+ // block8: g();
+ // block9: return n;
+ // }
+ //
+ // Starting from the maybe-uninitialized use in block 9:
+ // * Block 7 is not visited because we have only visited one of its two
+ // successors.
+ // * Block 8 is visited because we've visited its only successor.
+ // From block 8:
+ // * Block 7 is visited because we've now visited both of its successors.
+ // From block 7:
+ // * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all
+ // of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively).
+ // * Block 3 is not visited because it initializes 'n'.
+ // Now the algorithm terminates, having visited blocks 7 and 8, and having
+ // found the frontier is blocks 2, 4, and 5.
+ //
+ // 'n' is definitely uninitialized for two edges into block 7 (from blocks 2
+ // and 4), so we report that any time either of those edges is taken (in
+ // each case when 'b == false'), 'n' is used uninitialized.
+ llvm::SmallVector<const CFGBlock*, 32> Queue;
+ llvm::SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);
+ Queue.push_back(block);
+ // Specify that we've already visited all successors of the starting block.
+ // This has the dual purpose of ensuring we never add it to the queue, and
+ // of marking it as not being a candidate element of the frontier.
+ SuccsVisited[block->getBlockID()] = block->succ_size();
+ while (!Queue.empty()) {
+ const CFGBlock *B = Queue.back();
+ Queue.pop_back();
+ for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end();
+ I != E; ++I) {
+ const CFGBlock *Pred = *I;
+ if (vals.getValue(Pred, B, vd) == Initialized)
+ // This block initializes the variable.
+ continue;
+
+ unsigned &SV = SuccsVisited[Pred->getBlockID()];
+ if (!SV) {
+ // When visiting the first successor of a block, mark all NULL
+ // successors as having been visited.
+ for (CFGBlock::const_succ_iterator SI = Pred->succ_begin(),
+ SE = Pred->succ_end();
+ SI != SE; ++SI)
+ if (!*SI)
+ ++SV;
+ }
+
+ if (++SV == Pred->succ_size())
+ // All paths from this block lead to the use and don't initialize the
+ // variable.
+ Queue.push_back(Pred);
+ }
+ }
+
+ // Scan the frontier, looking for blocks where the variable was
+ // uninitialized.
+ for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) {
+ const CFGBlock *Block = *BI;
+ unsigned BlockID = Block->getBlockID();
+ const Stmt *Term = Block->getTerminator();
+ if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&
+ Term) {
+ // This block inevitably leads to the use. If we have an edge from here
+ // to a post-dominator block, and the variable is uninitialized on that
+ // edge, we have found a bug.
+ for (CFGBlock::const_succ_iterator I = Block->succ_begin(),
+ E = Block->succ_end(); I != E; ++I) {
+ const CFGBlock *Succ = *I;
+ if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size() &&
+ vals.getValue(Block, Succ, vd) == Uninitialized) {
+ // Switch cases are a special case: report the label to the caller
+ // as the 'terminator', not the switch statement itself. Suppress
+ // situations where no label matched: we can't be sure that's
+ // possible.
+ if (isa<SwitchStmt>(Term)) {
+ const Stmt *Label = Succ->getLabel();
+ if (!Label || !isa<SwitchCase>(Label))
+ // Might not be possible.
+ continue;
+ UninitUse::Branch Branch;
+ Branch.Terminator = Label;
+ Branch.Output = 0; // Ignored.
+ Use.addUninitBranch(Branch);
+ } else {
+ UninitUse::Branch Branch;
+ Branch.Terminator = Term;
+ Branch.Output = I - Block->succ_begin();
+ Use.addUninitBranch(Branch);
+ }
+ }
+ }
}
}
- break;
- }
- return Ex;
-}
-void TransferFunctions::reportUninit(const DeclRefExpr *ex,
- const VarDecl *vd, bool isAlwaysUnit) {
- if (handler) handler->handleUseOfUninitVariable(ex, vd, isAlwaysUnit);
+ return Use;
+ }
+};
}
-FindVarResult TransferFunctions::findBlockVarDecl(Expr *ex) {
- if (DeclRefExpr *dr = dyn_cast<DeclRefExpr>(ex->IgnoreParenCasts()))
- if (VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl()))
- if (isTrackedVar(vd))
- return FindVarResult(vd, dr);
- return FindVarResult(0, 0);
+void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {
+ if (!handler)
+ return;
+ Value v = vals[vd];
+ if (isUninitialized(v))
+ handler->handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
}
-void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs) {
+void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
// This represents an initialization of the 'element' value.
- Stmt *element = fs->getElement();
- const VarDecl *vd = 0;
-
- if (DeclStmt *ds = dyn_cast<DeclStmt>(element)) {
- vd = cast<VarDecl>(ds->getSingleDecl());
- if (!isTrackedVar(vd))
- vd = 0;
- } else {
- // Initialize the value of the reference variable.
- const FindVarResult &res = findBlockVarDecl(cast<Expr>(element));
- vd = res.getDecl();
+ if (DeclStmt *DS = dyn_cast<DeclStmt>(FS->getElement())) {
+ const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
+ if (isTrackedVar(VD))
+ vals[VD] = Initialized;
}
-
- if (vd)
- vals[vd] = Initialized;
}
void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
@@ -442,231 +600,112 @@ void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
vals[vd] = Initialized;
continue;
}
- Value v = vals[vd];
- if (handler && isUninitialized(v))
- handler->handleUseOfUninitVariable(be, vd, isAlwaysUninit(v));
+ reportUse(be, vd);
}
}
-void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
- // Record the last DeclRefExpr seen. This is an lvalue computation.
- // We use this value to later detect if a variable "escapes" the analysis.
- if (const VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl()))
- if (isTrackedVar(vd)) {
- ProcessUses();
- lastDR = dr;
- }
-}
-
-void TransferFunctions::VisitDeclStmt(DeclStmt *ds) {
- for (DeclStmt::decl_iterator DI = ds->decl_begin(), DE = ds->decl_end();
- DI != DE; ++DI) {
- if (VarDecl *vd = dyn_cast<VarDecl>(*DI)) {
- if (isTrackedVar(vd)) {
- if (Expr *init = vd->getInit()) {
- // If the initializer consists solely of a reference to itself, we
- // explicitly mark the variable as uninitialized. This allows code
- // like the following:
- //
- // int x = x;
- //
- // to deliberately leave a variable uninitialized. Different analysis
- // clients can detect this pattern and adjust their reporting
- // appropriately, but we need to continue to analyze subsequent uses
- // of the variable.
- if (init == lastLoad) {
- const DeclRefExpr *DR
- = cast<DeclRefExpr>(stripCasts(ac.getASTContext(),
- lastLoad->getSubExpr()));
- if (DR->getDecl() == vd) {
- // int x = x;
- // Propagate uninitialized value, but don't immediately report
- // a problem.
- vals[vd] = Uninitialized;
- lastLoad = 0;
- lastDR = 0;
- if (handler)
- handler->handleSelfInit(vd);
- return;
- }
- }
-
- // All other cases: treat the new variable as initialized.
- // This is a minor optimization to reduce the propagation
- // of the analysis, since we will have already reported
- // the use of the uninitialized value (which visiting the
- // initializer).
- vals[vd] = Initialized;
- }
- }
- }
- }
+void TransferFunctions::VisitCallExpr(CallExpr *ce) {
+ // After a call to a function like setjmp or vfork, any variable which is
+ // initialized anywhere within this function may now be initialized. For now,
+ // just assume such a call initializes all variables.
+ // FIXME: Only mark variables as initialized if they have an initializer which
+ // is reachable from here.
+ Decl *Callee = ce->getCalleeDecl();
+ if (Callee && Callee->hasAttr<ReturnsTwiceAttr>())
+ vals.setAllScratchValues(Initialized);
}
-void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) {
- if (bo->isAssignmentOp()) {
- const FindVarResult &res = findBlockVarDecl(bo->getLHS());
- if (const VarDecl *vd = res.getDecl()) {
- ValueVector::reference val = vals[vd];
- if (isUninitialized(val)) {
- if (bo->getOpcode() != BO_Assign)
- reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val));
- else
- val = Initialized;
- }
- }
+void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
+ switch (classification.get(dr)) {
+ case ClassifyRefs::Ignore:
+ break;
+ case ClassifyRefs::Use:
+ reportUse(dr, cast<VarDecl>(dr->getDecl()));
+ break;
+ case ClassifyRefs::Init:
+ vals[cast<VarDecl>(dr->getDecl())] = Initialized;
+ break;
+ case ClassifyRefs::SelfInit:
+ if (handler)
+ handler->handleSelfInit(cast<VarDecl>(dr->getDecl()));
+ break;
}
}
-void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) {
- switch (uo->getOpcode()) {
- case clang::UO_PostDec:
- case clang::UO_PostInc:
- case clang::UO_PreDec:
- case clang::UO_PreInc: {
- const FindVarResult &res = findBlockVarDecl(uo->getSubExpr());
- if (const VarDecl *vd = res.getDecl()) {
- assert(res.getDeclRefExpr() == lastDR);
- // We null out lastDR to indicate we have fully processed it
- // and we don't want the auto-value setting in Visit().
- lastDR = 0;
-
- ValueVector::reference val = vals[vd];
- if (isUninitialized(val))
- reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val));
- }
- break;
- }
- default:
- break;
+void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) {
+ if (BO->getOpcode() == BO_Assign) {
+ FindVarResult Var = findVar(BO->getLHS());
+ if (const VarDecl *VD = Var.getDecl())
+ vals[VD] = Initialized;
}
}
-void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) {
- if (ce->getCastKind() == CK_LValueToRValue) {
- const FindVarResult &res = findBlockVarDecl(ce->getSubExpr());
- if (res.getDecl()) {
- assert(res.getDeclRefExpr() == lastDR);
- lastLoad = ce;
- }
- }
- else if (ce->getCastKind() == CK_NoOp ||
- ce->getCastKind() == CK_LValueBitCast) {
- skipProcessUses = true;
- }
- else if (CStyleCastExpr *cse = dyn_cast<CStyleCastExpr>(ce)) {
- if (cse->getType()->isVoidType()) {
- // e.g. (void) x;
- if (lastLoad == cse->getSubExpr()) {
- // Squelch any detected load of an uninitialized value if
- // we cast it to void.
- lastLoad = 0;
- lastDR = 0;
+void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
+ for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end();
+ DI != DE; ++DI) {
+ VarDecl *VD = dyn_cast<VarDecl>(*DI);
+ if (VD && isTrackedVar(VD)) {
+ if (getSelfInitExpr(VD)) {
+ // If the initializer consists solely of a reference to itself, we
+ // explicitly mark the variable as uninitialized. This allows code
+ // like the following:
+ //
+ // int x = x;
+ //
+ // to deliberately leave a variable uninitialized. Different analysis
+ // clients can detect this pattern and adjust their reporting
+ // appropriately, but we need to continue to analyze subsequent uses
+ // of the variable.
+ vals[VD] = Uninitialized;
+ } else if (VD->getInit()) {
+ // Treat the new variable as initialized.
+ vals[VD] = Initialized;
+ } else {
+ // No initializer: the variable is now uninitialized. This matters
+ // for cases like:
+ // while (...) {
+ // int n;
+ // use(n);
+ // n = 0;
+ // }
+ // FIXME: Mark the variable as uninitialized whenever its scope is
+ // left, since its scope could be re-entered by a jump over the
+ // declaration.
+ vals[VD] = Uninitialized;
}
}
}
}
-void TransferFunctions::Visit(clang::Stmt *s) {
- skipProcessUses = false;
- StmtVisitor<TransferFunctions>::Visit(s);
- if (!skipProcessUses)
- ProcessUses(s);
-}
-
-void TransferFunctions::ProcessUses(Stmt *s) {
- // This method is typically called after visiting a CFGElement statement
- // in the CFG. We delay processing of reporting many loads of uninitialized
- // values until here.
- if (lastLoad) {
- // If we just visited the lvalue-to-rvalue cast, there is nothing
- // left to do.
- if (lastLoad == s)
- return;
-
- const DeclRefExpr *DR =
- cast<DeclRefExpr>(stripCasts(ac.getASTContext(),
- lastLoad->getSubExpr()));
- const VarDecl *VD = cast<VarDecl>(DR->getDecl());
-
- // If we reach here, we may have seen a load of an uninitialized value
- // and it hasn't been casted to void or otherwise handled. In this
- // situation, report the incident.
- if (isUninitialized(vals[VD]))
- reportUninit(DR, VD, isAlwaysUninit(vals[VD]));
-
- lastLoad = 0;
-
- if (DR == lastDR) {
- lastDR = 0;
- return;
- }
- }
-
- // Any other uses of 'lastDR' involve taking an lvalue of variable.
- // In this case, it "escapes" the analysis.
- if (lastDR && lastDR != s) {
- vals[cast<VarDecl>(lastDR->getDecl())] = Initialized;
- lastDR = 0;
- }
-}
-
//------------------------------------------------------------------------====//
// High-level "driver" logic for uninitialized values analysis.
//====------------------------------------------------------------------------//
static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
AnalysisDeclContext &ac, CFGBlockValues &vals,
+ const ClassifyRefs &classification,
llvm::BitVector &wasAnalyzed,
UninitVariablesHandler *handler = 0) {
-
wasAnalyzed[block->getBlockID()] = true;
-
- if (const BinaryOperator *b = getLogicalOperatorInChain(block)) {
- CFGBlock::const_pred_iterator itr = block->pred_begin();
- BVPair vA = vals.getValueVectors(*itr, false);
- ++itr;
- BVPair vB = vals.getValueVectors(*itr, false);
-
- BVPair valsAB;
-
- if (b->getOpcode() == BO_LAnd) {
- // Merge the 'F' bits from the first and second.
- vals.mergeIntoScratch(*(vA.second ? vA.second : vA.first), true);
- vals.mergeIntoScratch(*(vB.second ? vB.second : vB.first), false);
- valsAB.first = vA.first;
- valsAB.second = &vals.getScratch();
- } else {
- // Merge the 'T' bits from the first and second.
- assert(b->getOpcode() == BO_LOr);
- vals.mergeIntoScratch(*vA.first, true);
- vals.mergeIntoScratch(*vB.first, false);
- valsAB.first = &vals.getScratch();
- valsAB.second = vA.second ? vA.second : vA.first;
- }
- return vals.updateValueVectors(block, valsAB);
- }
-
- // Default behavior: merge in values of predecessor blocks.
vals.resetScratch();
+ // Merge in values of predecessor blocks.
bool isFirst = true;
for (CFGBlock::const_pred_iterator I = block->pred_begin(),
E = block->pred_end(); I != E; ++I) {
const CFGBlock *pred = *I;
if (wasAnalyzed[pred->getBlockID()]) {
- vals.mergeIntoScratch(vals.getValueVector(pred, block), isFirst);
+ vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);
isFirst = false;
}
}
// Apply the transfer function.
- TransferFunctions tf(vals, cfg, ac, handler);
+ TransferFunctions tf(vals, cfg, block, ac, classification, handler);
for (CFGBlock::const_iterator I = block->begin(), E = block->end();
I != E; ++I) {
if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) {
tf.Visit(const_cast<Stmt*>(cs->getStmt()));
}
}
- tf.ProcessUses();
return vals.updateValueVectorWithScratch(block);
}
@@ -683,17 +722,16 @@ void clang::runUninitializedVariablesAnalysis(
stats.NumVariablesAnalyzed = vals.getNumEntries();
+ // Precompute which expressions are uses and which are initializations.
+ ClassifyRefs classification(ac);
+ cfg.VisitBlockStmts(classification);
+
// Mark all variables uninitialized at the entry.
const CFGBlock &entry = cfg.getEntry();
- for (CFGBlock::const_succ_iterator i = entry.succ_begin(),
- e = entry.succ_end(); i != e; ++i) {
- if (const CFGBlock *succ = *i) {
- ValueVector &vec = vals.getValueVector(&entry, succ);
- const unsigned n = vals.getNumEntries();
- for (unsigned j = 0; j < n ; ++j) {
- vec[j] = Uninitialized;
- }
- }
+ ValueVector &vec = vals.getValueVector(&entry);
+ const unsigned n = vals.getNumEntries();
+ for (unsigned j = 0; j < n ; ++j) {
+ vec[j] = Uninitialized;
}
// Proceed with the workist.
@@ -705,7 +743,8 @@ void clang::runUninitializedVariablesAnalysis(
while (const CFGBlock *block = worklist.dequeue()) {
// Did the block change?
- bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed);
+ bool changed = runOnBlock(block, cfg, ac, vals,
+ classification, wasAnalyzed);
++stats.NumBlockVisits;
if (changed || !previouslyVisited[block->getBlockID()])
worklist.enqueueSuccessors(block);
@@ -716,7 +755,7 @@ void clang::runUninitializedVariablesAnalysis(
for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) {
const CFGBlock *block = *BI;
if (wasAnalyzed[block->getBlockID()]) {
- runOnBlock(block, cfg, ac, vals, wasAnalyzed, &handler);
+ runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, &handler);
++stats.NumBlockVisits;
}
}
diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt
index ef2e93c..73e693b 100644
--- a/lib/Basic/CMakeLists.txt
+++ b/lib/Basic/CMakeLists.txt
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS mc)
add_clang_library(clangBasic
Builtins.cpp
ConvertUTF.c
+ ConvertUTFWrapper.cpp
Diagnostic.cpp
DiagnosticIDs.cpp
FileManager.cpp
@@ -10,6 +11,7 @@ add_clang_library(clangBasic
IdentifierTable.cpp
LangOptions.cpp
Module.cpp
+ ObjCRuntime.cpp
SourceLocation.cpp
SourceManager.cpp
TargetInfo.cpp
@@ -31,18 +33,19 @@ if (Subversion_FOUND AND EXISTS "${CLANG_SOURCE_DIR}/.svn")
PROPERTIES COMPILE_DEFINITIONS "SVN_REVISION=\"${CLANG_WC_REVISION}\"")
endif()
-add_dependencies(clangBasic
- ClangARMNeon
- ClangAttrList
- ClangDiagnosticAnalysis
- ClangDiagnosticAST
- ClangDiagnosticCommon
- ClangDiagnosticDriver
- ClangDiagnosticFrontend
- ClangDiagnosticGroups
- ClangDiagnosticLex
- ClangDiagnosticParse
- ClangDiagnosticSema
- ClangDiagnosticSerialization
- ClangDiagnosticIndexName)
-
+add_dependencies(clangBasic
+ ClangARMNeon
+ ClangAttrList
+ ClangDiagnosticAnalysis
+ ClangDiagnosticAST
+ ClangDiagnosticComment
+ ClangDiagnosticCommon
+ ClangDiagnosticDriver
+ ClangDiagnosticFrontend
+ ClangDiagnosticGroups
+ ClangDiagnosticIndexName
+ ClangDiagnosticLex
+ ClangDiagnosticParse
+ ClangDiagnosticSema
+ ClangDiagnosticSerialization
+ )
diff --git a/lib/Basic/ConvertUTF.c b/lib/Basic/ConvertUTF.c
index e197003..4793b25 100644
--- a/lib/Basic/ConvertUTF.c
+++ b/lib/Basic/ConvertUTF.c
@@ -285,6 +285,7 @@ ConversionResult ConvertUTF16toUTF8 (
*targetStart = target;
return result;
}
+#endif
/* --------------------------------------------------------------------- */
@@ -339,8 +340,6 @@ ConversionResult ConvertUTF32toUTF8 (
return result;
}
-#endif
-
/* --------------------------------------------------------------------- */
/*
diff --git a/lib/Basic/ConvertUTFWrapper.cpp b/lib/Basic/ConvertUTFWrapper.cpp
new file mode 100644
index 0000000..a1b3f7f
--- /dev/null
+++ b/lib/Basic/ConvertUTFWrapper.cpp
@@ -0,0 +1,70 @@
+//===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----===
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/ConvertUTF.h"
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+
+bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source,
+ char *&ResultPtr) {
+ assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4);
+ ConversionResult result = conversionOK;
+ // Copy the character span over.
+ if (WideCharWidth == 1) {
+ if (!isLegalUTF8String(reinterpret_cast<const UTF8*>(Source.begin()),
+ reinterpret_cast<const UTF8*>(Source.end())))
+ result = sourceIllegal;
+ memcpy(ResultPtr, Source.data(), Source.size());
+ ResultPtr += Source.size();
+ } else if (WideCharWidth == 2) {
+ const UTF8 *sourceStart = (const UTF8*)Source.data();
+ // FIXME: Make the type of the result buffer correct instead of
+ // using reinterpret_cast.
+ UTF16 *targetStart = reinterpret_cast<UTF16*>(ResultPtr);
+ ConversionFlags flags = strictConversion;
+ result = ConvertUTF8toUTF16(
+ &sourceStart, sourceStart + Source.size(),
+ &targetStart, targetStart + 2*Source.size(), flags);
+ if (result == conversionOK)
+ ResultPtr = reinterpret_cast<char*>(targetStart);
+ } else if (WideCharWidth == 4) {
+ const UTF8 *sourceStart = (const UTF8*)Source.data();
+ // FIXME: Make the type of the result buffer correct instead of
+ // using reinterpret_cast.
+ UTF32 *targetStart = reinterpret_cast<UTF32*>(ResultPtr);
+ ConversionFlags flags = strictConversion;
+ result = ConvertUTF8toUTF32(
+ &sourceStart, sourceStart + Source.size(),
+ &targetStart, targetStart + 4*Source.size(), flags);
+ if (result == conversionOK)
+ ResultPtr = reinterpret_cast<char*>(targetStart);
+ }
+ assert((result != targetExhausted)
+ && "ConvertUTF8toUTFXX exhausted target buffer");
+ return result == conversionOK;
+}
+
+bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
+ const UTF32 *SourceStart = &Source;
+ const UTF32 *SourceEnd = SourceStart + 1;
+ UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr);
+ UTF8 *TargetEnd = TargetStart + 4;
+ ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd,
+ &TargetStart, TargetEnd,
+ strictConversion);
+ if (CR != conversionOK)
+ return false;
+
+ ResultPtr = reinterpret_cast<char*>(TargetStart);
+ return true;
+}
+
+} // end namespace clang
+
diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp
index f7d5d87..e689502 100644
--- a/lib/Basic/Diagnostic.cpp
+++ b/lib/Basic/Diagnostic.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/CrashRecoveryContext.h"
+#include <cctype>
using namespace clang;
@@ -48,6 +49,9 @@ DiagnosticsEngine::DiagnosticsEngine(
ErrorsAsFatal = false;
SuppressSystemWarnings = false;
SuppressAllDiagnostics = false;
+ ElideType = true;
+ PrintTemplateTree = false;
+ ShowColors = false;
ShowOverloads = Ovl_All;
ExtBehavior = Ext_Ignore;
@@ -115,7 +119,7 @@ void DiagnosticsEngine::Reset() {
// Create a DiagState and DiagStatePoint representing diagnostic changes
// through command-line.
DiagStates.push_back(DiagState());
- PushDiagStatePoint(&DiagStates.back(), SourceLocation());
+ DiagStatePoints.push_back(DiagStatePoint(&DiagStates.back(), FullSourceLoc()));
}
void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1,
@@ -155,12 +159,6 @@ DiagnosticsEngine::GetDiagStatePointForLoc(SourceLocation L) const {
return Pos;
}
-/// \brief This allows the client to specify that certain
-/// warnings are ignored. Notes can never be mapped, errors can only be
-/// mapped to fatal, and WARNINGs and EXTENSIONs can be mapped arbitrarily.
-///
-/// \param The source location that this change of diagnostic state should
-/// take affect. It can be null if we are setting the latest state.
void DiagnosticsEngine::setDiagnosticMapping(diag::kind Diag, diag::Mapping Map,
SourceLocation L) {
assert(Diag < diag::DIAG_UPPER_LIMIT &&
@@ -385,17 +383,34 @@ void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
CurDiagID = ~0U;
}
-bool DiagnosticsEngine::EmitCurrentDiagnostic() {
- // Process the diagnostic, sending the accumulated information to the
- // DiagnosticConsumer.
- bool Emitted = ProcessDiag();
+bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) {
+ assert(getClient() && "DiagnosticClient not set!");
+
+ bool Emitted;
+ if (Force) {
+ Diagnostic Info(this);
+
+ // Figure out the diagnostic level of this message.
+ DiagnosticIDs::Level DiagLevel
+ = Diags->getDiagnosticLevel(Info.getID(), Info.getLocation(), *this);
+
+ Emitted = (DiagLevel != DiagnosticIDs::Ignored);
+ if (Emitted) {
+ // Emit the diagnostic regardless of suppression level.
+ Diags->EmitDiag(*this, DiagLevel);
+ }
+ } else {
+ // Process the diagnostic, sending the accumulated information to the
+ // DiagnosticConsumer.
+ Emitted = ProcessDiag();
+ }
// Clear out the current diagnostic object.
unsigned DiagID = CurDiagID;
Clear();
// If there was a delayed diagnostic, emit it now.
- if (DelayedDiagID && DelayedDiagID != DiagID)
+ if (!Force && DelayedDiagID && DelayedDiagID != DiagID)
ReportDelayed();
return Emitted;
@@ -666,6 +681,8 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
/// QualTypeVals - Pass a vector of arrays so that QualType names can be
/// compared to see if more information is needed to be printed.
SmallVector<intptr_t, 2> QualTypeVals;
+ SmallVector<char, 64> Tree;
+
for (unsigned i = 0, e = getNumArgs(); i < e; ++i)
if (getArgKind(i) == DiagnosticsEngine::ak_qualtype)
QualTypeVals.push_back(getRawArg(i));
@@ -717,7 +734,20 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
assert(isdigit(*DiagStr) && "Invalid format for argument in diagnostic");
unsigned ArgNo = *DiagStr++ - '0';
+ // Only used for type diffing.
+ unsigned ArgNo2 = ArgNo;
+
DiagnosticsEngine::ArgumentKind Kind = getArgKind(ArgNo);
+ if (Kind == DiagnosticsEngine::ak_qualtype &&
+ ModifierIs(Modifier, ModifierLen, "diff")) {
+ Kind = DiagnosticsEngine::ak_qualtype_pair;
+ assert(*DiagStr == ',' && isdigit(*(DiagStr + 1)) &&
+ "Invalid format for diff modifier");
+ ++DiagStr; // Comma.
+ ArgNo2 = *DiagStr++ - '0';
+ assert(getArgKind(ArgNo2) == DiagnosticsEngine::ak_qualtype &&
+ "Second value of type diff must be a qualtype");
+ }
switch (Kind) {
// ---- STRINGS ----
@@ -802,18 +832,91 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
FormattedArgs.data(), FormattedArgs.size(),
OutStr, QualTypeVals);
break;
+ case DiagnosticsEngine::ak_qualtype_pair:
+ // Create a struct with all the info needed for printing.
+ TemplateDiffTypes TDT;
+ TDT.FromType = getRawArg(ArgNo);
+ TDT.ToType = getRawArg(ArgNo2);
+ TDT.ElideType = getDiags()->ElideType;
+ TDT.ShowColors = getDiags()->ShowColors;
+ TDT.TemplateDiffUsed = false;
+ intptr_t val = reinterpret_cast<intptr_t>(&TDT);
+
+ const char *ArgumentEnd = Argument + ArgumentLen;
+ const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
+
+ // Print the tree. If this diagnostic already has a tree, skip the
+ // second tree.
+ if (getDiags()->PrintTemplateTree && Tree.empty()) {
+ TDT.PrintFromType = true;
+ TDT.PrintTree = true;
+ getDiags()->ConvertArgToString(Kind, val,
+ Modifier, ModifierLen,
+ Argument, ArgumentLen,
+ FormattedArgs.data(),
+ FormattedArgs.size(),
+ Tree, QualTypeVals);
+ // If there is no tree information, fall back to regular printing.
+ if (!Tree.empty()) {
+ FormatDiagnostic(Pipe + 1, ArgumentEnd, OutStr);
+ break;
+ }
+ }
+
+ // Non-tree printing, also the fall-back when tree printing fails.
+ // The fall-back is triggered when the types compared are not templates.
+ const char *FirstDollar = ScanFormat(Argument, ArgumentEnd, '$');
+ const char *SecondDollar = ScanFormat(FirstDollar + 1, ArgumentEnd, '$');
+
+ // Append before text
+ FormatDiagnostic(Argument, FirstDollar, OutStr);
+
+ // Append first type
+ TDT.PrintTree = false;
+ TDT.PrintFromType = true;
+ getDiags()->ConvertArgToString(Kind, val,
+ Modifier, ModifierLen,
+ Argument, ArgumentLen,
+ FormattedArgs.data(), FormattedArgs.size(),
+ OutStr, QualTypeVals);
+ if (!TDT.TemplateDiffUsed)
+ FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype,
+ TDT.FromType));
+
+ // Append middle text
+ FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
+
+ // Append second type
+ TDT.PrintFromType = false;
+ getDiags()->ConvertArgToString(Kind, val,
+ Modifier, ModifierLen,
+ Argument, ArgumentLen,
+ FormattedArgs.data(), FormattedArgs.size(),
+ OutStr, QualTypeVals);
+ if (!TDT.TemplateDiffUsed)
+ FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype,
+ TDT.ToType));
+
+ // Append end text
+ FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
+ break;
}
// Remember this argument info for subsequent formatting operations. Turn
// std::strings into a null terminated string to make it be the same case as
// all the other ones.
- if (Kind != DiagnosticsEngine::ak_std_string)
+ if (Kind == DiagnosticsEngine::ak_qualtype_pair)
+ continue;
+ else if (Kind != DiagnosticsEngine::ak_std_string)
FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo)));
else
FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_c_string,
(intptr_t)getArgStdStr(ArgNo).c_str()));
}
+
+ // Append the type tree to the end of the diagnostics.
+ OutStr.append(Tree.begin(), Tree.end());
}
StoredDiagnostic::StoredDiagnostic() { }
diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp
index 8c33a96..ca96fd2 100644
--- a/lib/Basic/DiagnosticIDs.cpp
+++ b/lib/Basic/DiagnosticIDs.cpp
@@ -79,6 +79,7 @@ static const StaticDiagInfoRec StaticDiagInfo[] = {
#include "clang/Basic/DiagnosticLexKinds.inc"
#include "clang/Basic/DiagnosticParseKinds.inc"
#include "clang/Basic/DiagnosticASTKinds.inc"
+#include "clang/Basic/DiagnosticCommentKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
#undef DIAG
@@ -357,7 +358,7 @@ DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
return CustomDiagInfo->getLevel(DiagID);
unsigned DiagClass = getBuiltinDiagClass(DiagID);
- assert(DiagClass != CLASS_NOTE && "Cannot get diagnostic level of a note!");
+ if (DiagClass == CLASS_NOTE) return DiagnosticIDs::Note;
return getDiagnosticLevel(DiagID, DiagClass, Loc, Diag);
}
@@ -583,24 +584,9 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
assert(Diag.getClient() && "DiagnosticClient not set!");
// Figure out the diagnostic level of this message.
- DiagnosticIDs::Level DiagLevel;
unsigned DiagID = Info.getID();
-
- if (DiagID >= diag::DIAG_UPPER_LIMIT) {
- // Handle custom diagnostics, which cannot be mapped.
- DiagLevel = CustomDiagInfo->getLevel(DiagID);
- } else {
- // Get the class of the diagnostic. If this is a NOTE, map it onto whatever
- // the diagnostic level was for the previous diagnostic so that it is
- // filtered the same as the previous diagnostic.
- unsigned DiagClass = getBuiltinDiagClass(DiagID);
- if (DiagClass == CLASS_NOTE) {
- DiagLevel = DiagnosticIDs::Note;
- } else {
- DiagLevel = getDiagnosticLevel(DiagID, DiagClass, Info.getLocation(),
- Diag);
- }
- }
+ DiagnosticIDs::Level DiagLevel
+ = getDiagnosticLevel(DiagID, Info.getLocation(), Diag);
if (DiagLevel != DiagnosticIDs::Note) {
// Record that a fatal error occurred only when we see a second
@@ -658,6 +644,14 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
}
// Finally, report it.
+ EmitDiag(Diag, DiagLevel);
+ return true;
+}
+
+void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const {
+ Diagnostic Info(&Diag);
+ assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!");
+
Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info);
if (Diag.Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == DiagnosticIDs::Warning)
@@ -665,8 +659,6 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
}
Diag.CurDiagID = ~0U;
-
- return true;
}
bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const {
diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp
index fd6d334..c6b894c 100644
--- a/lib/Basic/FileManager.cpp
+++ b/lib/Basic/FileManager.cpp
@@ -111,6 +111,14 @@ public:
}
size_t size() const { return UniqueFiles.size(); }
+
+ void erase(const FileEntry *Entry) {
+ std::string FullPath(GetFullPath(Entry->getName()));
+
+ // Lowercase string because Windows filesystem is case insensitive.
+ FullPath = StringRef(FullPath).lower();
+ UniqueFiles.erase(FullPath);
+ }
};
//===----------------------------------------------------------------------===//
@@ -152,6 +160,8 @@ public:
}
size_t size() const { return UniqueFiles.size(); }
+
+ void erase(const FileEntry *Entry) { UniqueFiles.erase(*Entry); }
};
#endif
@@ -213,6 +223,10 @@ void FileManager::removeStatCache(FileSystemStatCache *statCache) {
PrevCache->setNextStatCache(statCache->getNextStatCache());
}
+void FileManager::clearStatCaches() {
+ StatCache.reset(0);
+}
+
/// \brief Retrieve the directory that the given file name resides in.
/// Filename can point to either a real file or a virtual file.
static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
@@ -259,16 +273,14 @@ void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
addAncestorsAsVirtualDirs(DirName);
}
-/// getDirectory - Lookup, cache, and verify the specified directory
-/// (real or virtual). This returns NULL if the directory doesn't
-/// exist.
-///
const DirectoryEntry *FileManager::getDirectory(StringRef DirName,
bool CacheFailure) {
- // stat doesn't like trailing separators.
+ // stat doesn't like trailing separators except for root directory.
// At least, on Win32 MSVCRT, stat() cannot strip trailing '/'.
// (though it can strip '\\')
- if (DirName.size() > 1 && llvm::sys::path::is_separator(DirName.back()))
+ if (DirName.size() > 1 &&
+ DirName != llvm::sys::path::root_path(DirName) &&
+ llvm::sys::path::is_separator(DirName.back()))
DirName = DirName.substr(0, DirName.size()-1);
++NumDirLookups;
@@ -315,9 +327,6 @@ const DirectoryEntry *FileManager::getDirectory(StringRef DirName,
return &UDE;
}
-/// getFile - Lookup, cache, and verify the specified file (real or
-/// virtual). This returns NULL if the file doesn't exist.
-///
const FileEntry *FileManager::getFile(StringRef Filename, bool openFile,
bool CacheFailure) {
++NumFileLookups;
@@ -483,15 +492,21 @@ void FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const {
}
llvm::MemoryBuffer *FileManager::
-getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) {
+getBufferForFile(const FileEntry *Entry, std::string *ErrorStr,
+ bool isVolatile) {
OwningPtr<llvm::MemoryBuffer> Result;
llvm::error_code ec;
+ uint64_t FileSize = Entry->getSize();
+ // If there's a high enough chance that the file have changed since we
+ // got its size, force a stat before opening it.
+ if (isVolatile)
+ FileSize = -1;
+
const char *Filename = Entry->getName();
// If the file is already open, use the open file descriptor.
if (Entry->FD != -1) {
- ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result,
- Entry->getSize());
+ ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result, FileSize);
if (ErrorStr)
*ErrorStr = ec.message();
@@ -503,7 +518,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) {
// Otherwise, open the file.
if (FileSystemOpts.WorkingDir.empty()) {
- ec = llvm::MemoryBuffer::getFile(Filename, Result, Entry->getSize());
+ ec = llvm::MemoryBuffer::getFile(Filename, Result, FileSize);
if (ec && ErrorStr)
*ErrorStr = ec.message();
return Result.take();
@@ -511,7 +526,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) {
SmallString<128> FilePath(Entry->getName());
FixupRelativePath(FilePath);
- ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, Entry->getSize());
+ ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, FileSize);
if (ec && ErrorStr)
*ErrorStr = ec.message();
return Result.take();
@@ -564,6 +579,18 @@ bool FileManager::getNoncachedStatValue(StringRef Path,
return ::stat(FilePath.c_str(), &StatBuf) != 0;
}
+void FileManager::invalidateCache(const FileEntry *Entry) {
+ assert(Entry && "Cannot invalidate a NULL FileEntry");
+
+ SeenFileEntries.erase(Entry->getName());
+
+ // FileEntry invalidation should not block future optimizations in the file
+ // caches. Possible alternatives are cache truncation (invalidate last N) or
+ // invalidation of the whole cache.
+ UniqueRealFiles.erase(Entry);
+}
+
+
void FileManager::GetUniqueIDMapping(
SmallVectorImpl<const FileEntry *> &UIDToFiles) const {
UIDToFiles.clear();
@@ -584,6 +611,12 @@ void FileManager::GetUniqueIDMapping(
UIDToFiles[(*VFE)->getUID()] = *VFE;
}
+void FileManager::modifyFileEntry(FileEntry *File,
+ off_t Size, time_t ModificationTime) {
+ File->Size = Size;
+ File->ModTime = ModificationTime;
+}
+
void FileManager::PrintStats() const {
llvm::errs() << "\n*** File Manager Stats:\n";
diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp
index 43899f0..4869ae1 100644
--- a/lib/Basic/IdentifierTable.cpp
+++ b/lib/Basic/IdentifierTable.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cctype>
#include <cstdio>
using namespace clang;
@@ -103,7 +104,8 @@ namespace {
KEYOPENCL = 0x200,
KEYC11 = 0x400,
KEYARC = 0x800,
- KEYALL = 0x0fff
+ KEYNOMS = 0x01000,
+ KEYALL = (0xffff & ~KEYNOMS) // Because KEYNOMS is used to exclude.
};
}
@@ -136,6 +138,9 @@ static void AddKeyword(StringRef Keyword,
else if (LangOpts.ObjC2 && (Flags & KEYARC)) AddResult = 2;
else if (LangOpts.CPlusPlus && (Flags & KEYCXX0X)) AddResult = 3;
+ // Don't add this keyword under MicrosoftMode.
+ if (LangOpts.MicrosoftMode && (Flags & KEYNOMS))
+ return;
// Don't add this keyword if disabled in this language.
if (AddResult == 0) return;
@@ -154,8 +159,8 @@ static void AddCXXOperatorKeyword(StringRef Keyword,
Info.setIsCPlusPlusOperatorKeyword();
}
-/// AddObjCKeyword - Register an Objective-C @keyword like "class" "selector" or
-/// "property".
+/// AddObjCKeyword - Register an Objective-C \@keyword like "class" "selector"
+/// or "property".
static void AddObjCKeyword(StringRef Name,
tok::ObjCKeywordKind ObjCID,
IdentifierTable &Table) {
@@ -335,22 +340,22 @@ public:
unsigned Selector::getNumArgs() const {
unsigned IIF = getIdentifierInfoFlag();
- if (IIF == ZeroArg)
+ if (IIF <= ZeroArg)
return 0;
if (IIF == OneArg)
return 1;
- // We point to a MultiKeywordSelector (pointer doesn't contain any flags).
- MultiKeywordSelector *SI = reinterpret_cast<MultiKeywordSelector *>(InfoPtr);
+ // We point to a MultiKeywordSelector.
+ MultiKeywordSelector *SI = getMultiKeywordSelector();
return SI->getNumArgs();
}
IdentifierInfo *Selector::getIdentifierInfoForSlot(unsigned argIndex) const {
- if (getIdentifierInfoFlag()) {
+ if (getIdentifierInfoFlag() < MultiArg) {
assert(argIndex == 0 && "illegal keyword index");
return getAsIdentifierInfo();
}
- // We point to a MultiKeywordSelector (pointer doesn't contain any flags).
- MultiKeywordSelector *SI = reinterpret_cast<MultiKeywordSelector *>(InfoPtr);
+ // We point to a MultiKeywordSelector.
+ MultiKeywordSelector *SI = getMultiKeywordSelector();
return SI->getIdentifierInfoForSlot(argIndex);
}
@@ -375,7 +380,7 @@ std::string Selector::getAsString() const {
if (InfoPtr == 0)
return "<null selector>";
- if (InfoPtr & ArgFlags) {
+ if (getIdentifierInfoFlag() < MultiArg) {
IdentifierInfo *II = getAsIdentifierInfo();
// If the number of arguments is 0 then II is guaranteed to not be null.
@@ -388,8 +393,8 @@ std::string Selector::getAsString() const {
return II->getName().str() + ":";
}
- // We have a multiple keyword selector (no embedded flags).
- return reinterpret_cast<MultiKeywordSelector *>(InfoPtr)->getName();
+ // We have a multiple keyword selector.
+ return getMultiKeywordSelector()->getName();
}
/// Interpreting the given string using the normal CamelCase
diff --git a/lib/Basic/ObjCRuntime.cpp b/lib/Basic/ObjCRuntime.cpp
new file mode 100644
index 0000000..9bd433a
--- /dev/null
+++ b/lib/Basic/ObjCRuntime.cpp
@@ -0,0 +1,86 @@
+//===- ObjCRuntime.cpp - Objective-C Runtime Handling -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the ObjCRuntime class, which represents the
+// target Objective-C runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Basic/ObjCRuntime.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+std::string ObjCRuntime::getAsString() const {
+ std::string Result;
+ {
+ llvm::raw_string_ostream Out(Result);
+ Out << *this;
+ }
+ return Result;
+}
+
+raw_ostream &clang::operator<<(raw_ostream &out, const ObjCRuntime &value) {
+ switch (value.getKind()) {
+ case ObjCRuntime::MacOSX: out << "macosx"; break;
+ case ObjCRuntime::FragileMacOSX: out << "macosx-fragile"; break;
+ case ObjCRuntime::iOS: out << "ios"; break;
+ case ObjCRuntime::GNUstep: out << "gnustep"; break;
+ case ObjCRuntime::GCC: out << "gcc"; break;
+ case ObjCRuntime::ObjFW: out << "objfw"; break;
+ }
+ if (value.getVersion() > VersionTuple(0)) {
+ out << '-' << value.getVersion();
+ }
+ return out;
+}
+
+bool ObjCRuntime::tryParse(StringRef input) {
+ // Look for the last dash.
+ std::size_t dash = input.rfind('-');
+
+ // We permit dashes in the runtime name, and we also permit the
+ // version to be omitted, so if we see a dash not followed by a
+ // digit then we need to ignore it.
+ if (dash != StringRef::npos && dash + 1 != input.size() &&
+ (input[dash+1] < '0' || input[dash+1] > '9')) {
+ dash = StringRef::npos;
+ }
+
+ // Everything prior to that must be a valid string name.
+ Kind kind;
+ StringRef runtimeName = input.substr(0, dash);
+ Version = VersionTuple(0);
+ if (runtimeName == "macosx") {
+ kind = ObjCRuntime::MacOSX;
+ } else if (runtimeName == "macosx-fragile") {
+ kind = ObjCRuntime::FragileMacOSX;
+ } else if (runtimeName == "ios") {
+ kind = ObjCRuntime::iOS;
+ } else if (runtimeName == "gnustep") {
+ // If no version is specified then default to the most recent one that we
+ // know about.
+ Version = VersionTuple(1, 6);
+ kind = ObjCRuntime::GNUstep;
+ } else if (runtimeName == "gcc") {
+ kind = ObjCRuntime::GCC;
+ } else if (runtimeName == "objfw") {
+ kind = ObjCRuntime::ObjFW;
+ } else {
+ return true;
+ }
+ TheKind = kind;
+
+ if (dash != StringRef::npos) {
+ StringRef verString = input.substr(dash + 1);
+ if (Version.tryParse(verString))
+ return true;
+ }
+
+ return false;
+}
diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp
index cef091c..9ec2474 100644
--- a/lib/Basic/SourceManager.cpp
+++ b/lib/Basic/SourceManager.cpp
@@ -71,7 +71,7 @@ unsigned ContentCache::getSize() const {
void ContentCache::replaceBuffer(const llvm::MemoryBuffer *B,
bool DoNotFree) {
- if (B == Buffer.getPointer()) {
+ if (B && B == Buffer.getPointer()) {
assert(0 && "Replacing with the same buffer");
Buffer.setInt(DoNotFree? DoNotFreeFlag : 0);
return;
@@ -97,7 +97,10 @@ const llvm::MemoryBuffer *ContentCache::getBuffer(DiagnosticsEngine &Diag,
}
std::string ErrorStr;
- Buffer.setPointer(SM.getFileManager().getBufferForFile(ContentsEntry, &ErrorStr));
+ bool isVolatile = SM.userFilesAreVolatile() && !IsSystemFile;
+ Buffer.setPointer(SM.getFileManager().getBufferForFile(ContentsEntry,
+ &ErrorStr,
+ isVolatile));
// If we were unable to open the file, then we are in an inconsistent
// situation where the content cache referenced a file which no longer
@@ -189,9 +192,9 @@ unsigned LineTableInfo::getLineTableFilenameID(StringRef Name) {
}
/// AddLineNote - Add a line note to the line table that indicates that there
-/// is a #line at the specified FID/Offset location which changes the presumed
+/// is a \#line at the specified FID/Offset location which changes the presumed
/// location to LineNo/FilenameID.
-void LineTableInfo::AddLineNote(int FID, unsigned Offset,
+void LineTableInfo::AddLineNote(FileID FID, unsigned Offset,
unsigned LineNo, int FilenameID) {
std::vector<LineEntry> &Entries = LineEntries[FID];
@@ -219,10 +222,10 @@ void LineTableInfo::AddLineNote(int FID, unsigned Offset,
/// AddLineNote This is the same as the previous version of AddLineNote, but is
/// used for GNU line markers. If EntryExit is 0, then this doesn't change the
-/// presumed #include stack. If it is 1, this is a file entry, if it is 2 then
+/// presumed \#include stack. If it is 1, this is a file entry, if it is 2 then
/// this is a file exit. FileKind specifies whether this is a system header or
/// extern C system header.
-void LineTableInfo::AddLineNote(int FID, unsigned Offset,
+void LineTableInfo::AddLineNote(FileID FID, unsigned Offset,
unsigned LineNo, int FilenameID,
unsigned EntryExit,
SrcMgr::CharacteristicKind FileKind) {
@@ -256,7 +259,7 @@ void LineTableInfo::AddLineNote(int FID, unsigned Offset,
/// FindNearestLineEntry - Find the line entry nearest to FID that is before
/// it. If there is no line entry before Offset in FID, return null.
-const LineEntry *LineTableInfo::FindNearestLineEntry(int FID,
+const LineEntry *LineTableInfo::FindNearestLineEntry(FileID FID,
unsigned Offset) {
const std::vector<LineEntry> &Entries = LineEntries[FID];
assert(!Entries.empty() && "No #line entries for this FID after all!");
@@ -275,7 +278,7 @@ const LineEntry *LineTableInfo::FindNearestLineEntry(int FID,
/// \brief Add a new line entry that has already been encoded into
/// the internal representation of the line table.
-void LineTableInfo::AddEntry(int FID,
+void LineTableInfo::AddEntry(FileID FID,
const std::vector<LineEntry> &Entries) {
LineEntries[FID] = Entries;
}
@@ -308,7 +311,7 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo,
if (LineTable == 0)
LineTable = new LineTableInfo();
- LineTable->AddLineNote(LocInfo.first.ID, LocInfo.second, LineNo, FilenameID);
+ LineTable->AddLineNote(LocInfo.first, LocInfo.second, LineNo, FilenameID);
}
/// AddLineNote - Add a GNU line marker to the line table.
@@ -353,7 +356,7 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo,
else if (IsFileExit)
EntryExit = 2;
- LineTable->AddLineNote(LocInfo.first.ID, LocInfo.second, LineNo, FilenameID,
+ LineTable->AddLineNote(LocInfo.first, LocInfo.second, LineNo, FilenameID,
EntryExit, FileKind);
}
@@ -367,8 +370,10 @@ LineTableInfo &SourceManager::getLineTable() {
// Private 'Create' methods.
//===----------------------------------------------------------------------===//
-SourceManager::SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr)
+SourceManager::SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr,
+ bool UserFilesAreVolatile)
: Diag(Diag), FileMgr(FileMgr), OverridenFilesKeepOriginalName(true),
+ UserFilesAreVolatile(UserFilesAreVolatile),
ExternalSLocEntries(0), LineTable(0), NumLinearScans(0),
NumBinaryProbes(0), FakeBufferForRecovery(0),
FakeContentCacheForRecovery(0) {
@@ -426,7 +431,8 @@ void SourceManager::clearIDTables() {
/// getOrCreateContentCache - Create or return a cached ContentCache for the
/// specified file.
const ContentCache *
-SourceManager::getOrCreateContentCache(const FileEntry *FileEnt) {
+SourceManager::getOrCreateContentCache(const FileEntry *FileEnt,
+ bool isSystemFile) {
assert(FileEnt && "Didn't specify a file entry to use?");
// Do we already have information about this file?
@@ -440,16 +446,22 @@ SourceManager::getOrCreateContentCache(const FileEntry *FileEnt) {
EntryAlign = std::max(8U, EntryAlign);
Entry = ContentCacheAlloc.Allocate<ContentCache>(1, EntryAlign);
- // If the file contents are overridden with contents from another file,
- // pass that file to ContentCache.
- llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
- overI = OverriddenFiles.find(FileEnt);
- if (overI == OverriddenFiles.end())
+ if (OverriddenFilesInfo) {
+ // If the file contents are overridden with contents from another file,
+ // pass that file to ContentCache.
+ llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
+ overI = OverriddenFilesInfo->OverriddenFiles.find(FileEnt);
+ if (overI == OverriddenFilesInfo->OverriddenFiles.end())
+ new (Entry) ContentCache(FileEnt);
+ else
+ new (Entry) ContentCache(OverridenFilesKeepOriginalName ? FileEnt
+ : overI->second,
+ overI->second);
+ } else {
new (Entry) ContentCache(FileEnt);
- else
- new (Entry) ContentCache(OverridenFilesKeepOriginalName ? FileEnt
- : overI->second,
- overI->second);
+ }
+
+ Entry->IsSystemFile = isSystemFile;
return Entry;
}
@@ -622,6 +634,8 @@ void SourceManager::overrideFileContents(const FileEntry *SourceFile,
const_cast<SrcMgr::ContentCache *>(IR)->replaceBuffer(Buffer, DoNotFree);
const_cast<SrcMgr::ContentCache *>(IR)->BufferOverridden = true;
+
+ getOverriddenFilesInfo().OverriddenFilesWithBuffer.insert(SourceFile);
}
void SourceManager::overrideFileContents(const FileEntry *SourceFile,
@@ -632,7 +646,20 @@ void SourceManager::overrideFileContents(const FileEntry *SourceFile,
assert(FileInfos.count(SourceFile) == 0 &&
"This function should be called at the initialization stage, before "
"any parsing occurs.");
- OverriddenFiles[SourceFile] = NewFile;
+ getOverriddenFilesInfo().OverriddenFiles[SourceFile] = NewFile;
+}
+
+void SourceManager::disableFileContentsOverride(const FileEntry *File) {
+ if (!isFileOverridden(File))
+ return;
+
+ const SrcMgr::ContentCache *IR = getOrCreateContentCache(File);
+ const_cast<SrcMgr::ContentCache *>(IR)->replaceBuffer(0);
+ const_cast<SrcMgr::ContentCache *>(IR)->ContentsEntry = IR->OrigEntry;
+
+ assert(OverriddenFilesInfo);
+ OverriddenFilesInfo->OverriddenFiles.erase(File);
+ OverriddenFilesInfo->OverriddenFilesWithBuffer.erase(File);
}
StringRef SourceManager::getBufferData(FileID FID, bool *Invalid) const {
@@ -995,9 +1022,10 @@ unsigned SourceManager::getColumnNumber(FileID FID, unsigned FilePos,
if (MyInvalid)
return 1;
- if (FilePos >= MemBuf->getBufferSize()) {
+ // It is okay to request a position just past the end of the buffer.
+ if (FilePos > MemBuf->getBufferSize()) {
if (Invalid)
- *Invalid = MyInvalid;
+ *Invalid = true;
return 1;
}
@@ -1295,7 +1323,7 @@ SourceManager::getFileCharacteristic(SourceLocation Loc) const {
assert(LineTable && "Can't have linetable entries without a LineTable!");
// See if there is a #line directive before the location.
const LineEntry *Entry =
- LineTable->FindNearestLineEntry(LocInfo.first.ID, LocInfo.second);
+ LineTable->FindNearestLineEntry(LocInfo.first, LocInfo.second);
// If this is before the first line marker, use the file characteristic.
if (!Entry)
@@ -1305,7 +1333,7 @@ SourceManager::getFileCharacteristic(SourceLocation Loc) const {
}
/// Return the filename or buffer identifier of the buffer the location is in.
-/// Note that this name does not respect #line directives. Use getPresumedLoc
+/// Note that this name does not respect \#line directives. Use getPresumedLoc
/// for normal clients.
const char *SourceManager::getBufferName(SourceLocation Loc,
bool *Invalid) const {
@@ -1316,7 +1344,7 @@ const char *SourceManager::getBufferName(SourceLocation Loc,
/// getPresumedLoc - This method returns the "presumed" location of a
-/// SourceLocation specifies. A "presumed location" can be modified by #line
+/// SourceLocation specifies. A "presumed location" can be modified by \#line
/// or GNU line marker directives. This provides a view on the data that a
/// user should see in diagnostics, for example.
///
@@ -1360,7 +1388,7 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const {
assert(LineTable && "Can't have linetable entries without a LineTable!");
// See if there is a #line directive before this. If so, get it.
if (const LineEntry *Entry =
- LineTable->FindNearestLineEntry(LocInfo.first.ID, LocInfo.second)) {
+ LineTable->FindNearestLineEntry(LocInfo.first, LocInfo.second)) {
// If the LineEntry indicates a filename, use it.
if (Entry->FilenameID != -1)
Filename = LineTable->getFilename(Entry->FilenameID);
@@ -1834,8 +1862,6 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
return LOffs.first < ROffs.first;
}
-/// PrintStats - Print statistics to stderr.
-///
void SourceManager::PrintStats() const {
llvm::errs() << "\n*** Source Manager Stats:\n";
llvm::errs() << FileInfos.size() << " files mapped, " << MemBufferInfos.size()
@@ -1887,10 +1913,14 @@ SourceManager::MemoryBufferSizes SourceManager::getMemoryBufferSizes() const {
}
size_t SourceManager::getDataStructureSizes() const {
- return llvm::capacity_in_bytes(MemBufferInfos)
+ size_t size = llvm::capacity_in_bytes(MemBufferInfos)
+ llvm::capacity_in_bytes(LocalSLocEntryTable)
+ llvm::capacity_in_bytes(LoadedSLocEntryTable)
+ llvm::capacity_in_bytes(SLocEntryLoaded)
- + llvm::capacity_in_bytes(FileInfos)
- + llvm::capacity_in_bytes(OverriddenFiles);
+ + llvm::capacity_in_bytes(FileInfos);
+
+ if (OverriddenFilesInfo)
+ size += llvm::capacity_in_bytes(OverriddenFilesInfo->OverriddenFiles);
+
+ return size;
}
diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp
index 8c49486..db5941a 100644
--- a/lib/Basic/TargetInfo.cpp
+++ b/lib/Basic/TargetInfo.cpp
@@ -47,6 +47,7 @@ TargetInfo::TargetInfo(const std::string &T) : Triple(T) {
LargeArrayMinWidth = 0;
LargeArrayAlign = 0;
MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0;
+ MaxVectorAlign = 0;
SizeType = UnsignedLong;
PtrDiffType = SignedLong;
IntMaxType = SignedLongLong;
diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp
index dd2a89a..883864f 100644
--- a/lib/Basic/Targets.cpp
+++ b/lib/Basic/Targets.cpp
@@ -316,6 +316,8 @@ protected:
DefineStd(Builder, "linux", Opts);
Builder.defineMacro("__gnu_linux__");
Builder.defineMacro("__ELF__");
+ if (Triple.getEnvironment() == llvm::Triple::ANDROIDEABI)
+ Builder.defineMacro("__ANDROID__", "1");
if (Opts.POSIXThreads)
Builder.defineMacro("_REENTRANT");
if (Opts.CPlusPlus)
@@ -371,6 +373,7 @@ public:
OpenBSDTargetInfo(const std::string &triple)
: OSTargetInfo<Target>(triple) {
this->UserLabelPrefix = "";
+ this->TLSSupported = false;
llvm::Triple Triple(triple);
switch (Triple.getArch()) {
@@ -391,6 +394,29 @@ public:
}
};
+// Bitrig Target
+template<typename Target>
+class BitrigTargetInfo : public OSTargetInfo<Target> {
+protected:
+ virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
+ MacroBuilder &Builder) const {
+ // Bitrig defines; list based off of gcc output
+
+ Builder.defineMacro("__Bitrig__");
+ DefineStd(Builder, "unix", Opts);
+ Builder.defineMacro("__ELF__");
+ if (Opts.POSIXThreads)
+ Builder.defineMacro("_REENTRANT");
+ }
+public:
+ BitrigTargetInfo(const std::string &triple)
+ : OSTargetInfo<Target>(triple) {
+ this->UserLabelPrefix = "";
+ this->TLSSupported = false;
+ this->MCountName = "__mcount";
+ }
+};
+
// PSP Target
template<typename Target>
class PSPTargetInfo : public OSTargetInfo<Target> {
@@ -573,12 +599,60 @@ class PPCTargetInfo : public TargetInfo {
static const Builtin::Info BuiltinInfo[];
static const char * const GCCRegNames[];
static const TargetInfo::GCCRegAlias GCCRegAliases[];
+ std::string CPU;
public:
PPCTargetInfo(const std::string& triple) : TargetInfo(triple) {
LongDoubleWidth = LongDoubleAlign = 128;
LongDoubleFormat = &llvm::APFloat::PPCDoubleDouble;
}
+ /// \brief Flags for architecture specific defines.
+ typedef enum {
+ ArchDefineNone = 0,
+ ArchDefineName = 1 << 0, // <name> is substituted for arch name.
+ ArchDefinePpcgr = 1 << 1,
+ ArchDefinePpcsq = 1 << 2,
+ ArchDefine440 = 1 << 3,
+ ArchDefine603 = 1 << 4,
+ ArchDefine604 = 1 << 5,
+ ArchDefinePwr4 = 1 << 6,
+ ArchDefinePwr6 = 1 << 7
+ } ArchDefineTypes;
+
+ virtual bool setCPU(const std::string &Name) {
+ bool CPUKnown = llvm::StringSwitch<bool>(Name)
+ .Case("generic", true)
+ .Case("440", true)
+ .Case("450", true)
+ .Case("601", true)
+ .Case("602", true)
+ .Case("603", true)
+ .Case("603e", true)
+ .Case("603ev", true)
+ .Case("604", true)
+ .Case("604e", true)
+ .Case("620", true)
+ .Case("g3", true)
+ .Case("7400", true)
+ .Case("g4", true)
+ .Case("7450", true)
+ .Case("g4+", true)
+ .Case("750", true)
+ .Case("970", true)
+ .Case("g5", true)
+ .Case("a2", true)
+ .Case("pwr6", true)
+ .Case("pwr7", true)
+ .Case("ppc", true)
+ .Case("ppc64", true)
+ .Default(false);
+
+ if (CPUKnown)
+ CPU = Name;
+
+ return CPUKnown;
+ }
+
virtual void getTargetBuiltins(const Builtin::Info *&Records,
unsigned &NumRecords) const {
Records = BuiltinInfo;
@@ -718,8 +792,6 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__POWERPC__");
if (PointerWidth == 64) {
Builder.defineMacro("_ARCH_PPC64");
- Builder.defineMacro("_LP64");
- Builder.defineMacro("__LP64__");
Builder.defineMacro("__powerpc64__");
Builder.defineMacro("__ppc64__");
} else {
@@ -727,7 +799,8 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts,
}
// Target properties.
- if (getTriple().getOS() != llvm::Triple::NetBSD)
+ if (getTriple().getOS() != llvm::Triple::NetBSD &&
+ getTriple().getOS() != llvm::Triple::OpenBSD)
Builder.defineMacro("_BIG_ENDIAN");
Builder.defineMacro("__BIG_ENDIAN__");
@@ -742,6 +815,47 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__VEC__", "10206");
Builder.defineMacro("__ALTIVEC__");
}
+
+ // CPU identification.
+ ArchDefineTypes defs = (ArchDefineTypes)llvm::StringSwitch<int>(CPU)
+ .Case("440", ArchDefineName)
+ .Case("450", ArchDefineName | ArchDefine440)
+ .Case("601", ArchDefineName)
+ .Case("602", ArchDefineName | ArchDefinePpcgr)
+ .Case("603", ArchDefineName | ArchDefinePpcgr)
+ .Case("603e", ArchDefineName | ArchDefine603 | ArchDefinePpcgr)
+ .Case("603ev", ArchDefineName | ArchDefine603 | ArchDefinePpcgr)
+ .Case("604", ArchDefineName | ArchDefinePpcgr)
+ .Case("604e", ArchDefineName | ArchDefine604 | ArchDefinePpcgr)
+ .Case("620", ArchDefineName | ArchDefinePpcgr)
+ .Case("7400", ArchDefineName | ArchDefinePpcgr)
+ .Case("7450", ArchDefineName | ArchDefinePpcgr)
+ .Case("750", ArchDefineName | ArchDefinePpcgr)
+ .Case("970", ArchDefineName | ArchDefinePwr4 | ArchDefinePpcgr
+ | ArchDefinePpcsq)
+ .Case("pwr6", ArchDefinePwr6 | ArchDefinePpcgr | ArchDefinePpcsq)
+ .Case("pwr7", ArchDefineName | ArchDefinePwr6 | ArchDefinePpcgr
+ | ArchDefinePpcsq)
+ .Default(ArchDefineNone);
+
+ if (defs & ArchDefineName)
+ Builder.defineMacro(Twine("_ARCH_", StringRef(CPU).upper()));
+ if (defs & ArchDefinePpcgr)
+ Builder.defineMacro("_ARCH_PPCGR");
+ if (defs & ArchDefinePpcsq)
+ Builder.defineMacro("_ARCH_PPCSQ");
+ if (defs & ArchDefine440)
+ Builder.defineMacro("_ARCH_440");
+ if (defs & ArchDefine603)
+ Builder.defineMacro("_ARCH_603");
+ if (defs & ArchDefine604)
+ Builder.defineMacro("_ARCH_604");
+ if (defs & (ArchDefinePwr4 | ArchDefinePwr6))
+ Builder.defineMacro("_ARCH_PWR4");
+ if (defs & ArchDefinePwr6) {
+ Builder.defineMacro("_ARCH_PWR5");
+ Builder.defineMacro("_ARCH_PWR6");
+ }
}
bool PPCTargetInfo::hasFeature(StringRef Feature) const {
@@ -878,15 +992,9 @@ public:
}
}
- virtual const char *getVAListDeclaration() const {
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
// This is the ELF definition, and is overridden by the Darwin sub-target
- return "typedef struct __va_list_tag {"
- " unsigned char gpr;"
- " unsigned char fpr;"
- " unsigned short reserved;"
- " void* overflow_arg_area;"
- " void* reg_save_area;"
- "} __builtin_va_list[1];";
+ return TargetInfo::PowerABIBuiltinVaList;
}
};
} // end anonymous namespace.
@@ -907,8 +1015,8 @@ public:
LongDoubleFormat = &llvm::APFloat::IEEEdouble;
}
}
- virtual const char *getVAListDeclaration() const {
- return "typedef char* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
}
};
} // end anonymous namespace.
@@ -927,8 +1035,8 @@ public:
DescriptionString = "E-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-"
"i64:32:64-f32:32:32-f64:64:64-v128:128:128-n32";
}
- virtual const char *getVAListDeclaration() const {
- return "typedef char* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
}
};
@@ -944,54 +1052,40 @@ public:
} // end anonymous namespace.
namespace {
- static const unsigned PTXAddrSpaceMap[] = {
- 0, // opencl_global
- 4, // opencl_local
- 1 // opencl_constant
+ static const unsigned NVPTXAddrSpaceMap[] = {
+ 1, // opencl_global
+ 3, // opencl_local
+ 4, // opencl_constant
+ 1, // cuda_device
+ 4, // cuda_constant
+ 3, // cuda_shared
};
- class PTXTargetInfo : public TargetInfo {
+ class NVPTXTargetInfo : public TargetInfo {
static const char * const GCCRegNames[];
static const Builtin::Info BuiltinInfo[];
std::vector<llvm::StringRef> AvailableFeatures;
public:
- PTXTargetInfo(const std::string& triple) : TargetInfo(triple) {
+ NVPTXTargetInfo(const std::string& triple) : TargetInfo(triple) {
BigEndian = false;
TLSSupported = false;
LongWidth = LongAlign = 64;
- AddrSpaceMap = &PTXAddrSpaceMap;
+ AddrSpaceMap = &NVPTXAddrSpaceMap;
// Define available target features
- // These must be defined in sorted order!
- AvailableFeatures.push_back("compute10");
- AvailableFeatures.push_back("compute11");
- AvailableFeatures.push_back("compute12");
- AvailableFeatures.push_back("compute13");
- AvailableFeatures.push_back("compute20");
- AvailableFeatures.push_back("double");
- AvailableFeatures.push_back("no-fma");
- AvailableFeatures.push_back("ptx20");
- AvailableFeatures.push_back("ptx21");
- AvailableFeatures.push_back("ptx22");
- AvailableFeatures.push_back("ptx23");
- AvailableFeatures.push_back("sm10");
- AvailableFeatures.push_back("sm11");
- AvailableFeatures.push_back("sm12");
- AvailableFeatures.push_back("sm13");
- AvailableFeatures.push_back("sm20");
- AvailableFeatures.push_back("sm21");
- AvailableFeatures.push_back("sm22");
- AvailableFeatures.push_back("sm23");
+ // These must be defined in sorted order!
+ NoAsmVariants = true;
}
virtual void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
Builder.defineMacro("__PTX__");
+ Builder.defineMacro("__NVPTX__");
}
virtual void getTargetBuiltins(const Builtin::Info *&Records,
unsigned &NumRecords) const {
Records = BuiltinInfo;
- NumRecords = clang::PTX::LastTSBuiltin-Builtin::FirstTSBuiltin;
+ NumRecords = clang::NVPTX::LastTSBuiltin-Builtin::FirstTSBuiltin;
}
virtual bool hasFeature(StringRef Feature) const {
- return Feature == "ptx";
+ return Feature == "ptx" || Feature == "nvptx";
}
virtual void getGCCRegNames(const char * const *&Names,
@@ -1011,36 +1105,38 @@ namespace {
// FIXME: Is this really right?
return "";
}
- virtual const char *getVAListDeclaration() const {
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
// FIXME: implement
- return "typedef char* __builtin_va_list;";
+ return TargetInfo::CharPtrBuiltinVaList;
+ }
+ virtual bool setCPU(const std::string &Name) {
+ return Name == "sm_10" || Name == "sm_13" || Name == "sm_20";
}
-
virtual bool setFeatureEnabled(llvm::StringMap<bool> &Features,
StringRef Name,
bool Enabled) const;
};
- const Builtin::Info PTXTargetInfo::BuiltinInfo[] = {
+ const Builtin::Info NVPTXTargetInfo::BuiltinInfo[] = {
#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES },
#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\
ALL_LANGUAGES },
-#include "clang/Basic/BuiltinsPTX.def"
+#include "clang/Basic/BuiltinsNVPTX.def"
};
- const char * const PTXTargetInfo::GCCRegNames[] = {
+ const char * const NVPTXTargetInfo::GCCRegNames[] = {
"r0"
};
- void PTXTargetInfo::getGCCRegNames(const char * const *&Names,
+ void NVPTXTargetInfo::getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const {
Names = GCCRegNames;
NumNames = llvm::array_lengthof(GCCRegNames);
}
- bool PTXTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
- StringRef Name,
- bool Enabled) const {
+ bool NVPTXTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
+ StringRef Name,
+ bool Enabled) const {
if(std::binary_search(AvailableFeatures.begin(), AvailableFeatures.end(),
Name)) {
Features[Name] = Enabled;
@@ -1050,24 +1146,28 @@ namespace {
}
}
- class PTX32TargetInfo : public PTXTargetInfo {
+ class NVPTX32TargetInfo : public NVPTXTargetInfo {
public:
- PTX32TargetInfo(const std::string& triple) : PTXTargetInfo(triple) {
+ NVPTX32TargetInfo(const std::string& triple) : NVPTXTargetInfo(triple) {
PointerWidth = PointerAlign = 32;
- SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedInt;
+ SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedInt;
DescriptionString
- = "e-p:32:32-i64:64:64-f64:64:64-n1:8:16:32:64";
- }
+ = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"
+ "f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-"
+ "n16:32:64";
+ }
};
- class PTX64TargetInfo : public PTXTargetInfo {
+ class NVPTX64TargetInfo : public NVPTXTargetInfo {
public:
- PTX64TargetInfo(const std::string& triple) : PTXTargetInfo(triple) {
+ NVPTX64TargetInfo(const std::string& triple) : NVPTXTargetInfo(triple) {
PointerWidth = PointerAlign = 64;
- SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedLongLong;
+ SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedLongLong;
DescriptionString
- = "e-p:64:64-i64:64:64-f64:64:64-n1:8:16:32:64";
- }
+ = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"
+ "f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-"
+ "n16:32:64";
+ }
};
}
@@ -1096,8 +1196,8 @@ public:
return Feature == "mblaze";
}
- virtual const char *getVAListDeclaration() const {
- return "typedef char* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
}
virtual const char *getTargetPrefix() const {
return "mblaze";
@@ -1245,11 +1345,16 @@ class X86TargetInfo : public TargetInfo {
} MMX3DNowLevel;
bool HasAES;
+ bool HasPCLMUL;
bool HasLZCNT;
+ bool HasRDRND;
bool HasBMI;
bool HasBMI2;
bool HasPOPCNT;
+ bool HasSSE4a;
bool HasFMA4;
+ bool HasFMA;
+ bool HasXOP;
/// \brief Enumeration of all of the X86 CPUs supported by Clang.
///
@@ -1394,8 +1499,9 @@ class X86TargetInfo : public TargetInfo {
public:
X86TargetInfo(const std::string& triple)
: TargetInfo(triple), SSELevel(NoSSE), MMX3DNowLevel(NoMMX3DNow),
- HasAES(false), HasLZCNT(false), HasBMI(false), HasBMI2(false),
- HasPOPCNT(false), HasFMA4(false), CPU(CK_Generic) {
+ HasAES(false), HasPCLMUL(false), HasLZCNT(false), HasRDRND(false),
+ HasBMI(false), HasBMI2(false), HasPOPCNT(false), HasSSE4a(false),
+ HasFMA4(false), HasFMA(false), HasXOP(false), CPU(CK_Generic) {
BigEndian = false;
LongDoubleFormat = &llvm::APFloat::x87DoubleExtended;
}
@@ -1577,13 +1683,17 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap<bool> &Features) const {
Features["sse42"] = false;
Features["sse4a"] = false;
Features["aes"] = false;
+ Features["pclmul"] = false;
Features["avx"] = false;
Features["avx2"] = false;
Features["lzcnt"] = false;
+ Features["rdrand"] = false;
Features["bmi"] = false;
Features["bmi2"] = false;
Features["popcnt"] = false;
Features["fma4"] = false;
+ Features["fma"] = false;
+ Features["xop"] = false;
// FIXME: This *really* should not be here.
@@ -1637,23 +1747,30 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap<bool> &Features) const {
case CK_Corei7:
setFeatureEnabled(Features, "mmx", true);
setFeatureEnabled(Features, "sse4", true);
- setFeatureEnabled(Features, "aes", true);
break;
case CK_Corei7AVX:
+ setFeatureEnabled(Features, "mmx", true);
+ setFeatureEnabled(Features, "avx", true);
+ setFeatureEnabled(Features, "aes", true);
+ setFeatureEnabled(Features, "pclmul", true);
+ break;
case CK_CoreAVXi:
setFeatureEnabled(Features, "mmx", true);
- setFeatureEnabled(Features, "sse4", true);
+ setFeatureEnabled(Features, "avx", true);
setFeatureEnabled(Features, "aes", true);
- //setFeatureEnabled(Features, "avx", true);
+ setFeatureEnabled(Features, "pclmul", true);
+ setFeatureEnabled(Features, "rdrnd", true);
break;
case CK_CoreAVX2:
setFeatureEnabled(Features, "mmx", true);
- setFeatureEnabled(Features, "sse4", true);
+ setFeatureEnabled(Features, "avx2", true);
setFeatureEnabled(Features, "aes", true);
+ setFeatureEnabled(Features, "pclmul", true);
setFeatureEnabled(Features, "lzcnt", true);
+ setFeatureEnabled(Features, "rdrnd", true);
setFeatureEnabled(Features, "bmi", true);
setFeatureEnabled(Features, "bmi2", true);
- //setFeatureEnabled(Features, "avx2", true);
+ setFeatureEnabled(Features, "fma", true);
break;
case CK_K6:
case CK_WinChipC6:
@@ -1697,11 +1814,13 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap<bool> &Features) const {
case CK_BTVER1:
setFeatureEnabled(Features, "ssse3", true);
setFeatureEnabled(Features, "sse4a", true);
+ break;
case CK_BDVER1:
case CK_BDVER2:
- setFeatureEnabled(Features, "sse4", true);
- setFeatureEnabled(Features, "sse4a", true);
+ setFeatureEnabled(Features, "avx", true);
+ setFeatureEnabled(Features, "xop", true);
setFeatureEnabled(Features, "aes", true);
+ setFeatureEnabled(Features, "pclmul", true);
break;
case CK_C3_2:
setFeatureEnabled(Features, "mmx", true);
@@ -1716,7 +1835,8 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
// FIXME: This *really* should not be here. We need some way of translating
// options into llvm subtarget features.
if (!Features.count(Name) &&
- (Name != "sse4" && Name != "sse4.2" && Name != "sse4.1"))
+ (Name != "sse4" && Name != "sse4.2" && Name != "sse4.1" &&
+ Name != "rdrnd"))
return false;
// FIXME: this should probably use a switch with fall through.
@@ -1746,7 +1866,9 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
else if (Name == "3dnowa")
Features["mmx"] = Features["3dnow"] = Features["3dnowa"] = true;
else if (Name == "aes")
- Features["aes"] = true;
+ Features["sse"] = Features["sse2"] = Features["aes"] = true;
+ else if (Name == "pclmul")
+ Features["sse"] = Features["sse2"] = Features["pclmul"] = true;
else if (Name == "avx")
Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] =
Features["ssse3"] = Features["sse41"] = Features["sse42"] =
@@ -1755,15 +1877,27 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] =
Features["ssse3"] = Features["sse41"] = Features["sse42"] =
Features["popcnt"] = Features["avx"] = Features["avx2"] = true;
+ else if (Name == "fma")
+ Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] =
+ Features["ssse3"] = Features["sse41"] = Features["sse42"] =
+ Features["popcnt"] = Features["avx"] = Features["fma"] = true;
else if (Name == "fma4")
Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] =
Features["ssse3"] = Features["sse41"] = Features["sse42"] =
- Features["popcnt"] = Features["avx"] = Features["fma4"] = true;
+ Features["popcnt"] = Features["avx"] = Features["sse4a"] =
+ Features["fma4"] = true;
+ else if (Name == "xop")
+ Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] =
+ Features["ssse3"] = Features["sse41"] = Features["sse42"] =
+ Features["popcnt"] = Features["avx"] = Features["sse4a"] =
+ Features["fma4"] = Features["xop"] = true;
else if (Name == "sse4a")
Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] =
- Features["lzcnt"] = Features["popcnt"] = Features["sse4a"] = true;
+ Features["sse4a"] = true;
else if (Name == "lzcnt")
Features["lzcnt"] = true;
+ else if (Name == "rdrnd")
+ Features["rdrand"] = true;
else if (Name == "bmi")
Features["bmi"] = true;
else if (Name == "bmi2")
@@ -1776,33 +1910,50 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
else if (Name == "sse")
Features["sse"] = Features["sse2"] = Features["sse3"] =
Features["ssse3"] = Features["sse41"] = Features["sse42"] =
- Features["sse4a"] = false;
+ Features["sse4a"] = Features["avx"] = Features["avx2"] =
+ Features["fma"] = Features["fma4"] = Features["aes"] =
+ Features["pclmul"] = Features["xop"] = false;
else if (Name == "sse2")
Features["sse2"] = Features["sse3"] = Features["ssse3"] =
- Features["sse41"] = Features["sse42"] = Features["sse4a"] = false;
+ Features["sse41"] = Features["sse42"] = Features["sse4a"] =
+ Features["avx"] = Features["avx2"] = Features["fma"] =
+ Features["fma4"] = Features["aes"] = Features["pclmul"] =
+ Features["xop"] = false;
else if (Name == "sse3")
Features["sse3"] = Features["ssse3"] = Features["sse41"] =
- Features["sse42"] = Features["sse4a"] = false;
+ Features["sse42"] = Features["sse4a"] = Features["avx"] =
+ Features["avx2"] = Features["fma"] = Features["fma4"] =
+ Features["xop"] = false;
else if (Name == "ssse3")
- Features["ssse3"] = Features["sse41"] = Features["sse42"] = false;
+ Features["ssse3"] = Features["sse41"] = Features["sse42"] =
+ Features["avx"] = Features["avx2"] = Features["fma"] = false;
else if (Name == "sse4" || Name == "sse4.1")
- Features["sse41"] = Features["sse42"] = false;
+ Features["sse41"] = Features["sse42"] = Features["avx"] =
+ Features["avx2"] = Features["fma"] = false;
else if (Name == "sse4.2")
- Features["sse42"] = false;
+ Features["sse42"] = Features["avx"] = Features["avx2"] =
+ Features["fma"] = false;
else if (Name == "3dnow")
Features["3dnow"] = Features["3dnowa"] = false;
else if (Name == "3dnowa")
Features["3dnowa"] = false;
else if (Name == "aes")
Features["aes"] = false;
+ else if (Name == "pclmul")
+ Features["pclmul"] = false;
else if (Name == "avx")
- Features["avx"] = Features["avx2"] = Features["fma4"] = false;
+ Features["avx"] = Features["avx2"] = Features["fma"] =
+ Features["fma4"] = Features["xop"] = false;
else if (Name == "avx2")
Features["avx2"] = false;
+ else if (Name == "fma")
+ Features["fma"] = false;
else if (Name == "sse4a")
- Features["sse4a"] = false;
+ Features["sse4a"] = Features["fma4"] = Features["xop"] = false;
else if (Name == "lzcnt")
Features["lzcnt"] = false;
+ else if (Name == "rdrnd")
+ Features["rdrand"] = false;
else if (Name == "bmi")
Features["bmi"] = false;
else if (Name == "bmi2")
@@ -1810,7 +1961,9 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
else if (Name == "popcnt")
Features["popcnt"] = false;
else if (Name == "fma4")
- Features["fma4"] = false;
+ Features["fma4"] = Features["xop"] = false;
+ else if (Name == "xop")
+ Features["xop"] = false;
}
return true;
@@ -1832,11 +1985,21 @@ void X86TargetInfo::HandleTargetFeatures(std::vector<std::string> &Features) {
continue;
}
+ if (Feature == "pclmul") {
+ HasPCLMUL = true;
+ continue;
+ }
+
if (Feature == "lzcnt") {
HasLZCNT = true;
continue;
}
+ if (Feature == "rdrand") {
+ HasRDRND = true;
+ continue;
+ }
+
if (Feature == "bmi") {
HasBMI = true;
continue;
@@ -1852,11 +2015,26 @@ void X86TargetInfo::HandleTargetFeatures(std::vector<std::string> &Features) {
continue;
}
+ if (Feature == "sse4a") {
+ HasSSE4a = true;
+ continue;
+ }
+
if (Feature == "fma4") {
HasFMA4 = true;
continue;
}
+ if (Feature == "fma") {
+ HasFMA = true;
+ continue;
+ }
+
+ if (Feature == "xop") {
+ HasXOP = true;
+ continue;
+ }
+
assert(Features[i][0] == '+' && "Invalid target feature!");
X86SSEEnum Level = llvm::StringSwitch<X86SSEEnum>(Feature)
.Case("avx2", AVX2)
@@ -1894,10 +2072,6 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
// Target identification.
if (PointerWidth == 64) {
- if (getLongWidth() == 64) {
- Builder.defineMacro("_LP64");
- Builder.defineMacro("__LP64__");
- }
Builder.defineMacro("__amd64__");
Builder.defineMacro("__amd64");
Builder.defineMacro("__x86_64");
@@ -2039,9 +2213,15 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts,
if (HasAES)
Builder.defineMacro("__AES__");
+ if (HasPCLMUL)
+ Builder.defineMacro("__PCLMUL__");
+
if (HasLZCNT)
Builder.defineMacro("__LZCNT__");
+ if (HasRDRND)
+ Builder.defineMacro("__RDRND__");
+
if (HasBMI)
Builder.defineMacro("__BMI__");
@@ -2051,9 +2231,18 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts,
if (HasPOPCNT)
Builder.defineMacro("__POPCNT__");
+ if (HasSSE4a)
+ Builder.defineMacro("__SSE4A__");
+
if (HasFMA4)
Builder.defineMacro("__FMA4__");
+ if (HasFMA)
+ Builder.defineMacro("__FMA__");
+
+ if (HasXOP)
+ Builder.defineMacro("__XOP__");
+
// Each case falls through to the previous one here.
switch (SSELevel) {
case AVX2:
@@ -2117,11 +2306,14 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const {
.Case("avx2", SSELevel >= AVX2)
.Case("bmi", HasBMI)
.Case("bmi2", HasBMI2)
+ .Case("fma", HasFMA)
.Case("fma4", HasFMA4)
.Case("lzcnt", HasLZCNT)
+ .Case("rdrnd", HasRDRND)
.Case("mm3dnow", MMX3DNowLevel >= AMD3DNow)
.Case("mm3dnowa", MMX3DNowLevel >= AMD3DNowAthlon)
.Case("mmx", MMX3DNowLevel >= MMX)
+ .Case("pclmul", HasPCLMUL)
.Case("popcnt", HasPOPCNT)
.Case("sse", SSELevel >= SSE1)
.Case("sse2", SSELevel >= SSE2)
@@ -2129,9 +2321,11 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const {
.Case("ssse3", SSELevel >= SSSE3)
.Case("sse41", SSELevel >= SSE41)
.Case("sse42", SSELevel >= SSE42)
+ .Case("sse4a", HasSSE4a)
.Case("x86", true)
.Case("x86_32", PointerWidth == 32)
.Case("x86_64", PointerWidth == 64)
+ .Case("xop", HasXOP)
.Default(false);
}
@@ -2227,8 +2421,8 @@ public:
// MaxAtomicInlineWidth. (cmpxchg8b is an i586 instruction.)
MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64;
}
- virtual const char *getVAListDeclaration() const {
- return "typedef char* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
}
int getEHDataRegisterNumber(unsigned RegNo) const {
@@ -2266,6 +2460,18 @@ public:
} // end anonymous namespace
namespace {
+class BitrigI386TargetInfo : public BitrigTargetInfo<X86_32TargetInfo> {
+public:
+ BitrigI386TargetInfo(const std::string& triple) :
+ BitrigTargetInfo<X86_32TargetInfo>(triple) {
+ SizeType = UnsignedLong;
+ IntPtrType = SignedLong;
+ PtrDiffType = SignedLong;
+ }
+};
+} // end anonymous namespace
+
+namespace {
class DarwinI386TargetInfo : public DarwinTargetInfo<X86_32TargetInfo> {
public:
DarwinI386TargetInfo(const std::string& triple) :
@@ -2273,6 +2479,7 @@ public:
LongDoubleWidth = 128;
LongDoubleAlign = 128;
SuitableAlign = 128;
+ MaxVectorAlign = 256;
SizeType = UnsignedLong;
IntPtrType = SignedLong;
DescriptionString = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-"
@@ -2487,14 +2694,8 @@ public:
MaxAtomicPromoteWidth = 128;
MaxAtomicInlineWidth = 64;
}
- virtual const char *getVAListDeclaration() const {
- return "typedef struct __va_list_tag {"
- " unsigned gp_offset;"
- " unsigned fp_offset;"
- " void* overflow_arg_area;"
- " void* reg_save_area;"
- "} __va_list_tag;"
- "typedef __va_list_tag __builtin_va_list[1];";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::X86_64ABIBuiltinVaList;
}
int getEHDataRegisterNumber(unsigned RegNo) const {
@@ -2528,8 +2729,8 @@ public:
WindowsTargetInfo<X86_64TargetInfo>::getTargetDefines(Opts, Builder);
Builder.defineMacro("_WIN64");
}
- virtual const char *getVAListDeclaration() const {
- return "typedef char* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
}
};
} // end anonymous namespace
@@ -2586,6 +2787,7 @@ public:
DarwinX86_64TargetInfo(const std::string& triple)
: DarwinTargetInfo<X86_64TargetInfo>(triple) {
Int64Type = SignedLongLong;
+ MaxVectorAlign = 256;
}
};
} // end anonymous namespace
@@ -2603,6 +2805,18 @@ public:
} // end anonymous namespace
namespace {
+class BitrigX86_64TargetInfo : public BitrigTargetInfo<X86_64TargetInfo> {
+public:
+ BitrigX86_64TargetInfo(const std::string& triple)
+ : BitrigTargetInfo<X86_64TargetInfo>(triple) {
+ IntMaxType = SignedLongLong;
+ UIntMaxType = UnsignedLongLong;
+ Int64Type = SignedLongLong;
+ }
+};
+} // end anonymous namespace
+
+namespace {
class ARMTargetInfo : public TargetInfo {
// Possible FPU choices.
enum FPUMode {
@@ -2860,8 +3074,8 @@ public:
NumRecords = clang::ARM::LastTSBuiltin-Builtin::FirstTSBuiltin;
}
virtual bool isCLZForZeroUndef() const { return false; }
- virtual const char *getVAListDeclaration() const {
- return "typedef void* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::VoidPtrBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const;
@@ -3015,8 +3229,8 @@ public:
HexagonTargetInfo(const std::string& triple) : TargetInfo(triple) {
BigEndian = false;
DescriptionString = ("e-p:32:32:32-"
- "i64:64:64-i32:32:32-"
- "i16:16:16-i1:32:32-a:0:0");
+ "i64:64:64-i32:32:32-i16:16:16-i1:32:32"
+ "f64:64:64-f32:32:32-a0:0-n32");
// {} in inline assembly are packet specifiers, not assembly variant
// specifiers.
@@ -3041,8 +3255,8 @@ public:
return Feature == "hexagon";
}
- virtual const char *getVAListDeclaration() const {
- return "typedef char* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::CharPtrBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const;
@@ -3057,6 +3271,7 @@ public:
.Case("hexagonv2", "2")
.Case("hexagonv3", "3")
.Case("hexagonv4", "4")
+ .Case("hexagonv5", "5")
.Default(0);
}
@@ -3111,6 +3326,14 @@ void HexagonTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__QDSP6_ARCH__", "4");
}
}
+ else if(CPU == "hexagonv5") {
+ Builder.defineMacro("__HEXAGON_V5__");
+ Builder.defineMacro("__HEXAGON_ARCH__", "5");
+ if(Opts.HexagonQdsp6Compat) {
+ Builder.defineMacro("__QDSP6_V5__");
+ Builder.defineMacro("__QDSP6_ARCH__", "5");
+ }
+ }
}
const char * const HexagonTargetInfo::GCCRegNames[] = {
@@ -3159,7 +3382,6 @@ class SparcV8TargetInfo : public TargetInfo {
public:
SparcV8TargetInfo(const std::string& triple) : TargetInfo(triple) {
// FIXME: Support Sparc quad-precision long double?
- BigEndian = false;
DescriptionString = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-"
"i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32";
}
@@ -3200,8 +3422,8 @@ public:
unsigned &NumRecords) const {
// FIXME: Implement!
}
- virtual const char *getVAListDeclaration() const {
- return "typedef void* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::VoidPtrBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const;
@@ -3344,9 +3566,9 @@ namespace {
// FIXME: Is this really right?
return "";
}
- virtual const char *getVAListDeclaration() const {
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
// FIXME: implement
- return "typedef char* __builtin_va_list;";
+ return TargetInfo::CharPtrBuiltinVaList;
}
};
@@ -3375,7 +3597,10 @@ namespace {
static const unsigned TCEOpenCLAddrSpaceMap[] = {
3, // opencl_global
4, // opencl_local
- 5 // opencl_constant
+ 5, // opencl_constant
+ 0, // cuda_device
+ 0, // cuda_constant
+ 0 // cuda_shared
};
class TCETargetInfo : public TargetInfo{
@@ -3425,8 +3650,8 @@ namespace {
virtual const char *getClobbers() const {
return "";
}
- virtual const char *getVAListDeclaration() const {
- return "typedef void* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::VoidPtrBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const {}
@@ -3441,9 +3666,15 @@ namespace {
namespace {
class MipsTargetInfoBase : public TargetInfo {
+ static const Builtin::Info BuiltinInfo[];
std::string CPU;
- bool SoftFloat;
- bool SingleFloat;
+ bool IsMips16;
+ enum MipsFloatABI {
+ HardFloat, SingleFloat, SoftFloat
+ } FloatABI;
+ enum DspRevEnum {
+ NoDSP, DSP1, DSP2
+ } DspRev;
protected:
std::string ABI;
@@ -3454,7 +3685,9 @@ public:
const std::string& CPUStr)
: TargetInfo(triple),
CPU(CPUStr),
- SoftFloat(false), SingleFloat(false),
+ IsMips16(false),
+ FloatABI(HardFloat),
+ DspRev(NoDSP),
ABI(ABIStr)
{}
@@ -3471,14 +3704,35 @@ public:
virtual void getArchDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
- if (SoftFloat)
- Builder.defineMacro("__mips_soft_float", Twine(1));
- else if (SingleFloat)
- Builder.defineMacro("__mips_single_float", Twine(1));
- else if (!SoftFloat && !SingleFloat)
+ switch (FloatABI) {
+ case HardFloat:
Builder.defineMacro("__mips_hard_float", Twine(1));
- else
- llvm_unreachable("Invalid float ABI for Mips.");
+ break;
+ case SingleFloat:
+ Builder.defineMacro("__mips_hard_float", Twine(1));
+ Builder.defineMacro("__mips_single_float", Twine(1));
+ break;
+ case SoftFloat:
+ Builder.defineMacro("__mips_soft_float", Twine(1));
+ break;
+ }
+
+ if (IsMips16)
+ Builder.defineMacro("__mips16", Twine(1));
+
+ switch (DspRev) {
+ default:
+ break;
+ case DSP1:
+ Builder.defineMacro("__mips_dsp_rev", Twine(1));
+ Builder.defineMacro("__mips_dsp", Twine(1));
+ break;
+ case DSP2:
+ Builder.defineMacro("__mips_dsp_rev", Twine(2));
+ Builder.defineMacro("__mips_dspr2", Twine(1));
+ Builder.defineMacro("__mips_dsp", Twine(1));
+ break;
+ }
Builder.defineMacro("_MIPS_SZPTR", Twine(getPointerWidth(0)));
Builder.defineMacro("_MIPS_SZINT", Twine(getIntWidth()));
@@ -3489,13 +3743,14 @@ public:
MacroBuilder &Builder) const = 0;
virtual void getTargetBuiltins(const Builtin::Info *&Records,
unsigned &NumRecords) const {
- // FIXME: Implement!
+ Records = BuiltinInfo;
+ NumRecords = clang::Mips::LastTSBuiltin - Builtin::FirstTSBuiltin;
}
virtual bool hasFeature(StringRef Feature) const {
return Feature == "mips";
}
- virtual const char *getVAListDeclaration() const {
- return "typedef void* __builtin_va_list;";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::VoidPtrBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const {
@@ -3549,7 +3804,8 @@ public:
if (Name == "soft-float" || Name == "single-float" ||
Name == "o32" || Name == "n32" || Name == "n64" || Name == "eabi" ||
Name == "mips32" || Name == "mips32r2" ||
- Name == "mips64" || Name == "mips64r2") {
+ Name == "mips64" || Name == "mips64r2" ||
+ Name == "mips16" || Name == "dsp" || Name == "dspr2") {
Features[Name] = Enabled;
return true;
}
@@ -3557,27 +3813,39 @@ public:
}
virtual void HandleTargetFeatures(std::vector<std::string> &Features) {
- SoftFloat = false;
- SingleFloat = false;
+ IsMips16 = false;
+ FloatABI = HardFloat;
+ DspRev = NoDSP;
for (std::vector<std::string>::iterator it = Features.begin(),
ie = Features.end(); it != ie; ++it) {
- if (*it == "+single-float") {
- SingleFloat = true;
- break;
- }
-
- if (*it == "+soft-float") {
- SoftFloat = true;
- // This option is front-end specific.
- // Do not need to pass it to the backend.
- Features.erase(it);
- break;
- }
+ if (*it == "+single-float")
+ FloatABI = SingleFloat;
+ else if (*it == "+soft-float")
+ FloatABI = SoftFloat;
+ else if (*it == "+mips16")
+ IsMips16 = true;
+ else if (*it == "+dsp")
+ DspRev = std::max(DspRev, DSP1);
+ else if (*it == "+dspr2")
+ DspRev = std::max(DspRev, DSP2);
}
+
+ // Remove front-end specific option.
+ std::vector<std::string>::iterator it =
+ std::find(Features.begin(), Features.end(), "+soft-float");
+ if (it != Features.end())
+ Features.erase(it);
}
};
+const Builtin::Info MipsTargetInfoBase::BuiltinInfo[] = {
+#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES },
+#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\
+ ALL_LANGUAGES },
+#include "clang/Basic/BuiltinsMips.def"
+};
+
class Mips32TargetInfoBase : public MipsTargetInfoBase {
public:
Mips32TargetInfoBase(const std::string& triple) :
@@ -3868,8 +4136,8 @@ public:
virtual void getTargetBuiltins(const Builtin::Info *&Records,
unsigned &NumRecords) const {
}
- virtual const char *getVAListDeclaration() const {
- return "typedef int __builtin_va_list[4];";
+ virtual BuiltinVaListKind getBuiltinVaListKind() const {
+ return TargetInfo::PNaClABIBuiltinVaList;
}
virtual void getGCCRegNames(const char * const *&Names,
unsigned &NumNames) const;
@@ -3926,6 +4194,10 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new FreeBSDTargetInfo<ARMTargetInfo>(T);
case llvm::Triple::NetBSD:
return new NetBSDTargetInfo<ARMTargetInfo>(T);
+ case llvm::Triple::OpenBSD:
+ return new OpenBSDTargetInfo<ARMTargetInfo>(T);
+ case llvm::Triple::Bitrig:
+ return new BitrigTargetInfo<ARMTargetInfo>(T);
case llvm::Triple::RTEMS:
return new RTEMSTargetInfo<ARMTargetInfo>(T);
default:
@@ -3973,6 +4245,8 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new FreeBSDTargetInfo<Mips64EBTargetInfo>(T);
case llvm::Triple::NetBSD:
return new NetBSDTargetInfo<Mips64EBTargetInfo>(T);
+ case llvm::Triple::OpenBSD:
+ return new OpenBSDTargetInfo<Mips64EBTargetInfo>(T);
default:
return new Mips64EBTargetInfo(T);
}
@@ -3987,6 +4261,8 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new FreeBSDTargetInfo<Mips64ELTargetInfo>(T);
case llvm::Triple::NetBSD:
return new NetBSDTargetInfo<Mips64ELTargetInfo>(T);
+ case llvm::Triple::OpenBSD:
+ return new OpenBSDTargetInfo<Mips64ELTargetInfo>(T);
default:
return new Mips64ELTargetInfo(T);
}
@@ -4009,6 +4285,8 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new FreeBSDTargetInfo<PPC32TargetInfo>(T);
case llvm::Triple::NetBSD:
return new NetBSDTargetInfo<PPC32TargetInfo>(T);
+ case llvm::Triple::OpenBSD:
+ return new OpenBSDTargetInfo<PPC32TargetInfo>(T);
case llvm::Triple::RTEMS:
return new RTEMSTargetInfo<PPC32TargetInfo>(T);
default:
@@ -4031,10 +4309,10 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new PPC64TargetInfo(T);
}
- case llvm::Triple::ptx32:
- return new PTX32TargetInfo(T);
- case llvm::Triple::ptx64:
- return new PTX64TargetInfo(T);
+ case llvm::Triple::nvptx:
+ return new NVPTX32TargetInfo(T);
+ case llvm::Triple::nvptx64:
+ return new NVPTX64TargetInfo(T);
case llvm::Triple::mblaze:
return new MBlazeTargetInfo(T);
@@ -4049,6 +4327,8 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new SolarisSparcV8TargetInfo(T);
case llvm::Triple::NetBSD:
return new NetBSDTargetInfo<SparcV8TargetInfo>(T);
+ case llvm::Triple::OpenBSD:
+ return new OpenBSDTargetInfo<SparcV8TargetInfo>(T);
case llvm::Triple::RTEMS:
return new RTEMSTargetInfo<SparcV8TargetInfo>(T);
default:
@@ -4077,6 +4357,8 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new NetBSDI386TargetInfo(T);
case llvm::Triple::OpenBSD:
return new OpenBSDI386TargetInfo(T);
+ case llvm::Triple::Bitrig:
+ return new BitrigI386TargetInfo(T);
case llvm::Triple::FreeBSD:
return new FreeBSDTargetInfo<X86_32TargetInfo>(T);
case llvm::Triple::Minix:
@@ -4112,6 +4394,8 @@ static TargetInfo *AllocateTarget(const std::string &T) {
return new NetBSDTargetInfo<X86_64TargetInfo>(T);
case llvm::Triple::OpenBSD:
return new OpenBSDX86_64TargetInfo(T);
+ case llvm::Triple::Bitrig:
+ return new BitrigX86_64TargetInfo(T);
case llvm::Triple::FreeBSD:
return new FreeBSDTargetInfo<X86_64TargetInfo>(T);
case llvm::Triple::Solaris:
diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp
index 8cb2386..9daa30a 100644
--- a/lib/Basic/Version.cpp
+++ b/lib/Basic/Version.cpp
@@ -32,7 +32,7 @@ std::string getClangRepositoryPath() {
// If the SVN_REPOSITORY is empty, try to use the SVN keyword. This helps us
// pick up a tag in an SVN export, for example.
- static StringRef SVNRepository("$URL: http://llvm.org/svn/llvm-project/cfe/branches/release_31/lib/Basic/Version.cpp $");
+ static StringRef SVNRepository("$URL: http://llvm.org/svn/llvm-project/cfe/trunk/lib/Basic/Version.cpp $");
if (URL.empty()) {
URL = SVNRepository.slice(SVNRepository.find(':'),
SVNRepository.find("/lib/Basic"));
@@ -136,8 +136,7 @@ std::string getClangFullCPPVersion() {
#ifdef CLANG_VENDOR
OS << CLANG_VENDOR;
#endif
- OS << "Clang " CLANG_VERSION_STRING " ("
- << getClangFullRepositoryVersion() << ')';
+ OS << "Clang " CLANG_VERSION_STRING " " << getClangFullRepositoryVersion();
return OS.str();
}
diff --git a/lib/Basic/VersionTuple.cpp b/lib/Basic/VersionTuple.cpp
index 77aad39..4f479d0 100644
--- a/lib/Basic/VersionTuple.cpp
+++ b/lib/Basic/VersionTuple.cpp
@@ -34,3 +34,55 @@ raw_ostream& clang::operator<<(raw_ostream &Out,
Out << '.' << *Subminor;
return Out;
}
+
+static bool parseInt(StringRef &input, unsigned &value) {
+ assert(value == 0);
+ if (input.empty()) return true;
+
+ char next = input[0];
+ input = input.substr(1);
+ if (next < '0' || next > '9') return true;
+ value = (unsigned) (next - '0');
+
+ while (!input.empty()) {
+ next = input[0];
+ if (next < '0' || next > '9') return false;
+ input = input.substr(1);
+ value = value * 10 + (unsigned) (next - '0');
+ }
+
+ return false;
+}
+
+bool VersionTuple::tryParse(StringRef input) {
+ unsigned major = 0, minor = 0, micro = 0;
+
+ // Parse the major version, [0-9]+
+ if (parseInt(input, major)) return true;
+
+ if (input.empty()) {
+ *this = VersionTuple(major);
+ return false;
+ }
+
+ // If we're not done, parse the minor version, \.[0-9]+
+ if (input[0] != '.') return true;
+ input = input.substr(1);
+ if (parseInt(input, minor)) return true;
+
+ if (input.empty()) {
+ *this = VersionTuple(major, minor);
+ return false;
+ }
+
+ // If we're not done, parse the micro version, \.[0-9]+
+ if (input[0] != '.') return true;
+ input = input.substr(1);
+ if (parseInt(input, micro)) return true;
+
+ // If we have characters left over, it's an error.
+ if (!input.empty()) return true;
+
+ *this = VersionTuple(major, minor, micro);
+ return false;
+}
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index dfb9d61..206c228 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(Basic)
add_subdirectory(Lex)
add_subdirectory(Parse)
add_subdirectory(AST)
+add_subdirectory(ASTMatchers)
add_subdirectory(Sema)
add_subdirectory(CodeGen)
add_subdirectory(Analysis)
diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h
index 2853bc8..86f5380 100644
--- a/lib/CodeGen/ABIInfo.h
+++ b/lib/CodeGen/ABIInfo.h
@@ -74,31 +74,42 @@ namespace clang {
unsigned UIntData;
bool BoolData0;
bool BoolData1;
+ bool InReg;
- ABIArgInfo(Kind K, llvm::Type *TD=0, unsigned UI=0,
- bool B0 = false, bool B1 = false, llvm::Type* P = 0)
+ ABIArgInfo(Kind K, llvm::Type *TD, unsigned UI, bool B0, bool B1, bool IR,
+ llvm::Type* P)
: TheKind(K), TypeData(TD), PaddingType(P), UIntData(UI), BoolData0(B0),
- BoolData1(B1) {}
+ BoolData1(B1), InReg(IR) {}
public:
ABIArgInfo() : TheKind(Direct), TypeData(0), UIntData(0) {}
static ABIArgInfo getDirect(llvm::Type *T = 0, unsigned Offset = 0,
llvm::Type *Padding = 0) {
- return ABIArgInfo(Direct, T, Offset, false, false, Padding);
+ return ABIArgInfo(Direct, T, Offset, false, false, false, Padding);
+ }
+ static ABIArgInfo getDirectInReg(llvm::Type *T) {
+ return ABIArgInfo(Direct, T, 0, false, false, true, 0);
}
static ABIArgInfo getExtend(llvm::Type *T = 0) {
- return ABIArgInfo(Extend, T, 0);
+ return ABIArgInfo(Extend, T, 0, false, false, false, 0);
+ }
+ static ABIArgInfo getExtendInReg(llvm::Type *T = 0) {
+ return ABIArgInfo(Extend, T, 0, false, false, true, 0);
}
static ABIArgInfo getIgnore() {
- return ABIArgInfo(Ignore);
+ return ABIArgInfo(Ignore, 0, 0, false, false, false, 0);
}
static ABIArgInfo getIndirect(unsigned Alignment, bool ByVal = true
, bool Realign = false) {
- return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign);
+ return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, false, 0);
+ }
+ static ABIArgInfo getIndirectInReg(unsigned Alignment, bool ByVal = true
+ , bool Realign = false) {
+ return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, true, 0);
}
static ABIArgInfo getExpand() {
- return ABIArgInfo(Expand);
+ return ABIArgInfo(Expand, 0, 0, false, false, false, 0);
}
Kind getKind() const { return TheKind; }
@@ -132,6 +143,11 @@ namespace clang {
TypeData = T;
}
+ bool getInReg() const {
+ assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!");
+ return InReg;
+ }
+
// Indirect accessors
unsigned getIndirectAlign() const {
assert(TheKind == Indirect && "Invalid kind!");
diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp
index 2f44711..0a1915b 100644
--- a/lib/CodeGen/BackendUtil.cpp
+++ b/lib/CodeGen/BackendUtil.cpp
@@ -121,6 +121,12 @@ static void addObjCARCOptPass(const PassManagerBuilder &Builder, PassManagerBase
PM.add(createObjCARCOptPass());
}
+static unsigned BoundsChecking;
+static void addBoundsCheckingPass(const PassManagerBuilder &Builder,
+ PassManagerBase &PM) {
+ PM.add(createBoundsCheckingPass(BoundsChecking));
+}
+
static void addAddressSanitizerPass(const PassManagerBuilder &Builder,
PassManagerBase &PM) {
PM.add(createAddressSanitizerPass());
@@ -160,6 +166,14 @@ void EmitAssemblyHelper::CreatePasses() {
addObjCARCOptPass);
}
+ if (CodeGenOpts.BoundsChecking > 0) {
+ BoundsChecking = CodeGenOpts.BoundsChecking;
+ PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate,
+ addBoundsCheckingPass);
+ PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
+ addBoundsCheckingPass);
+ }
+
if (LangOpts.AddressSanitizer) {
PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate,
addAddressSanitizerPass);
@@ -219,7 +233,7 @@ void EmitAssemblyHelper::CreatePasses() {
CodeGenOpts.EmitGcovArcs,
TargetTriple.isMacOSX()));
- if (!CodeGenOpts.DebugInfo)
+ if (CodeGenOpts.DebugInfo == CodeGenOptions::NoDebugInfo)
MPM->add(createStripSymbolsPass(true));
}
@@ -324,6 +338,9 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action,
Options.NoFramePointerElimNonLeaf = true;
}
+ if (CodeGenOpts.UseInitArray)
+ Options.UseInitArray = true;
+
// Set float ABI type.
if (CodeGenOpts.FloatABI == "soft" || CodeGenOpts.FloatABI == "softfp")
Options.FloatABIType = llvm::FloatABI::Soft;
@@ -334,6 +351,19 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action,
Options.FloatABIType = llvm::FloatABI::Default;
}
+ // Set FP fusion mode.
+ switch (LangOpts.getFPContractMode()) {
+ case LangOptions::FPC_Off:
+ Options.AllowFPOpFusion = llvm::FPOpFusion::Strict;
+ break;
+ case LangOptions::FPC_On:
+ Options.AllowFPOpFusion = llvm::FPOpFusion::Standard;
+ break;
+ case LangOptions::FPC_Fast:
+ Options.AllowFPOpFusion = llvm::FPOpFusion::Fast;
+ break;
+ }
+
Options.LessPreciseFPMADOption = CodeGenOpts.LessPreciseFPMAD;
Options.NoInfsFPMath = CodeGenOpts.NoInfsFPMath;
Options.NoNaNsFPMath = CodeGenOpts.NoNaNsFPMath;
diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp
index f8c7bcd..37ef4af 100644
--- a/lib/CodeGen/CGBlocks.cpp
+++ b/lib/CodeGen/CGBlocks.cpp
@@ -458,19 +458,23 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
}
}
+ assert(endAlign == getLowBit(blockSize));
+
// At this point, we just have to add padding if the end align still
// isn't aligned right.
if (endAlign < maxFieldAlign) {
- CharUnits padding = maxFieldAlign - endAlign;
+ CharUnits newBlockSize = blockSize.RoundUpToAlignment(maxFieldAlign);
+ CharUnits padding = newBlockSize - blockSize;
elementTypes.push_back(llvm::ArrayType::get(CGM.Int8Ty,
padding.getQuantity()));
- blockSize += padding;
-
- endAlign = getLowBit(blockSize);
- assert(endAlign >= maxFieldAlign);
+ blockSize = newBlockSize;
+ endAlign = getLowBit(blockSize); // might be > maxFieldAlign
}
+ assert(endAlign >= maxFieldAlign);
+ assert(endAlign == getLowBit(blockSize));
+
// Slam everything else on now. This works because they have
// strictly decreasing alignment and we expect that size is always a
// multiple of alignment.
@@ -626,7 +630,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// Using the computed layout, generate the actual block function.
bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda();
llvm::Constant *blockFn
- = CodeGenFunction(CGM).GenerateBlockFunction(CurGD, blockInfo,
+ = CodeGenFunction(CGM, true).GenerateBlockFunction(CurGD, blockInfo,
CurFuncDecl, LocalDeclMap,
isLambdaConv);
blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
@@ -694,7 +698,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// Compute the address of the thing we're going to move into the
// block literal.
llvm::Value *src;
- if (ci->isNested()) {
+ if (BlockInfo && ci->isNested()) {
// We need to use the capture from the enclosing block.
const CGBlockInfo::Capture &enclosingCapture =
BlockInfo->getCapture(variable);
@@ -872,7 +876,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E,
const FunctionType *FuncTy = FnType->castAs<FunctionType>();
const CGFunctionInfo &FnInfo =
- CGM.getTypes().arrangeFunctionCall(Args, FuncTy);
+ CGM.getTypes().arrangeFreeFunctionCall(Args, FuncTy);
// Cast the function pointer to the right type.
llvm::Type *BlockFTy = CGM.getTypes().GetFunctionType(FnInfo);
@@ -999,7 +1003,8 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD,
// Check if we should generate debug info for this block function.
if (CGM.getModuleDebugInfo())
DebugInfo = CGM.getModuleDebugInfo();
-
+ CurGD = GD;
+
BlockInfo = &blockInfo;
// Arrange for local static and local extern declarations to appear
@@ -1130,15 +1135,17 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD,
const VarDecl *variable = ci->getVariable();
DI->EmitLocation(Builder, variable->getLocation());
- const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable);
- if (capture.isConstant()) {
- DI->EmitDeclareOfAutoVariable(variable, LocalDeclMap[variable],
- Builder);
- continue;
- }
+ if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) {
+ const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable);
+ if (capture.isConstant()) {
+ DI->EmitDeclareOfAutoVariable(variable, LocalDeclMap[variable],
+ Builder);
+ continue;
+ }
- DI->EmitDeclareOfBlockDeclRefVariable(variable, BlockPointer,
- Builder, blockInfo);
+ DI->EmitDeclareOfBlockDeclRefVariable(variable, BlockPointer,
+ Builder, blockInfo);
+ }
}
}
diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h
index 8120217..a790a74 100644
--- a/lib/CodeGen/CGBuilder.h
+++ b/lib/CodeGen/CGBuilder.h
@@ -10,7 +10,7 @@
#ifndef CLANG_CODEGEN_CGBUILDER_H
#define CLANG_CODEGEN_CGBUILDER_H
-#include "llvm/Support/IRBuilder.h"
+#include "llvm/IRBuilder.h"
namespace clang {
namespace CodeGen {
diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp
index e30b513..65c782e 100644
--- a/lib/CodeGen/CGBuiltin.cpp
+++ b/lib/CodeGen/CGBuiltin.cpp
@@ -335,6 +335,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
return RValue::get(Builder.CreateCall(F, ArgValue));
}
case Builtin::BI__builtin_object_size: {
+ // We rely on constant folding to deal with expressions with side effects.
+ assert(!E->getArg(0)->HasSideEffects(getContext()) &&
+ "should have been constant folded");
+
// We pass this builtin onto the optimizer so that it can
// figure out the object size in more complex cases.
llvm::Type *ResType = ConvertType(E->getType());
@@ -348,9 +352,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
CI = ConstantInt::get(Builder.getInt1Ty(), (val & 0x2) >> 1);
Value *F = CGM.getIntrinsic(Intrinsic::objectsize, ResType);
- return RValue::get(Builder.CreateCall2(F,
- EmitScalarExpr(E->getArg(0)),
- CI));
+ return RValue::get(Builder.CreateCall2(F, EmitScalarExpr(E->getArg(0)),CI));
}
case Builtin::BI__builtin_prefetch: {
Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
@@ -363,6 +365,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
Value *F = CGM.getIntrinsic(Intrinsic::prefetch);
return RValue::get(Builder.CreateCall4(F, Address, RW, Locality, Data));
}
+ case Builtin::BI__builtin_readcyclecounter: {
+ Value *F = CGM.getIntrinsic(Intrinsic::readcyclecounter);
+ return RValue::get(Builder.CreateCall(F));
+ }
case Builtin::BI__builtin_trap: {
Value *F = CGM.getIntrinsic(Intrinsic::trap);
return RValue::get(Builder.CreateCall(F));
@@ -982,9 +988,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
Args.add(RValue::get(llvm::Constant::getNullValue(VoidPtrTy)),
getContext().VoidPtrTy);
const CGFunctionInfo &FuncInfo =
- CGM.getTypes().arrangeFunctionCall(E->getType(), Args,
- FunctionType::ExtInfo(),
- RequiredArgs::All);
+ CGM.getTypes().arrangeFreeFunctionCall(E->getType(), Args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All);
llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo);
llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName);
return EmitCall(FuncInfo, Func, ReturnValueSlot(), Args);
@@ -1376,8 +1382,6 @@ Value *CodeGenFunction::EmitTargetBuiltinExpr(unsigned BuiltinID,
case llvm::Triple::ppc:
case llvm::Triple::ppc64:
return EmitPPCBuiltinExpr(BuiltinID, E);
- case llvm::Triple::hexagon:
- return EmitHexagonBuiltinExpr(BuiltinID, E);
default:
return 0;
}
@@ -1629,13 +1633,17 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID,
}
case ARM::BI__builtin_neon_vclz_v:
case ARM::BI__builtin_neon_vclzq_v: {
- Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vclz, Ty);
+ // Generate target-independent intrinsic; also need to add second argument
+ // for whether or not clz of zero is undefined; on ARM it isn't.
+ Function *F = CGM.getIntrinsic(Intrinsic::ctlz, Ty);
+ Ops.push_back(Builder.getInt1(Target.isCLZForZeroUndef()));
return EmitNeonCall(F, Ops, "vclz");
}
case ARM::BI__builtin_neon_vcnt_v:
case ARM::BI__builtin_neon_vcntq_v: {
- Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vcnt, Ty);
- return EmitNeonCall(F, Ops, "vcnt");
+ // generate target-independent intrinsic
+ Function *F = CGM.getIntrinsic(Intrinsic::ctpop, Ty);
+ return EmitNeonCall(F, Ops, "vctpop");
}
case ARM::BI__builtin_neon_vcvt_f16_v: {
assert(Type.getEltType() == NeonTypeFlags::Float16 && !quad &&
@@ -2411,8 +2419,11 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
return llvm::Constant::getNullValue(ConvertType(E->getType()));
}
case X86::BI__builtin_ia32_movntps:
+ case X86::BI__builtin_ia32_movntps256:
case X86::BI__builtin_ia32_movntpd:
+ case X86::BI__builtin_ia32_movntpd256:
case X86::BI__builtin_ia32_movntdq:
+ case X86::BI__builtin_ia32_movntdq256:
case X86::BI__builtin_ia32_movnti: {
llvm::MDNode *Node = llvm::MDNode::get(getLLVMContext(),
Builder.getInt32(1));
@@ -2444,1996 +2455,31 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
llvm::Function *F = CGM.getIntrinsic(ID);
return Builder.CreateCall(F, Ops, name);
}
- }
-}
-
-
-Value *CodeGenFunction::EmitHexagonBuiltinExpr(unsigned BuiltinID,
- const CallExpr *E) {
- llvm::SmallVector<Value*, 4> Ops;
-
- for (unsigned i = 0, e = E->getNumArgs(); i != e; i++)
- Ops.push_back(EmitScalarExpr(E->getArg(i)));
-
- Intrinsic::ID ID = Intrinsic::not_intrinsic;
-
- switch (BuiltinID) {
- default: return 0;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpeq:
- ID = Intrinsic::hexagon_C2_cmpeq; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgt:
- ID = Intrinsic::hexagon_C2_cmpgt; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgtu:
- ID = Intrinsic::hexagon_C2_cmpgtu; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpeqp:
- ID = Intrinsic::hexagon_C2_cmpeqp; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgtp:
- ID = Intrinsic::hexagon_C2_cmpgtp; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgtup:
- ID = Intrinsic::hexagon_C2_cmpgtup; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_bitsset:
- ID = Intrinsic::hexagon_C2_bitsset; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_bitsclr:
- ID = Intrinsic::hexagon_C2_bitsclr; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpeqi:
- ID = Intrinsic::hexagon_C2_cmpeqi; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgti:
- ID = Intrinsic::hexagon_C2_cmpgti; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgtui:
- ID = Intrinsic::hexagon_C2_cmpgtui; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgei:
- ID = Intrinsic::hexagon_C2_cmpgei; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpgeui:
- ID = Intrinsic::hexagon_C2_cmpgeui; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmplt:
- ID = Intrinsic::hexagon_C2_cmplt; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_cmpltu:
- ID = Intrinsic::hexagon_C2_cmpltu; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_bitsclri:
- ID = Intrinsic::hexagon_C2_bitsclri; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_and:
- ID = Intrinsic::hexagon_C2_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_or:
- ID = Intrinsic::hexagon_C2_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_xor:
- ID = Intrinsic::hexagon_C2_xor; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_andn:
- ID = Intrinsic::hexagon_C2_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_not:
- ID = Intrinsic::hexagon_C2_not; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_orn:
- ID = Intrinsic::hexagon_C2_orn; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_pxfer_map:
- ID = Intrinsic::hexagon_C2_pxfer_map; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_any8:
- ID = Intrinsic::hexagon_C2_any8; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_all8:
- ID = Intrinsic::hexagon_C2_all8; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_vitpack:
- ID = Intrinsic::hexagon_C2_vitpack; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_mux:
- ID = Intrinsic::hexagon_C2_mux; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_muxii:
- ID = Intrinsic::hexagon_C2_muxii; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_muxir:
- ID = Intrinsic::hexagon_C2_muxir; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_muxri:
- ID = Intrinsic::hexagon_C2_muxri; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_vmux:
- ID = Intrinsic::hexagon_C2_vmux; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_mask:
- ID = Intrinsic::hexagon_C2_mask; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmpbeq:
- ID = Intrinsic::hexagon_A2_vcmpbeq; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmpbgtu:
- ID = Intrinsic::hexagon_A2_vcmpbgtu; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmpheq:
- ID = Intrinsic::hexagon_A2_vcmpheq; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmphgt:
- ID = Intrinsic::hexagon_A2_vcmphgt; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmphgtu:
- ID = Intrinsic::hexagon_A2_vcmphgtu; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmpweq:
- ID = Intrinsic::hexagon_A2_vcmpweq; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmpwgt:
- ID = Intrinsic::hexagon_A2_vcmpwgt; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vcmpwgtu:
- ID = Intrinsic::hexagon_A2_vcmpwgtu; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_tfrpr:
- ID = Intrinsic::hexagon_C2_tfrpr; break;
-
- case Hexagon::BI__builtin_HEXAGON_C2_tfrrp:
- ID = Intrinsic::hexagon_C2_tfrrp; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_acc_sat_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_nac_sat_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_rnd_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_rnd_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_rnd_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_rnd_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_rnd_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_rnd_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_rnd_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_rnd_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s0:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s1:
- ID = Intrinsic::hexagon_M2_mpy_sat_rnd_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_acc_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_acc_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyd_acc_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyd_acc_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_acc_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_acc_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyd_acc_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyd_acc_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_nac_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_nac_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyd_nac_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyd_nac_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_nac_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_nac_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyd_nac_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyd_nac_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyd_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyd_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyd_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyd_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyd_rnd_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyu_acc_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyu_acc_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyu_acc_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyu_acc_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyu_acc_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyu_acc_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyu_acc_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyu_acc_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyu_nac_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyu_nac_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyu_nac_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyu_nac_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyu_nac_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyu_nac_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyu_nac_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyu_nac_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyu_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyu_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyu_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyu_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyu_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyu_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyu_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyu_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyud_acc_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyud_acc_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyud_acc_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyud_acc_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyud_acc_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyud_acc_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyud_acc_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyud_acc_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyud_nac_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyud_nac_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyud_nac_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyud_nac_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyud_nac_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyud_nac_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyud_nac_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyud_nac_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hh_s0:
- ID = Intrinsic::hexagon_M2_mpyud_hh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hh_s1:
- ID = Intrinsic::hexagon_M2_mpyud_hh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hl_s0:
- ID = Intrinsic::hexagon_M2_mpyud_hl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hl_s1:
- ID = Intrinsic::hexagon_M2_mpyud_hl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_lh_s0:
- ID = Intrinsic::hexagon_M2_mpyud_lh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_lh_s1:
- ID = Intrinsic::hexagon_M2_mpyud_lh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_ll_s0:
- ID = Intrinsic::hexagon_M2_mpyud_ll_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyud_ll_s1:
- ID = Intrinsic::hexagon_M2_mpyud_ll_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpysmi:
- ID = Intrinsic::hexagon_M2_mpysmi; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_macsip:
- ID = Intrinsic::hexagon_M2_macsip; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_macsin:
- ID = Intrinsic::hexagon_M2_macsin; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_s0:
- ID = Intrinsic::hexagon_M2_dpmpyss_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_acc_s0:
- ID = Intrinsic::hexagon_M2_dpmpyss_acc_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_nac_s0:
- ID = Intrinsic::hexagon_M2_dpmpyss_nac_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyuu_s0:
- ID = Intrinsic::hexagon_M2_dpmpyuu_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyuu_acc_s0:
- ID = Intrinsic::hexagon_M2_dpmpyuu_acc_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyuu_nac_s0:
- ID = Intrinsic::hexagon_M2_dpmpyuu_nac_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpy_up:
- ID = Intrinsic::hexagon_M2_mpy_up; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyu_up:
- ID = Intrinsic::hexagon_M2_mpyu_up; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_rnd_s0:
- ID = Intrinsic::hexagon_M2_dpmpyss_rnd_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyi:
- ID = Intrinsic::hexagon_M2_mpyi; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mpyui:
- ID = Intrinsic::hexagon_M2_mpyui; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_maci:
- ID = Intrinsic::hexagon_M2_maci; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_acci:
- ID = Intrinsic::hexagon_M2_acci; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_accii:
- ID = Intrinsic::hexagon_M2_accii; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_nacci:
- ID = Intrinsic::hexagon_M2_nacci; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_naccii:
- ID = Intrinsic::hexagon_M2_naccii; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_subacc:
- ID = Intrinsic::hexagon_M2_subacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s0:
- ID = Intrinsic::hexagon_M2_vmpy2s_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s1:
- ID = Intrinsic::hexagon_M2_vmpy2s_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmac2s_s0:
- ID = Intrinsic::hexagon_M2_vmac2s_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmac2s_s1:
- ID = Intrinsic::hexagon_M2_vmac2s_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s0pack:
- ID = Intrinsic::hexagon_M2_vmpy2s_s0pack; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s1pack:
- ID = Intrinsic::hexagon_M2_vmpy2s_s1pack; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmac2:
- ID = Intrinsic::hexagon_M2_vmac2; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmpy2es_s0:
- ID = Intrinsic::hexagon_M2_vmpy2es_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmpy2es_s1:
- ID = Intrinsic::hexagon_M2_vmpy2es_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmac2es_s0:
- ID = Intrinsic::hexagon_M2_vmac2es_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmac2es_s1:
- ID = Intrinsic::hexagon_M2_vmac2es_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vmac2es:
- ID = Intrinsic::hexagon_M2_vmac2es; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrmac_s0:
- ID = Intrinsic::hexagon_M2_vrmac_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrmpy_s0:
- ID = Intrinsic::hexagon_M2_vrmpy_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vdmpyrs_s0:
- ID = Intrinsic::hexagon_M2_vdmpyrs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vdmpyrs_s1:
- ID = Intrinsic::hexagon_M2_vdmpyrs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vdmacs_s0:
- ID = Intrinsic::hexagon_M2_vdmacs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vdmacs_s1:
- ID = Intrinsic::hexagon_M2_vdmacs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vdmpys_s0:
- ID = Intrinsic::hexagon_M2_vdmpys_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vdmpys_s1:
- ID = Intrinsic::hexagon_M2_vdmpys_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpyrs_s0:
- ID = Intrinsic::hexagon_M2_cmpyrs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpyrs_s1:
- ID = Intrinsic::hexagon_M2_cmpyrs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpyrsc_s0:
- ID = Intrinsic::hexagon_M2_cmpyrsc_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpyrsc_s1:
- ID = Intrinsic::hexagon_M2_cmpyrsc_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmacs_s0:
- ID = Intrinsic::hexagon_M2_cmacs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmacs_s1:
- ID = Intrinsic::hexagon_M2_cmacs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmacsc_s0:
- ID = Intrinsic::hexagon_M2_cmacsc_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmacsc_s1:
- ID = Intrinsic::hexagon_M2_cmacsc_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpys_s0:
- ID = Intrinsic::hexagon_M2_cmpys_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpys_s1:
- ID = Intrinsic::hexagon_M2_cmpys_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpysc_s0:
- ID = Intrinsic::hexagon_M2_cmpysc_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpysc_s1:
- ID = Intrinsic::hexagon_M2_cmpysc_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cnacs_s0:
- ID = Intrinsic::hexagon_M2_cnacs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cnacs_s1:
- ID = Intrinsic::hexagon_M2_cnacs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cnacsc_s0:
- ID = Intrinsic::hexagon_M2_cnacsc_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cnacsc_s1:
- ID = Intrinsic::hexagon_M2_cnacsc_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpys_s1:
- ID = Intrinsic::hexagon_M2_vrcmpys_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpys_acc_s1:
- ID = Intrinsic::hexagon_M2_vrcmpys_acc_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpys_s1rp:
- ID = Intrinsic::hexagon_M2_vrcmpys_s1rp; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacls_s0:
- ID = Intrinsic::hexagon_M2_mmacls_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacls_s1:
- ID = Intrinsic::hexagon_M2_mmacls_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmachs_s0:
- ID = Intrinsic::hexagon_M2_mmachs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmachs_s1:
- ID = Intrinsic::hexagon_M2_mmachs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_s0:
- ID = Intrinsic::hexagon_M2_mmpyl_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_s1:
- ID = Intrinsic::hexagon_M2_mmpyl_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_s0:
- ID = Intrinsic::hexagon_M2_mmpyh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_s1:
- ID = Intrinsic::hexagon_M2_mmpyh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacls_rs0:
- ID = Intrinsic::hexagon_M2_mmacls_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacls_rs1:
- ID = Intrinsic::hexagon_M2_mmacls_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmachs_rs0:
- ID = Intrinsic::hexagon_M2_mmachs_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmachs_rs1:
- ID = Intrinsic::hexagon_M2_mmachs_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_rs0:
- ID = Intrinsic::hexagon_M2_mmpyl_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_rs1:
- ID = Intrinsic::hexagon_M2_mmpyl_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_rs0:
- ID = Intrinsic::hexagon_M2_mmpyh_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_rs1:
- ID = Intrinsic::hexagon_M2_mmpyh_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_hmmpyl_rs1:
- ID = Intrinsic::hexagon_M2_hmmpyl_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_hmmpyh_rs1:
- ID = Intrinsic::hexagon_M2_hmmpyh_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_s0:
- ID = Intrinsic::hexagon_M2_mmaculs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_s1:
- ID = Intrinsic::hexagon_M2_mmaculs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_s0:
- ID = Intrinsic::hexagon_M2_mmacuhs_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_s1:
- ID = Intrinsic::hexagon_M2_mmacuhs_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_s0:
- ID = Intrinsic::hexagon_M2_mmpyul_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_s1:
- ID = Intrinsic::hexagon_M2_mmpyul_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_s0:
- ID = Intrinsic::hexagon_M2_mmpyuh_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_s1:
- ID = Intrinsic::hexagon_M2_mmpyuh_s1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_rs0:
- ID = Intrinsic::hexagon_M2_mmaculs_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_rs1:
- ID = Intrinsic::hexagon_M2_mmaculs_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_rs0:
- ID = Intrinsic::hexagon_M2_mmacuhs_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_rs1:
- ID = Intrinsic::hexagon_M2_mmacuhs_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_rs0:
- ID = Intrinsic::hexagon_M2_mmpyul_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_rs1:
- ID = Intrinsic::hexagon_M2_mmpyul_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_rs0:
- ID = Intrinsic::hexagon_M2_mmpyuh_rs0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_rs1:
- ID = Intrinsic::hexagon_M2_mmpyuh_rs1; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmaci_s0:
- ID = Intrinsic::hexagon_M2_vrcmaci_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmacr_s0:
- ID = Intrinsic::hexagon_M2_vrcmacr_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmaci_s0c:
- ID = Intrinsic::hexagon_M2_vrcmaci_s0c; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmacr_s0c:
- ID = Intrinsic::hexagon_M2_vrcmacr_s0c; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmaci_s0:
- ID = Intrinsic::hexagon_M2_cmaci_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmacr_s0:
- ID = Intrinsic::hexagon_M2_cmacr_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyi_s0:
- ID = Intrinsic::hexagon_M2_vrcmpyi_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyr_s0:
- ID = Intrinsic::hexagon_M2_vrcmpyr_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyi_s0c:
- ID = Intrinsic::hexagon_M2_vrcmpyi_s0c; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyr_s0c:
- ID = Intrinsic::hexagon_M2_vrcmpyr_s0c; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpyi_s0:
- ID = Intrinsic::hexagon_M2_cmpyi_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_cmpyr_s0:
- ID = Intrinsic::hexagon_M2_cmpyr_s0; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s0_sat_i:
- ID = Intrinsic::hexagon_M2_vcmpy_s0_sat_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s0_sat_r:
- ID = Intrinsic::hexagon_M2_vcmpy_s0_sat_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s1_sat_i:
- ID = Intrinsic::hexagon_M2_vcmpy_s1_sat_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s1_sat_r:
- ID = Intrinsic::hexagon_M2_vcmpy_s1_sat_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vcmac_s0_sat_i:
- ID = Intrinsic::hexagon_M2_vcmac_s0_sat_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vcmac_s0_sat_r:
- ID = Intrinsic::hexagon_M2_vcmac_s0_sat_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vcrotate:
- ID = Intrinsic::hexagon_S2_vcrotate; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_add:
- ID = Intrinsic::hexagon_A2_add; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_sub:
- ID = Intrinsic::hexagon_A2_sub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addsat:
- ID = Intrinsic::hexagon_A2_addsat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subsat:
- ID = Intrinsic::hexagon_A2_subsat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addi:
- ID = Intrinsic::hexagon_A2_addi; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_ll:
- ID = Intrinsic::hexagon_A2_addh_l16_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_hl:
- ID = Intrinsic::hexagon_A2_addh_l16_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_sat_ll:
- ID = Intrinsic::hexagon_A2_addh_l16_sat_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_sat_hl:
- ID = Intrinsic::hexagon_A2_addh_l16_sat_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_ll:
- ID = Intrinsic::hexagon_A2_subh_l16_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_hl:
- ID = Intrinsic::hexagon_A2_subh_l16_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_sat_ll:
- ID = Intrinsic::hexagon_A2_subh_l16_sat_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_sat_hl:
- ID = Intrinsic::hexagon_A2_subh_l16_sat_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_ll:
- ID = Intrinsic::hexagon_A2_addh_h16_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_lh:
- ID = Intrinsic::hexagon_A2_addh_h16_lh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_hl:
- ID = Intrinsic::hexagon_A2_addh_h16_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_hh:
- ID = Intrinsic::hexagon_A2_addh_h16_hh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_ll:
- ID = Intrinsic::hexagon_A2_addh_h16_sat_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_lh:
- ID = Intrinsic::hexagon_A2_addh_h16_sat_lh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_hl:
- ID = Intrinsic::hexagon_A2_addh_h16_sat_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_hh:
- ID = Intrinsic::hexagon_A2_addh_h16_sat_hh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_ll:
- ID = Intrinsic::hexagon_A2_subh_h16_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_lh:
- ID = Intrinsic::hexagon_A2_subh_h16_lh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_hl:
- ID = Intrinsic::hexagon_A2_subh_h16_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_hh:
- ID = Intrinsic::hexagon_A2_subh_h16_hh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_ll:
- ID = Intrinsic::hexagon_A2_subh_h16_sat_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_lh:
- ID = Intrinsic::hexagon_A2_subh_h16_sat_lh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_hl:
- ID = Intrinsic::hexagon_A2_subh_h16_sat_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_hh:
- ID = Intrinsic::hexagon_A2_subh_h16_sat_hh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_aslh:
- ID = Intrinsic::hexagon_A2_aslh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_asrh:
- ID = Intrinsic::hexagon_A2_asrh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addp:
- ID = Intrinsic::hexagon_A2_addp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addpsat:
- ID = Intrinsic::hexagon_A2_addpsat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_addsp:
- ID = Intrinsic::hexagon_A2_addsp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subp:
- ID = Intrinsic::hexagon_A2_subp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_neg:
- ID = Intrinsic::hexagon_A2_neg; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_negsat:
- ID = Intrinsic::hexagon_A2_negsat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_abs:
- ID = Intrinsic::hexagon_A2_abs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_abssat:
- ID = Intrinsic::hexagon_A2_abssat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vconj:
- ID = Intrinsic::hexagon_A2_vconj; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_negp:
- ID = Intrinsic::hexagon_A2_negp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_absp:
- ID = Intrinsic::hexagon_A2_absp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_max:
- ID = Intrinsic::hexagon_A2_max; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_maxu:
- ID = Intrinsic::hexagon_A2_maxu; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_min:
- ID = Intrinsic::hexagon_A2_min; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_minu:
- ID = Intrinsic::hexagon_A2_minu; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_maxp:
- ID = Intrinsic::hexagon_A2_maxp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_maxup:
- ID = Intrinsic::hexagon_A2_maxup; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_minp:
- ID = Intrinsic::hexagon_A2_minp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_minup:
- ID = Intrinsic::hexagon_A2_minup; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_tfr:
- ID = Intrinsic::hexagon_A2_tfr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_tfrsi:
- ID = Intrinsic::hexagon_A2_tfrsi; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_tfrp:
- ID = Intrinsic::hexagon_A2_tfrp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_tfrpi:
- ID = Intrinsic::hexagon_A2_tfrpi; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_zxtb:
- ID = Intrinsic::hexagon_A2_zxtb; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_sxtb:
- ID = Intrinsic::hexagon_A2_sxtb; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_zxth:
- ID = Intrinsic::hexagon_A2_zxth; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_sxth:
- ID = Intrinsic::hexagon_A2_sxth; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_combinew:
- ID = Intrinsic::hexagon_A2_combinew; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_combineii:
- ID = Intrinsic::hexagon_A2_combineii; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_combine_hh:
- ID = Intrinsic::hexagon_A2_combine_hh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_combine_hl:
- ID = Intrinsic::hexagon_A2_combine_hl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_combine_lh:
- ID = Intrinsic::hexagon_A2_combine_lh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_combine_ll:
- ID = Intrinsic::hexagon_A2_combine_ll; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_tfril:
- ID = Intrinsic::hexagon_A2_tfril; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_tfrih:
- ID = Intrinsic::hexagon_A2_tfrih; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_and:
- ID = Intrinsic::hexagon_A2_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_or:
- ID = Intrinsic::hexagon_A2_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_xor:
- ID = Intrinsic::hexagon_A2_xor; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_not:
- ID = Intrinsic::hexagon_A2_not; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_xor_xacc:
- ID = Intrinsic::hexagon_M2_xor_xacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_subri:
- ID = Intrinsic::hexagon_A2_subri; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_andir:
- ID = Intrinsic::hexagon_A2_andir; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_orir:
- ID = Intrinsic::hexagon_A2_orir; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_andp:
- ID = Intrinsic::hexagon_A2_andp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_orp:
- ID = Intrinsic::hexagon_A2_orp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_xorp:
- ID = Intrinsic::hexagon_A2_xorp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_notp:
- ID = Intrinsic::hexagon_A2_notp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_sxtw:
- ID = Intrinsic::hexagon_A2_sxtw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_sat:
- ID = Intrinsic::hexagon_A2_sat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_sath:
- ID = Intrinsic::hexagon_A2_sath; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_satuh:
- ID = Intrinsic::hexagon_A2_satuh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_satub:
- ID = Intrinsic::hexagon_A2_satub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_satb:
- ID = Intrinsic::hexagon_A2_satb; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vaddub:
- ID = Intrinsic::hexagon_A2_vaddub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vaddubs:
- ID = Intrinsic::hexagon_A2_vaddubs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vaddh:
- ID = Intrinsic::hexagon_A2_vaddh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vaddhs:
- ID = Intrinsic::hexagon_A2_vaddhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vadduhs:
- ID = Intrinsic::hexagon_A2_vadduhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vaddw:
- ID = Intrinsic::hexagon_A2_vaddw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vaddws:
- ID = Intrinsic::hexagon_A2_vaddws; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svavgh:
- ID = Intrinsic::hexagon_A2_svavgh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svavghs:
- ID = Intrinsic::hexagon_A2_svavghs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svnavgh:
- ID = Intrinsic::hexagon_A2_svnavgh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svaddh:
- ID = Intrinsic::hexagon_A2_svaddh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svaddhs:
- ID = Intrinsic::hexagon_A2_svaddhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svadduhs:
- ID = Intrinsic::hexagon_A2_svadduhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svsubh:
- ID = Intrinsic::hexagon_A2_svsubh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svsubhs:
- ID = Intrinsic::hexagon_A2_svsubhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_svsubuhs:
- ID = Intrinsic::hexagon_A2_svsubuhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vraddub:
- ID = Intrinsic::hexagon_A2_vraddub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vraddub_acc:
- ID = Intrinsic::hexagon_A2_vraddub_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vradduh:
- ID = Intrinsic::hexagon_M2_vradduh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsubub:
- ID = Intrinsic::hexagon_A2_vsubub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsububs:
- ID = Intrinsic::hexagon_A2_vsububs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsubh:
- ID = Intrinsic::hexagon_A2_vsubh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsubhs:
- ID = Intrinsic::hexagon_A2_vsubhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsubuhs:
- ID = Intrinsic::hexagon_A2_vsubuhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsubw:
- ID = Intrinsic::hexagon_A2_vsubw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vsubws:
- ID = Intrinsic::hexagon_A2_vsubws; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vabsh:
- ID = Intrinsic::hexagon_A2_vabsh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vabshsat:
- ID = Intrinsic::hexagon_A2_vabshsat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vabsw:
- ID = Intrinsic::hexagon_A2_vabsw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vabswsat:
- ID = Intrinsic::hexagon_A2_vabswsat; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vabsdiffw:
- ID = Intrinsic::hexagon_M2_vabsdiffw; break;
-
- case Hexagon::BI__builtin_HEXAGON_M2_vabsdiffh:
- ID = Intrinsic::hexagon_M2_vabsdiffh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vrsadub:
- ID = Intrinsic::hexagon_A2_vrsadub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vrsadub_acc:
- ID = Intrinsic::hexagon_A2_vrsadub_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavgub:
- ID = Intrinsic::hexagon_A2_vavgub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavguh:
- ID = Intrinsic::hexagon_A2_vavguh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavgh:
- ID = Intrinsic::hexagon_A2_vavgh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vnavgh:
- ID = Intrinsic::hexagon_A2_vnavgh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavgw:
- ID = Intrinsic::hexagon_A2_vavgw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vnavgw:
- ID = Intrinsic::hexagon_A2_vnavgw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavgwr:
- ID = Intrinsic::hexagon_A2_vavgwr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vnavgwr:
- ID = Intrinsic::hexagon_A2_vnavgwr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavgwcr:
- ID = Intrinsic::hexagon_A2_vavgwcr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vnavgwcr:
- ID = Intrinsic::hexagon_A2_vnavgwcr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavghcr:
- ID = Intrinsic::hexagon_A2_vavghcr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vnavghcr:
- ID = Intrinsic::hexagon_A2_vnavghcr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavguw:
- ID = Intrinsic::hexagon_A2_vavguw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavguwr:
- ID = Intrinsic::hexagon_A2_vavguwr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavgubr:
- ID = Intrinsic::hexagon_A2_vavgubr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavguhr:
- ID = Intrinsic::hexagon_A2_vavguhr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vavghr:
- ID = Intrinsic::hexagon_A2_vavghr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vnavghr:
- ID = Intrinsic::hexagon_A2_vnavghr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vminh:
- ID = Intrinsic::hexagon_A2_vminh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vmaxh:
- ID = Intrinsic::hexagon_A2_vmaxh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vminub:
- ID = Intrinsic::hexagon_A2_vminub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vmaxub:
- ID = Intrinsic::hexagon_A2_vmaxub; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vminuh:
- ID = Intrinsic::hexagon_A2_vminuh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vmaxuh:
- ID = Intrinsic::hexagon_A2_vmaxuh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vminw:
- ID = Intrinsic::hexagon_A2_vminw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vmaxw:
- ID = Intrinsic::hexagon_A2_vmaxw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vminuw:
- ID = Intrinsic::hexagon_A2_vminuw; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_vmaxuw:
- ID = Intrinsic::hexagon_A2_vmaxuw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r:
- ID = Intrinsic::hexagon_S2_asr_r_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r:
- ID = Intrinsic::hexagon_S2_asl_r_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r:
- ID = Intrinsic::hexagon_S2_lsr_r_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r:
- ID = Intrinsic::hexagon_S2_lsl_r_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p:
- ID = Intrinsic::hexagon_S2_asr_r_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p:
- ID = Intrinsic::hexagon_S2_asl_r_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p:
- ID = Intrinsic::hexagon_S2_lsr_r_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p:
- ID = Intrinsic::hexagon_S2_lsl_r_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_acc:
- ID = Intrinsic::hexagon_S2_asr_r_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_acc:
- ID = Intrinsic::hexagon_S2_asl_r_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_acc:
- ID = Intrinsic::hexagon_S2_lsr_r_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_acc:
- ID = Intrinsic::hexagon_S2_lsl_r_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_acc:
- ID = Intrinsic::hexagon_S2_asr_r_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_acc:
- ID = Intrinsic::hexagon_S2_asl_r_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_acc:
- ID = Intrinsic::hexagon_S2_lsr_r_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_acc:
- ID = Intrinsic::hexagon_S2_lsl_r_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_nac:
- ID = Intrinsic::hexagon_S2_asr_r_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_nac:
- ID = Intrinsic::hexagon_S2_asl_r_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_nac:
- ID = Intrinsic::hexagon_S2_lsr_r_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_nac:
- ID = Intrinsic::hexagon_S2_lsl_r_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_nac:
- ID = Intrinsic::hexagon_S2_asr_r_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_nac:
- ID = Intrinsic::hexagon_S2_asl_r_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_nac:
- ID = Intrinsic::hexagon_S2_lsr_r_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_nac:
- ID = Intrinsic::hexagon_S2_lsl_r_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_and:
- ID = Intrinsic::hexagon_S2_asr_r_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_and:
- ID = Intrinsic::hexagon_S2_asl_r_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_and:
- ID = Intrinsic::hexagon_S2_lsr_r_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_and:
- ID = Intrinsic::hexagon_S2_lsl_r_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_or:
- ID = Intrinsic::hexagon_S2_asr_r_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_or:
- ID = Intrinsic::hexagon_S2_asl_r_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_or:
- ID = Intrinsic::hexagon_S2_lsr_r_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_or:
- ID = Intrinsic::hexagon_S2_lsl_r_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_and:
- ID = Intrinsic::hexagon_S2_asr_r_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_and:
- ID = Intrinsic::hexagon_S2_asl_r_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_and:
- ID = Intrinsic::hexagon_S2_lsr_r_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_and:
- ID = Intrinsic::hexagon_S2_lsl_r_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_or:
- ID = Intrinsic::hexagon_S2_asr_r_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_or:
- ID = Intrinsic::hexagon_S2_asl_r_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_or:
- ID = Intrinsic::hexagon_S2_lsr_r_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_or:
- ID = Intrinsic::hexagon_S2_lsl_r_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_sat:
- ID = Intrinsic::hexagon_S2_asr_r_r_sat; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_sat:
- ID = Intrinsic::hexagon_S2_asl_r_r_sat; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r:
- ID = Intrinsic::hexagon_S2_asr_i_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r:
- ID = Intrinsic::hexagon_S2_lsr_i_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r:
- ID = Intrinsic::hexagon_S2_asl_i_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p:
- ID = Intrinsic::hexagon_S2_asr_i_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p:
- ID = Intrinsic::hexagon_S2_lsr_i_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p:
- ID = Intrinsic::hexagon_S2_asl_i_p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_acc:
- ID = Intrinsic::hexagon_S2_asr_i_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_acc:
- ID = Intrinsic::hexagon_S2_lsr_i_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_acc:
- ID = Intrinsic::hexagon_S2_asl_i_r_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_acc:
- ID = Intrinsic::hexagon_S2_asr_i_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_acc:
- ID = Intrinsic::hexagon_S2_lsr_i_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_acc:
- ID = Intrinsic::hexagon_S2_asl_i_p_acc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_nac:
- ID = Intrinsic::hexagon_S2_asr_i_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_nac:
- ID = Intrinsic::hexagon_S2_lsr_i_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_nac:
- ID = Intrinsic::hexagon_S2_asl_i_r_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_nac:
- ID = Intrinsic::hexagon_S2_asr_i_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_nac:
- ID = Intrinsic::hexagon_S2_lsr_i_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_nac:
- ID = Intrinsic::hexagon_S2_asl_i_p_nac; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_xacc:
- ID = Intrinsic::hexagon_S2_lsr_i_r_xacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_xacc:
- ID = Intrinsic::hexagon_S2_asl_i_r_xacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_xacc:
- ID = Intrinsic::hexagon_S2_lsr_i_p_xacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_xacc:
- ID = Intrinsic::hexagon_S2_asl_i_p_xacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_and:
- ID = Intrinsic::hexagon_S2_asr_i_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_and:
- ID = Intrinsic::hexagon_S2_lsr_i_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_and:
- ID = Intrinsic::hexagon_S2_asl_i_r_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_or:
- ID = Intrinsic::hexagon_S2_asr_i_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_or:
- ID = Intrinsic::hexagon_S2_lsr_i_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_or:
- ID = Intrinsic::hexagon_S2_asl_i_r_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_and:
- ID = Intrinsic::hexagon_S2_asr_i_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_and:
- ID = Intrinsic::hexagon_S2_lsr_i_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_and:
- ID = Intrinsic::hexagon_S2_asl_i_p_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_or:
- ID = Intrinsic::hexagon_S2_asr_i_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_or:
- ID = Intrinsic::hexagon_S2_lsr_i_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_or:
- ID = Intrinsic::hexagon_S2_asl_i_p_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_sat:
- ID = Intrinsic::hexagon_S2_asl_i_r_sat; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_rnd:
- ID = Intrinsic::hexagon_S2_asr_i_r_rnd; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_rnd_goodsyntax:
- ID = Intrinsic::hexagon_S2_asr_i_r_rnd_goodsyntax; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_addasl_rrri:
- ID = Intrinsic::hexagon_S2_addasl_rrri; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_valignib:
- ID = Intrinsic::hexagon_S2_valignib; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_valignrb:
- ID = Intrinsic::hexagon_S2_valignrb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vspliceib:
- ID = Intrinsic::hexagon_S2_vspliceib; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsplicerb:
- ID = Intrinsic::hexagon_S2_vsplicerb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsplatrh:
- ID = Intrinsic::hexagon_S2_vsplatrh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsplatrb:
- ID = Intrinsic::hexagon_S2_vsplatrb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_insert:
- ID = Intrinsic::hexagon_S2_insert; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_tableidxb_goodsyntax:
- ID = Intrinsic::hexagon_S2_tableidxb_goodsyntax; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_tableidxh_goodsyntax:
- ID = Intrinsic::hexagon_S2_tableidxh_goodsyntax; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_tableidxw_goodsyntax:
- ID = Intrinsic::hexagon_S2_tableidxw_goodsyntax; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_tableidxd_goodsyntax:
- ID = Intrinsic::hexagon_S2_tableidxd_goodsyntax; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_extractu:
- ID = Intrinsic::hexagon_S2_extractu; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_insertp:
- ID = Intrinsic::hexagon_S2_insertp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_extractup:
- ID = Intrinsic::hexagon_S2_extractup; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_insert_rp:
- ID = Intrinsic::hexagon_S2_insert_rp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_extractu_rp:
- ID = Intrinsic::hexagon_S2_extractu_rp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_insertp_rp:
- ID = Intrinsic::hexagon_S2_insertp_rp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_extractup_rp:
- ID = Intrinsic::hexagon_S2_extractup_rp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_tstbit_i:
- ID = Intrinsic::hexagon_S2_tstbit_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_setbit_i:
- ID = Intrinsic::hexagon_S2_setbit_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_togglebit_i:
- ID = Intrinsic::hexagon_S2_togglebit_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_clrbit_i:
- ID = Intrinsic::hexagon_S2_clrbit_i; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_tstbit_r:
- ID = Intrinsic::hexagon_S2_tstbit_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_setbit_r:
- ID = Intrinsic::hexagon_S2_setbit_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_togglebit_r:
- ID = Intrinsic::hexagon_S2_togglebit_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_clrbit_r:
- ID = Intrinsic::hexagon_S2_clrbit_r; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_vh:
- ID = Intrinsic::hexagon_S2_asr_i_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_vh:
- ID = Intrinsic::hexagon_S2_lsr_i_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_vh:
- ID = Intrinsic::hexagon_S2_asl_i_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_vh:
- ID = Intrinsic::hexagon_S2_asr_r_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_vh:
- ID = Intrinsic::hexagon_S2_asl_r_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_vh:
- ID = Intrinsic::hexagon_S2_lsr_r_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_vh:
- ID = Intrinsic::hexagon_S2_lsl_r_vh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_vw:
- ID = Intrinsic::hexagon_S2_asr_i_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_i_svw_trun:
- ID = Intrinsic::hexagon_S2_asr_i_svw_trun; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_svw_trun:
- ID = Intrinsic::hexagon_S2_asr_r_svw_trun; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_vw:
- ID = Intrinsic::hexagon_S2_lsr_i_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_i_vw:
- ID = Intrinsic::hexagon_S2_asl_i_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asr_r_vw:
- ID = Intrinsic::hexagon_S2_asr_r_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_asl_r_vw:
- ID = Intrinsic::hexagon_S2_asl_r_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_vw:
- ID = Intrinsic::hexagon_S2_lsr_r_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_vw:
- ID = Intrinsic::hexagon_S2_lsl_r_vw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vrndpackwh:
- ID = Intrinsic::hexagon_S2_vrndpackwh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vrndpackwhs:
- ID = Intrinsic::hexagon_S2_vrndpackwhs; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsxtbh:
- ID = Intrinsic::hexagon_S2_vsxtbh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vzxtbh:
- ID = Intrinsic::hexagon_S2_vzxtbh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsathub:
- ID = Intrinsic::hexagon_S2_vsathub; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_svsathub:
- ID = Intrinsic::hexagon_S2_svsathub; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_svsathb:
- ID = Intrinsic::hexagon_S2_svsathb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsathb:
- ID = Intrinsic::hexagon_S2_vsathb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vtrunohb:
- ID = Intrinsic::hexagon_S2_vtrunohb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vtrunewh:
- ID = Intrinsic::hexagon_S2_vtrunewh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vtrunowh:
- ID = Intrinsic::hexagon_S2_vtrunowh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vtrunehb:
- ID = Intrinsic::hexagon_S2_vtrunehb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsxthw:
- ID = Intrinsic::hexagon_S2_vsxthw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vzxthw:
- ID = Intrinsic::hexagon_S2_vzxthw; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsatwh:
- ID = Intrinsic::hexagon_S2_vsatwh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsatwuh:
- ID = Intrinsic::hexagon_S2_vsatwuh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_packhl:
- ID = Intrinsic::hexagon_S2_packhl; break;
-
- case Hexagon::BI__builtin_HEXAGON_A2_swiz:
- ID = Intrinsic::hexagon_A2_swiz; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsathub_nopack:
- ID = Intrinsic::hexagon_S2_vsathub_nopack; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsathb_nopack:
- ID = Intrinsic::hexagon_S2_vsathb_nopack; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsatwh_nopack:
- ID = Intrinsic::hexagon_S2_vsatwh_nopack; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_vsatwuh_nopack:
- ID = Intrinsic::hexagon_S2_vsatwuh_nopack; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_shuffob:
- ID = Intrinsic::hexagon_S2_shuffob; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_shuffeb:
- ID = Intrinsic::hexagon_S2_shuffeb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_shuffoh:
- ID = Intrinsic::hexagon_S2_shuffoh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_shuffeh:
- ID = Intrinsic::hexagon_S2_shuffeh; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_parityp:
- ID = Intrinsic::hexagon_S2_parityp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_lfsp:
- ID = Intrinsic::hexagon_S2_lfsp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_clbnorm:
- ID = Intrinsic::hexagon_S2_clbnorm; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_clb:
- ID = Intrinsic::hexagon_S2_clb; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_cl0:
- ID = Intrinsic::hexagon_S2_cl0; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_cl1:
- ID = Intrinsic::hexagon_S2_cl1; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_clbp:
- ID = Intrinsic::hexagon_S2_clbp; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_cl0p:
- ID = Intrinsic::hexagon_S2_cl0p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_cl1p:
- ID = Intrinsic::hexagon_S2_cl1p; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_brev:
- ID = Intrinsic::hexagon_S2_brev; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_ct0:
- ID = Intrinsic::hexagon_S2_ct0; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_ct1:
- ID = Intrinsic::hexagon_S2_ct1; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_interleave:
- ID = Intrinsic::hexagon_S2_interleave; break;
-
- case Hexagon::BI__builtin_HEXAGON_S2_deinterleave:
- ID = Intrinsic::hexagon_S2_deinterleave; break;
-
- case Hexagon::BI__builtin_SI_to_SXTHI_asrh:
- ID = Intrinsic::hexagon_SI_to_SXTHI_asrh; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_orn:
- ID = Intrinsic::hexagon_A4_orn; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_andn:
- ID = Intrinsic::hexagon_A4_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_ornp:
- ID = Intrinsic::hexagon_A4_ornp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_andnp:
- ID = Intrinsic::hexagon_A4_andnp; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_combineir:
- ID = Intrinsic::hexagon_A4_combineir; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_combineri:
- ID = Intrinsic::hexagon_A4_combineri; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_cmpneqi:
- ID = Intrinsic::hexagon_C4_cmpneqi; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_cmpneq:
- ID = Intrinsic::hexagon_C4_cmpneq; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_cmpltei:
- ID = Intrinsic::hexagon_C4_cmpltei; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_cmplte:
- ID = Intrinsic::hexagon_C4_cmplte; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_cmplteui:
- ID = Intrinsic::hexagon_C4_cmplteui; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_cmplteu:
- ID = Intrinsic::hexagon_C4_cmplteu; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_rcmpneq:
- ID = Intrinsic::hexagon_A4_rcmpneq; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_rcmpneqi:
- ID = Intrinsic::hexagon_A4_rcmpneqi; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_rcmpeq:
- ID = Intrinsic::hexagon_A4_rcmpeq; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_rcmpeqi:
- ID = Intrinsic::hexagon_A4_rcmpeqi; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_fastcorner9:
- ID = Intrinsic::hexagon_C4_fastcorner9; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_fastcorner9_not:
- ID = Intrinsic::hexagon_C4_fastcorner9_not; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_and_andn:
- ID = Intrinsic::hexagon_C4_and_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_and_and:
- ID = Intrinsic::hexagon_C4_and_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_and_orn:
- ID = Intrinsic::hexagon_C4_and_orn; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_and_or:
- ID = Intrinsic::hexagon_C4_and_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_or_andn:
- ID = Intrinsic::hexagon_C4_or_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_or_and:
- ID = Intrinsic::hexagon_C4_or_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_or_orn:
- ID = Intrinsic::hexagon_C4_or_orn; break;
-
- case Hexagon::BI__builtin_HEXAGON_C4_or_or:
- ID = Intrinsic::hexagon_C4_or_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_S4_addaddi:
- ID = Intrinsic::hexagon_S4_addaddi; break;
-
- case Hexagon::BI__builtin_HEXAGON_S4_subaddi:
- ID = Intrinsic::hexagon_S4_subaddi; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_xor_xacc:
- ID = Intrinsic::hexagon_M4_xor_xacc; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_and_and:
- ID = Intrinsic::hexagon_M4_and_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_and_or:
- ID = Intrinsic::hexagon_M4_and_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_and_xor:
- ID = Intrinsic::hexagon_M4_and_xor; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_and_andn:
- ID = Intrinsic::hexagon_M4_and_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_xor_and:
- ID = Intrinsic::hexagon_M4_xor_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_xor_or:
- ID = Intrinsic::hexagon_M4_xor_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_xor_andn:
- ID = Intrinsic::hexagon_M4_xor_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_or_and:
- ID = Intrinsic::hexagon_M4_or_and; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_or_or:
- ID = Intrinsic::hexagon_M4_or_or; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_or_xor:
- ID = Intrinsic::hexagon_M4_or_xor; break;
-
- case Hexagon::BI__builtin_HEXAGON_M4_or_andn:
- ID = Intrinsic::hexagon_M4_or_andn; break;
-
- case Hexagon::BI__builtin_HEXAGON_S4_or_andix:
- ID = Intrinsic::hexagon_S4_or_andix; break;
-
- case Hexagon::BI__builtin_HEXAGON_S4_or_andi:
- ID = Intrinsic::hexagon_S4_or_andi; break;
-
- case Hexagon::BI__builtin_HEXAGON_S4_or_ori:
- ID = Intrinsic::hexagon_S4_or_ori; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_modwrapu:
- ID = Intrinsic::hexagon_A4_modwrapu; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_cround_rr:
- ID = Intrinsic::hexagon_A4_cround_rr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_round_ri:
- ID = Intrinsic::hexagon_A4_round_ri; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_round_rr:
- ID = Intrinsic::hexagon_A4_round_rr; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_round_ri_sat:
- ID = Intrinsic::hexagon_A4_round_ri_sat; break;
-
- case Hexagon::BI__builtin_HEXAGON_A4_round_rr_sat:
- ID = Intrinsic::hexagon_A4_round_rr_sat; break;
+ case X86::BI__builtin_ia32_rdrand16_step:
+ case X86::BI__builtin_ia32_rdrand32_step:
+ case X86::BI__builtin_ia32_rdrand64_step: {
+ Intrinsic::ID ID;
+ switch (BuiltinID) {
+ default: llvm_unreachable("Unsupported intrinsic!");
+ case X86::BI__builtin_ia32_rdrand16_step:
+ ID = Intrinsic::x86_rdrand_16;
+ break;
+ case X86::BI__builtin_ia32_rdrand32_step:
+ ID = Intrinsic::x86_rdrand_32;
+ break;
+ case X86::BI__builtin_ia32_rdrand64_step:
+ ID = Intrinsic::x86_rdrand_64;
+ break;
+ }
+ Value *Call = Builder.CreateCall(CGM.getIntrinsic(ID));
+ Builder.CreateStore(Builder.CreateExtractValue(Call, 0), Ops[0]);
+ return Builder.CreateExtractValue(Call, 1);
+ }
}
-
- llvm::Function *F = CGM.getIntrinsic(ID);
- return Builder.CreateCall(F, Ops, "");
}
+
Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
SmallVector<Value*, 4> Ops;
diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp
index 7c08650..003fef5 100644
--- a/lib/CodeGen/CGCXX.cpp
+++ b/lib/CodeGen/CGCXX.cpp
@@ -53,7 +53,7 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
// destructor separately.
for (CXXRecordDecl::field_iterator I = Class->field_begin(),
E = Class->field_end(); I != E; ++I)
- if ((*I)->getType().isDestructedType())
+ if (I->getType().isDestructedType())
return true;
// Try to find a unique base class with a non-trivial destructor.
@@ -91,7 +91,7 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
// If the base is at a non-zero offset, give up.
const ASTRecordLayout &ClassLayout = Context.getASTRecordLayout(Class);
- if (ClassLayout.getBaseClassOffsetInBits(UniqueBase) != 0)
+ if (!ClassLayout.getBaseClassOffset(UniqueBase).isZero())
return true;
return TryEmitDefinitionAsAlias(GlobalDecl(D, Dtor_Base),
diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp
index befebbe..aba5d75 100644
--- a/lib/CodeGen/CGCXXABI.cpp
+++ b/lib/CodeGen/CGCXXABI.cpp
@@ -23,7 +23,7 @@ static void ErrorUnsupportedABI(CodeGenFunction &CGF,
StringRef S) {
DiagnosticsEngine &Diags = CGF.CGM.getDiags();
unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
- "cannot yet compile %1 in this ABI");
+ "cannot yet compile %0 in this ABI");
Diags.Report(CGF.getContext().getFullLoc(CGF.CurCodeDecl->getLocation()),
DiagID)
<< S;
@@ -145,6 +145,13 @@ void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
}
CharUnits CGCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
+ if (!requiresArrayCookie(expr))
+ return CharUnits::Zero();
+ return getArrayCookieSizeImpl(expr->getAllocatedType());
+}
+
+CharUnits CGCXXABI::getArrayCookieSizeImpl(QualType elementType) {
+ // BOGUS
return CharUnits::Zero();
}
@@ -158,16 +165,53 @@ llvm::Value *CGCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
return 0;
}
-void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
- const CXXDeleteExpr *expr, QualType ElementType,
- llvm::Value *&NumElements,
- llvm::Value *&AllocPtr, CharUnits &CookieSize) {
- ErrorUnsupportedABI(CGF, "array cookie reading");
+bool CGCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr,
+ QualType elementType) {
+ // If the class's usual deallocation function takes two arguments,
+ // it needs a cookie.
+ if (expr->doesUsualArrayDeleteWantSize())
+ return true;
- // This should be enough to avoid assertions.
- NumElements = 0;
- AllocPtr = llvm::Constant::getNullValue(CGF.Builder.getInt8PtrTy());
- CookieSize = CharUnits::Zero();
+ return elementType.isDestructedType();
+}
+
+bool CGCXXABI::requiresArrayCookie(const CXXNewExpr *expr) {
+ // If the class's usual deallocation function takes two arguments,
+ // it needs a cookie.
+ if (expr->doesUsualArrayDeleteWantSize())
+ return true;
+
+ return expr->getAllocatedType().isDestructedType();
+}
+
+void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *ptr,
+ const CXXDeleteExpr *expr, QualType eltTy,
+ llvm::Value *&numElements,
+ llvm::Value *&allocPtr, CharUnits &cookieSize) {
+ // Derive a char* in the same address space as the pointer.
+ unsigned AS = cast<llvm::PointerType>(ptr->getType())->getAddressSpace();
+ llvm::Type *charPtrTy = CGF.Int8Ty->getPointerTo(AS);
+ ptr = CGF.Builder.CreateBitCast(ptr, charPtrTy);
+
+ // If we don't need an array cookie, bail out early.
+ if (!requiresArrayCookie(expr, eltTy)) {
+ allocPtr = ptr;
+ numElements = 0;
+ cookieSize = CharUnits::Zero();
+ return;
+ }
+
+ cookieSize = getArrayCookieSizeImpl(eltTy);
+ allocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(ptr,
+ -cookieSize.getQuantity());
+ numElements = readArrayCookieImpl(CGF, allocPtr, cookieSize);
+}
+
+llvm::Value *CGCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
+ llvm::Value *ptr,
+ CharUnits cookieSize) {
+ ErrorUnsupportedABI(CGF, "reading a new[] cookie");
+ return llvm::ConstantInt::get(CGF.SizeTy, 0);
}
void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
@@ -177,6 +221,13 @@ void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
ErrorUnsupportedABI(CGF, "static local variable initialization");
}
+void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
+ llvm::Constant *dtor,
+ llvm::Constant *addr) {
+ // The default behavior is to use atexit.
+ CGF.registerGlobalDtorWithAtExit(dtor, addr);
+}
+
/// Returns the adjustment, in bytes, required for the given
/// member-pointer operation. Returns null if no adjustment is
/// required.
diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h
index 4e045f5..a0dcdfd 100644
--- a/lib/CodeGen/CGCXXABI.h
+++ b/lib/CodeGen/CGCXXABI.h
@@ -38,7 +38,7 @@ namespace CodeGen {
class CodeGenFunction;
class CodeGenModule;
-/// Implements C++ ABI-specific code generation functions.
+/// \brief Implements C++ ABI-specific code generation functions.
class CGCXXABI {
protected:
CodeGenModule &CGM;
@@ -71,6 +71,9 @@ protected:
ASTContext &getContext() const { return CGM.getContext(); }
+ virtual bool requiresArrayCookie(const CXXDeleteExpr *E, QualType eltType);
+ virtual bool requiresArrayCookie(const CXXNewExpr *E);
+
public:
virtual ~CGCXXABI();
@@ -190,18 +193,20 @@ public:
virtual void EmitReturnFromThunk(CodeGenFunction &CGF,
RValue RV, QualType ResultType);
+ /// Gets the pure virtual member call function.
+ virtual StringRef GetPureVirtualCallName() = 0;
+
/**************************** Array cookies ******************************/
/// Returns the extra size required in order to store the array
- /// cookie for the given type. May return 0 to indicate that no
+ /// cookie for the given new-expression. May return 0 to indicate that no
/// array cookie is required.
///
/// Several cases are filtered out before this method is called:
/// - non-array allocations never need a cookie
- /// - calls to ::operator new(size_t, void*) never need a cookie
+ /// - calls to \::operator new(size_t, void*) never need a cookie
///
- /// \param ElementType - the allocated type of the expression,
- /// i.e. the pointee type of the expression result type
+ /// \param expr - the new-expression being allocated.
virtual CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
/// Initialize the array cookie for the given allocation.
@@ -209,7 +214,8 @@ public:
/// \param NewPtr - a char* which is the presumed-non-null
/// return value of the allocation function
/// \param NumElements - the computed number of elements,
- /// potentially collapsed from the multidimensional array case
+ /// potentially collapsed from the multidimensional array case;
+ /// always a size_t
/// \param ElementType - the base element allocated type,
/// i.e. the allocated type after stripping all array types
virtual llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
@@ -236,6 +242,27 @@ public:
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
+protected:
+ /// Returns the extra size required in order to store the array
+ /// cookie for the given type. Assumes that an array cookie is
+ /// required.
+ virtual CharUnits getArrayCookieSizeImpl(QualType elementType);
+
+ /// Reads the array cookie for an allocation which is known to have one.
+ /// This is called by the standard implementation of ReadArrayCookie.
+ ///
+ /// \param ptr - a pointer to the allocation made for an array, as a char*
+ /// \param cookieSize - the computed cookie size of an array
+ ///
+ /// Other parameters are as above.
+ ///
+ /// \return a size_t
+ virtual llvm::Value *readArrayCookieImpl(CodeGenFunction &IGF,
+ llvm::Value *ptr,
+ CharUnits cookieSize);
+
+public:
+
/*************************** Static local guards ****************************/
/// Emits the guarded initializer and destructor setup for the given
@@ -249,6 +276,18 @@ public:
virtual void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::GlobalVariable *DeclPtr, bool PerformInit);
+ /// Emit code to force the execution of a destructor during global
+ /// teardown. The default implementation of this uses atexit.
+ ///
+ /// \param dtor - a function taking a single pointer argument
+ /// \param addr - a pointer to pass to the destructor function.
+ virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,
+ llvm::Constant *addr);
+
+ /***************************** Virtual Tables *******************************/
+
+ /// Generates and emits the virtual tables for a class.
+ virtual void EmitVTables(const CXXRecordDecl *Class) = 0;
};
/// Creates an instance of a C++ ABI class.
diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp
index 82ee4fc..7d2b9d3 100644
--- a/lib/CodeGen/CGCall.cpp
+++ b/lib/CodeGen/CGCall.cpp
@@ -67,39 +67,68 @@ static CanQualType GetReturnType(QualType RetTy) {
return RetTy->getCanonicalTypeUnqualified().getUnqualifiedType();
}
-/// Arrange the argument and result information for a value of the
-/// given unprototyped function type.
+/// Arrange the argument and result information for a value of the given
+/// unprototyped freestanding function type.
const CGFunctionInfo &
-CodeGenTypes::arrangeFunctionType(CanQual<FunctionNoProtoType> FTNP) {
+CodeGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> FTNP) {
// When translating an unprototyped function type, always use a
// variadic type.
- return arrangeFunctionType(FTNP->getResultType().getUnqualifiedType(),
- ArrayRef<CanQualType>(),
- FTNP->getExtInfo(),
- RequiredArgs(0));
+ return arrangeLLVMFunctionInfo(FTNP->getResultType().getUnqualifiedType(),
+ ArrayRef<CanQualType>(),
+ FTNP->getExtInfo(),
+ RequiredArgs(0));
}
-/// Arrange the argument and result information for a value of the
-/// given function type, on top of any implicit parameters already
-/// stored.
-static const CGFunctionInfo &arrangeFunctionType(CodeGenTypes &CGT,
- SmallVectorImpl<CanQualType> &argTypes,
- CanQual<FunctionProtoType> FTP) {
- RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, argTypes.size());
+/// Arrange the LLVM function layout for a value of the given function
+/// type, on top of any implicit parameters already stored. Use the
+/// given ExtInfo instead of the ExtInfo from the function type.
+static const CGFunctionInfo &arrangeLLVMFunctionInfo(CodeGenTypes &CGT,
+ SmallVectorImpl<CanQualType> &prefix,
+ CanQual<FunctionProtoType> FTP,
+ FunctionType::ExtInfo extInfo) {
+ RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, prefix.size());
// FIXME: Kill copy.
for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i)
- argTypes.push_back(FTP->getArgType(i));
+ prefix.push_back(FTP->getArgType(i));
CanQualType resultType = FTP->getResultType().getUnqualifiedType();
- return CGT.arrangeFunctionType(resultType, argTypes,
- FTP->getExtInfo(), required);
+ return CGT.arrangeLLVMFunctionInfo(resultType, prefix, extInfo, required);
+}
+
+/// Arrange the argument and result information for a free function (i.e.
+/// not a C++ or ObjC instance method) of the given type.
+static const CGFunctionInfo &arrangeFreeFunctionType(CodeGenTypes &CGT,
+ SmallVectorImpl<CanQualType> &prefix,
+ CanQual<FunctionProtoType> FTP) {
+ return arrangeLLVMFunctionInfo(CGT, prefix, FTP, FTP->getExtInfo());
+}
+
+/// Given the formal ext-info of a C++ instance method, adjust it
+/// according to the C++ ABI in effect.
+static void adjustCXXMethodInfo(CodeGenTypes &CGT,
+ FunctionType::ExtInfo &extInfo,
+ bool isVariadic) {
+ if (extInfo.getCC() == CC_Default) {
+ CallingConv CC = CGT.getContext().getDefaultCXXMethodCallConv(isVariadic);
+ extInfo = extInfo.withCallingConv(CC);
+ }
+}
+
+/// Arrange the argument and result information for a free function (i.e.
+/// not a C++ or ObjC instance method) of the given type.
+static const CGFunctionInfo &arrangeCXXMethodType(CodeGenTypes &CGT,
+ SmallVectorImpl<CanQualType> &prefix,
+ CanQual<FunctionProtoType> FTP) {
+ FunctionType::ExtInfo extInfo = FTP->getExtInfo();
+ adjustCXXMethodInfo(CGT, extInfo, FTP->isVariadic());
+ return arrangeLLVMFunctionInfo(CGT, prefix, FTP, extInfo);
}
/// Arrange the argument and result information for a value of the
-/// given function type.
+/// given freestanding function type.
const CGFunctionInfo &
-CodeGenTypes::arrangeFunctionType(CanQual<FunctionProtoType> FTP) {
+CodeGenTypes::arrangeFreeFunctionType(CanQual<FunctionProtoType> FTP) {
SmallVector<CanQualType, 16> argTypes;
- return ::arrangeFunctionType(*this, argTypes, FTP);
+ return ::arrangeFreeFunctionType(*this, argTypes, FTP);
}
static CallingConv getCallingConventionForDecl(const Decl *D) {
@@ -134,7 +163,7 @@ CodeGenTypes::arrangeCXXMethodType(const CXXRecordDecl *RD,
// Add the 'this' pointer.
argTypes.push_back(GetThisType(Context, RD));
- return ::arrangeFunctionType(*this, argTypes,
+ return ::arrangeCXXMethodType(*this, argTypes,
FTP->getCanonicalTypeUnqualified().getAs<FunctionProtoType>());
}
@@ -154,7 +183,7 @@ CodeGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *MD) {
return arrangeCXXMethodType(MD->getParent(), prototype.getTypePtr());
}
- return arrangeFunctionType(prototype);
+ return arrangeFreeFunctionType(prototype);
}
/// Arrange the argument and result information for a declaration
@@ -176,7 +205,9 @@ CodeGenTypes::arrangeCXXConstructorDeclaration(const CXXConstructorDecl *D,
for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i)
argTypes.push_back(FTP->getArgType(i));
- return arrangeFunctionType(resultType, argTypes, FTP->getExtInfo(), required);
+ FunctionType::ExtInfo extInfo = FTP->getExtInfo();
+ adjustCXXMethodInfo(*this, extInfo, FTP->isVariadic());
+ return arrangeLLVMFunctionInfo(resultType, argTypes, extInfo, required);
}
/// Arrange the argument and result information for a declaration,
@@ -193,9 +224,12 @@ CodeGenTypes::arrangeCXXDestructor(const CXXDestructorDecl *D,
CanQual<FunctionProtoType> FTP = GetFormalType(D);
assert(FTP->getNumArgs() == 0 && "dtor with formal parameters");
+ assert(FTP->isVariadic() == 0 && "dtor with formal parameters");
- return arrangeFunctionType(resultType, argTypes, FTP->getExtInfo(),
- RequiredArgs::All);
+ FunctionType::ExtInfo extInfo = FTP->getExtInfo();
+ adjustCXXMethodInfo(*this, extInfo, false);
+ return arrangeLLVMFunctionInfo(resultType, argTypes, extInfo,
+ RequiredArgs::All);
}
/// Arrange the argument and result information for the declaration or
@@ -214,14 +248,14 @@ CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) {
// non-variadic type.
if (isa<FunctionNoProtoType>(FTy)) {
CanQual<FunctionNoProtoType> noProto = FTy.getAs<FunctionNoProtoType>();
- return arrangeFunctionType(noProto->getResultType(),
- ArrayRef<CanQualType>(),
- noProto->getExtInfo(),
- RequiredArgs::All);
+ return arrangeLLVMFunctionInfo(noProto->getResultType(),
+ ArrayRef<CanQualType>(),
+ noProto->getExtInfo(),
+ RequiredArgs::All);
}
assert(isa<FunctionProtoType>(FTy));
- return arrangeFunctionType(FTy.getAs<FunctionProtoType>());
+ return arrangeFreeFunctionType(FTy.getAs<FunctionProtoType>());
}
/// Arrange the argument and result information for the declaration or
@@ -261,8 +295,8 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD,
RequiredArgs required =
(MD->isVariadic() ? RequiredArgs(argTys.size()) : RequiredArgs::All);
- return arrangeFunctionType(GetReturnType(MD->getResultType()), argTys,
- einfo, required);
+ return arrangeLLVMFunctionInfo(GetReturnType(MD->getResultType()), argTys,
+ einfo, required);
}
const CGFunctionInfo &
@@ -284,8 +318,8 @@ CodeGenTypes::arrangeGlobalDeclaration(GlobalDecl GD) {
/// because the function might be unprototyped, in which case it's
/// target-dependent in crazy ways.
const CGFunctionInfo &
-CodeGenTypes::arrangeFunctionCall(const CallArgList &args,
- const FunctionType *fnType) {
+CodeGenTypes::arrangeFreeFunctionCall(const CallArgList &args,
+ const FunctionType *fnType) {
RequiredArgs required = RequiredArgs::All;
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) {
if (proto->isVariadic())
@@ -295,22 +329,39 @@ CodeGenTypes::arrangeFunctionCall(const CallArgList &args,
required = RequiredArgs(0);
}
- return arrangeFunctionCall(fnType->getResultType(), args,
- fnType->getExtInfo(), required);
+ return arrangeFreeFunctionCall(fnType->getResultType(), args,
+ fnType->getExtInfo(), required);
+}
+
+const CGFunctionInfo &
+CodeGenTypes::arrangeFreeFunctionCall(QualType resultType,
+ const CallArgList &args,
+ FunctionType::ExtInfo info,
+ RequiredArgs required) {
+ // FIXME: Kill copy.
+ SmallVector<CanQualType, 16> argTypes;
+ for (CallArgList::const_iterator i = args.begin(), e = args.end();
+ i != e; ++i)
+ argTypes.push_back(Context.getCanonicalParamType(i->Ty));
+ return arrangeLLVMFunctionInfo(GetReturnType(resultType), argTypes, info,
+ required);
}
+/// Arrange a call to a C++ method, passing the given arguments.
const CGFunctionInfo &
-CodeGenTypes::arrangeFunctionCall(QualType resultType,
- const CallArgList &args,
- const FunctionType::ExtInfo &info,
- RequiredArgs required) {
+CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args,
+ const FunctionProtoType *FPT,
+ RequiredArgs required) {
// FIXME: Kill copy.
SmallVector<CanQualType, 16> argTypes;
for (CallArgList::const_iterator i = args.begin(), e = args.end();
i != e; ++i)
argTypes.push_back(Context.getCanonicalParamType(i->Ty));
- return arrangeFunctionType(GetReturnType(resultType), argTypes, info,
- required);
+
+ FunctionType::ExtInfo info = FPT->getExtInfo();
+ adjustCXXMethodInfo(*this, info, FPT->isVariadic());
+ return arrangeLLVMFunctionInfo(GetReturnType(FPT->getResultType()),
+ argTypes, info, required);
}
const CGFunctionInfo &
@@ -326,23 +377,23 @@ CodeGenTypes::arrangeFunctionDeclaration(QualType resultType,
RequiredArgs required =
(isVariadic ? RequiredArgs(args.size()) : RequiredArgs::All);
- return arrangeFunctionType(GetReturnType(resultType), argTypes, info,
- required);
+ return arrangeLLVMFunctionInfo(GetReturnType(resultType), argTypes, info,
+ required);
}
const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() {
- return arrangeFunctionType(getContext().VoidTy, ArrayRef<CanQualType>(),
- FunctionType::ExtInfo(), RequiredArgs::All);
+ return arrangeLLVMFunctionInfo(getContext().VoidTy, ArrayRef<CanQualType>(),
+ FunctionType::ExtInfo(), RequiredArgs::All);
}
/// Arrange the argument and result information for an abstract value
/// of a given function type. This is the method which all of the
/// above functions ultimately defer to.
const CGFunctionInfo &
-CodeGenTypes::arrangeFunctionType(CanQualType resultType,
- ArrayRef<CanQualType> argTypes,
- const FunctionType::ExtInfo &info,
- RequiredArgs required) {
+CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType,
+ ArrayRef<CanQualType> argTypes,
+ FunctionType::ExtInfo info,
+ RequiredArgs required) {
#ifndef NDEBUG
for (ArrayRef<CanQualType>::const_iterator
I = argTypes.begin(), E = argTypes.end(); I != E; ++I)
@@ -445,10 +496,9 @@ void CodeGenTypes::GetExpandedTypes(QualType type,
} else {
for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
i != e; ++i) {
- const FieldDecl *FD = *i;
- assert(!FD->isBitField() &&
+ assert(!i->isBitField() &&
"Cannot expand structure with bit-field members.");
- GetExpandedTypes(FD->getType(), expandedTypes);
+ GetExpandedTypes(i->getType(), expandedTypes);
}
}
} else if (const ComplexType *CT = type->getAs<ComplexType>()) {
@@ -933,14 +983,18 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
case ABIArgInfo::Ignore:
break;
- case ABIArgInfo::Indirect:
- PAL.push_back(llvm::AttributeWithIndex::get(Index,
- llvm::Attribute::StructRet));
+ case ABIArgInfo::Indirect: {
+ llvm::Attributes SRETAttrs = llvm::Attribute::StructRet;
+ if (RetAI.getInReg())
+ SRETAttrs |= llvm::Attribute::InReg;
+ PAL.push_back(llvm::AttributeWithIndex::get(Index, SRETAttrs));
+
++Index;
// sret disables readnone and readonly
FuncAttrs &= ~(llvm::Attribute::ReadOnly |
llvm::Attribute::ReadNone);
break;
+ }
case ABIArgInfo::Expand:
llvm_unreachable("Invalid ABI kind for return argument");
@@ -949,14 +1003,6 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
if (RetAttrs)
PAL.push_back(llvm::AttributeWithIndex::get(0, RetAttrs));
- // FIXME: RegParm should be reduced in case of global register variable.
- signed RegParm;
- if (FI.getHasRegParm())
- RegParm = FI.getRegParm();
- else
- RegParm = CodeGenOpts.NumRegisterParameters;
-
- unsigned PointerWidth = getContext().getTargetInfo().getPointerWidth(0);
for (CGFunctionInfo::const_arg_iterator it = FI.arg_begin(),
ie = FI.arg_end(); it != ie; ++it) {
QualType ParamType = it->type;
@@ -974,22 +1020,22 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
Attrs |= llvm::Attribute::ZExt;
// FALL THROUGH
case ABIArgInfo::Direct:
- if (RegParm > 0 &&
- (ParamType->isIntegerType() || ParamType->isPointerType() ||
- ParamType->isReferenceType())) {
- RegParm -=
- (Context.getTypeSize(ParamType) + PointerWidth - 1) / PointerWidth;
- if (RegParm >= 0)
+ if (AI.getInReg())
Attrs |= llvm::Attribute::InReg;
- }
+
// FIXME: handle sseregparm someday...
// Increment Index if there is padding.
Index += (AI.getPaddingType() != 0);
if (llvm::StructType *STy =
- dyn_cast<llvm::StructType>(AI.getCoerceToType()))
- Index += STy->getNumElements()-1; // 1 will be added below.
+ dyn_cast<llvm::StructType>(AI.getCoerceToType())) {
+ unsigned Extra = STy->getNumElements()-1; // 1 will be added below.
+ if (Attrs != llvm::Attribute::None)
+ for (unsigned I = 0; I < Extra; ++I)
+ PAL.push_back(llvm::AttributeWithIndex::get(Index + I, Attrs));
+ Index += Extra;
+ }
break;
case ABIArgInfo::Indirect:
@@ -1355,7 +1401,8 @@ static llvm::Value *tryEmitFusedAutoreleaseOfResult(CodeGenFunction &CGF,
static llvm::Value *tryRemoveRetainOfSelf(CodeGenFunction &CGF,
llvm::Value *result) {
// This is only applicable to a method with an immutable 'self'.
- const ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CGF.CurCodeDecl);
+ const ObjCMethodDecl *method =
+ dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl);
if (!method) return 0;
const VarDecl *self = method->getSelfDecl();
if (!self->getType().isConstQualified()) return 0;
@@ -2066,8 +2113,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
unsigned CallingConv;
CodeGen::AttributeListType AttributeList;
CGM.ConstructAttributeList(CallInfo, TargetDecl, AttributeList, CallingConv);
- llvm::AttrListPtr Attrs = llvm::AttrListPtr::get(AttributeList.begin(),
- AttributeList.end());
+ llvm::AttrListPtr Attrs = llvm::AttrListPtr::get(AttributeList);
llvm::BasicBlock *InvokeDest = 0;
if (!(Attrs.getFnAttributes() & llvm::Attribute::NoUnwind))
diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp
index 2aedf95..e37fa3a 100644
--- a/lib/CodeGen/CGClass.cpp
+++ b/lib/CodeGen/CGClass.cpp
@@ -105,30 +105,28 @@ CodeGenFunction::GetAddressOfDirectBaseInCompleteClass(llvm::Value *This,
}
static llvm::Value *
-ApplyNonVirtualAndVirtualOffset(CodeGenFunction &CGF, llvm::Value *ThisPtr,
- CharUnits NonVirtual, llvm::Value *Virtual) {
- llvm::Type *PtrDiffTy =
- CGF.ConvertType(CGF.getContext().getPointerDiffType());
-
- llvm::Value *NonVirtualOffset = 0;
- if (!NonVirtual.isZero())
- NonVirtualOffset = llvm::ConstantInt::get(PtrDiffTy,
- NonVirtual.getQuantity());
-
- llvm::Value *BaseOffset;
- if (Virtual) {
- if (NonVirtualOffset)
- BaseOffset = CGF.Builder.CreateAdd(Virtual, NonVirtualOffset);
- else
- BaseOffset = Virtual;
- } else
- BaseOffset = NonVirtualOffset;
+ApplyNonVirtualAndVirtualOffset(CodeGenFunction &CGF, llvm::Value *ptr,
+ CharUnits nonVirtualOffset,
+ llvm::Value *virtualOffset) {
+ // Assert that we have something to do.
+ assert(!nonVirtualOffset.isZero() || virtualOffset != 0);
+
+ // Compute the offset from the static and dynamic components.
+ llvm::Value *baseOffset;
+ if (!nonVirtualOffset.isZero()) {
+ baseOffset = llvm::ConstantInt::get(CGF.PtrDiffTy,
+ nonVirtualOffset.getQuantity());
+ if (virtualOffset) {
+ baseOffset = CGF.Builder.CreateAdd(virtualOffset, baseOffset);
+ }
+ } else {
+ baseOffset = virtualOffset;
+ }
// Apply the base offset.
- ThisPtr = CGF.Builder.CreateBitCast(ThisPtr, CGF.Int8PtrTy);
- ThisPtr = CGF.Builder.CreateGEP(ThisPtr, BaseOffset, "add.ptr");
-
- return ThisPtr;
+ ptr = CGF.Builder.CreateBitCast(ptr, CGF.Int8PtrTy);
+ ptr = CGF.Builder.CreateInBoundsGEP(ptr, baseOffset, "add.ptr");
+ return ptr;
}
llvm::Value *
@@ -142,72 +140,81 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value,
CastExpr::path_const_iterator Start = PathBegin;
const CXXRecordDecl *VBase = 0;
- // Get the virtual base.
+ // Sema has done some convenient canonicalization here: if the
+ // access path involved any virtual steps, the conversion path will
+ // *start* with a step down to the correct virtual base subobject,
+ // and hence will not require any further steps.
if ((*Start)->isVirtual()) {
VBase =
cast<CXXRecordDecl>((*Start)->getType()->getAs<RecordType>()->getDecl());
++Start;
}
-
+
+ // Compute the static offset of the ultimate destination within its
+ // allocating subobject (the virtual base, if there is one, or else
+ // the "complete" object that we see).
CharUnits NonVirtualOffset =
ComputeNonVirtualBaseClassOffset(getContext(), VBase ? VBase : Derived,
Start, PathEnd);
+ // If there's a virtual step, we can sometimes "devirtualize" it.
+ // For now, that's limited to when the derived type is final.
+ // TODO: "devirtualize" this for accesses to known-complete objects.
+ if (VBase && Derived->hasAttr<FinalAttr>()) {
+ const ASTRecordLayout &layout = getContext().getASTRecordLayout(Derived);
+ CharUnits vBaseOffset = layout.getVBaseClassOffset(VBase);
+ NonVirtualOffset += vBaseOffset;
+ VBase = 0; // we no longer have a virtual step
+ }
+
// Get the base pointer type.
llvm::Type *BasePtrTy =
ConvertType((PathEnd[-1])->getType())->getPointerTo();
-
+
+ // If the static offset is zero and we don't have a virtual step,
+ // just do a bitcast; null checks are unnecessary.
if (NonVirtualOffset.isZero() && !VBase) {
- // Just cast back.
return Builder.CreateBitCast(Value, BasePtrTy);
}
+
+ llvm::BasicBlock *origBB = 0;
+ llvm::BasicBlock *endBB = 0;
- llvm::BasicBlock *CastNull = 0;
- llvm::BasicBlock *CastNotNull = 0;
- llvm::BasicBlock *CastEnd = 0;
-
+ // Skip over the offset (and the vtable load) if we're supposed to
+ // null-check the pointer.
if (NullCheckValue) {
- CastNull = createBasicBlock("cast.null");
- CastNotNull = createBasicBlock("cast.notnull");
- CastEnd = createBasicBlock("cast.end");
+ origBB = Builder.GetInsertBlock();
+ llvm::BasicBlock *notNullBB = createBasicBlock("cast.notnull");
+ endBB = createBasicBlock("cast.end");
- llvm::Value *IsNull = Builder.CreateIsNull(Value);
- Builder.CreateCondBr(IsNull, CastNull, CastNotNull);
- EmitBlock(CastNotNull);
+ llvm::Value *isNull = Builder.CreateIsNull(Value);
+ Builder.CreateCondBr(isNull, endBB, notNullBB);
+ EmitBlock(notNullBB);
}
+ // Compute the virtual offset.
llvm::Value *VirtualOffset = 0;
-
if (VBase) {
- if (Derived->hasAttr<FinalAttr>()) {
- VirtualOffset = 0;
-
- const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Derived);
-
- CharUnits VBaseOffset = Layout.getVBaseClassOffset(VBase);
- NonVirtualOffset += VBaseOffset;
- } else
- VirtualOffset = GetVirtualBaseClassOffset(Value, Derived, VBase);
+ VirtualOffset = GetVirtualBaseClassOffset(Value, Derived, VBase);
}
- // Apply the offsets.
+ // Apply both offsets.
Value = ApplyNonVirtualAndVirtualOffset(*this, Value,
NonVirtualOffset,
VirtualOffset);
- // Cast back.
+ // Cast to the destination type.
Value = Builder.CreateBitCast(Value, BasePtrTy);
-
+
+ // Build a phi if we needed a null check.
if (NullCheckValue) {
- Builder.CreateBr(CastEnd);
- EmitBlock(CastNull);
- Builder.CreateBr(CastEnd);
- EmitBlock(CastEnd);
+ llvm::BasicBlock *notNullBB = Builder.GetInsertBlock();
+ Builder.CreateBr(endBB);
+ EmitBlock(endBB);
- llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2);
- PHI->addIncoming(Value, CastNotNull);
- PHI->addIncoming(llvm::Constant::getNullValue(Value->getType()),
- CastNull);
+ llvm::PHINode *PHI = Builder.CreatePHI(BasePtrTy, 2, "cast.result");
+ PHI->addIncoming(Value, notNullBB);
+ PHI->addIncoming(llvm::Constant::getNullValue(BasePtrTy), origBB);
Value = PHI;
}
@@ -556,16 +563,19 @@ static void EmitMemberInitializer(CodeGenFunction &CGF,
llvm::Value *ThisPtr = CGF.LoadCXXThis();
QualType RecordTy = CGF.getContext().getTypeDeclType(ClassDecl);
- LValue LHS;
+ LValue LHS = CGF.MakeNaturalAlignAddrLValue(ThisPtr, RecordTy);
- // If we are initializing an anonymous union field, drill down to the field.
if (MemberInit->isIndirectMemberInitializer()) {
- LHS = CGF.EmitLValueForAnonRecordField(ThisPtr,
- MemberInit->getIndirectMember(), 0);
+ // If we are initializing an anonymous union field, drill down to
+ // the field.
+ IndirectFieldDecl *IndirectField = MemberInit->getIndirectMember();
+ IndirectFieldDecl::chain_iterator I = IndirectField->chain_begin(),
+ IEnd = IndirectField->chain_end();
+ for ( ; I != IEnd; ++I)
+ LHS = CGF.EmitLValueForFieldInitialization(LHS, cast<FieldDecl>(*I));
FieldType = MemberInit->getIndirectMember()->getAnonField()->getType();
} else {
- LValue ThisLHSLV = CGF.MakeNaturalAlignAddrLValue(ThisPtr, RecordTy);
- LHS = CGF.EmitLValueForFieldInitialization(ThisLHSLV, Field);
+ LHS = CGF.EmitLValueForFieldInitialization(LHS, Field);
}
// Special case: if we are in a copy or move constructor, and we are copying
@@ -717,7 +727,8 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
// Before we go any further, try the complete->base constructor
// delegation optimization.
- if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor)) {
+ if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor) &&
+ CGM.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) {
if (CGDebugInfo *DI = getDebugInfo())
DI->EmitLocation(Builder, Ctor->getLocEnd());
EmitDelegateCXXConstructorCall(Ctor, Ctor_Base, Args);
@@ -916,7 +927,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// Enter the cleanup scopes for virtual bases.
EnterDtorCleanups(Dtor, Dtor_Complete);
- if (!isTryBody) {
+ if (!isTryBody && CGM.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) {
EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false,
LoadCXXThis());
break;
@@ -1226,7 +1237,8 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
CallExpr::const_arg_iterator ArgEnd) {
CGDebugInfo *DI = getDebugInfo();
- if (DI && CGM.getCodeGenOpts().LimitDebugInfo) {
+ if (DI &&
+ CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo) {
// If debug info for this class has not been emitted then this is the
// right time to do so.
const CXXRecordDecl *Parent = D->getParent();
@@ -1308,8 +1320,8 @@ CodeGenFunction::EmitSynthesizedCXXCopyCtorCall(const CXXConstructorDecl *D,
EmitCallArg(Args, *Arg, ArgType);
}
- EmitCall(CGM.getTypes().arrangeFunctionCall(Args, FPT), Callee,
- ReturnValueSlot(), Args, D);
+ EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, RequiredArgs::All),
+ Callee, ReturnValueSlot(), Args, D);
}
void
@@ -1742,38 +1754,42 @@ CodeGenFunction::EmitCXXOperatorMemberCallee(const CXXOperatorCallExpr *E,
return CGM.GetAddrOfFunction(MD, fnType);
}
-void CodeGenFunction::EmitForwardingCallToLambda(const CXXRecordDecl *Lambda,
- CallArgList &CallArgs) {
+void CodeGenFunction::EmitForwardingCallToLambda(const CXXRecordDecl *lambda,
+ CallArgList &callArgs) {
// Lookup the call operator
- DeclarationName Name
+ DeclarationName operatorName
= getContext().DeclarationNames.getCXXOperatorName(OO_Call);
- DeclContext::lookup_const_result Calls = Lambda->lookup(Name);
- CXXMethodDecl *CallOperator = cast<CXXMethodDecl>(*Calls.first++);
- const FunctionProtoType *FPT =
- CallOperator->getType()->getAs<FunctionProtoType>();
- QualType ResultType = FPT->getResultType();
+ CXXMethodDecl *callOperator =
+ cast<CXXMethodDecl>(*lambda->lookup(operatorName).first);
// Get the address of the call operator.
- GlobalDecl GD(CallOperator);
- const CGFunctionInfo &CalleeFnInfo =
- CGM.getTypes().arrangeFunctionCall(ResultType, CallArgs, FPT->getExtInfo(),
- RequiredArgs::forPrototypePlus(FPT, 1));
- llvm::Type *Ty = CGM.getTypes().GetFunctionType(CalleeFnInfo);
- llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty);
-
- // Determine whether we have a return value slot to use.
- ReturnValueSlot Slot;
- if (!ResultType->isVoidType() &&
- CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect &&
- hasAggregateLLVMType(CurFnInfo->getReturnType()))
- Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified());
+ const CGFunctionInfo &calleeFnInfo =
+ CGM.getTypes().arrangeCXXMethodDeclaration(callOperator);
+ llvm::Value *callee =
+ CGM.GetAddrOfFunction(GlobalDecl(callOperator),
+ CGM.getTypes().GetFunctionType(calleeFnInfo));
+
+ // Prepare the return slot.
+ const FunctionProtoType *FPT =
+ callOperator->getType()->castAs<FunctionProtoType>();
+ QualType resultType = FPT->getResultType();
+ ReturnValueSlot returnSlot;
+ if (!resultType->isVoidType() &&
+ calleeFnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect &&
+ hasAggregateLLVMType(calleeFnInfo.getReturnType()))
+ returnSlot = ReturnValueSlot(ReturnValue, resultType.isVolatileQualified());
+
+ // We don't need to separately arrange the call arguments because
+ // the call can't be variadic anyway --- it's impossible to forward
+ // variadic arguments.
// Now emit our call.
- RValue RV = EmitCall(CalleeFnInfo, Callee, Slot, CallArgs, CallOperator);
+ RValue RV = EmitCall(calleeFnInfo, callee, returnSlot,
+ callArgs, callOperator);
- // Forward the returned value
- if (!ResultType->isVoidType() && Slot.isNull())
- EmitReturnOfRValue(RV, ResultType);
+ // If necessary, copy the returned value into the slot.
+ if (!resultType->isVoidType() && returnSlot.isNull())
+ EmitReturnOfRValue(RV, resultType);
}
void CodeGenFunction::EmitLambdaBlockInvokeBody() {
diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp
index b00e2a2..f9ea7e0 100644
--- a/lib/CodeGen/CGCleanup.cpp
+++ b/lib/CodeGen/CGCleanup.cpp
@@ -831,8 +831,12 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
EmitBlock(EHEntry);
- cleanupFlags.setIsForEHCleanup();
- EmitCleanup(*this, Fn, cleanupFlags, EHActiveFlag);
+ // We only actually emit the cleanup code if the cleanup is either
+ // active or was used before it was deactivated.
+ if (EHActiveFlag || IsActive) {
+ cleanupFlags.setIsForEHCleanup();
+ EmitCleanup(*this, Fn, cleanupFlags, EHActiveFlag);
+ }
Builder.CreateBr(getEHDispatchBlock(EHParent));
diff --git a/lib/CodeGen/CGCleanup.h b/lib/CodeGen/CGCleanup.h
index 7726e44..d8dbe41 100644
--- a/lib/CodeGen/CGCleanup.h
+++ b/lib/CodeGen/CGCleanup.h
@@ -131,7 +131,7 @@ public:
/// A scope which attempts to handle some, possibly all, types of
/// exceptions.
///
-/// Objective C @finally blocks are represented using a cleanup scope
+/// Objective C \@finally blocks are represented using a cleanup scope
/// after the catch scope.
class EHCatchScope : public EHScope {
// In effect, we have a flexible array member
diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp
index d286d24..00127ac 100644
--- a/lib/CodeGen/CGDebugInfo.cpp
+++ b/lib/CodeGen/CGDebugInfo.cpp
@@ -320,7 +320,7 @@ void CGDebugInfo::CreateCompileUnit() {
// Figure out which version of the ObjC runtime we have.
unsigned RuntimeVers = 0;
if (LO.ObjC1)
- RuntimeVers = LO.ObjCNonFragileABI ? 2 : 1;
+ RuntimeVers = LO.ObjCRuntime.isNonFragile() ? 2 : 1;
// Create new compile unit.
DBuilder.createCompileUnit(
@@ -335,7 +335,7 @@ void CGDebugInfo::CreateCompileUnit() {
/// one if necessary.
llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) {
unsigned Encoding = 0;
- const char *BTName = NULL;
+ StringRef BTName;
switch (BT->getKind()) {
#define BUILTIN_TYPE(Id, SingletonId)
#define PLACEHOLDER_TYPE(Id, SingletonId) \
@@ -350,8 +350,8 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) {
return llvm::DIType();
case BuiltinType::ObjCClass:
return DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type,
- "objc_class", getOrCreateMainFile(),
- 0);
+ "objc_class", TheCU,
+ getOrCreateMainFile(), 0);
case BuiltinType::ObjCId: {
// typedef struct objc_class *Class;
// typedef struct objc_object {
@@ -361,8 +361,7 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) {
// TODO: Cache these two types to avoid duplicates.
llvm::DIType OCTy =
DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type,
- "objc_class", getOrCreateMainFile(),
- 0);
+ "objc_class", TheCU, getOrCreateMainFile(), 0);
unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy);
llvm::DIType ISATy = DBuilder.createPointerType(OCTy, Size);
@@ -382,7 +381,7 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) {
case BuiltinType::ObjCSel: {
return
DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type,
- "objc_selector", getOrCreateMainFile(),
+ "objc_selector", TheCU, getOrCreateMainFile(),
0);
}
case BuiltinType::UChar:
@@ -514,7 +513,7 @@ llvm::DIType CGDebugInfo::createRecordFwdDecl(const RecordDecl *RD,
llvm_unreachable("Unknown RecordDecl type!");
// Create the type.
- return DBuilder.createForwardDecl(Tag, RDName, DefUnit, Line);
+ return DBuilder.createForwardDecl(Tag, RDName, Ctx, DefUnit, Line);
}
// Walk up the context chain and create forward decls for record decls,
@@ -547,7 +546,7 @@ llvm::DIDescriptor CGDebugInfo::createContextChain(const Decl *Context) {
/// then emit record's fwd if debug info size reduction is enabled.
llvm::DIType CGDebugInfo::CreatePointeeType(QualType PointeeTy,
llvm::DIFile Unit) {
- if (!CGM.getCodeGenOpts().LimitDebugInfo)
+ if (CGM.getCodeGenOpts().DebugInfo != CodeGenOptions::LimitedDebugInfo)
return getOrCreateType(PointeeTy, Unit);
// Limit debug info for the pointee type.
@@ -577,8 +576,10 @@ llvm::DIType CGDebugInfo::CreatePointerLikeType(unsigned Tag,
const Type *Ty,
QualType PointeeTy,
llvm::DIFile Unit) {
- if (Tag == llvm::dwarf::DW_TAG_reference_type)
- return DBuilder.createReferenceType(CreatePointeeType(PointeeTy, Unit));
+ if (Tag == llvm::dwarf::DW_TAG_reference_type ||
+ Tag == llvm::dwarf::DW_TAG_rvalue_reference_type)
+ return DBuilder.createReferenceType(Tag,
+ CreatePointeeType(PointeeTy, Unit));
// Bit size, align and offset of the type.
// Size is always the size of a pointer. We can't use getTypeSize here
@@ -683,15 +684,13 @@ llvm::DIType CGDebugInfo::CreateType(const FunctionType *Ty,
// FIXME: IF NOT, HOW IS THIS REPRESENTED? llvm-gcc doesn't represent '...'!
if (isa<FunctionNoProtoType>(Ty))
EltTys.push_back(DBuilder.createUnspecifiedParameter());
- else if (const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(Ty)) {
- for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i)
- EltTys.push_back(getOrCreateType(FTP->getArgType(i), Unit));
+ else if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(Ty)) {
+ for (unsigned i = 0, e = FPT->getNumArgs(); i != e; ++i)
+ EltTys.push_back(getOrCreateType(FPT->getArgType(i), Unit));
}
llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(EltTys);
-
- llvm::DIType DbgTy = DBuilder.createSubroutineType(Unit, EltTypeArray);
- return DbgTy;
+ return DBuilder.createSubroutineType(Unit, EltTypeArray);
}
@@ -765,7 +764,7 @@ CollectRecordFields(const RecordDecl *record, llvm::DIFile tunit,
const ASTRecordLayout &layout = CGM.getContext().getASTRecordLayout(record);
const CXXRecordDecl *CXXDecl = dyn_cast<CXXRecordDecl>(record);
- // For C++11 Lambdas a Fields will be the same as a Capture, but the Capture
+ // For C++11 Lambdas a Field will be the same as a Capture, but the Capture
// has the name and the location of the variable so we should iterate over
// both concurrently.
if (CXXDecl && CXXDecl->isLambda()) {
@@ -912,7 +911,7 @@ CGDebugInfo::CreateCXXMemberFunction(const CXXMethodDecl *Method,
StringRef MethodName = getFunctionName(Method);
llvm::DIType MethodTy = getOrCreateMethodType(Method, Unit);
-
+
// Since a single ctor/dtor corresponds to multiple functions, it doesn't
// make sense to give a single ctor/dtor a linkage name.
StringRef MethodLinkageName;
@@ -992,15 +991,17 @@ CollectCXXMemberFunctions(const CXXRecordDecl *RD, llvm::DIFile Unit,
if (D->isImplicit() && !D->isUsed())
continue;
- if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D))
- EltTys.push_back(CreateCXXMemberFunction(Method, Unit, RecordTy));
+ if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
+ // Only emit debug information for user provided functions, we're
+ // unlikely to want info for artificial functions.
+ if (Method->isUserProvided())
+ EltTys.push_back(CreateCXXMemberFunction(Method, Unit, RecordTy));
+ }
else if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(D))
for (FunctionTemplateDecl::spec_iterator SI = FTD->spec_begin(),
- SE = FTD->spec_end(); SI != SE; ++SI) {
- FunctionDecl *FD = *SI;
- if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(FD))
- EltTys.push_back(CreateCXXMemberFunction(M, Unit, RecordTy));
- }
+ SE = FTD->spec_end(); SI != SE; ++SI)
+ EltTys.push_back(CreateCXXMemberFunction(cast<CXXMethodDecl>(*SI), Unit,
+ RecordTy));
}
}
@@ -1047,7 +1048,7 @@ CollectCXXBases(const CXXRecordDecl *RD, llvm::DIFile Unit,
.getVirtualBaseOffsetOffset(RD, Base).getQuantity();
BFlags = llvm::DIDescriptor::FlagVirtual;
} else
- BaseOffset = RL.getBaseClassOffsetInBits(Base);
+ BaseOffset = CGM.getContext().toBits(RL.getBaseClassOffset(Base));
// FIXME: Inconsistent units for BaseOffset. It is in bytes when
// BI->isVirtual() and bits when not.
@@ -1083,7 +1084,7 @@ CollectTemplateParams(const TemplateParameterList *TPList,
llvm::DIType TTy = getOrCreateType(TA.getIntegralType(), Unit);
llvm::DITemplateValueParameter TVP =
DBuilder.createTemplateValueParameter(TheCU, ND->getName(), TTy,
- TA.getAsIntegral()->getZExtValue());
+ TA.getAsIntegral().getZExtValue());
TemplateParams.push_back(TVP);
}
}
@@ -1177,6 +1178,7 @@ CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile Unit,
/// getOrCreateRecordType - Emit record type's standalone debug info.
llvm::DIType CGDebugInfo::getOrCreateRecordType(QualType RTy,
SourceLocation Loc) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
llvm::DIType T = getOrCreateType(RTy, getOrCreateFile(Loc));
return T;
}
@@ -1185,6 +1187,7 @@ llvm::DIType CGDebugInfo::getOrCreateRecordType(QualType RTy,
/// debug info.
llvm::DIType CGDebugInfo::getOrCreateInterfaceType(QualType D,
SourceLocation Loc) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
llvm::DIType T = getOrCreateType(D, getOrCreateFile(Loc));
DBuilder.retainType(T);
return T;
@@ -1287,7 +1290,7 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty,
if (!Def) {
llvm::DIType FwdDecl =
DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type,
- ID->getName(), DefUnit, Line,
+ ID->getName(), TheCU, DefUnit, Line,
RuntimeLang);
return FwdDecl;
}
@@ -1385,8 +1388,8 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty,
// the non-fragile abi and the debugger should ignore the value anyways.
// Call it the FieldNo+1 due to how debuggers use the information,
// e.g. negating the value when it needs a lookup in the dynamic table.
- uint64_t FieldOffset = CGM.getLangOpts().ObjCNonFragileABI ? FieldNo+1
- : RL.getFieldOffset(FieldNo);
+ uint64_t FieldOffset = CGM.getLangOpts().ObjCRuntime.isNonFragile()
+ ? FieldNo+1 : RL.getFieldOffset(FieldNo);
unsigned Flags = 0;
if (Field->getAccessControl() == ObjCIvarDecl::Protected)
@@ -1456,7 +1459,6 @@ llvm::DIType CGDebugInfo::CreateType(const ArrayType *Ty,
uint64_t Size;
uint64_t Align;
-
// FIXME: make getTypeAlign() aware of VLAs and incomplete array types
if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(Ty)) {
Size = 0;
@@ -1464,7 +1466,10 @@ llvm::DIType CGDebugInfo::CreateType(const ArrayType *Ty,
CGM.getContext().getTypeAlign(CGM.getContext().getBaseElementType(VAT));
} else if (Ty->isIncompleteArrayType()) {
Size = 0;
- Align = CGM.getContext().getTypeAlign(Ty->getElementType());
+ if (Ty->getElementType()->isIncompleteType())
+ Align = 0;
+ else
+ Align = CGM.getContext().getTypeAlign(Ty->getElementType());
} else if (Ty->isDependentSizedArrayType() || Ty->isIncompleteType()) {
Size = 0;
Align = 0;
@@ -1479,25 +1484,21 @@ llvm::DIType CGDebugInfo::CreateType(const ArrayType *Ty,
// obvious/recursive way?
SmallVector<llvm::Value *, 8> Subscripts;
QualType EltTy(Ty, 0);
- if (Ty->isIncompleteArrayType())
+ while ((Ty = dyn_cast<ArrayType>(EltTy))) {
+ int64_t UpperBound = 0;
+ int64_t LowerBound = 0;
+ if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) {
+ if (CAT->getSize().getZExtValue())
+ UpperBound = CAT->getSize().getZExtValue() - 1;
+ } else
+ // This is an unbounded array. Use Low = 1, Hi = 0 to express such
+ // arrays.
+ LowerBound = 1;
+
+ // FIXME: Verify this is right for VLAs.
+ Subscripts.push_back(DBuilder.getOrCreateSubrange(LowerBound,
+ UpperBound));
EltTy = Ty->getElementType();
- else {
- while ((Ty = dyn_cast<ArrayType>(EltTy))) {
- int64_t UpperBound = 0;
- int64_t LowerBound = 0;
- if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) {
- if (CAT->getSize().getZExtValue())
- UpperBound = CAT->getSize().getZExtValue() - 1;
- } else
- // This is an unbounded array. Use Low = 1, Hi = 0 to express such
- // arrays.
- LowerBound = 1;
-
- // FIXME: Verify this is right for VLAs.
- Subscripts.push_back(DBuilder.getOrCreateSubrange(LowerBound,
- UpperBound));
- EltTy = Ty->getElementType();
- }
}
llvm::DIArray SubscriptArray = DBuilder.getOrCreateArray(Subscripts);
@@ -1537,7 +1538,7 @@ llvm::DIType CGDebugInfo::CreateType(const MemberPointerType *Ty,
uint64_t FieldOffset = 0;
llvm::Value *ElementTypes[2];
- // FIXME: This should probably be a function type instead.
+ // FIXME: This should be a DW_TAG_pointer_to_member type.
ElementTypes[0] =
DBuilder.createMemberType(U, "ptr", U, 0,
Info.first, Info.second, FieldOffset, 0,
@@ -1565,7 +1566,6 @@ llvm::DIType CGDebugInfo::CreateType(const AtomicType *Ty,
/// CreateEnumType - get enumeration type.
llvm::DIType CGDebugInfo::CreateEnumType(const EnumDecl *ED) {
- llvm::DIFile Unit = getOrCreateFile(ED->getLocation());
SmallVector<llvm::Value *, 16> Enumerators;
// Create DIEnumerator elements for each enumerator.
@@ -1590,9 +1590,13 @@ llvm::DIType CGDebugInfo::CreateEnumType(const EnumDecl *ED) {
}
llvm::DIDescriptor EnumContext =
getContextDescriptor(cast<Decl>(ED->getDeclContext()));
+ llvm::DIType ClassTy = ED->isScopedUsingClassTag() ?
+ getOrCreateType(ED->getIntegerType(), DefUnit) : llvm::DIType();
+ unsigned Flags = !ED->isCompleteDefinition() ? llvm::DIDescriptor::FlagFwdDecl : 0;
llvm::DIType DbgTy =
DBuilder.createEnumerationType(EnumContext, ED->getName(), DefUnit, Line,
- Size, Align, EltArray);
+ Size, Align, EltArray,
+ ClassTy, Flags);
return DbgTy;
}
@@ -1626,8 +1630,13 @@ static QualType UnwrapTypeForDebugInfo(QualType T) {
case Type::Paren:
T = cast<ParenType>(T)->getInnerType();
break;
- case Type::SubstTemplateTypeParm:
+ case Type::SubstTemplateTypeParm: {
+ // We need to keep the qualifiers handy since getReplacementType()
+ // will strip them away.
+ unsigned Quals = T.getLocalFastQualifiers();
T = cast<SubstTemplateTypeParmType>(T)->getReplacementType();
+ T.addFastQualifiers(Quals);
+ }
break;
case Type::Auto:
T = cast<AutoType>(T)->getDeducedType();
@@ -1686,23 +1695,26 @@ llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) {
// Unwrap the type as needed for debug information.
Ty = UnwrapTypeForDebugInfo(Ty);
-
+
llvm::DIType T = getCompletedTypeOrNull(Ty);
- if (T.Verify()) return T;
+ if (T.Verify())
+ return T;
// Otherwise create the type.
llvm::DIType Res = CreateTypeNode(Ty, Unit);
llvm::DIType TC = getTypeOrNull(Ty);
if (TC.Verify() && TC.isForwardDecl())
- ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), TC));
+ ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(),
+ static_cast<llvm::Value*>(TC)));
// And update the type cache.
TypeCache[Ty.getAsOpaquePtr()] = Res;
if (!Res.isForwardDecl())
CompletedTypeCache[Ty.getAsOpaquePtr()] = Res;
+
return Res;
}
@@ -1807,7 +1819,8 @@ llvm::DIType CGDebugInfo::getOrCreateLimitedType(QualType Ty,
llvm::DIType Res = CreateLimitedTypeNode(Ty, Unit);
if (T.Verify() && T.isForwardDecl())
- ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), T));
+ ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(),
+ static_cast<llvm::Value*>(T)));
// And update the type cache.
TypeCache[Ty.getAsOpaquePtr()] = Res;
@@ -1824,7 +1837,7 @@ llvm::DIType CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
StringRef RDName = RD->getName();
llvm::DIDescriptor RDContext;
- if (CGM.getCodeGenOpts().LimitDebugInfo)
+ if (CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo)
RDContext = createContextChain(cast<Decl>(RD->getDeclContext()));
else
RDContext = getContextDescriptor(cast<Decl>(RD->getDeclContext()));
@@ -1953,6 +1966,7 @@ llvm::DISubprogram CGDebugInfo::getFunctionDeclaration(const Decl *D) {
llvm::DIType CGDebugInfo::getOrCreateFunctionType(const Decl * D,
QualType FnType,
llvm::DIFile F) {
+
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D))
return getOrCreateMethodType(Method, F);
if (const ObjCMethodDecl *OMethod = dyn_cast<ObjCMethodDecl>(D)) {
@@ -2013,18 +2027,21 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType,
LinkageName = CGM.getMangledName(GD);
Flags |= llvm::DIDescriptor::FlagPrototyped;
}
- if (LinkageName == Name)
+ if (LinkageName == Name ||
+ CGM.getCodeGenOpts().DebugInfo <= CodeGenOptions::DebugLineTablesOnly)
LinkageName = StringRef();
- if (const NamespaceDecl *NSDecl =
- dyn_cast_or_null<NamespaceDecl>(FD->getDeclContext()))
- FDContext = getOrCreateNameSpace(NSDecl);
- else if (const RecordDecl *RDecl =
- dyn_cast_or_null<RecordDecl>(FD->getDeclContext()))
- FDContext = getContextDescriptor(cast<Decl>(RDecl->getDeclContext()));
+ if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) {
+ if (const NamespaceDecl *NSDecl =
+ dyn_cast_or_null<NamespaceDecl>(FD->getDeclContext()))
+ FDContext = getOrCreateNameSpace(NSDecl);
+ else if (const RecordDecl *RDecl =
+ dyn_cast_or_null<RecordDecl>(FD->getDeclContext()))
+ FDContext = getContextDescriptor(cast<Decl>(RDecl->getDeclContext()));
- // Collect template parameters.
- TParamsArray = CollectFunctionTemplateParams(FD, Unit);
+ // Collect template parameters.
+ TParamsArray = CollectFunctionTemplateParams(FD, Unit);
+ }
} else if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) {
Name = getObjCMethodName(OMD);
Flags |= llvm::DIDescriptor::FlagPrototyped;
@@ -2040,14 +2057,27 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType,
if (D->isImplicit())
Flags |= llvm::DIDescriptor::FlagArtificial;
- llvm::DISubprogram SPDecl = getFunctionDeclaration(D);
- llvm::DISubprogram SP =
- DBuilder.createFunction(FDContext, Name, LinkageName, Unit,
- LineNo, getOrCreateFunctionType(D, FnType, Unit),
- Fn->hasInternalLinkage(), true/*definition*/,
- getLineNumber(CurLoc),
- Flags, CGM.getLangOpts().Optimize, Fn,
- TParamsArray, SPDecl);
+ llvm::DIType DIFnType;
+ llvm::DISubprogram SPDecl;
+ if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) {
+ DIFnType = getOrCreateFunctionType(D, FnType, Unit);
+ SPDecl = getFunctionDeclaration(D);
+ } else {
+ // Create fake but valid subroutine type. Otherwise
+ // llvm::DISubprogram::Verify() would return false, and
+ // subprogram DIE will miss DW_AT_decl_file and
+ // DW_AT_decl_line fields.
+ SmallVector<llvm::Value*, 16> Elts;
+ llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts);
+ DIFnType = DBuilder.createSubroutineType(Unit, EltTypeArray);
+ }
+ llvm::DISubprogram SP;
+ SP = DBuilder.createFunction(FDContext, Name, LinkageName, Unit,
+ LineNo, DIFnType,
+ Fn->hasInternalLinkage(), true/*definition*/,
+ getLineNumber(CurLoc), Flags,
+ CGM.getLangOpts().Optimize,
+ Fn, TParamsArray, SPDecl);
// Push function on region stack.
llvm::MDNode *SPN = SP;
@@ -2205,6 +2235,7 @@ llvm::DIType CGDebugInfo::EmitTypeForVarWithBlocksAttr(const ValueDecl *VD,
void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag,
llvm::Value *Storage,
unsigned ArgNo, CGBuilderTy &Builder) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!");
llvm::DIFile Unit = getOrCreateFile(VD->getLocation());
@@ -2224,14 +2255,14 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag,
// If Storage is an aggregate returned as 'sret' then let debugger know
// about this.
if (Arg->hasStructRetAttr())
- Ty = DBuilder.createReferenceType(Ty);
+ Ty = DBuilder.createReferenceType(llvm::dwarf::DW_TAG_reference_type, Ty);
else if (CXXRecordDecl *Record = VD->getType()->getAsCXXRecordDecl()) {
// If an aggregate variable has non trivial destructor or non trivial copy
// constructor than it is pass indirectly. Let debug info know about this
// by using reference of the aggregate type as a argument type.
if (!Record->hasTrivialCopyConstructor() ||
!Record->hasTrivialDestructor())
- Ty = DBuilder.createReferenceType(Ty);
+ Ty = DBuilder.createReferenceType(llvm::dwarf::DW_TAG_reference_type, Ty);
}
}
@@ -2272,8 +2303,25 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag,
DBuilder.insertDeclare(Storage, D, Builder.GetInsertBlock());
Call->setDebugLoc(llvm::DebugLoc::get(Line, Column, Scope));
return;
- }
+ } else if (isa<VariableArrayType>(VD->getType())) {
+ // These are "complex" variables in that they need an op_deref.
// Create the descriptor for the variable.
+ llvm::Value *Addr = llvm::ConstantInt::get(CGM.Int64Ty,
+ llvm::DIBuilder::OpDeref);
+ llvm::DIVariable D =
+ DBuilder.createComplexVariable(Tag,
+ llvm::DIDescriptor(Scope),
+ Name, Unit, Line, Ty,
+ Addr, ArgNo);
+
+ // Insert an llvm.dbg.declare into the current block.
+ llvm::Instruction *Call =
+ DBuilder.insertDeclare(Storage, D, Builder.GetInsertBlock());
+ Call->setDebugLoc(llvm::DebugLoc::get(Line, Column, Scope));
+ return;
+ }
+
+ // Create the descriptor for the variable.
llvm::DIVariable D =
DBuilder.createLocalVariable(Tag, llvm::DIDescriptor(Scope),
Name, Unit, Line, Ty,
@@ -2321,12 +2369,14 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag,
void CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD,
llvm::Value *Storage,
CGBuilderTy &Builder) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
EmitDeclare(VD, llvm::dwarf::DW_TAG_auto_variable, Storage, 0, Builder);
}
void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable(
const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder,
const CGBlockInfo &blockInfo) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!");
if (Builder.GetInsertBlock() == 0)
@@ -2387,6 +2437,7 @@ void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable(
void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI,
unsigned ArgNo,
CGBuilderTy &Builder) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
EmitDeclare(VD, llvm::dwarf::DW_TAG_arg_variable, AI, ArgNo, Builder);
}
@@ -2403,6 +2454,7 @@ namespace {
void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block,
llvm::Value *addr,
CGBuilderTy &Builder) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
ASTContext &C = CGM.getContext();
const BlockDecl *blockDecl = block.getBlockDecl();
@@ -2547,6 +2599,7 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block,
/// EmitGlobalVariable - Emit information about a global variable.
void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
const VarDecl *D) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
// Create global variable debug descriptor.
llvm::DIFile Unit = getOrCreateFile(D->getLocation());
unsigned LineNo = getLineNumber(D->getLocation());
@@ -2557,9 +2610,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
if (T->isIncompleteArrayType()) {
// CodeGen turns int[] into int[1] so we'll do the same here.
- llvm::APSInt ConstVal(32);
-
- ConstVal = 1;
+ llvm::APInt ConstVal(32, 1);
QualType ET = CGM.getContext().getAsArrayType(T)->getElementType();
T = CGM.getContext().getConstantArrayType(ET, ConstVal,
@@ -2582,6 +2633,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
/// EmitGlobalVariable - Emit information about an objective-c interface.
void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
ObjCInterfaceDecl *ID) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
// Create global variable debug descriptor.
llvm::DIFile Unit = getOrCreateFile(ID->getLocation());
unsigned LineNo = getLineNumber(ID->getLocation());
@@ -2592,9 +2644,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
if (T->isIncompleteArrayType()) {
// CodeGen turns int[] into int[1] so we'll do the same here.
- llvm::APSInt ConstVal(32);
-
- ConstVal = 1;
+ llvm::APInt ConstVal(32, 1);
QualType ET = CGM.getContext().getAsArrayType(T)->getElementType();
T = CGM.getContext().getConstantArrayType(ET, ConstVal,
@@ -2609,13 +2659,15 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
/// EmitGlobalVariable - Emit global variable's debug info.
void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD,
llvm::Constant *Init) {
+ assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo);
// Create the descriptor for the variable.
llvm::DIFile Unit = getOrCreateFile(VD->getLocation());
StringRef Name = VD->getName();
llvm::DIType Ty = getOrCreateType(VD->getType(), Unit);
if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(VD)) {
- if (const EnumDecl *ED = dyn_cast<EnumDecl>(ECD->getDeclContext()))
- Ty = CreateEnumType(ED);
+ const EnumDecl *ED = cast<EnumDecl>(ECD->getDeclContext());
+ assert(isa<EnumType>(ED->getTypeForDecl()) && "Enum without EnumType?");
+ Ty = getOrCreateType(QualType(ED->getTypeForDecl(), 0), Unit);
}
// Do not use DIGlobalVariable for enums.
if (Ty.getTag() == llvm::dwarf::DW_TAG_enumeration_type)
diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h
index ec7705c..44cc49a 100644
--- a/lib/CodeGen/CGDebugInfo.h
+++ b/lib/CodeGen/CGDebugInfo.h
@@ -17,9 +17,9 @@
#include "clang/AST/Type.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/SourceLocation.h"
+#include "llvm/DebugInfo.h"
+#include "llvm/DIBuilder.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/Analysis/DebugInfo.h"
-#include "llvm/Analysis/DIBuilder.h"
#include "llvm/Support/ValueHandle.h"
#include "llvm/Support/Allocator.h"
@@ -30,6 +30,7 @@ namespace llvm {
}
namespace clang {
+ class CXXMethodDecl;
class VarDecl;
class ObjCInterfaceDecl;
class ClassTemplateSpecializationDecl;
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 6447779..be6638e 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -188,11 +188,15 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D,
new llvm::GlobalVariable(CGM.getModule(), LTy,
Ty.isConstant(getContext()), Linkage,
CGM.EmitNullConstant(D.getType()), Name, 0,
- D.isThreadSpecified(),
+ llvm::GlobalVariable::NotThreadLocal,
CGM.getContext().getTargetAddressSpace(Ty));
GV->setAlignment(getContext().getDeclAlign(&D).getQuantity());
if (Linkage != llvm::GlobalValue::InternalLinkage)
GV->setVisibility(CurFn->getVisibility());
+
+ if (D.isThreadSpecified())
+ CGM.setTLSMode(GV, D);
+
return GV;
}
@@ -239,7 +243,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
OldGV->isConstant(),
OldGV->getLinkage(), Init, "",
/*InsertBefore*/ OldGV,
- D.isThreadSpecified(),
+ OldGV->getThreadLocalMode(),
CGM.getContext().getTargetAddressSpace(D.getType()));
GV->setVisibility(OldGV->getVisibility());
@@ -326,7 +330,8 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D,
// Emit global variable debug descriptor for static vars.
CGDebugInfo *DI = getDebugInfo();
- if (DI) {
+ if (DI &&
+ CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) {
DI->setLocation(D.getLocation());
DI->EmitGlobalVariable(var, &D);
}
@@ -489,6 +494,14 @@ static bool isAccessedBy(const VarDecl &var, const Stmt *s) {
if (const DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e))
return (ref->getDecl() == &var);
+ if (const BlockExpr *be = dyn_cast<BlockExpr>(e)) {
+ const BlockDecl *block = be->getBlockDecl();
+ for (BlockDecl::capture_const_iterator i = block->capture_begin(),
+ e = block->capture_end(); i != e; ++i) {
+ if (i->getVariable() == &var)
+ return true;
+ }
+ }
}
for (Stmt::const_child_range children = s->children(); children; ++children)
@@ -897,11 +910,14 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
// Emit debug info for local var declaration.
if (HaveInsertPoint())
if (CGDebugInfo *DI = getDebugInfo()) {
- DI->setLocation(D.getLocation());
- if (Target.useGlobalsForAutomaticVariables()) {
- DI->EmitGlobalVariable(static_cast<llvm::GlobalVariable *>(DeclPtr), &D);
- } else
- DI->EmitDeclareOfAutoVariable(&D, DeclPtr, Builder);
+ if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) {
+ DI->setLocation(D.getLocation());
+ if (Target.useGlobalsForAutomaticVariables()) {
+ DI->EmitGlobalVariable(static_cast<llvm::GlobalVariable *>(DeclPtr),
+ &D);
+ } else
+ DI->EmitDeclareOfAutoVariable(&D, DeclPtr, Builder);
+ }
}
if (D.hasAttr<AnnotateAttr>())
@@ -1054,7 +1070,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
llvm::GlobalVariable *GV =
new llvm::GlobalVariable(CGM.getModule(), constant->getType(), true,
llvm::GlobalValue::PrivateLinkage,
- constant, Name, 0, false, 0);
+ constant, Name);
GV->setAlignment(alignment.getQuantity());
GV->setUnnamedAddr(true);
@@ -1477,8 +1493,11 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg,
LocalDeclMap[&D] = Arg;
if (CGDebugInfo *DI = getDebugInfo()) {
- DI->setLocation(D.getLocation());
- DI->EmitDeclareOfBlockLiteralArgVariable(*BlockInfo, Arg, Builder);
+ if (CGM.getCodeGenOpts().DebugInfo >=
+ CodeGenOptions::LimitedDebugInfo) {
+ DI->setLocation(D.getLocation());
+ DI->EmitDeclareOfBlockLiteralArgVariable(*BlockInfo, Arg, Builder);
+ }
}
return;
@@ -1556,8 +1575,11 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg,
DMEntry = DeclPtr;
// Emit debug info for param declaration.
- if (CGDebugInfo *DI = getDebugInfo())
- DI->EmitDeclareOfArgVariable(&D, DeclPtr, ArgNo, Builder);
+ if (CGDebugInfo *DI = getDebugInfo()) {
+ if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) {
+ DI->EmitDeclareOfArgVariable(&D, DeclPtr, ArgNo, Builder);
+ }
+ }
if (D.hasAttr<AnnotateAttr>())
EmitVarAnnotations(&D, DeclPtr);
diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp
index 10f0b83..492b95a 100644
--- a/lib/CodeGen/CGDeclCXX.cpp
+++ b/lib/CodeGen/CGDeclCXX.cpp
@@ -98,7 +98,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
argument = llvm::Constant::getNullValue(CGF.Int8PtrTy);
}
- CGF.EmitCXXGlobalDtorRegistration(function, argument);
+ CGM.getCXXABI().registerGlobalDtor(CGF, function, argument);
}
/// Emit code to cause the variable at the given address to be considered as
@@ -145,39 +145,6 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,
EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, Alignment, T);
}
-/// Register a global destructor using __cxa_atexit.
-static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
- llvm::Constant *dtor,
- llvm::Constant *addr) {
- // We're assuming that the destructor function is something we can
- // reasonably call with the default CC. Go ahead and cast it to the
- // right prototype.
- llvm::Type *dtorTy =
- llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo();
-
- // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
- llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy };
- llvm::FunctionType *atexitTy =
- llvm::FunctionType::get(CGF.IntTy, paramTys, false);
-
- // Fetch the actual function.
- llvm::Constant *atexit =
- CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");
- if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))
- fn->setDoesNotThrow();
-
- // Create a variable that binds the atexit to this shared object.
- llvm::Constant *handle =
- CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle");
-
- llvm::Value *args[] = {
- llvm::ConstantExpr::getBitCast(dtor, dtorTy),
- llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy),
- handle
- };
- CGF.Builder.CreateCall(atexit, args);
-}
-
static llvm::Function *
CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
llvm::FunctionType *ty,
@@ -212,43 +179,22 @@ static llvm::Constant *createAtExitStub(CodeGenModule &CGM,
return fn;
}
-/// Register a global destructor using atexit.
-static void emitGlobalDtorWithAtExit(CodeGenFunction &CGF,
- llvm::Constant *dtor,
- llvm::Constant *addr) {
+/// Register a global destructor using the C atexit runtime function.
+void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtor,
+ llvm::Constant *addr) {
// Create a function which calls the destructor.
- llvm::Constant *dtorStub = createAtExitStub(CGF.CGM, dtor, addr);
+ llvm::Constant *dtorStub = createAtExitStub(CGM, dtor, addr);
// extern "C" int atexit(void (*f)(void));
llvm::FunctionType *atexitTy =
- llvm::FunctionType::get(CGF.IntTy, dtorStub->getType(), false);
+ llvm::FunctionType::get(IntTy, dtorStub->getType(), false);
llvm::Constant *atexit =
- CGF.CGM.CreateRuntimeFunction(atexitTy, "atexit");
+ CGM.CreateRuntimeFunction(atexitTy, "atexit");
if (llvm::Function *atexitFn = dyn_cast<llvm::Function>(atexit))
atexitFn->setDoesNotThrow();
- CGF.Builder.CreateCall(atexit, dtorStub);
-}
-
-void CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *dtor,
- llvm::Constant *addr) {
- // Use __cxa_atexit if available.
- if (CGM.getCodeGenOpts().CXAAtExit) {
- emitGlobalDtorWithCXAAtExit(*this, dtor, addr);
- return;
- }
-
- // In Apple kexts, we want to add a global destructor entry.
- // FIXME: shouldn't this be guarded by some variable?
- if (CGM.getContext().getLangOpts().AppleKext) {
- // Generate a global destructor entry.
- CGM.AddCXXDtorEntry(dtor, addr);
- return;
- }
-
- // Otherwise, we just use atexit.
- emitGlobalDtorWithAtExit(*this, dtor, addr);
+ Builder.CreateCall(atexit, dtorStub)->setDoesNotThrow();
}
void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
@@ -282,6 +228,9 @@ CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
if (!CGM.getLangOpts().Exceptions)
Fn->setDoesNotThrow();
+ if (CGM.getLangOpts().AddressSanitizer)
+ Fn->addFnAttr(llvm::Attribute::AddressSafety);
+
return Fn;
}
@@ -372,9 +321,12 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
const VarDecl *D,
llvm::GlobalVariable *Addr,
bool PerformInit) {
- StartFunction(GlobalDecl(), getContext().VoidTy, Fn,
+ if (CGM.getModuleDebugInfo() && !D->hasAttr<NoDebugAttr>())
+ DebugInfo = CGM.getModuleDebugInfo();
+
+ StartFunction(GlobalDecl(D), getContext().VoidTy, Fn,
getTypes().arrangeNullaryFunction(),
- FunctionArgList(), SourceLocation());
+ FunctionArgList(), D->getInit()->getExprLoc());
// Use guarded initialization if the global variable is weak. This
// occurs for, e.g., instantiated static data members and
diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp
index 95e0030..ba9c296 100644
--- a/lib/CodeGen/CGException.cpp
+++ b/lib/CodeGen/CGException.cpp
@@ -126,7 +126,7 @@ static llvm::Constant *getTerminateFn(CodeGenFunction &CGF) {
if (CGF.getLangOpts().CPlusPlus)
name = "_ZSt9terminatev"; // FIXME: mangling!
else if (CGF.getLangOpts().ObjC1 &&
- CGF.CGM.getCodeGenOpts().ObjCRuntimeHasTerminate)
+ CGF.getLangOpts().ObjCRuntime.hasTerminate())
name = "objc_terminate";
else
name = "abort";
@@ -180,12 +180,18 @@ static const EHPersonality &getCPersonality(const LangOptions &L) {
}
static const EHPersonality &getObjCPersonality(const LangOptions &L) {
- if (L.NeXTRuntime) {
- if (L.ObjCNonFragileABI) return EHPersonality::NeXT_ObjC;
- else return getCPersonality(L);
- } else {
+ switch (L.ObjCRuntime.getKind()) {
+ case ObjCRuntime::FragileMacOSX:
+ return getCPersonality(L);
+ case ObjCRuntime::MacOSX:
+ case ObjCRuntime::iOS:
+ return EHPersonality::NeXT_ObjC;
+ case ObjCRuntime::GNUstep:
+ case ObjCRuntime::GCC:
+ case ObjCRuntime::ObjFW:
return EHPersonality::GNU_ObjC;
}
+ llvm_unreachable("bad runtime kind");
}
static const EHPersonality &getCXXPersonality(const LangOptions &L) {
@@ -198,22 +204,28 @@ static const EHPersonality &getCXXPersonality(const LangOptions &L) {
/// Determines the personality function to use when both C++
/// and Objective-C exceptions are being caught.
static const EHPersonality &getObjCXXPersonality(const LangOptions &L) {
+ switch (L.ObjCRuntime.getKind()) {
// The ObjC personality defers to the C++ personality for non-ObjC
// handlers. Unlike the C++ case, we use the same personality
// function on targets using (backend-driven) SJLJ EH.
- if (L.NeXTRuntime) {
- if (L.ObjCNonFragileABI)
- return EHPersonality::NeXT_ObjC;
+ case ObjCRuntime::MacOSX:
+ case ObjCRuntime::iOS:
+ return EHPersonality::NeXT_ObjC;
- // In the fragile ABI, just use C++ exception handling and hope
- // they're not doing crazy exception mixing.
- else
- return getCXXPersonality(L);
- }
+ // In the fragile ABI, just use C++ exception handling and hope
+ // they're not doing crazy exception mixing.
+ case ObjCRuntime::FragileMacOSX:
+ return getCXXPersonality(L);
- // The GNU runtime's personality function inherently doesn't support
+ // The GCC runtime's personality function inherently doesn't support
// mixed EH. Use the C++ personality just to avoid returning null.
- return EHPersonality::GNU_ObjCXX;
+ case ObjCRuntime::GCC:
+ case ObjCRuntime::ObjFW: // XXX: this will change soon
+ return EHPersonality::GNU_ObjC;
+ case ObjCRuntime::GNUstep:
+ return EHPersonality::GNU_ObjCXX;
+ }
+ llvm_unreachable("bad runtime kind");
}
const EHPersonality &EHPersonality::get(const LangOptions &L) {
@@ -1127,14 +1139,6 @@ static void BeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *S) {
CGF.EmitAutoVarCleanups(var);
}
-namespace {
- struct CallRethrow : EHScopeStack::Cleanup {
- void Emit(CodeGenFunction &CGF, Flags flags) {
- CGF.EmitCallOrInvoke(getReThrowFn(CGF));
- }
- };
-}
-
/// Emit the structure of the dispatch block for the given catch scope.
/// It is an invariant that the dispatch block already exists.
static void emitCatchDispatchBlock(CodeGenFunction &CGF,
@@ -1246,11 +1250,12 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
if (HaveInsertPoint())
Builder.CreateBr(ContBB);
- // Determine if we need an implicit rethrow for all these catch handlers.
- bool ImplicitRethrow = false;
+ // Determine if we need an implicit rethrow for all these catch handlers;
+ // see the comment below.
+ bool doImplicitRethrow = false;
if (IsFnTryBlock)
- ImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) ||
- isa<CXXConstructorDecl>(CurCodeDecl);
+ doImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) ||
+ isa<CXXConstructorDecl>(CurCodeDecl);
// Perversely, we emit the handlers backwards precisely because we
// want them to appear in source order. In all of these cases, the
@@ -1273,15 +1278,24 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
// Initialize the catch variable and set up the cleanups.
BeginCatch(*this, C);
- // If there's an implicit rethrow, push a normal "cleanup" to call
- // _cxa_rethrow. This needs to happen before __cxa_end_catch is
- // called, and so it is pushed after BeginCatch.
- if (ImplicitRethrow)
- EHStack.pushCleanup<CallRethrow>(NormalCleanup);
-
// Perform the body of the catch.
EmitStmt(C->getHandlerBlock());
+ // [except.handle]p11:
+ // The currently handled exception is rethrown if control
+ // reaches the end of a handler of the function-try-block of a
+ // constructor or destructor.
+
+ // It is important that we only do this on fallthrough and not on
+ // return. Note that it's illegal to put a return in a
+ // constructor function-try-block's catch handler (p14), so this
+ // really only applies to destructors.
+ if (doImplicitRethrow && HaveInsertPoint()) {
+ EmitCallOrInvoke(getReThrowFn(*this));
+ Builder.CreateUnreachable();
+ Builder.ClearInsertionPoint();
+ }
+
// Fall out through the catch cleanups.
CatchScope.ForceCleanup();
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 5f2b1f0..ecee7b4 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -21,10 +21,11 @@
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/ConvertUTF.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/Intrinsics.h"
#include "llvm/LLVMContext.h"
-#include "llvm/Support/MDBuilder.h"
+#include "llvm/MDBuilder.h"
#include "llvm/Target/TargetData.h"
using namespace clang;
using namespace CodeGen;
@@ -108,15 +109,18 @@ void CodeGenFunction::EmitIgnoredExpr(const Expr *E) {
/// can have any type. The result is returned as an RValue struct.
/// If this is an aggregate expression, AggSlot indicates where the
/// result should be returned.
-RValue CodeGenFunction::EmitAnyExpr(const Expr *E, AggValueSlot AggSlot,
- bool IgnoreResult) {
+RValue CodeGenFunction::EmitAnyExpr(const Expr *E,
+ AggValueSlot aggSlot,
+ bool ignoreResult) {
if (!hasAggregateLLVMType(E->getType()))
- return RValue::get(EmitScalarExpr(E, IgnoreResult));
+ return RValue::get(EmitScalarExpr(E, ignoreResult));
else if (E->getType()->isAnyComplexType())
- return RValue::getComplex(EmitComplexExpr(E, IgnoreResult, IgnoreResult));
+ return RValue::getComplex(EmitComplexExpr(E, ignoreResult, ignoreResult));
- EmitAggExpr(E, AggSlot, IgnoreResult);
- return AggSlot.asRValue();
+ if (!ignoreResult && aggSlot.isIgnored())
+ aggSlot = CreateAggTemp(E->getType(), "agg-temp");
+ EmitAggExpr(E, aggSlot);
+ return aggSlot.asRValue();
}
/// EmitAnyExprToTemp - Similary to EmitAnyExpr(), however, the result will
@@ -156,7 +160,11 @@ namespace {
/// \brief An adjustment to be made to the temporary created when emitting a
/// reference binding, which accesses a particular subobject of that temporary.
struct SubobjectAdjustment {
- enum { DerivedToBaseAdjustment, FieldAdjustment } Kind;
+ enum {
+ DerivedToBaseAdjustment,
+ FieldAdjustment,
+ MemberPointerAdjustment
+ } Kind;
union {
struct {
@@ -165,6 +173,11 @@ namespace {
} DerivedToBase;
FieldDecl *Field;
+
+ struct {
+ const MemberPointerType *MPT;
+ llvm::Value *Ptr;
+ } Ptr;
};
SubobjectAdjustment(const CastExpr *BasePath,
@@ -178,6 +191,12 @@ namespace {
: Kind(FieldAdjustment) {
this->Field = Field;
}
+
+ SubobjectAdjustment(const MemberPointerType *MPT, llvm::Value *Ptr)
+ : Kind(MemberPointerAdjustment) {
+ this->Ptr.MPT = MPT;
+ this->Ptr.Ptr = Ptr;
+ }
};
}
@@ -345,6 +364,15 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
continue;
}
}
+ } else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+ if (BO->isPtrMemOp()) {
+ assert(BO->getLHS()->isRValue());
+ E = BO->getLHS();
+ const MemberPointerType *MPT =
+ BO->getRHS()->getType()->getAs<MemberPointerType>();
+ llvm::Value *Ptr = CGF.EmitScalarExpr(BO->getRHS());
+ Adjustments.push_back(SubobjectAdjustment(MPT, Ptr));
+ }
}
if (const OpaqueValueExpr *opaque = dyn_cast<OpaqueValueExpr>(E))
@@ -417,6 +445,11 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
break;
}
+ case SubobjectAdjustment::MemberPointerAdjustment: {
+ Object = CGF.CGM.getCXXABI().EmitMemberDataPointerAddress(
+ CGF, Object, Adjustment.Ptr.Ptr, Adjustment.Ptr.MPT);
+ break;
+ }
}
}
@@ -462,7 +495,7 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E,
if (ReferenceTemporaryDtor) {
llvm::Constant *DtorFn =
CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete);
- EmitCXXGlobalDtorRegistration(DtorFn,
+ CGM.getCXXABI().registerGlobalDtor(*this, DtorFn,
cast<llvm::Constant>(ReferenceTemporary));
} else {
assert(!ObjCARCReferenceLifetimeType.isNull());
@@ -525,15 +558,9 @@ void CodeGenFunction::EmitCheck(llvm::Value *Address, unsigned Size) {
llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::objectsize, IntPtrTy);
- // In time, people may want to control this and use a 1 here.
- llvm::Value *Arg = Builder.getFalse();
- llvm::Value *C = Builder.CreateCall2(F, Address, Arg);
+ llvm::Value *Min = Builder.getFalse();
+ llvm::Value *C = Builder.CreateCall2(F, Address, Min);
llvm::BasicBlock *Cont = createBasicBlock();
- llvm::BasicBlock *Check = createBasicBlock();
- llvm::Value *NegativeOne = llvm::ConstantInt::get(IntPtrTy, -1ULL);
- Builder.CreateCondBr(Builder.CreateICmpEQ(C, NegativeOne), Cont, Check);
-
- EmitBlock(Check);
Builder.CreateCondBr(Builder.CreateICmpUGE(C,
llvm::ConstantInt::get(IntPtrTy, Size)),
Cont, getTrapBB());
@@ -676,10 +703,7 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
case Expr::PseudoObjectExprClass:
return EmitPseudoObjectLValue(cast<PseudoObjectExpr>(E));
case Expr::InitListExprClass:
- assert(cast<InitListExpr>(E)->getNumInits() == 1 &&
- "Only single-element init list can be lvalue.");
- return EmitLValue(cast<InitListExpr>(E)->getInit(0));
-
+ return EmitInitListLValue(cast<InitListExpr>(E));
case Expr::CXXTemporaryObjectExprClass:
case Expr::CXXConstructExprClass:
return EmitCXXConstructLValue(cast<CXXConstructExpr>(E));
@@ -880,7 +904,6 @@ llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
CGM.getCodeGenOpts().StrictEnums &&
!ET->getDecl()->isFixed());
bool IsBool = hasBooleanRepresentation(Ty);
- llvm::Type *LTy;
if (!IsBool && !IsRegularCPlusPlusEnum)
return NULL;
@@ -889,10 +912,9 @@ llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
if (IsBool) {
Min = llvm::APInt(8, 0);
End = llvm::APInt(8, 2);
- LTy = Int8Ty;
} else {
const EnumDecl *ED = ET->getDecl();
- LTy = ConvertTypeForMem(ED->getIntegerType());
+ llvm::Type *LTy = ConvertTypeForMem(ED->getIntegerType());
unsigned Bitwidth = LTy->getScalarSizeInBits();
unsigned NumNegativeBits = ED->getNumNegativeBits();
unsigned NumPositiveBits = ED->getNumPositiveBits();
@@ -1028,6 +1050,9 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) {
llvm::Value *Res = 0;
for (unsigned i = 0, e = Info.getNumComponents(); i != e; ++i) {
const CGBitFieldInfo::AccessInfo &AI = Info.getComponent(i);
+ CharUnits AccessAlignment = AI.AccessAlignment;
+ if (!LV.getAlignment().isZero())
+ AccessAlignment = std::min(AccessAlignment, LV.getAlignment());
// Get the field pointer.
llvm::Value *Ptr = LV.getBitFieldBaseAddr();
@@ -1051,8 +1076,7 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) {
// Perform the load.
llvm::LoadInst *Load = Builder.CreateLoad(Ptr, LV.isVolatileQualified());
- if (!AI.AccessAlignment.isZero())
- Load->setAlignment(AI.AccessAlignment.getQuantity());
+ Load->setAlignment(AccessAlignment.getQuantity());
// Shift out unused low bits and mask out unused high bits.
llvm::Value *Val = Load;
@@ -1251,6 +1275,9 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
// Iterate over the components, writing each piece to memory.
for (unsigned i = 0, e = Info.getNumComponents(); i != e; ++i) {
const CGBitFieldInfo::AccessInfo &AI = Info.getComponent(i);
+ CharUnits AccessAlignment = AI.AccessAlignment;
+ if (!Dst.getAlignment().isZero())
+ AccessAlignment = std::min(AccessAlignment, Dst.getAlignment());
// Get the field pointer.
llvm::Value *Ptr = Dst.getBitFieldBaseAddr();
@@ -1297,8 +1324,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
// If necessary, load and OR in bits that are outside of the bit-field.
if (AI.TargetBitWidth != AI.AccessWidth) {
llvm::LoadInst *Load = Builder.CreateLoad(Ptr, Dst.isVolatileQualified());
- if (!AI.AccessAlignment.isZero())
- Load->setAlignment(AI.AccessAlignment.getQuantity());
+ Load->setAlignment(AccessAlignment.getQuantity());
// Compute the mask for zeroing the bits that are part of the bit-field.
llvm::APInt InvMask =
@@ -1312,8 +1338,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
// Write the value.
llvm::StoreInst *Store = Builder.CreateStore(Val, Ptr,
Dst.isVolatileQualified());
- if (!AI.AccessAlignment.isZero())
- Store->setAlignment(AI.AccessAlignment.getQuantity());
+ Store->setAlignment(AccessAlignment.getQuantity());
}
}
@@ -1683,6 +1708,39 @@ LValue CodeGenFunction::EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E) {
E->getType());
}
+static llvm::Constant*
+GetAddrOfConstantWideString(StringRef Str,
+ const char *GlobalName,
+ ASTContext &Context,
+ QualType Ty, SourceLocation Loc,
+ CodeGenModule &CGM) {
+
+ StringLiteral *SL = StringLiteral::Create(Context,
+ Str,
+ StringLiteral::Wide,
+ /*Pascal = */false,
+ Ty, Loc);
+ llvm::Constant *C = CGM.GetConstantArrayFromStringLiteral(SL);
+ llvm::GlobalVariable *GV =
+ new llvm::GlobalVariable(CGM.getModule(), C->getType(),
+ !CGM.getLangOpts().WritableStrings,
+ llvm::GlobalValue::PrivateLinkage,
+ C, GlobalName);
+ const unsigned WideAlignment =
+ Context.getTypeAlignInChars(Ty).getQuantity();
+ GV->setAlignment(WideAlignment);
+ return GV;
+}
+
+static void ConvertUTF8ToWideString(unsigned CharByteWidth, StringRef Source,
+ SmallString<32>& Target) {
+ Target.resize(CharByteWidth * (Source.size() + 1));
+ char* ResultPtr = &Target[0];
+ bool success = ConvertUTF8toWide(CharByteWidth, Source, ResultPtr);
+ (void)success;
+ assert(success);
+ Target.resize(ResultPtr - &Target[0]);
+}
LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
switch (E->getIdentType()) {
@@ -1691,11 +1749,12 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
case PredefinedExpr::Func:
case PredefinedExpr::Function:
+ case PredefinedExpr::LFunction:
case PredefinedExpr::PrettyFunction: {
- unsigned Type = E->getIdentType();
+ unsigned IdentType = E->getIdentType();
std::string GlobalVarName;
- switch (Type) {
+ switch (IdentType) {
default: llvm_unreachable("Invalid type");
case PredefinedExpr::Func:
GlobalVarName = "__func__.";
@@ -1703,6 +1762,9 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
case PredefinedExpr::Function:
GlobalVarName = "__FUNCTION__.";
break;
+ case PredefinedExpr::LFunction:
+ GlobalVarName = "L__FUNCTION__.";
+ break;
case PredefinedExpr::PrettyFunction:
GlobalVarName = "__PRETTY_FUNCTION__.";
break;
@@ -1720,10 +1782,27 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
std::string FunctionName =
(isa<BlockDecl>(CurDecl)
? FnName.str()
- : PredefinedExpr::ComputeName((PredefinedExpr::IdentType)Type, CurDecl));
-
- llvm::Constant *C =
- CGM.GetAddrOfConstantCString(FunctionName, GlobalVarName.c_str());
+ : PredefinedExpr::ComputeName((PredefinedExpr::IdentType)IdentType,
+ CurDecl));
+
+ const Type* ElemType = E->getType()->getArrayElementTypeNoTypeQual();
+ llvm::Constant *C;
+ if (ElemType->isWideCharType()) {
+ SmallString<32> RawChars;
+ ConvertUTF8ToWideString(
+ getContext().getTypeSizeInChars(ElemType).getQuantity(),
+ FunctionName, RawChars);
+ C = GetAddrOfConstantWideString(RawChars,
+ GlobalVarName.c_str(),
+ getContext(),
+ E->getType(),
+ E->getLocation(),
+ CGM);
+ } else {
+ C = CGM.GetAddrOfConstantCString(FunctionName,
+ GlobalVarName.c_str(),
+ 1);
+ }
return MakeAddrLValue(C, E->getType());
}
}
@@ -1794,25 +1873,6 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E) {
// Extend or truncate the index type to 32 or 64-bits.
if (Idx->getType() != IntPtrTy)
Idx = Builder.CreateIntCast(Idx, IntPtrTy, IdxSigned, "idxprom");
-
- // FIXME: As llvm implements the object size checking, this can come out.
- if (CatchUndefined) {
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E->getBase())){
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ICE->getSubExpr())) {
- if (ICE->getCastKind() == CK_ArrayToPointerDecay) {
- if (const ConstantArrayType *CAT
- = getContext().getAsConstantArrayType(DRE->getType())) {
- llvm::APInt Size = CAT->getSize();
- llvm::BasicBlock *Cont = createBasicBlock("cont");
- Builder.CreateCondBr(Builder.CreateICmpULE(Idx,
- llvm::ConstantInt::get(Idx->getType(), Size)),
- Cont, getTrapBB());
- EmitBlock(Cont);
- }
- }
- }
- }
- }
// We know that the pointer points to a type of the correct size, unless the
// size is a VLA or Objective-C interface.
@@ -1996,43 +2056,17 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) {
llvm_unreachable("Unhandled member declaration!");
}
-LValue CodeGenFunction::EmitLValueForBitfield(llvm::Value *BaseValue,
- const FieldDecl *Field,
- unsigned CVRQualifiers) {
- const CGRecordLayout &RL =
- CGM.getTypes().getCGRecordLayout(Field->getParent());
- const CGBitFieldInfo &Info = RL.getBitFieldInfo(Field);
- return LValue::MakeBitfield(BaseValue, Info,
- Field->getType().withCVRQualifiers(CVRQualifiers));
-}
-
-/// EmitLValueForAnonRecordField - Given that the field is a member of
-/// an anonymous struct or union buried inside a record, and given
-/// that the base value is a pointer to the enclosing record, derive
-/// an lvalue for the ultimate field.
-LValue CodeGenFunction::EmitLValueForAnonRecordField(llvm::Value *BaseValue,
- const IndirectFieldDecl *Field,
- unsigned CVRQualifiers) {
- IndirectFieldDecl::chain_iterator I = Field->chain_begin(),
- IEnd = Field->chain_end();
- while (true) {
- QualType RecordTy =
- getContext().getTypeDeclType(cast<FieldDecl>(*I)->getParent());
- LValue LV = EmitLValueForField(MakeAddrLValue(BaseValue, RecordTy),
- cast<FieldDecl>(*I));
- if (++I == IEnd) return LV;
-
- assert(LV.isSimple());
- BaseValue = LV.getAddress();
- CVRQualifiers |= LV.getVRQualifiers();
- }
-}
-
LValue CodeGenFunction::EmitLValueForField(LValue base,
const FieldDecl *field) {
- if (field->isBitField())
- return EmitLValueForBitfield(base.getAddress(), field,
- base.getVRQualifiers());
+ if (field->isBitField()) {
+ const CGRecordLayout &RL =
+ CGM.getTypes().getCGRecordLayout(field->getParent());
+ const CGBitFieldInfo &Info = RL.getBitFieldInfo(field);
+ QualType fieldType =
+ field->getType().withCVRQualifiers(base.getVRQualifiers());
+ return LValue::MakeBitfield(base.getAddress(), Info, fieldType,
+ base.getAlignment());
+ }
const RecordDecl *rec = field->getParent();
QualType type = field->getType();
@@ -2144,7 +2178,10 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
llvm::Value *GlobalPtr = CGM.GetAddrOfConstantCompoundLiteral(E);
return MakeAddrLValue(GlobalPtr, E->getType());
}
-
+ if (E->getType()->isVariablyModifiedType())
+ // make sure to emit the VLA size.
+ EmitVariablyModifiedType(E->getType());
+
llvm::Value *DeclPtr = CreateMemTemp(E->getType(), ".compoundliteral");
const Expr *InitExpr = E->getInitializer();
LValue Result = MakeAddrLValue(DeclPtr, E->getType());
@@ -2155,6 +2192,16 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
return Result;
}
+LValue CodeGenFunction::EmitInitListLValue(const InitListExpr *E) {
+ if (!E->isGLValue())
+ // Initializing an aggregate temporary in C++11: T{...}.
+ return EmitAggExprToLValue(E);
+
+ // An lvalue initializer list must be initializing a reference.
+ assert(E->getNumInits() == 1 && "reference init with multiple values");
+ return EmitLValue(E->getInit(0));
+}
+
LValue CodeGenFunction::
EmitConditionalOperatorLValue(const AbstractConditionalOperator *expr) {
if (!expr->isGLValue()) {
@@ -2214,11 +2261,11 @@ EmitConditionalOperatorLValue(const AbstractConditionalOperator *expr) {
return MakeAddrLValue(phi, expr->getType());
}
-/// EmitCastLValue - Casts are never lvalues unless that cast is a dynamic_cast.
-/// If the cast is a dynamic_cast, we can have the usual lvalue result,
+/// EmitCastLValue - Casts are never lvalues unless that cast is to a reference
+/// type. If the cast is to a reference, we can have the usual lvalue result,
/// otherwise if a cast is needed by the code generator in an lvalue context,
/// then it must mean that we need the address of an aggregate in order to
-/// access one of its fields. This can happen for all the reasons that casts
+/// access one of its members. This can happen for all the reasons that casts
/// are permitted with aggregate result, including noop aggregate casts, and
/// cast from scalar to union.
LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
@@ -2648,7 +2695,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), ArgBeg, ArgEnd);
const CGFunctionInfo &FnInfo =
- CGM.getTypes().arrangeFunctionCall(Args, FnType);
+ CGM.getTypes().arrangeFreeFunctionCall(Args, FnType);
// C99 6.5.2.2p6:
// If the expression that denotes the called function has a type
@@ -3038,7 +3085,7 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) {
getContext().IntTy);
const CGFunctionInfo &FuncInfo =
- CGM.getTypes().arrangeFunctionCall(RetTy, Args,
+ CGM.getTypes().arrangeFreeFunctionCall(RetTy, Args,
FunctionType::ExtInfo(), RequiredArgs::All);
llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo);
llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName);
diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp
index 7b0e0f5..61f7362 100644
--- a/lib/CodeGen/CGExprAgg.cpp
+++ b/lib/CodeGen/CGExprAgg.cpp
@@ -34,7 +34,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
CodeGenFunction &CGF;
CGBuilderTy &Builder;
AggValueSlot Dest;
- bool IgnoreResult;
/// We want to use 'dest' as the return slot except under two
/// conditions:
@@ -56,12 +55,14 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
if (!Dest.isIgnored()) return Dest;
return CGF.CreateAggTemp(T, "agg.tmp.ensured");
}
+ void EnsureDest(QualType T) {
+ if (!Dest.isIgnored()) return;
+ Dest = CGF.CreateAggTemp(T, "agg.tmp.ensured");
+ }
public:
- AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest,
- bool ignore)
- : CGF(cgf), Builder(CGF.Builder), Dest(Dest),
- IgnoreResult(ignore) {
+ AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest)
+ : CGF(cgf), Builder(CGF.Builder), Dest(Dest) {
}
//===--------------------------------------------------------------------===//
@@ -74,9 +75,11 @@ public:
void EmitAggLoadOfLValue(const Expr *E);
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
- void EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore = false);
- void EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore = false,
- unsigned Alignment = 0);
+ void EmitFinalDestCopy(QualType type, const LValue &src);
+ void EmitFinalDestCopy(QualType type, RValue src,
+ CharUnits srcAlignment = CharUnits::Zero());
+ void EmitCopy(QualType type, const AggValueSlot &dest,
+ const AggValueSlot &src);
void EmitMoveFromReturnSlot(const Expr *E, RValue Src);
@@ -119,7 +122,7 @@ public:
if (E->getDecl()->getType()->isReferenceType()) {
if (CodeGenFunction::ConstantEmission result
= CGF.tryEmitAsConstant(E)) {
- EmitFinalDestCopy(E, result.getReferenceLValue(CGF, E));
+ EmitFinalDestCopy(E->getType(), result.getReferenceLValue(CGF, E));
return;
}
}
@@ -171,7 +174,7 @@ public:
void VisitPseudoObjectExpr(PseudoObjectExpr *E) {
if (E->isGLValue()) {
LValue LV = CGF.EmitPseudoObjectLValue(E);
- return EmitFinalDestCopy(E, LV);
+ return EmitFinalDestCopy(E->getType(), LV);
}
CGF.EmitPseudoObjectRValue(E, EnsureSlot(E->getType()));
@@ -198,7 +201,7 @@ public:
/// then loads the result into DestPtr.
void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) {
LValue LV = CGF.EmitLValue(E);
- EmitFinalDestCopy(E, LV);
+ EmitFinalDestCopy(E->getType(), LV);
}
/// \brief True if the given aggregate type requires special GC API calls.
@@ -228,7 +231,7 @@ bool AggExprEmitter::TypeRequiresGCollection(QualType T) {
/// If nothing interferes, this will cause the result to be emitted
/// directly into the return value slot. Otherwise, a final move
/// will be performed.
-void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) {
+void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue src) {
if (shouldUseDestForReturnSlot()) {
// Logically, Dest.getAddr() should equal Src.getAggregateAddr().
// The possibility of undef rvalues complicates that a lot,
@@ -236,61 +239,58 @@ void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) {
return;
}
- // Otherwise, do a final copy,
- assert(Dest.getAddr() != Src.getAggregateAddr());
- std::pair<CharUnits, CharUnits> TypeInfo =
+ // Otherwise, copy from there to the destination.
+ assert(Dest.getAddr() != src.getAggregateAddr());
+ std::pair<CharUnits, CharUnits> typeInfo =
CGF.getContext().getTypeInfoInChars(E->getType());
- CharUnits Alignment = std::min(TypeInfo.second, Dest.getAlignment());
- EmitFinalDestCopy(E, Src, /*Ignore*/ true, Alignment.getQuantity());
+ EmitFinalDestCopy(E->getType(), src, typeInfo.second);
}
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
-void AggExprEmitter::EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore,
- unsigned Alignment) {
- assert(Src.isAggregate() && "value must be aggregate value!");
+void AggExprEmitter::EmitFinalDestCopy(QualType type, RValue src,
+ CharUnits srcAlign) {
+ assert(src.isAggregate() && "value must be aggregate value!");
+ LValue srcLV = CGF.MakeAddrLValue(src.getAggregateAddr(), type, srcAlign);
+ EmitFinalDestCopy(type, srcLV);
+}
+/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
+void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src) {
// If Dest is ignored, then we're evaluating an aggregate expression
- // in a context (like an expression statement) that doesn't care
- // about the result. C says that an lvalue-to-rvalue conversion is
- // performed in these cases; C++ says that it is not. In either
- // case, we don't actually need to do anything unless the value is
- // volatile.
- if (Dest.isIgnored()) {
- if (!Src.isVolatileQualified() ||
- CGF.CGM.getLangOpts().CPlusPlus ||
- (IgnoreResult && Ignore))
- return;
+ // in a context that doesn't care about the result. Note that loads
+ // from volatile l-values force the existence of a non-ignored
+ // destination.
+ if (Dest.isIgnored())
+ return;
- // If the source is volatile, we must read from it; to do that, we need
- // some place to put it.
- Dest = CGF.CreateAggTemp(E->getType(), "agg.tmp");
- }
+ AggValueSlot srcAgg =
+ AggValueSlot::forLValue(src, AggValueSlot::IsDestructed,
+ needsGC(type), AggValueSlot::IsAliased);
+ EmitCopy(type, Dest, srcAgg);
+}
- if (Dest.requiresGCollection()) {
- CharUnits size = CGF.getContext().getTypeSizeInChars(E->getType());
- llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType());
- llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size.getQuantity());
+/// Perform a copy from the source into the destination.
+///
+/// \param type - the type of the aggregate being copied; qualifiers are
+/// ignored
+void AggExprEmitter::EmitCopy(QualType type, const AggValueSlot &dest,
+ const AggValueSlot &src) {
+ if (dest.requiresGCollection()) {
+ CharUnits sz = CGF.getContext().getTypeSizeInChars(type);
+ llvm::Value *size = llvm::ConstantInt::get(CGF.SizeTy, sz.getQuantity());
CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF,
- Dest.getAddr(),
- Src.getAggregateAddr(),
- SizeVal);
+ dest.getAddr(),
+ src.getAddr(),
+ size);
return;
}
- // If the result of the assignment is used, copy the LHS there also.
- // FIXME: Pass VolatileDest as well. I think we also need to merge volatile
- // from the source as well, as we can't eliminate it if either operand
- // is volatile, unless copy has volatile for both source and destination..
- CGF.EmitAggregateCopy(Dest.getAddr(), Src.getAggregateAddr(), E->getType(),
- Dest.isVolatile()|Src.isVolatileQualified(),
- Alignment);
-}
-/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
-void AggExprEmitter::EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore) {
- assert(Src.isSimple() && "Can't have aggregate bitfield, vector, etc");
-
- CharUnits Alignment = std::min(Src.getAlignment(), Dest.getAlignment());
- EmitFinalDestCopy(E, Src.asAggregateRValue(), Ignore, Alignment.getQuantity());
+ // If the result of the assignment is used, copy the LHS there also.
+ // It's volatile if either side is. Use the minimum alignment of
+ // the two sides.
+ CGF.EmitAggregateCopy(dest.getAddr(), src.getAddr(), type,
+ dest.isVolatile() || src.isVolatile(),
+ std::min(dest.getAlignment(), src.getAlignment()));
}
static QualType GetStdInitializerListElementType(QualType T) {
@@ -526,7 +526,7 @@ void AggExprEmitter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E){
}
void AggExprEmitter::VisitOpaqueValueExpr(OpaqueValueExpr *e) {
- EmitFinalDestCopy(e, CGF.getOpaqueLValueMapping(e));
+ EmitFinalDestCopy(e->getType(), CGF.getOpaqueLValueMapping(e));
}
void
@@ -582,7 +582,15 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
"should have been unpacked before we got here");
}
- case CK_LValueToRValue: // hope for downstream optimization
+ case CK_LValueToRValue:
+ // If we're loading from a volatile type, force the destination
+ // into existence.
+ if (E->getSubExpr()->getType().isVolatileQualified()) {
+ EnsureDest(E->getType());
+ return Visit(E->getSubExpr());
+ }
+ // fallthrough
+
case CK_NoOp:
case CK_AtomicToNonAtomic:
case CK_NonAtomicToAtomic:
@@ -676,7 +684,73 @@ void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) {
void AggExprEmitter::VisitPointerToDataMemberBinaryOperator(
const BinaryOperator *E) {
LValue LV = CGF.EmitPointerToDataMemberBinaryExpr(E);
- EmitFinalDestCopy(E, LV);
+ EmitFinalDestCopy(E->getType(), LV);
+}
+
+/// Is the value of the given expression possibly a reference to or
+/// into a __block variable?
+static bool isBlockVarRef(const Expr *E) {
+ // Make sure we look through parens.
+ E = E->IgnoreParens();
+
+ // Check for a direct reference to a __block variable.
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ const VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl());
+ return (var && var->hasAttr<BlocksAttr>());
+ }
+
+ // More complicated stuff.
+
+ // Binary operators.
+ if (const BinaryOperator *op = dyn_cast<BinaryOperator>(E)) {
+ // For an assignment or pointer-to-member operation, just care
+ // about the LHS.
+ if (op->isAssignmentOp() || op->isPtrMemOp())
+ return isBlockVarRef(op->getLHS());
+
+ // For a comma, just care about the RHS.
+ if (op->getOpcode() == BO_Comma)
+ return isBlockVarRef(op->getRHS());
+
+ // FIXME: pointer arithmetic?
+ return false;
+
+ // Check both sides of a conditional operator.
+ } else if (const AbstractConditionalOperator *op
+ = dyn_cast<AbstractConditionalOperator>(E)) {
+ return isBlockVarRef(op->getTrueExpr())
+ || isBlockVarRef(op->getFalseExpr());
+
+ // OVEs are required to support BinaryConditionalOperators.
+ } else if (const OpaqueValueExpr *op
+ = dyn_cast<OpaqueValueExpr>(E)) {
+ if (const Expr *src = op->getSourceExpr())
+ return isBlockVarRef(src);
+
+ // Casts are necessary to get things like (*(int*)&var) = foo().
+ // We don't really care about the kind of cast here, except
+ // we don't want to look through l2r casts, because it's okay
+ // to get the *value* in a __block variable.
+ } else if (const CastExpr *cast = dyn_cast<CastExpr>(E)) {
+ if (cast->getCastKind() == CK_LValueToRValue)
+ return false;
+ return isBlockVarRef(cast->getSubExpr());
+
+ // Handle unary operators. Again, just aggressively look through
+ // it, ignoring the operation.
+ } else if (const UnaryOperator *uop = dyn_cast<UnaryOperator>(E)) {
+ return isBlockVarRef(uop->getSubExpr());
+
+ // Look into the base of a field access.
+ } else if (const MemberExpr *mem = dyn_cast<MemberExpr>(E)) {
+ return isBlockVarRef(mem->getBase());
+
+ // Look into the base of a subscript.
+ } else if (const ArraySubscriptExpr *sub = dyn_cast<ArraySubscriptExpr>(E)) {
+ return isBlockVarRef(sub->getBase());
+ }
+
+ return false;
}
void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) {
@@ -686,20 +760,26 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) {
E->getRHS()->getType())
&& "Invalid assignment");
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->getLHS()))
- if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()))
- if (VD->hasAttr<BlocksAttr>() &&
- E->getRHS()->HasSideEffects(CGF.getContext())) {
- // When __block variable on LHS, the RHS must be evaluated first
- // as it may change the 'forwarding' field via call to Block_copy.
- LValue RHS = CGF.EmitLValue(E->getRHS());
- LValue LHS = CGF.EmitLValue(E->getLHS());
- Dest = AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed,
- needsGC(E->getLHS()->getType()),
- AggValueSlot::IsAliased);
- EmitFinalDestCopy(E, RHS, true);
- return;
- }
+ // If the LHS might be a __block variable, and the RHS can
+ // potentially cause a block copy, we need to evaluate the RHS first
+ // so that the assignment goes the right place.
+ // This is pretty semantically fragile.
+ if (isBlockVarRef(E->getLHS()) &&
+ E->getRHS()->HasSideEffects(CGF.getContext())) {
+ // Ensure that we have a destination, and evaluate the RHS into that.
+ EnsureDest(E->getRHS()->getType());
+ Visit(E->getRHS());
+
+ // Now emit the LHS and copy into it.
+ LValue LHS = CGF.EmitLValue(E->getLHS());
+
+ EmitCopy(E->getLHS()->getType(),
+ AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed,
+ needsGC(E->getLHS()->getType()),
+ AggValueSlot::IsAliased),
+ Dest);
+ return;
+ }
LValue LHS = CGF.EmitLValue(E->getLHS());
@@ -708,8 +788,10 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) {
AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed,
needsGC(E->getLHS()->getType()),
AggValueSlot::IsAliased);
- CGF.EmitAggExpr(E->getRHS(), LHSSlot, false);
- EmitFinalDestCopy(E, LHS, true);
+ CGF.EmitAggExpr(E->getRHS(), LHSSlot);
+
+ // Copy into the destination if the assignment isn't ignored.
+ EmitFinalDestCopy(E->getType(), LHS);
}
void AggExprEmitter::
@@ -762,14 +844,14 @@ void AggExprEmitter::VisitVAArgExpr(VAArgExpr *VE) {
return;
}
- EmitFinalDestCopy(VE, CGF.MakeAddrLValue(ArgPtr, VE->getType()));
+ EmitFinalDestCopy(VE->getType(), CGF.MakeAddrLValue(ArgPtr, VE->getType()));
}
void AggExprEmitter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) {
// Ensure that we have a slot, but if we already do, remember
// whether it was externally destructed.
bool wasExternallyDestructed = Dest.isExternallyDestructed();
- Dest = EnsureSlot(E->getType());
+ EnsureDest(E->getType());
// We're going to push a destructor if there isn't already one.
Dest.setExternallyDestructed();
@@ -904,7 +986,7 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
llvm::GlobalVariable* GV =
new llvm::GlobalVariable(CGF.CGM.getModule(), C->getType(), true,
llvm::GlobalValue::InternalLinkage, C, "");
- EmitFinalDestCopy(E, CGF.MakeAddrLValue(GV, E->getType()));
+ EmitFinalDestCopy(E->getType(), CGF.MakeAddrLValue(GV, E->getType()));
return;
}
#endif
@@ -1164,11 +1246,7 @@ static void CheckAggExprForMemSetUse(AggValueSlot &Slot, const Expr *E,
/// type. The result is computed into DestPtr. Note that if DestPtr is null,
/// the value of the aggregate expression is not needed. If VolatileDest is
/// true, DestPtr cannot be 0.
-///
-/// \param IsInitializer - true if this evaluation is initializing an
-/// object whose lifetime is already being managed.
-void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot,
- bool IgnoreResult) {
+void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot) {
assert(E && hasAggregateLLVMType(E->getType()) &&
"Invalid aggregate expression to emit");
assert((Slot.getAddr() != 0 || Slot.isIgnored()) &&
@@ -1177,7 +1255,7 @@ void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot,
// Optimize the slot if possible.
CheckAggExprForMemSetUse(Slot, E, *this);
- AggExprEmitter(*this, Slot, IgnoreResult).Visit(const_cast<Expr*>(E));
+ AggExprEmitter(*this, Slot).Visit(const_cast<Expr*>(E));
}
LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) {
@@ -1192,7 +1270,8 @@ LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) {
void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr,
llvm::Value *SrcPtr, QualType Ty,
- bool isVolatile, unsigned Alignment) {
+ bool isVolatile,
+ CharUnits alignment) {
assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex");
if (getContext().getLangOpts().CPlusPlus) {
@@ -1225,8 +1304,8 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr,
std::pair<CharUnits, CharUnits> TypeInfo =
getContext().getTypeInfoInChars(Ty);
- if (!Alignment)
- Alignment = TypeInfo.second.getQuantity();
+ if (alignment.isZero())
+ alignment = TypeInfo.second;
// FIXME: Handle variable sized types.
@@ -1284,7 +1363,7 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr,
Builder.CreateMemCpy(DestPtr, SrcPtr,
llvm::ConstantInt::get(IntPtrTy,
TypeInfo.first.getQuantity()),
- Alignment, isVolatile);
+ alignment.getQuantity(), isVolatile);
}
void CodeGenFunction::MaybeEmitStdInitializerListCleanup(llvm::Value *loc,
diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp
index c69c883..7c2c9f1 100644
--- a/lib/CodeGen/CGExprCXX.cpp
+++ b/lib/CodeGen/CGExprCXX.cpp
@@ -50,36 +50,10 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD,
// And the rest of the call args.
EmitCallArgs(Args, FPT, ArgBeg, ArgEnd);
- return EmitCall(CGM.getTypes().arrangeFunctionCall(FPT->getResultType(), Args,
- FPT->getExtInfo(),
- required),
+ return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required),
Callee, ReturnValue, Args, MD);
}
-static const CXXRecordDecl *getMostDerivedClassDecl(const Expr *Base) {
- const Expr *E = Base;
-
- while (true) {
- E = E->IgnoreParens();
- if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
- if (CE->getCastKind() == CK_DerivedToBase ||
- CE->getCastKind() == CK_UncheckedDerivedToBase ||
- CE->getCastKind() == CK_NoOp) {
- E = CE->getSubExpr();
- continue;
- }
- }
-
- break;
- }
-
- QualType DerivedType = E->getType();
- if (const PointerType *PTy = DerivedType->getAs<PointerType>())
- DerivedType = PTy->getPointeeType();
-
- return cast<CXXRecordDecl>(DerivedType->castAs<RecordType>()->getDecl());
-}
-
// FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do
// quite what we want.
static const Expr *skipNoOpCastsAndParens(const Expr *E) {
@@ -126,7 +100,7 @@ static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context,
// b->f();
// }
//
- const CXXRecordDecl *MostDerivedClassDecl = getMostDerivedClassDecl(Base);
+ const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType();
if (MostDerivedClassDecl->hasAttr<FinalAttr>())
return true;
@@ -166,6 +140,14 @@ static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context,
return false;
}
+static CXXRecordDecl *getCXXRecord(const Expr *E) {
+ QualType T = E->getType();
+ if (const PointerType *PTy = T->getAs<PointerType>())
+ T = PTy->getPointeeType();
+ const RecordType *Ty = T->castAs<RecordType>();
+ return cast<CXXRecordDecl>(Ty->getDecl());
+}
+
// Note: This function also emit constructor calls to support a MSVC
// extensions allowing explicit constructor function call.
RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
@@ -179,7 +161,7 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
const CXXMethodDecl *MD = cast<CXXMethodDecl>(ME->getMemberDecl());
CGDebugInfo *DI = getDebugInfo();
- if (DI && CGM.getCodeGenOpts().LimitDebugInfo
+ if (DI && CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo
&& !isa<CallExpr>(ME->getBase())) {
QualType PQTy = ME->getBase()->IgnoreParenImpCasts()->getType();
if (const PointerType * PTy = dyn_cast<PointerType>(PQTy)) {
@@ -196,11 +178,45 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
}
// Compute the object pointer.
+ const Expr *Base = ME->getBase();
+ bool CanUseVirtualCall = MD->isVirtual() && !ME->hasQualifier();
+
+ const CXXMethodDecl *DevirtualizedMethod = NULL;
+ if (CanUseVirtualCall &&
+ canDevirtualizeMemberFunctionCalls(getContext(), Base, MD)) {
+ const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
+ DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl);
+ assert(DevirtualizedMethod);
+ const CXXRecordDecl *DevirtualizedClass = DevirtualizedMethod->getParent();
+ const Expr *Inner = Base->ignoreParenBaseCasts();
+ if (getCXXRecord(Inner) == DevirtualizedClass)
+ // If the class of the Inner expression is where the dynamic method
+ // is defined, build the this pointer from it.
+ Base = Inner;
+ else if (getCXXRecord(Base) != DevirtualizedClass) {
+ // If the method is defined in a class that is not the best dynamic
+ // one or the one of the full expression, we would have to build
+ // a derived-to-base cast to compute the correct this pointer, but
+ // we don't have support for that yet, so do a virtual call.
+ DevirtualizedMethod = NULL;
+ }
+ // If the return types are not the same, this might be a case where more
+ // code needs to run to compensate for it. For example, the derived
+ // method might return a type that inherits form from the return
+ // type of MD and has a prefix.
+ // For now we just avoid devirtualizing these covariant cases.
+ if (DevirtualizedMethod &&
+ DevirtualizedMethod->getResultType().getCanonicalType() !=
+ MD->getResultType().getCanonicalType())
+ DevirtualizedMethod = NULL;
+ }
+
llvm::Value *This;
if (ME->isArrow())
- This = EmitScalarExpr(ME->getBase());
+ This = EmitScalarExpr(Base);
else
- This = EmitLValue(ME->getBase()).getAddress();
+ This = EmitLValue(Base).getAddress();
+
if (MD->isTrivial()) {
if (isa<CXXDestructorDecl>(MD)) return RValue::get(0);
@@ -247,10 +263,8 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
//
// We also don't emit a virtual call if the base expression has a record type
// because then we know what the type is.
- bool UseVirtualCall;
- UseVirtualCall = MD->isVirtual() && !ME->hasQualifier()
- && !canDevirtualizeMemberFunctionCalls(getContext(),
- ME->getBase(), MD);
+ bool UseVirtualCall = CanUseVirtualCall && !DevirtualizedMethod;
+
llvm::Value *Callee;
if (const CXXDestructorDecl *Dtor = dyn_cast<CXXDestructorDecl>(MD)) {
if (UseVirtualCall) {
@@ -260,8 +274,13 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
MD->isVirtual() &&
ME->hasQualifier())
Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty);
- else
+ else if (!DevirtualizedMethod)
Callee = CGM.GetAddrOfFunction(GlobalDecl(Dtor, Dtor_Complete), Ty);
+ else {
+ const CXXDestructorDecl *DDtor =
+ cast<CXXDestructorDecl>(DevirtualizedMethod);
+ Callee = CGM.GetAddrOfFunction(GlobalDecl(DDtor, Dtor_Complete), Ty);
+ }
}
} else if (const CXXConstructorDecl *Ctor =
dyn_cast<CXXConstructorDecl>(MD)) {
@@ -273,8 +292,11 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
MD->isVirtual() &&
ME->hasQualifier())
Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty);
- else
+ else if (!DevirtualizedMethod)
Callee = CGM.GetAddrOfFunction(MD, Ty);
+ else {
+ Callee = CGM.GetAddrOfFunction(DevirtualizedMethod, Ty);
+ }
}
return EmitCXXMemberCall(MD, Callee, ReturnValue, This, /*VTT=*/0,
@@ -319,10 +341,12 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E,
// Push the this ptr.
Args.add(RValue::get(This), ThisType);
+
+ RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, 1);
// And the rest of the call args
EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end());
- return EmitCall(CGM.getTypes().arrangeFunctionCall(Args, FPT), Callee,
+ return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee,
ReturnValue, Args);
}
@@ -409,7 +433,6 @@ CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E,
if (E->requiresZeroInitialization() && !Dest.isZeroed()) {
switch (E->getConstructionKind()) {
case CXXConstructExpr::CK_Delegating:
- assert(0 && "Delegating constructor should not need zeroing");
case CXXConstructExpr::CK_Complete:
EmitNullInitialization(Dest.getAddr(), E->getType());
break;
@@ -1006,7 +1029,7 @@ namespace {
DeleteArgs.add(getPlacementArgs()[I], *AI++);
// Call 'operator delete'.
- CGF.EmitCall(CGF.CGM.getTypes().arrangeFunctionCall(DeleteArgs, FPT),
+ CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, FPT),
CGF.CGM.GetAddrOfFunction(OperatorDelete),
ReturnValueSlot(), DeleteArgs, OperatorDelete);
}
@@ -1067,7 +1090,7 @@ namespace {
}
// Call 'operator delete'.
- CGF.EmitCall(CGF.CGM.getTypes().arrangeFunctionCall(DeleteArgs, FPT),
+ CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, FPT),
CGF.CGM.GetAddrOfFunction(OperatorDelete),
ReturnValueSlot(), DeleteArgs, OperatorDelete);
}
@@ -1182,8 +1205,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
// TODO: kill any unnecessary computations done for the size
// argument.
} else {
- RV = EmitCall(CGM.getTypes().arrangeFunctionCall(allocatorArgs,
- allocatorType),
+ RV = EmitCall(CGM.getTypes().arrangeFreeFunctionCall(allocatorArgs,
+ allocatorType),
CGM.GetAddrOfFunction(allocator), ReturnValueSlot(),
allocatorArgs, allocator);
}
@@ -1306,7 +1329,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
DeleteArgs.add(RValue::get(Size), SizeTy);
// Emit the call to delete.
- EmitCall(CGM.getTypes().arrangeFunctionCall(DeleteArgs, DeleteFTy),
+ EmitCall(CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, DeleteFTy),
CGM.GetAddrOfFunction(DeleteFD), ReturnValueSlot(),
DeleteArgs, DeleteFD);
}
@@ -1462,7 +1485,7 @@ namespace {
}
// Emit the call to delete.
- CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(Args, DeleteFTy),
+ CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Args, DeleteFTy),
CGF.CGM.GetAddrOfFunction(OperatorDelete),
ReturnValueSlot(), Args, OperatorDelete);
}
@@ -1510,18 +1533,7 @@ static void EmitArrayDelete(CodeGenFunction &CGF,
}
void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
-
- // Get at the argument before we performed the implicit conversion
- // to void*.
const Expr *Arg = E->getArgument();
- while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
- if (ICE->getCastKind() != CK_UserDefinedConversion &&
- ICE->getType()->isVoidPointerType())
- Arg = ICE->getSubExpr();
- else
- break;
- }
-
llvm::Value *Ptr = EmitScalarExpr(Arg);
// Null check the pointer.
@@ -1631,15 +1643,9 @@ llvm::Value *CodeGenFunction::EmitCXXTypeidExpr(const CXXTypeidExpr *E) {
// polymorphic class type, the result refers to a std::type_info object
// representing the type of the most derived object (that is, the dynamic
// type) to which the glvalue refers.
- if (E->getExprOperand()->isGLValue()) {
- if (const RecordType *RT =
- E->getExprOperand()->getType()->getAs<RecordType>()) {
- const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
- if (RD->isPolymorphic())
- return EmitTypeidFromVTable(*this, E->getExprOperand(),
- StdTypeInfoPtrTy);
- }
- }
+ if (E->isPotentiallyEvaluated())
+ return EmitTypeidFromVTable(*this, E->getExprOperand(),
+ StdTypeInfoPtrTy);
QualType OperandTy = E->getExprOperand()->getType();
return Builder.CreateBitCast(CGM.GetAddrOfRTTIDescriptor(OperandTy),
diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp
index bc9f9ef..a17a436 100644
--- a/lib/CodeGen/CGExprConstant.cpp
+++ b/lib/CodeGen/CGExprConstant.cpp
@@ -386,11 +386,11 @@ bool ConstStructBuilder::Build(InitListExpr *ILE) {
if (IsMsStruct) {
// Zero-length bitfields following non-bitfield members are
// ignored:
- if (CGM.getContext().ZeroBitfieldFollowsNonBitfield((*Field), LastFD)) {
+ if (CGM.getContext().ZeroBitfieldFollowsNonBitfield(*Field, LastFD)) {
--FieldNo;
continue;
}
- LastFD = (*Field);
+ LastFD = *Field;
}
// If this is a union, skip all the fields that aren't being initialized.
@@ -399,7 +399,7 @@ bool ConstStructBuilder::Build(InitListExpr *ILE) {
// Don't emit anonymous bitfields, they just affect layout.
if (Field->isUnnamedBitfield()) {
- LastFD = (*Field);
+ LastFD = *Field;
continue;
}
@@ -486,11 +486,11 @@ void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
if (IsMsStruct) {
// Zero-length bitfields following non-bitfield members are
// ignored:
- if (CGM.getContext().ZeroBitfieldFollowsNonBitfield((*Field), LastFD)) {
+ if (CGM.getContext().ZeroBitfieldFollowsNonBitfield(*Field, LastFD)) {
--FieldNo;
continue;
}
- LastFD = (*Field);
+ LastFD = *Field;
}
// If this is a union, skip all the fields that aren't being initialized.
@@ -499,7 +499,7 @@ void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
// Don't emit anonymous bitfields, they just affect layout.
if (Field->isUnnamedBitfield()) {
- LastFD = (*Field);
+ LastFD = *Field;
continue;
}
@@ -932,7 +932,8 @@ public:
C = new llvm::GlobalVariable(CGM.getModule(), C->getType(),
E->getType().isConstant(CGM.getContext()),
llvm::GlobalValue::InternalLinkage,
- C, ".compoundliteral", 0, false,
+ C, ".compoundliteral", 0,
+ llvm::GlobalVariable::NotThreadLocal,
CGM.getContext().getTargetAddressSpace(E->getType()));
return C;
}
@@ -1300,7 +1301,8 @@ FillInNullDataMemberPointers(CodeGenModule &CGM, QualType T,
if (CGM.getTypes().isZeroInitializable(BaseDecl))
continue;
- uint64_t BaseOffset = Layout.getBaseClassOffsetInBits(BaseDecl);
+ uint64_t BaseOffset =
+ CGM.getContext().toBits(Layout.getBaseClassOffset(BaseDecl));
FillInNullDataMemberPointers(CGM, I->getType(),
Elements, StartOffset + BaseOffset);
}
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index 18891f7..1cccafe 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -498,8 +498,8 @@ public:
Value *VisitObjCStringLiteral(const ObjCStringLiteral *E) {
return CGF.EmitObjCStringLiteral(E);
}
- Value *VisitObjCNumericLiteral(ObjCNumericLiteral *E) {
- return CGF.EmitObjCNumericLiteral(E);
+ Value *VisitObjCBoxedExpr(ObjCBoxedExpr *E) {
+ return CGF.EmitObjCBoxedExpr(E);
}
Value *VisitObjCArrayLiteral(ObjCArrayLiteral *E) {
return CGF.EmitObjCArrayLiteral(E);
@@ -798,14 +798,15 @@ Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
return Builder.getInt(Value);
}
- // Emit debug info for aggregate now, if it was delayed to reduce
+ // Emit debug info for aggregate now, if it was delayed to reduce
// debug info size.
CGDebugInfo *DI = CGF.getDebugInfo();
- if (DI && CGF.CGM.getCodeGenOpts().LimitDebugInfo) {
+ if (DI &&
+ CGF.CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo) {
QualType PQTy = E->getBase()->IgnoreParenImpCasts()->getType();
if (const PointerType * PTy = dyn_cast<PointerType>(PQTy))
if (FieldDecl *M = dyn_cast<FieldDecl>(E->getMemberDecl()))
- DI->getOrCreateRecordType(PTy->getPointeeType(),
+ DI->getOrCreateRecordType(PTy->getPointeeType(),
M->getParent()->getLocation());
}
return EmitLoadOfLValue(E);
@@ -1520,7 +1521,7 @@ Value *ScalarExprEmitter::VisitOffsetOfExpr(OffsetOfExpr *E) {
// FIXME: It would be nice if we didn't have to loop here!
for (RecordDecl::field_iterator Field = RD->field_begin(),
FieldEnd = RD->field_end();
- Field != FieldEnd; (void)++Field, ++i) {
+ Field != FieldEnd; ++Field, ++i) {
if (*Field == MemberDecl)
break;
}
@@ -1554,9 +1555,8 @@ Value *ScalarExprEmitter::VisitOffsetOfExpr(OffsetOfExpr *E) {
// Compute the offset to the base.
const RecordType *BaseRT = CurrentType->getAs<RecordType>();
CXXRecordDecl *BaseRD = cast<CXXRecordDecl>(BaseRT->getDecl());
- int64_t OffsetInt = RL.getBaseClassOffsetInBits(BaseRD) /
- CGF.getContext().getCharWidth();
- Offset = llvm::ConstantInt::get(ResultType, OffsetInt);
+ CharUnits OffsetInt = RL.getBaseClassOffset(BaseRD);
+ Offset = llvm::ConstantInt::get(ResultType, OffsetInt.getQuantity());
break;
}
}
@@ -1682,11 +1682,9 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
// Load/convert the LHS.
LValue LHSLV = EmitCheckedLValue(E->getLHS());
OpInfo.LHS = EmitLoadOfLValue(LHSLV);
- OpInfo.LHS = EmitScalarConversion(OpInfo.LHS, LHSTy,
- E->getComputationLHSType());
llvm::PHINode *atomicPHI = 0;
- if (const AtomicType *atomicTy = OpInfo.Ty->getAs<AtomicType>()) {
+ if (LHSTy->isAtomicType()) {
// FIXME: For floating point types, we should be saving and restoring the
// floating point environment in the loop.
llvm::BasicBlock *startBB = Builder.GetInsertBlock();
@@ -1695,10 +1693,12 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
Builder.SetInsertPoint(opBB);
atomicPHI = Builder.CreatePHI(OpInfo.LHS->getType(), 2);
atomicPHI->addIncoming(OpInfo.LHS, startBB);
- OpInfo.Ty = atomicTy->getValueType();
OpInfo.LHS = atomicPHI;
}
-
+
+ OpInfo.LHS = EmitScalarConversion(OpInfo.LHS, LHSTy,
+ E->getComputationLHSType());
+
// Expand the binary operator.
Result = (this->*Func)(OpInfo);
@@ -2592,7 +2592,7 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
llvm::Value *LHSTmp = LHS;
bool wasCast = false;
llvm::VectorType *rhsVTy = cast<llvm::VectorType>(RHS->getType());
- if (rhsVTy->getElementType()->isFloatTy()) {
+ if (rhsVTy->getElementType()->isFloatingPointTy()) {
RHSTmp = Builder.CreateBitCast(RHS, tmp2->getType());
LHSTmp = Builder.CreateBitCast(LHS, tmp->getType());
wasCast = true;
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index d0aa0f5..4ac172d 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -30,7 +30,7 @@ typedef llvm::PointerIntPair<llvm::Value*,1,bool> TryEmitResult;
static TryEmitResult
tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e);
static RValue AdjustRelatedResultType(CodeGenFunction &CGF,
- const Expr *E,
+ QualType ET,
const ObjCMethodDecl *Method,
RValue Result);
@@ -51,36 +51,36 @@ llvm::Value *CodeGenFunction::EmitObjCStringLiteral(const ObjCStringLiteral *E)
return llvm::ConstantExpr::getBitCast(C, ConvertType(E->getType()));
}
-/// EmitObjCNumericLiteral - This routine generates code for
-/// the appropriate +[NSNumber numberWith<Type>:] method.
+/// EmitObjCBoxedExpr - This routine generates code to call
+/// the appropriate expression boxing method. This will either be
+/// one of +[NSNumber numberWith<Type>:], or +[NSString stringWithUTF8String:].
///
llvm::Value *
-CodeGenFunction::EmitObjCNumericLiteral(const ObjCNumericLiteral *E) {
+CodeGenFunction::EmitObjCBoxedExpr(const ObjCBoxedExpr *E) {
// Generate the correct selector for this literal's concrete type.
- const Expr *NL = E->getNumber();
+ const Expr *SubExpr = E->getSubExpr();
// Get the method.
- const ObjCMethodDecl *Method = E->getObjCNumericLiteralMethod();
- assert(Method && "NSNumber method is null");
- Selector Sel = Method->getSelector();
+ const ObjCMethodDecl *BoxingMethod = E->getBoxingMethod();
+ assert(BoxingMethod && "BoxingMethod is null");
+ assert(BoxingMethod->isClassMethod() && "BoxingMethod must be a class method");
+ Selector Sel = BoxingMethod->getSelector();
// Generate a reference to the class pointer, which will be the receiver.
- QualType ResultType = E->getType(); // should be NSNumber *
- const ObjCObjectPointerType *InterfacePointerType =
- ResultType->getAsObjCInterfacePointerType();
- ObjCInterfaceDecl *NSNumberDecl =
- InterfacePointerType->getObjectType()->getInterface();
+ // Assumes that the method was introduced in the class that should be
+ // messaged (avoids pulling it out of the result type).
CGObjCRuntime &Runtime = CGM.getObjCRuntime();
- llvm::Value *Receiver = Runtime.GetClass(Builder, NSNumberDecl);
-
- const ParmVarDecl *argDecl = *Method->param_begin();
+ const ObjCInterfaceDecl *ClassDecl = BoxingMethod->getClassInterface();
+ llvm::Value *Receiver = Runtime.GetClass(Builder, ClassDecl);
+
+ const ParmVarDecl *argDecl = *BoxingMethod->param_begin();
QualType ArgQT = argDecl->getType().getUnqualifiedType();
- RValue RV = EmitAnyExpr(NL);
+ RValue RV = EmitAnyExpr(SubExpr);
CallArgList Args;
Args.add(RV, ArgQT);
-
+
RValue result = Runtime.GenerateMessageSend(*this, ReturnValueSlot(),
- ResultType, Sel, Receiver, Args,
- NSNumberDecl, Method);
+ BoxingMethod->getResultType(), Sel, Receiver, Args,
+ ClassDecl, BoxingMethod);
return Builder.CreateBitCast(result.getScalarVal(),
ConvertType(E->getType()));
}
@@ -202,20 +202,20 @@ llvm::Value *CodeGenFunction::EmitObjCProtocolExpr(const ObjCProtocolExpr *E) {
/// \brief Adjust the type of the result of an Objective-C message send
/// expression when the method has a related result type.
static RValue AdjustRelatedResultType(CodeGenFunction &CGF,
- const Expr *E,
+ QualType ExpT,
const ObjCMethodDecl *Method,
RValue Result) {
if (!Method)
return Result;
if (!Method->hasRelatedResultType() ||
- CGF.getContext().hasSameType(E->getType(), Method->getResultType()) ||
+ CGF.getContext().hasSameType(ExpT, Method->getResultType()) ||
!Result.isScalar())
return Result;
// We have applied a related result type. Cast the rvalue appropriately.
return RValue::get(CGF.Builder.CreateBitCast(Result.getScalarVal(),
- CGF.ConvertType(E->getType())));
+ CGF.ConvertType(ExpT)));
}
/// Decide whether to extend the lifetime of the receiver of a
@@ -401,7 +401,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
Builder.CreateStore(newSelf, selfAddr);
}
- return AdjustRelatedResultType(*this, E, method, result);
+ return AdjustRelatedResultType(*this, E->getType(), method, result);
}
namespace {
@@ -507,9 +507,9 @@ static void emitStructGetterCall(CodeGenFunction &CGF, ObjCIvarDecl *ivar,
args.add(RValue::get(CGF.Builder.getInt1(hasStrong)), Context.BoolTy);
llvm::Value *fn = CGF.CGM.getObjCRuntime().GetGetStructFunction();
- CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(Context.VoidTy, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Context.VoidTy, args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
fn, ReturnValueSlot(), args);
}
@@ -580,7 +580,7 @@ namespace {
};
}
-/// Pick an implementation strategy for the the given property synthesis.
+/// Pick an implementation strategy for the given property synthesis.
PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM,
const ObjCPropertyImplDecl *propImpl) {
const ObjCPropertyDecl *prop = propImpl->getPropertyDecl();
@@ -698,8 +698,9 @@ PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM,
Kind = Native;
}
-/// GenerateObjCGetter - Generate an Objective-C property getter
-/// function. The given Decl must be an ObjCImplementationDecl. @synthesize
+/// \brief Generate an Objective-C property getter function.
+///
+/// The given Decl must be an ObjCImplementationDecl. \@synthesize
/// is illegal within a category.
void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP,
const ObjCPropertyImplDecl *PID) {
@@ -710,7 +711,7 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP,
assert(OMD && "Invalid call to generate getter (empty method)");
StartObjCMethod(OMD, IMP->getClassInterface(), OMD->getLocStart());
- generateObjCGetterBody(IMP, PID, AtomicHelperFn);
+ generateObjCGetterBody(IMP, PID, OMD, AtomicHelperFn);
FinishFunction();
}
@@ -763,15 +764,17 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF,
llvm::Value *copyCppAtomicObjectFn =
CGF.CGM.getObjCRuntime().GetCppAtomicObjectFunction();
- CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(CGF.getContext().VoidTy, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy,
+ args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
copyCppAtomicObjectFn, ReturnValueSlot(), args);
}
void
CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
const ObjCPropertyImplDecl *propImpl,
+ const ObjCMethodDecl *GetterMethodDecl,
llvm::Constant *AtomicHelperFn) {
// If there's a non-trivial 'get' expression, we just have to emit that.
if (!hasTrivialGetExpr(propImpl)) {
@@ -850,16 +853,16 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
// FIXME: We shouldn't need to get the function info here, the
// runtime already should have computed it to build the function.
- RValue RV = EmitCall(getTypes().arrangeFunctionCall(propType, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ RValue RV = EmitCall(getTypes().arrangeFreeFunctionCall(propType, args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
getPropertyFn, ReturnValueSlot(), args);
// We need to fix the type here. Ivars with copy & retain are
// always objects so we don't need to worry about complex or
// aggregates.
RV = RValue::get(Builder.CreateBitCast(RV.getScalarVal(),
- getTypes().ConvertType(propType)));
+ getTypes().ConvertType(getterMethod->getResultType())));
EmitReturnOfRValue(RV, propType);
@@ -905,6 +908,8 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
}
value = Builder.CreateBitCast(value, ConvertType(propType));
+ value = Builder.CreateBitCast(value,
+ ConvertType(GetterMethodDecl->getResultType()));
}
EmitReturnOfRValue(RValue::get(value), propType);
@@ -952,9 +957,10 @@ static void emitStructSetterCall(CodeGenFunction &CGF, ObjCMethodDecl *OMD,
args.add(RValue::get(CGF.Builder.getFalse()), CGF.getContext().BoolTy);
llvm::Value *copyStructFn = CGF.CGM.getObjCRuntime().GetSetStructFunction();
- CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(CGF.getContext().VoidTy, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy,
+ args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
copyStructFn, ReturnValueSlot(), args);
}
@@ -989,9 +995,10 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF,
llvm::Value *copyCppAtomicObjectFn =
CGF.CGM.getObjCRuntime().GetCppAtomicObjectFunction();
- CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(CGF.getContext().VoidTy, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy,
+ args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
copyCppAtomicObjectFn, ReturnValueSlot(), args);
@@ -1125,9 +1132,9 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
if (setOptimizedPropertyFn) {
args.add(RValue::get(arg), getContext().getObjCIdType());
args.add(RValue::get(ivarOffset), getContext().getPointerDiffType());
- EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
setOptimizedPropertyFn, ReturnValueSlot(), args);
} else {
args.add(RValue::get(ivarOffset), getContext().getPointerDiffType());
@@ -1138,9 +1145,9 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
getContext().BoolTy);
// FIXME: We shouldn't need to get the function info here, the runtime
// already should have computed it to build the function.
- EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
setPropertyFn, ReturnValueSlot(), args);
}
@@ -1206,8 +1213,9 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
EmitStmt(&assign);
}
-/// GenerateObjCSetter - Generate an Objective-C property setter
-/// function. The given Decl must be an ObjCImplementationDecl. @synthesize
+/// \brief Generate an Objective-C property setter function.
+///
+/// The given Decl must be an ObjCImplementationDecl. \@synthesize
/// is illegal within a category.
void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP,
const ObjCPropertyImplDecl *PID) {
@@ -1502,9 +1510,9 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){
Args2.add(RValue::get(V), getContext().getObjCIdType());
// FIXME: We shouldn't need to get the function info here, the runtime already
// should have computed it to build the function.
- EmitCall(CGM.getTypes().arrangeFunctionCall(getContext().VoidTy, Args2,
- FunctionType::ExtInfo(),
- RequiredArgs::All),
+ EmitCall(CGM.getTypes().arrangeFreeFunctionCall(getContext().VoidTy, Args2,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All),
EnumerationMutationFn, ReturnValueSlot(), Args2);
// Otherwise, or if the mutation function returns, just continue.
@@ -1685,11 +1693,16 @@ static llvm::Constant *createARCRuntimeFunction(CodeGenModule &CGM,
StringRef fnName) {
llvm::Constant *fn = CGM.CreateRuntimeFunction(type, fnName);
- // In -fobjc-no-arc-runtime, emit weak references to the runtime
- // support library.
- if (!CGM.getCodeGenOpts().ObjCRuntimeHasARC)
- if (llvm::Function *f = dyn_cast<llvm::Function>(fn))
+ // If the target runtime doesn't naturally support ARC, emit weak
+ // references to the runtime support library. We don't really
+ // permit this to fail, but we need a particular relocation style.
+ if (llvm::Function *f = dyn_cast<llvm::Function>(fn)) {
+ if (!CGM.getLangOpts().ObjCRuntime.hasARC())
f->setLinkage(llvm::Function::ExternalWeakLinkage);
+ // set nonlazybind attribute for these APIs for performance.
+ if (fnName == "objc_retain" || fnName == "objc_release")
+ f->addFnAttr(llvm::Attribute::NonLazyBind);
+ }
return fn;
}
@@ -1808,8 +1821,8 @@ static void emitARCCopyOperation(CodeGenFunction &CGF,
}
/// Produce the code to do a retain. Based on the type, calls one of:
-/// call i8* @objc_retain(i8* %value)
-/// call i8* @objc_retainBlock(i8* %value)
+/// call i8* \@objc_retain(i8* %value)
+/// call i8* \@objc_retainBlock(i8* %value)
llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) {
if (type->isBlockPointerType())
return EmitARCRetainBlock(value, /*mandatory*/ false);
@@ -1818,7 +1831,7 @@ llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) {
}
/// Retain the given object, with normal retain semantics.
-/// call i8* @objc_retain(i8* %value)
+/// call i8* \@objc_retain(i8* %value)
llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) {
return emitARCValueOperation(*this, value,
CGM.getARCEntrypoints().objc_retain,
@@ -1826,7 +1839,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) {
}
/// Retain the given block, with _Block_copy semantics.
-/// call i8* @objc_retainBlock(i8* %value)
+/// call i8* \@objc_retainBlock(i8* %value)
///
/// \param mandatory - If false, emit the call with metadata
/// indicating that it's okay for the optimizer to eliminate this call
@@ -1856,7 +1869,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value,
}
/// Retain the given object which is the result of a function call.
-/// call i8* @objc_retainAutoreleasedReturnValue(i8* %value)
+/// call i8* \@objc_retainAutoreleasedReturnValue(i8* %value)
///
/// Yes, this function name is one character away from a different
/// call with completely different semantics.
@@ -1906,7 +1919,7 @@ CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) {
}
/// Release the given object.
-/// call void @objc_release(i8* %value)
+/// call void \@objc_release(i8* %value)
void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) {
if (isa<llvm::ConstantPointerNull>(value)) return;
@@ -1933,7 +1946,7 @@ void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) {
}
/// Store into a strong object. Always calls this:
-/// call void @objc_storeStrong(i8** %addr, i8* %value)
+/// call void \@objc_storeStrong(i8** %addr, i8* %value)
llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr,
llvm::Value *value,
bool ignored) {
@@ -1958,7 +1971,7 @@ llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr,
}
/// Store into a strong object. Sometimes calls this:
-/// call void @objc_storeStrong(i8** %addr, i8* %value)
+/// call void \@objc_storeStrong(i8** %addr, i8* %value)
/// Other times, breaks it down into components.
llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst,
llvm::Value *newValue,
@@ -1994,7 +2007,7 @@ llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst,
}
/// Autorelease the given object.
-/// call i8* @objc_autorelease(i8* %value)
+/// call i8* \@objc_autorelease(i8* %value)
llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) {
return emitARCValueOperation(*this, value,
CGM.getARCEntrypoints().objc_autorelease,
@@ -2002,7 +2015,7 @@ llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) {
}
/// Autorelease the given object.
-/// call i8* @objc_autoreleaseReturnValue(i8* %value)
+/// call i8* \@objc_autoreleaseReturnValue(i8* %value)
llvm::Value *
CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) {
return emitARCValueOperation(*this, value,
@@ -2011,7 +2024,7 @@ CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) {
}
/// Do a fused retain/autorelease of the given object.
-/// call i8* @objc_retainAutoreleaseReturnValue(i8* %value)
+/// call i8* \@objc_retainAutoreleaseReturnValue(i8* %value)
llvm::Value *
CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) {
return emitARCValueOperation(*this, value,
@@ -2020,10 +2033,10 @@ CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) {
}
/// Do a fused retain/autorelease of the given object.
-/// call i8* @objc_retainAutorelease(i8* %value)
+/// call i8* \@objc_retainAutorelease(i8* %value)
/// or
-/// %retain = call i8* @objc_retainBlock(i8* %value)
-/// call i8* @objc_autorelease(i8* %retain)
+/// %retain = call i8* \@objc_retainBlock(i8* %value)
+/// call i8* \@objc_autorelease(i8* %retain)
llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type,
llvm::Value *value) {
if (!type->isBlockPointerType())
@@ -2039,7 +2052,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type,
}
/// Do a fused retain/autorelease of the given object.
-/// call i8* @objc_retainAutorelease(i8* %value)
+/// call i8* \@objc_retainAutorelease(i8* %value)
llvm::Value *
CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) {
return emitARCValueOperation(*this, value,
@@ -2047,7 +2060,7 @@ CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) {
"objc_retainAutorelease");
}
-/// i8* @objc_loadWeak(i8** %addr)
+/// i8* \@objc_loadWeak(i8** %addr)
/// Essentially objc_autorelease(objc_loadWeakRetained(addr)).
llvm::Value *CodeGenFunction::EmitARCLoadWeak(llvm::Value *addr) {
return emitARCLoadOperation(*this, addr,
@@ -2055,14 +2068,14 @@ llvm::Value *CodeGenFunction::EmitARCLoadWeak(llvm::Value *addr) {
"objc_loadWeak");
}
-/// i8* @objc_loadWeakRetained(i8** %addr)
+/// i8* \@objc_loadWeakRetained(i8** %addr)
llvm::Value *CodeGenFunction::EmitARCLoadWeakRetained(llvm::Value *addr) {
return emitARCLoadOperation(*this, addr,
CGM.getARCEntrypoints().objc_loadWeakRetained,
"objc_loadWeakRetained");
}
-/// i8* @objc_storeWeak(i8** %addr, i8* %value)
+/// i8* \@objc_storeWeak(i8** %addr, i8* %value)
/// Returns %value.
llvm::Value *CodeGenFunction::EmitARCStoreWeak(llvm::Value *addr,
llvm::Value *value,
@@ -2072,7 +2085,7 @@ llvm::Value *CodeGenFunction::EmitARCStoreWeak(llvm::Value *addr,
"objc_storeWeak", ignored);
}
-/// i8* @objc_initWeak(i8** %addr, i8* %value)
+/// i8* \@objc_initWeak(i8** %addr, i8* %value)
/// Returns %value. %addr is known to not have a current weak entry.
/// Essentially equivalent to:
/// *addr = nil; objc_storeWeak(addr, value);
@@ -2092,7 +2105,7 @@ void CodeGenFunction::EmitARCInitWeak(llvm::Value *addr, llvm::Value *value) {
"objc_initWeak", /*ignored*/ true);
}
-/// void @objc_destroyWeak(i8** %addr)
+/// void \@objc_destroyWeak(i8** %addr)
/// Essentially objc_storeWeak(addr, nil).
void CodeGenFunction::EmitARCDestroyWeak(llvm::Value *addr) {
llvm::Constant *&fn = CGM.getARCEntrypoints().objc_destroyWeak;
@@ -2110,7 +2123,7 @@ void CodeGenFunction::EmitARCDestroyWeak(llvm::Value *addr) {
call->setDoesNotThrow();
}
-/// void @objc_moveWeak(i8** %dest, i8** %src)
+/// void \@objc_moveWeak(i8** %dest, i8** %src)
/// Disregards the current value in %dest. Leaves %src pointing to nothing.
/// Essentially (objc_copyWeak(dest, src), objc_destroyWeak(src)).
void CodeGenFunction::EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src) {
@@ -2119,7 +2132,7 @@ void CodeGenFunction::EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src) {
"objc_moveWeak");
}
-/// void @objc_copyWeak(i8** %dest, i8** %src)
+/// void \@objc_copyWeak(i8** %dest, i8** %src)
/// Disregards the current value in %dest. Essentially
/// objc_release(objc_initWeak(dest, objc_readWeakRetained(src)))
void CodeGenFunction::EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src) {
@@ -2129,7 +2142,7 @@ void CodeGenFunction::EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src) {
}
/// Produce the code to do a objc_autoreleasepool_push.
-/// call i8* @objc_autoreleasePoolPush(void)
+/// call i8* \@objc_autoreleasePoolPush(void)
llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() {
llvm::Constant *&fn = CGM.getRREntrypoints().objc_autoreleasePoolPush;
if (!fn) {
@@ -2145,7 +2158,7 @@ llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() {
}
/// Produce the code to do a primitive release.
-/// call void @objc_autoreleasePoolPop(i8* %ptr)
+/// call void \@objc_autoreleasePoolPop(i8* %ptr)
void CodeGenFunction::EmitObjCAutoreleasePoolPop(llvm::Value *value) {
assert(value->getType() == Int8PtrTy);
@@ -2717,7 +2730,7 @@ void CodeGenFunction::EmitObjCAutoreleasePoolStmt(
// Keep track of the current cleanup stack depth.
RunCleanupsScope Scope(*this);
- if (CGM.getCodeGenOpts().ObjCRuntimeHasARC) {
+ if (CGM.getLangOpts().ObjCRuntime.hasARC()) {
llvm::Value *token = EmitObjCAutoreleasePoolPush();
EHStack.pushCleanup<CallObjCAutoreleasePoolObject>(NormalCleanup, token);
} else {
@@ -2749,6 +2762,11 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) {
Builder.CreateCall(extender, object)->setDoesNotThrow();
}
+static bool hasAtomicCopyHelperAPI(const ObjCRuntime &runtime) {
+ // For now, only NeXT has these APIs.
+ return runtime.isNeXTFamily();
+}
+
/// GenerateObjCAtomicSetterCopyHelperFunction - Given a c++ object type with
/// non-trivial copy assignment function, produce following helper function.
/// static void copyHelper(Ty *dest, const Ty *source) { *dest = *source; }
@@ -2757,7 +2775,8 @@ llvm::Constant *
CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction(
const ObjCPropertyImplDecl *PID) {
// FIXME. This api is for NeXt runtime only for now.
- if (!getLangOpts().CPlusPlus || !getLangOpts().NeXTRuntime)
+ if (!getLangOpts().CPlusPlus ||
+ !hasAtomicCopyHelperAPI(getLangOpts().ObjCRuntime))
return 0;
QualType Ty = PID->getPropertyIvarDecl()->getType();
if (!Ty->isRecordType())
@@ -2841,7 +2860,8 @@ llvm::Constant *
CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction(
const ObjCPropertyImplDecl *PID) {
// FIXME. This api is for NeXt runtime only for now.
- if (!getLangOpts().CPlusPlus || !getLangOpts().NeXTRuntime)
+ if (!getLangOpts().CPlusPlus ||
+ !hasAtomicCopyHelperAPI(getLangOpts().ObjCRuntime))
return 0;
const ObjCPropertyDecl *PD = PID->getPropertyDecl();
QualType Ty = PD->getType();
diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp
index db0bd95..6d129d0 100644
--- a/lib/CodeGen/CGObjCGNU.cpp
+++ b/lib/CodeGen/CGObjCGNU.cpp
@@ -99,8 +99,8 @@ class LazyRuntimeFunction {
/// GNU Objective-C runtime code generation. This class implements the parts of
-/// Objective-C support that are specific to the GNU family of runtimes (GCC and
-/// GNUstep).
+/// Objective-C support that are specific to the GNU family of runtimes (GCC,
+/// GNUstep and ObjFW).
class CGObjCGNU : public CGObjCRuntime {
protected:
/// The LLVM module into which output is inserted
@@ -292,8 +292,8 @@ private:
protected:
/// Function used for throwing Objective-C exceptions.
LazyRuntimeFunction ExceptionThrowFn;
- /// Function used for rethrowing exceptions, used at the end of @finally or
- /// @synchronize blocks.
+ /// Function used for rethrowing exceptions, used at the end of \@finally or
+ /// \@synchronize blocks.
LazyRuntimeFunction ExceptionReThrowFn;
/// Function called when entering a catch function. This is required for
/// differentiating Objective-C exceptions and foreign exceptions.
@@ -301,9 +301,9 @@ protected:
/// Function called when exiting from a catch block. Used to do exception
/// cleanup.
LazyRuntimeFunction ExitCatchFn;
- /// Function called when entering an @synchronize block. Acquires the lock.
+ /// Function called when entering an \@synchronize block. Acquires the lock.
LazyRuntimeFunction SyncEnterFn;
- /// Function called when exiting an @synchronize block. Releases the lock.
+ /// Function called when exiting an \@synchronize block. Releases the lock.
LazyRuntimeFunction SyncExitFn;
private:
@@ -350,7 +350,7 @@ private:
ArrayRef<Selector> MethodSels,
ArrayRef<llvm::Constant *> MethodTypes,
bool isClassMethodList);
- /// Emits an empty protocol. This is used for @protocol() where no protocol
+ /// Emits an empty protocol. This is used for \@protocol() where no protocol
/// is found. The runtime will (hopefully) fix up the pointer to refer to the
/// real protocol.
llvm::Constant *GenerateEmptyProtocol(const std::string &ProtocolName);
@@ -397,11 +397,11 @@ private:
const ObjCIvarDecl *Ivar);
/// Emits a reference to a class. This allows the linker to object if there
/// is no class of the matching name.
+protected:
void EmitClassRef(const std::string &className);
/// Emits a pointer to the named class
- llvm::Value *GetClassNamed(CGBuilderTy &Builder, const std::string &Name,
- bool isWeak);
-protected:
+ virtual llvm::Value *GetClassNamed(CGBuilderTy &Builder,
+ const std::string &Name, bool isWeak);
/// Looks up the method for sending a message to the specified object. This
/// mechanism differs between the GCC and GNU runtimes, so this method must be
/// overridden in subclasses.
@@ -653,6 +653,33 @@ class CGObjCGNUstep : public CGObjCGNU {
}
};
+/// The ObjFW runtime, which closely follows the GCC runtime's
+/// compiler ABI. Support here is due to Jonathan Schleifer, the
+/// ObjFW maintainer.
+class CGObjCObjFW : public CGObjCGCC {
+ /// Emit class references unconditionally as direct symbol references.
+ virtual llvm::Value *GetClassNamed(CGBuilderTy &Builder,
+ const std::string &Name, bool isWeak) {
+ if (isWeak)
+ return CGObjCGNU::GetClassNamed(Builder, Name, isWeak);
+
+ EmitClassRef(Name);
+
+ std::string SymbolName = "_OBJC_CLASS_" + Name;
+
+ llvm::GlobalVariable *ClassSymbol = TheModule.getGlobalVariable(SymbolName);
+
+ if (!ClassSymbol)
+ ClassSymbol = new llvm::GlobalVariable(TheModule, LongTy, false,
+ llvm::GlobalValue::ExternalLinkage,
+ 0, SymbolName);
+
+ return ClassSymbol;
+ }
+
+public:
+ CGObjCObjFW(CodeGenModule &Mod): CGObjCGCC(Mod) {}
+};
} // end anonymous namespace
@@ -889,7 +916,7 @@ llvm::Constant *CGObjCGNU::GetEHType(QualType T) {
// foreign exceptions. With the new ABI, we use __objc_id_typeinfo as
// a pointer indicating object catchalls, and NULL to indicate real
// catchalls
- if (CGM.getLangOpts().ObjCNonFragileABI) {
+ if (CGM.getLangOpts().ObjCRuntime.isNonFragile()) {
return MakeConstantString("@id");
} else {
return 0;
@@ -1627,7 +1654,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) {
iter = PD->prop_begin(), endIter = PD->prop_end();
iter != endIter ; iter++) {
std::vector<llvm::Constant*> Fields;
- ObjCPropertyDecl *property = (*iter);
+ ObjCPropertyDecl *property = *iter;
Fields.push_back(MakeConstantString(property->getNameAsString()));
Fields.push_back(llvm::ConstantInt::get(Int8Ty,
@@ -1877,7 +1904,7 @@ llvm::Constant *CGObjCGNU::GeneratePropertyList(const ObjCImplementationDecl *OI
iter = OID->propimpl_begin(), endIter = OID->propimpl_end();
iter != endIter ; iter++) {
std::vector<llvm::Constant*> Fields;
- ObjCPropertyDecl *property = (*iter)->getPropertyDecl();
+ ObjCPropertyDecl *property = iter->getPropertyDecl();
ObjCPropertyImplDecl *propertyImpl = *iter;
bool isSynthesized = (propertyImpl->getPropertyImplementation() ==
ObjCPropertyImplDecl::Synthesize);
@@ -1984,7 +2011,7 @@ void CGObjCGNU::GenerateClass(const ObjCImplementationDecl *OID) {
Context.getASTObjCInterfaceLayout(SuperClassDecl).getSize().getQuantity();
// For non-fragile ivars, set the instance size to 0 - {the size of just this
// class}. The runtime will then set this to the correct value on load.
- if (CGM.getContext().getLangOpts().ObjCNonFragileABI) {
+ if (CGM.getContext().getLangOpts().ObjCRuntime.isNonFragile()) {
instanceSize = 0 - (instanceSize - superInstanceSize);
}
@@ -1999,7 +2026,7 @@ void CGObjCGNU::GenerateClass(const ObjCImplementationDecl *OID) {
// Get the offset
uint64_t BaseOffset = ComputeIvarBaseOffset(CGM, OID, IVD);
uint64_t Offset = BaseOffset;
- if (CGM.getContext().getLangOpts().ObjCNonFragileABI) {
+ if (CGM.getContext().getLangOpts().ObjCRuntime.isNonFragile()) {
Offset = BaseOffset - superInstanceSize;
}
llvm::Constant *OffsetValue = llvm::ConstantInt::get(IntTy, Offset);
@@ -2486,25 +2513,8 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF,
ExceptionAsObject = CGF.ObjCEHValueStack.back();
}
ExceptionAsObject = CGF.Builder.CreateBitCast(ExceptionAsObject, IdTy);
-
- // Note: This may have to be an invoke, if we want to support constructs like:
- // @try {
- // @throw(obj);
- // }
- // @catch(id) ...
- //
- // This is effectively turning @throw into an incredibly-expensive goto, but
- // it may happen as a result of inlining followed by missed optimizations, or
- // as a result of stupidity.
- llvm::BasicBlock *UnwindBB = CGF.getInvokeDest();
- if (!UnwindBB) {
- CGF.Builder.CreateCall(ExceptionThrowFn, ExceptionAsObject);
- CGF.Builder.CreateUnreachable();
- } else {
- CGF.Builder.CreateInvoke(ExceptionThrowFn, UnwindBB, UnwindBB,
- ExceptionAsObject);
- }
- // Clear the insertion point to indicate we are in unreachable code.
+ CGF.EmitCallOrInvoke(ExceptionThrowFn, ExceptionAsObject);
+ CGF.Builder.CreateUnreachable();
CGF.Builder.ClearInsertionPoint();
}
@@ -2640,7 +2650,7 @@ static const ObjCInterfaceDecl *FindIvarInterface(ASTContext &Context,
llvm::Value *CGObjCGNU::EmitIvarOffset(CodeGenFunction &CGF,
const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar) {
- if (CGM.getLangOpts().ObjCNonFragileABI) {
+ if (CGM.getLangOpts().ObjCRuntime.isNonFragile()) {
Interface = FindIvarInterface(CGM.getContext(), Interface, Ivar);
if (RuntimeVersion < 10)
return CGF.Builder.CreateZExtOrBitCast(
@@ -2665,7 +2675,20 @@ llvm::Value *CGObjCGNU::EmitIvarOffset(CodeGenFunction &CGF,
CGObjCRuntime *
clang::CodeGen::CreateGNUObjCRuntime(CodeGenModule &CGM) {
- if (CGM.getLangOpts().ObjCNonFragileABI)
+ switch (CGM.getLangOpts().ObjCRuntime.getKind()) {
+ case ObjCRuntime::GNUstep:
return new CGObjCGNUstep(CGM);
- return new CGObjCGCC(CGM);
+
+ case ObjCRuntime::GCC:
+ return new CGObjCGCC(CGM);
+
+ case ObjCRuntime::ObjFW:
+ return new CGObjCObjFW(CGM);
+
+ case ObjCRuntime::FragileMacOSX:
+ case ObjCRuntime::MacOSX:
+ case ObjCRuntime::iOS:
+ llvm_unreachable("these runtimes are not GNU runtimes");
+ }
+ llvm_unreachable("bad runtime");
}
diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp
index e5246f1..ef802a3 100644
--- a/lib/CodeGen/CGObjCMac.cpp
+++ b/lib/CodeGen/CGObjCMac.cpp
@@ -241,9 +241,9 @@ public:
Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified());
Params.push_back(Ctx.BoolTy);
llvm::FunctionType *FTy =
- Types.GetFunctionType(Types.arrangeFunctionType(IdType, Params,
- FunctionType::ExtInfo(),
- RequiredArgs::All));
+ Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(IdType, Params,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All));
return CGM.CreateRuntimeFunction(FTy, "objc_getProperty");
}
@@ -261,9 +261,9 @@ public:
Params.push_back(Ctx.BoolTy);
Params.push_back(Ctx.BoolTy);
llvm::FunctionType *FTy =
- Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params,
- FunctionType::ExtInfo(),
- RequiredArgs::All));
+ Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All));
return CGM.CreateRuntimeFunction(FTy, "objc_setProperty");
}
@@ -287,9 +287,9 @@ public:
Params.push_back(IdType);
Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified());
llvm::FunctionType *FTy =
- Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params,
- FunctionType::ExtInfo(),
- RequiredArgs::All));
+ Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All));
const char *name;
if (atomic && copy)
name = "objc_setProperty_atomic_copy";
@@ -314,9 +314,9 @@ public:
Params.push_back(Ctx.BoolTy);
Params.push_back(Ctx.BoolTy);
llvm::FunctionType *FTy =
- Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params,
- FunctionType::ExtInfo(),
- RequiredArgs::All));
+ Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All));
return CGM.CreateRuntimeFunction(FTy, "objc_copyStruct");
}
@@ -333,9 +333,9 @@ public:
Params.push_back(Ctx.VoidPtrTy);
Params.push_back(Ctx.VoidPtrTy);
llvm::FunctionType *FTy =
- Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params,
- FunctionType::ExtInfo(),
- RequiredArgs::All));
+ Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All));
return CGM.CreateRuntimeFunction(FTy, "objc_copyCppObjectAtomic");
}
@@ -346,7 +346,7 @@ public:
SmallVector<CanQualType,1> Params;
Params.push_back(Ctx.getCanonicalParamType(Ctx.getObjCIdType()));
llvm::FunctionType *FTy =
- Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params,
+ Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params,
FunctionType::ExtInfo(),
RequiredArgs::All));
return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation");
@@ -2515,7 +2515,7 @@ llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID,
Values);
std::string Name("\01L_OBJC_METACLASS_");
- Name += ID->getNameAsCString();
+ Name += ID->getName();
// Check for a forward reference.
llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name);
@@ -3612,7 +3612,8 @@ enum ImageInfoFlags {
// A flag indicating that the module has no instances of a @synthesize of a
// superclass variable. <rdar://problem/6803242>
- eImageInfo_CorrectedSynthesize = (1 << 4)
+ eImageInfo_CorrectedSynthesize = (1 << 4),
+ eImageInfo_ImageIsSimulated = (1 << 5)
};
void CGObjCCommonMac::EmitImageInfo() {
@@ -3657,6 +3658,14 @@ void CGObjCCommonMac::EmitImageInfo() {
llvm::MDNode::get(VMContext, Ops));
}
}
+
+ // Indicate whether we're compiling this to run on a simulator.
+ const llvm::Triple &Triple = CGM.getTarget().getTriple();
+ if (Triple.getOS() == llvm::Triple::IOS &&
+ (Triple.getArch() == llvm::Triple::x86 ||
+ Triple.getArch() == llvm::Triple::x86_64))
+ Mod.addModuleFlag(llvm::Module::Error, "Objective-C Is Simulated",
+ eImageInfo_ImageIsSimulated);
}
// struct objc_module {
@@ -3809,7 +3818,10 @@ void CGObjCCommonMac::BuildAggrIvarRecordLayout(const RecordType *RT,
bool &HasUnion) {
const RecordDecl *RD = RT->getDecl();
// FIXME - Use iterator.
- SmallVector<const FieldDecl*, 16> Fields(RD->field_begin(), RD->field_end());
+ SmallVector<const FieldDecl*, 16> Fields;
+ for (RecordDecl::field_iterator i = RD->field_begin(),
+ e = RD->field_end(); i != e; ++i)
+ Fields.push_back(*i);
llvm::Type *Ty = CGM.getTypes().ConvertType(QualType(RT, 0));
const llvm::StructLayout *RecLayout =
CGM.getTargetData().getStructLayout(cast<llvm::StructType>(Ty));
@@ -4374,9 +4386,10 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm)
SourceLocation(), SourceLocation(),
&Ctx.Idents.get("_objc_super"));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
- Ctx.getObjCIdType(), 0, 0, false, false));
+ Ctx.getObjCIdType(), 0, 0, false, ICIS_NoInit));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
- Ctx.getObjCClassType(), 0, 0, false, false));
+ Ctx.getObjCClassType(), 0, 0, false,
+ ICIS_NoInit));
RD->completeDefinition();
SuperCTy = Ctx.getTagDeclType(RD);
@@ -4755,9 +4768,10 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul
SourceLocation(), SourceLocation(),
&Ctx.Idents.get("_message_ref_t"));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
- Ctx.VoidPtrTy, 0, 0, false, false));
+ Ctx.VoidPtrTy, 0, 0, false, ICIS_NoInit));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
- Ctx.getObjCSelType(), 0, 0, false, false));
+ Ctx.getObjCSelType(), 0, 0, false,
+ ICIS_NoInit));
RD->completeDefinition();
MessageRefCTy = Ctx.getTagDeclType(RD);
@@ -6367,7 +6381,18 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID,
CodeGen::CGObjCRuntime *
CodeGen::CreateMacObjCRuntime(CodeGen::CodeGenModule &CGM) {
- if (CGM.getLangOpts().ObjCNonFragileABI)
- return new CGObjCNonFragileABIMac(CGM);
+ switch (CGM.getLangOpts().ObjCRuntime.getKind()) {
+ case ObjCRuntime::FragileMacOSX:
return new CGObjCMac(CGM);
+
+ case ObjCRuntime::MacOSX:
+ case ObjCRuntime::iOS:
+ return new CGObjCNonFragileABIMac(CGM);
+
+ case ObjCRuntime::GNUstep:
+ case ObjCRuntime::GCC:
+ case ObjCRuntime::ObjFW:
+ llvm_unreachable("these runtimes are not Mac runtimes");
+ }
+ llvm_unreachable("bad runtime");
}
diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp
index 9370096..9aa6837 100644
--- a/lib/CodeGen/CGObjCRuntime.cpp
+++ b/lib/CodeGen/CGObjCRuntime.cpp
@@ -120,6 +120,8 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF,
uint64_t ContainingTypeAlign = CGF.CGM.getContext().getTargetInfo().getCharAlign();
uint64_t ContainingTypeSize = TypeSizeInBits - (FieldBitOffset - BitOffset);
uint64_t BitFieldSize = Ivar->getBitWidthValue(CGF.getContext());
+ CharUnits ContainingTypeAlignCharUnits =
+ CGF.CGM.getContext().toCharUnitsFromBits(ContainingTypeAlign);
// Allocate a new CGBitFieldInfo object to describe this access.
//
@@ -132,7 +134,8 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF,
ContainingTypeSize, ContainingTypeAlign));
return LValue::MakeBitfield(V, *Info,
- IvarTy.withCVRQualifiers(CVRQualifiers));
+ IvarTy.withCVRQualifiers(CVRQualifiers),
+ ContainingTypeAlignCharUnits);
}
namespace {
@@ -334,7 +337,7 @@ void CGObjCRuntime::EmitAtSynchronizedStmt(CodeGenFunction &CGF,
///
/// \param method - may be null
/// \param resultType - the result type to use if there's no method
-/// \param argInfo - the actual arguments, including implicit ones
+/// \param callArgs - the actual arguments, including implicit ones
CGObjCRuntime::MessageSendInfo
CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method,
QualType resultType,
@@ -355,17 +358,17 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method,
// Otherwise, there is.
FunctionType::ExtInfo einfo = signature.getExtInfo();
const CGFunctionInfo &argsInfo =
- CGM.getTypes().arrangeFunctionCall(resultType, callArgs, einfo,
- signature.getRequiredArgs());
+ CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, einfo,
+ signature.getRequiredArgs());
return MessageSendInfo(argsInfo, signatureType);
}
// There's no method; just use a default CC.
const CGFunctionInfo &argsInfo =
- CGM.getTypes().arrangeFunctionCall(resultType, callArgs,
- FunctionType::ExtInfo(),
- RequiredArgs::All);
+ CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs,
+ FunctionType::ExtInfo(),
+ RequiredArgs::All);
// Derive the signature to call from that.
llvm::PointerType *signatureType =
diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h
index ccf4d4d..219a3e4 100644
--- a/lib/CodeGen/CGObjCRuntime.h
+++ b/lib/CodeGen/CGObjCRuntime.h
@@ -91,20 +91,20 @@ protected:
llvm::Value *Offset);
/// Emits a try / catch statement. This function is intended to be called by
/// subclasses, and provides a generic mechanism for generating these, which
- /// should be usable by all runtimes. The caller must provide the functions to
- /// call when entering and exiting a @catch() block, and the function used to
- /// rethrow exceptions. If the begin and end catch functions are NULL, then
- /// the function assumes that the EH personality function provides the
- /// thrown object directly.
+ /// should be usable by all runtimes. The caller must provide the functions
+ /// to call when entering and exiting a \@catch() block, and the function
+ /// used to rethrow exceptions. If the begin and end catch functions are
+ /// NULL, then the function assumes that the EH personality function provides
+ /// the thrown object directly.
void EmitTryCatchStmt(CodeGenFunction &CGF,
const ObjCAtTryStmt &S,
llvm::Constant *beginCatchFn,
llvm::Constant *endCatchFn,
llvm::Constant *exceptionRethrowFn);
- /// Emits an @synchronize() statement, using the syncEnterFn and syncExitFn
- /// arguments as the functions called to lock and unlock the object. This
- /// function can be called by subclasses that use zero-cost exception
- /// handling.
+ /// Emits an \@synchronize() statement, using the \p syncEnterFn and
+ /// \p syncExitFn arguments as the functions called to lock and unlock
+ /// the object. This function can be called by subclasses that use
+ /// zero-cost exception handling.
void EmitAtSynchronizedStmt(CodeGenFunction &CGF,
const ObjCAtSynchronizedStmt &S,
llvm::Function *syncEnterFn,
@@ -179,7 +179,7 @@ public:
const ObjCMethodDecl *Method = 0) = 0;
/// Emit the code to return the named protocol as an object, as in a
- /// @protocol expression.
+ /// \@protocol expression.
virtual llvm::Value *GenerateProtocolRef(CGBuilderTy &Builder,
const ObjCProtocolDecl *OPD) = 0;
diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp
index 19973b4..d1b370a 100644
--- a/lib/CodeGen/CGRTTI.cpp
+++ b/lib/CodeGen/CGRTTI.cpp
@@ -985,7 +985,8 @@ llvm::Constant *CodeGenModule::GetAddrOfRTTIDescriptor(QualType Ty,
if (!ForEH && !getContext().getLangOpts().RTTI)
return llvm::Constant::getNullValue(Int8PtrTy);
- if (ForEH && Ty->isObjCObjectPointerType() && !LangOpts.NeXTRuntime)
+ if (ForEH && Ty->isObjCObjectPointerType() &&
+ LangOpts.ObjCRuntime.isGNUFamily())
return ObjCRuntime->GetEHType(Ty);
return RTTIBuilder(*this).BuildTypeInfo(Ty);
diff --git a/lib/CodeGen/CGRecordLayout.h b/lib/CodeGen/CGRecordLayout.h
index 25a0a50..94c822f 100644
--- a/lib/CodeGen/CGRecordLayout.h
+++ b/lib/CodeGen/CGRecordLayout.h
@@ -64,12 +64,7 @@ public:
/// Bit width of the memory access to perform.
unsigned AccessWidth;
- /// The alignment of the memory access, or 0 if the default alignment should
- /// be used.
- //
- // FIXME: Remove use of 0 to encode default, instead have IRgen do the right
- // thing when it generates the code, if avoiding align directives is
- // desired.
+ /// The alignment of the memory access, assuming the parent is aligned.
CharUnits AccessAlignment;
/// Offset for the target value.
diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp
index 1193e97..d642ef8 100644
--- a/lib/CodeGen/CGRecordLayoutBuilder.cpp
+++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp
@@ -235,6 +235,8 @@ CGBitFieldInfo CGBitFieldInfo::MakeInfo(CodeGenTypes &Types,
uint64_t FieldSize,
uint64_t ContainingTypeSizeInBits,
unsigned ContainingTypeAlign) {
+ assert(ContainingTypeAlign && "Expected alignment to be specified");
+
llvm::Type *Ty = Types.ConvertTypeForMem(FD->getType());
CharUnits TypeSizeInBytes =
CharUnits::fromQuantity(Types.getTargetData().getTypeAllocSize(Ty));
@@ -714,14 +716,18 @@ CGRecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD,
}
// Otherwise, add a vtable / vf-table if the layout says to do so.
- } else if (Types.getContext().getTargetInfo().getCXXABI() == CXXABI_Microsoft
- ? Layout.getVFPtrOffset() != CharUnits::fromQuantity(-1)
- : RD->isDynamicClass()) {
+ } else if (Layout.hasOwnVFPtr()) {
llvm::Type *FunctionType =
llvm::FunctionType::get(llvm::Type::getInt32Ty(Types.getLLVMContext()),
/*isVarArg=*/true);
llvm::Type *VTableTy = FunctionType->getPointerTo();
-
+
+ if (getTypeAlignment(VTableTy) > Alignment) {
+ // FIXME: Should we allow this to happen in Sema?
+ assert(!Packed && "Alignment is wrong even with packed struct!");
+ return false;
+ }
+
assert(NextFieldOffset.isZero() &&
"VTable pointer must come first!");
AppendField(CharUnits::Zero(), VTableTy->getPointerTo());
@@ -814,7 +820,7 @@ bool CGRecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
if (IsMsStruct) {
// Zero-length bitfields following non-bitfield members are
// ignored:
- const FieldDecl *FD = (*Field);
+ const FieldDecl *FD = *Field;
if (Types.getContext().ZeroBitfieldFollowsNonBitfield(FD, LastFD)) {
--FieldNo;
continue;
diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp
index a1d0789..467c779 100644
--- a/lib/CodeGen/CGStmt.cpp
+++ b/lib/CodeGen/CGStmt.cpp
@@ -133,6 +133,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
case Stmt::SwitchStmtClass: EmitSwitchStmt(cast<SwitchStmt>(*S)); break;
case Stmt::AsmStmtClass: EmitAsmStmt(cast<AsmStmt>(*S)); break;
+ case Stmt::MSAsmStmtClass: EmitMSAsmStmt(cast<MSAsmStmt>(*S)); break;
case Stmt::ObjCAtTryStmtClass:
EmitObjCAtTryStmt(cast<ObjCAtTryStmt>(*S));
@@ -155,7 +156,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
case Stmt::ObjCAutoreleasePoolStmtClass:
EmitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(*S));
break;
-
+
case Stmt::CXXTryStmtClass:
EmitCXXTryStmt(cast<CXXTryStmt>(*S));
break;
@@ -360,15 +361,14 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) {
llvm::Value *V = Builder.CreateBitCast(EmitScalarExpr(S.getTarget()),
Int8PtrTy, "addr");
llvm::BasicBlock *CurBB = Builder.GetInsertBlock();
-
// Get the basic block for the indirect goto.
llvm::BasicBlock *IndGotoBB = GetIndirectGotoBlock();
-
+
// The first instruction in the block has to be the PHI for the switch dest,
// add an entry for this branch.
cast<llvm::PHINode>(IndGotoBB->begin())->addIncoming(V, CurBB);
-
+
EmitBranch(IndGotoBB);
}
@@ -462,12 +462,12 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S) {
if (S.getConditionVariable())
EmitAutoVarDecl(*S.getConditionVariable());
-
+
// Evaluate the conditional in the while header. C99 6.8.5.1: The
// evaluation of the controlling expression takes place before each
// execution of the loop body.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
-
+
// while(1) is common, avoid extra exit blocks. Be sure
// to correctly handle break/continue though.
bool EmitBoolCondBranch = true;
@@ -489,7 +489,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S) {
EmitBranchThroughCleanup(LoopExit);
}
}
-
+
// Emit the loop body. We have to emit this in a cleanup scope
// because it might be a singleton DeclStmt.
{
@@ -584,7 +584,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) {
// Create a cleanup scope for the condition variable cleanups.
RunCleanupsScope ConditionScope(*this);
-
+
llvm::Value *BoolCondVal = 0;
if (S.getCond()) {
// If the for statement has a condition scope, emit the local variable
@@ -598,7 +598,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) {
// create a block to stage a loop exit along.
if (ForScope.requiresCleanups())
ExitBlock = createBasicBlock("for.cond.cleanup");
-
+
// As long as the condition is true, iterate the loop.
llvm::BasicBlock *ForBody = createBasicBlock("for.body");
@@ -679,7 +679,7 @@ void CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S) {
llvm::BasicBlock *ExitBlock = LoopExit.getBlock();
if (ForScope.requiresCleanups())
ExitBlock = createBasicBlock("for.cond.cleanup");
-
+
// The loop body, consisting of the specified body and the loop variable.
llvm::BasicBlock *ForBody = createBasicBlock("for.body");
@@ -750,7 +750,7 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
// Apply the named return value optimization for this return statement,
// which means doing nothing: the appropriate result has already been
// constructed into the NRVO variable.
-
+
// If there is an NRVO flag for this variable, set it to 1 into indicate
// that the cleanup code should not destroy the variable.
if (llvm::Value *NRVOFlag = NRVOFlags[S.getNRVOCandidate()])
@@ -901,7 +901,7 @@ void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) {
// try to not emit an empty block.
if ((CGM.getCodeGenOpts().OptimizationLevel > 0) && isa<BreakStmt>(S.getSubStmt())) {
JumpDest Block = BreakContinueStack.back().BreakBlock;
-
+
// Only do this optimization if there are no cleanups that need emitting.
if (isObviouslyBranchWithoutCleanups(Block)) {
SwitchInsn->addCase(CaseVal, Block.getBlock());
@@ -915,7 +915,7 @@ void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) {
return;
}
}
-
+
EmitBlock(createBasicBlock("sw.bb"));
llvm::BasicBlock *CaseDest = Builder.GetInsertBlock();
SwitchInsn->addCase(CaseVal, CaseDest);
@@ -984,7 +984,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
// If this is a null statement, just succeed.
if (S == 0)
return Case ? CSFC_Success : CSFC_FallThrough;
-
+
// If this is the switchcase (case 4: or default) that we're looking for, then
// we're in business. Just add the substatement.
if (const SwitchCase *SC = dyn_cast<SwitchCase>(S)) {
@@ -993,7 +993,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
return CollectStatementsForCase(SC->getSubStmt(), 0, FoundCase,
ResultStmts);
}
-
+
// Otherwise, this is some other case or default statement, just ignore it.
return CollectStatementsForCase(SC->getSubStmt(), Case, FoundCase,
ResultStmts);
@@ -1003,7 +1003,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
// return a success!
if (Case == 0 && isa<BreakStmt>(S))
return CSFC_Success;
-
+
// If this is a switch statement, then it might contain the SwitchCase, the
// break, or neither.
if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) {
@@ -1015,12 +1015,12 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
// using the declaration even if it is skipped, so we can't optimize out
// the decl if the kept statements might refer to it.
bool HadSkippedDecl = false;
-
+
// If we're looking for the case, just see if we can skip each of the
// substatements.
for (; Case && I != E; ++I) {
HadSkippedDecl |= isa<DeclStmt>(*I);
-
+
switch (CollectStatementsForCase(*I, Case, FoundCase, ResultStmts)) {
case CSFC_Failure: return CSFC_Failure;
case CSFC_Success:
@@ -1033,7 +1033,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
// optimization.
if (HadSkippedDecl)
return CSFC_Failure;
-
+
for (++I; I != E; ++I)
if (CodeGenFunction::ContainsLabel(*I, true))
return CSFC_Failure;
@@ -1047,7 +1047,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
assert(FoundCase && "Didn't find case but returned fallthrough?");
// We recursively found Case, so we're not looking for it anymore.
Case = 0;
-
+
// If we found the case and skipped declarations, we can't do the
// optimization.
if (HadSkippedDecl)
@@ -1074,9 +1074,9 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
if (CodeGenFunction::ContainsLabel(*I, true))
return CSFC_Failure;
return CSFC_Success;
- }
+ }
}
-
+
return Case ? CSFC_Success : CSFC_FallThrough;
}
@@ -1088,11 +1088,11 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
return CSFC_Failure;
return CSFC_Success;
}
-
+
// Otherwise, we want to include this statement. Everything is cool with that
// so long as it doesn't contain a break out of the switch we're in.
if (CodeGenFunction::containsBreak(S)) return CSFC_Failure;
-
+
// Otherwise, everything is great. Include the statement and tell the caller
// that we fall through and include the next statement as well.
ResultStmts.push_back(S);
@@ -1104,14 +1104,14 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S,
/// for a switch on constant. See the comment above CollectStatementsForCase
/// for more details.
static bool FindCaseStatementsForValue(const SwitchStmt &S,
- const llvm::APInt &ConstantCondValue,
+ const llvm::APSInt &ConstantCondValue,
SmallVectorImpl<const Stmt*> &ResultStmts,
ASTContext &C) {
// First step, find the switch case that is being branched to. We can do this
// efficiently by scanning the SwitchCase list.
const SwitchCase *Case = S.getSwitchCaseList();
const DefaultStmt *DefaultCase = 0;
-
+
for (; Case; Case = Case->getNextSwitchCase()) {
// It's either a default or case. Just remember the default statement in
// case we're not jumping to any numbered cases.
@@ -1119,17 +1119,17 @@ static bool FindCaseStatementsForValue(const SwitchStmt &S,
DefaultCase = DS;
continue;
}
-
+
// Check to see if this case is the one we're looking for.
const CaseStmt *CS = cast<CaseStmt>(Case);
// Don't handle case ranges yet.
if (CS->getRHS()) return false;
-
+
// If we found our case, remember it as 'case'.
if (CS->getLHS()->EvaluateKnownConstInt(C) == ConstantCondValue)
break;
}
-
+
// If we didn't find a matching case, we use a default if it exists, or we
// elide the whole switch body!
if (Case == 0) {
@@ -1168,7 +1168,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
// See if we can constant fold the condition of the switch and therefore only
// emit the live case statement (if any) of the switch.
- llvm::APInt ConstantCondValue;
+ llvm::APSInt ConstantCondValue;
if (ConstantFoldsToSimpleInteger(S.getCond(), ConstantCondValue)) {
SmallVector<const Stmt*, 4> CaseStmts;
if (FindCaseStatementsForValue(S, ConstantCondValue, CaseStmts,
@@ -1192,7 +1192,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
return;
}
}
-
+
llvm::Value *CondV = EmitScalarExpr(S.getCond());
// Create basic block to hold stuff that comes after switch
@@ -1380,7 +1380,7 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str,
if (!StrVal.empty()) {
const SourceManager &SM = CGF.CGM.getContext().getSourceManager();
const LangOptions &LangOpts = CGF.CGM.getLangOpts();
-
+
// Add the location of the start of each subsequent line of the asm to the
// MDNode.
for (unsigned i = 0, e = StrVal.size()-1; i != e; ++i) {
@@ -1390,8 +1390,8 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str,
Locs.push_back(llvm::ConstantInt::get(CGF.Int32Ty,
LineLoc.getRawEncoding()));
}
- }
-
+ }
+
return llvm::MDNode::get(CGF.getLLVMContext(), Locs);
}
@@ -1441,7 +1441,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
std::vector<QualType> ResultRegQualTys;
std::vector<llvm::Type *> ResultRegTypes;
std::vector<llvm::Type *> ResultTruncRegTypes;
- std::vector<llvm::Type*> ArgTypes;
+ std::vector<llvm::Type *> ArgTypes;
std::vector<llvm::Value*> Args;
// Keep track of inout constraints.
@@ -1656,7 +1656,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
// the expression, do the conversion.
if (ResultRegTypes[i] != ResultTruncRegTypes[i]) {
llvm::Type *TruncTy = ResultTruncRegTypes[i];
-
+
// Truncate the integer result to the right size, note that TruncTy can be
// a pointer.
if (TruncTy->isFloatingPointTy())
@@ -1681,3 +1681,25 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
EmitStoreThroughLValue(RValue::get(Tmp), ResultRegDests[i]);
}
}
+
+void CodeGenFunction::EmitMSAsmStmt(const MSAsmStmt &S) {
+ // MS-style inline assembly is not fully supported, so sema emits a warning.
+ if (!CGM.getCodeGenOpts().EmitMicrosoftInlineAsm)
+ return;
+
+ assert (S.isSimple() && "CodeGen can only handle simple MSAsmStmts.");
+
+ std::vector<llvm::Value*> Args;
+ std::vector<llvm::Type *> ArgTypes;
+
+ std::string MachineClobbers = Target.getClobbers();
+
+ llvm::FunctionType *FTy =
+ llvm::FunctionType::get(VoidTy, ArgTypes, false);
+
+ llvm::InlineAsm *IA =
+ llvm::InlineAsm::get(FTy, *S.getAsmString(), MachineClobbers, true);
+ llvm::CallInst *Result = Builder.CreateCall(IA, Args);
+ Result->addAttribute(~0, llvm::Attribute::NoUnwind);
+ Result->addAttribute(~0, llvm::Attribute::IANSDialect);
+}
diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp
index 17a0537..cdaa26a 100644
--- a/lib/CodeGen/CGVTables.cpp
+++ b/lib/CodeGen/CGVTables.cpp
@@ -355,13 +355,14 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true);
#ifndef NDEBUG
- const CGFunctionInfo &CallFnInfo =
- CGM.getTypes().arrangeFunctionCall(ResultType, CallArgs, FPT->getExtInfo(),
+ const CGFunctionInfo &CallFnInfo =
+ CGM.getTypes().arrangeCXXMethodCall(CallArgs, FPT,
RequiredArgs::forPrototypePlus(FPT, 1));
assert(CallFnInfo.getRegParm() == FnInfo.getRegParm() &&
CallFnInfo.isNoReturn() == FnInfo.isNoReturn() &&
CallFnInfo.getCallingConvention() == FnInfo.getCallingConvention());
- assert(similar(CallFnInfo.getReturnInfo(), CallFnInfo.getReturnType(),
+ assert(isa<CXXDestructorDecl>(MD) || // ignore dtor return types
+ similar(CallFnInfo.getReturnInfo(), CallFnInfo.getReturnType(),
FnInfo.getReturnInfo(), FnInfo.getReturnType()));
assert(CallFnInfo.arg_size() == FnInfo.arg_size());
for (unsigned i = 0, e = FnInfo.arg_size(); i != e; ++i)
@@ -386,6 +387,9 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
if (!ResultType->isVoidType() && Slot.isNull())
CGM.getCXXABI().EmitReturnFromThunk(*this, RV, ResultType);
+ // Disable the final ARC autorelease.
+ AutoreleaseResult = false;
+
FinishFunction();
// Set the right linkage.
@@ -569,14 +573,13 @@ CodeGenVTables::CreateVTableInitializer(const CXXRecordDecl *RD,
if (cast<CXXMethodDecl>(GD.getDecl())->isPure()) {
// We have a pure virtual member function.
if (!PureVirtualFn) {
- llvm::FunctionType *Ty =
- llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
- PureVirtualFn =
- CGM.CreateRuntimeFunction(Ty, "__cxa_pure_virtual");
- PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn,
- Int8PtrTy);
+ llvm::FunctionType *Ty =
+ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
+ StringRef PureCallName = CGM.getCXXABI().GetPureVirtualCallName();
+ PureVirtualFn = CGM.CreateRuntimeFunction(Ty, PureCallName);
+ PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn,
+ CGM.Int8PtrTy);
}
-
Init = PureVirtualFn;
} else {
// Check if we should use a thunk.
diff --git a/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h
index ac704e7..a46f313 100644
--- a/lib/CodeGen/CGValue.h
+++ b/lib/CodeGen/CGValue.h
@@ -153,7 +153,7 @@ class LValue {
private:
void Initialize(QualType Type, Qualifiers Quals,
- CharUnits Alignment = CharUnits(),
+ CharUnits Alignment,
llvm::MDNode *TBAAInfo = 0) {
this->Type = Type;
this->Quals = Quals;
@@ -295,12 +295,12 @@ public:
/// access.
static LValue MakeBitfield(llvm::Value *BaseValue,
const CGBitFieldInfo &Info,
- QualType type) {
+ QualType type, CharUnits Alignment) {
LValue R;
R.LVType = BitField;
R.V = BaseValue;
R.BitFieldInfo = &Info;
- R.Initialize(type, type.getQualifiers());
+ R.Initialize(type, type.getQualifiers(), Alignment);
return R;
}
@@ -389,7 +389,8 @@ public:
return AV;
}
- static AggValueSlot forLValue(LValue LV, IsDestructed_t isDestructed,
+ static AggValueSlot forLValue(const LValue &LV,
+ IsDestructed_t isDestructed,
NeedsGCBarriers_t needsGC,
IsAliased_t isAliased,
IsZeroed_t isZeroed = IsNotZeroed) {
diff --git a/lib/CodeGen/CMakeLists.txt b/lib/CodeGen/CMakeLists.txt
index 7b1dbce..76be85f 100644
--- a/lib/CodeGen/CMakeLists.txt
+++ b/lib/CodeGen/CMakeLists.txt
@@ -8,8 +8,6 @@ set(LLVM_LINK_COMPONENTS
vectorize
)
-set(LLVM_USED_LIBS clangBasic clangAST clangFrontend)
-
add_clang_library(clangCodeGen
BackendUtil.cpp
CGBlocks.cpp
@@ -52,5 +50,19 @@ add_clang_library(clangCodeGen
TargetInfo.cpp
)
-add_dependencies(clangCodeGen ClangAttrClasses ClangAttrList ClangDeclNodes
- ClangStmtNodes)
+add_dependencies(clangCodeGen
+ ClangARMNeon
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangDiagnosticFrontend
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangCodeGen
+ clangBasic
+ clangAST
+ clangFrontend
+ )
diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp
index 2939062..1d02861 100644
--- a/lib/CodeGen/CodeGenFunction.cpp
+++ b/lib/CodeGen/CodeGenFunction.cpp
@@ -23,12 +23,12 @@
#include "clang/AST/StmtCXX.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/Intrinsics.h"
-#include "llvm/Support/MDBuilder.h"
+#include "llvm/MDBuilder.h"
#include "llvm/Target/TargetData.h"
using namespace clang;
using namespace CodeGen;
-CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
+CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
: CodeGenTypeCache(cgm), CGM(cgm),
Target(CGM.getContext().getTargetInfo()),
Builder(cgm.getModule().getContext()),
@@ -42,7 +42,8 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
TerminateHandler(0), TrapBB(0) {
CatchUndefined = getContext().getLangOpts().CatchUndefined;
- CGM.getCXXABI().getMangleContext().startNewFunction();
+ if (!suppressNewContext)
+ CGM.getCXXABI().getMangleContext().startNewFunction();
}
CodeGenFunction::~CodeGenFunction() {
@@ -251,6 +252,81 @@ void CodeGenFunction::EmitMCountInstrumentation() {
Builder.CreateCall(MCountFn);
}
+// OpenCL v1.2 s5.6.4.6 allows the compiler to store kernel argument
+// information in the program executable. The argument information stored
+// includes the argument name, its type, the address and access qualifiers used.
+// FIXME: Add type, address, and access qualifiers.
+static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn,
+ CodeGenModule &CGM,llvm::LLVMContext &Context,
+ llvm::SmallVector <llvm::Value*, 5> &kernelMDArgs) {
+
+ // Create MDNodes that represents the kernel arg metadata.
+ // Each MDNode is a list in the form of "key", N number of values which is
+ // the same number of values as their are kernel arguments.
+
+ // MDNode for the kernel argument names.
+ SmallVector<llvm::Value*, 8> argNames;
+ argNames.push_back(llvm::MDString::get(Context, "kernel_arg_name"));
+
+ for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
+ const ParmVarDecl *parm = FD->getParamDecl(i);
+
+ // Get argument name.
+ argNames.push_back(llvm::MDString::get(Context, parm->getName()));
+
+ }
+ // Add MDNode to the list of all metadata.
+ kernelMDArgs.push_back(llvm::MDNode::get(Context, argNames));
+}
+
+void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
+ llvm::Function *Fn)
+{
+ if (!FD->hasAttr<OpenCLKernelAttr>())
+ return;
+
+ llvm::LLVMContext &Context = getLLVMContext();
+
+ llvm::SmallVector <llvm::Value*, 5> kernelMDArgs;
+ kernelMDArgs.push_back(Fn);
+
+ if (CGM.getCodeGenOpts().EmitOpenCLArgMetadata)
+ GenOpenCLArgMetadata(FD, Fn, CGM, Context, kernelMDArgs);
+
+ if (FD->hasAttr<WorkGroupSizeHintAttr>()) {
+ llvm::SmallVector <llvm::Value*, 5> attrMDArgs;
+ attrMDArgs.push_back(llvm::MDString::get(Context, "work_group_size_hint"));
+ WorkGroupSizeHintAttr *attr = FD->getAttr<WorkGroupSizeHintAttr>();
+ llvm::Type *iTy = llvm::IntegerType::get(Context, 32);
+ attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+ llvm::APInt(32, (uint64_t)attr->getXDim())));
+ attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+ llvm::APInt(32, (uint64_t)attr->getYDim())));
+ attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+ llvm::APInt(32, (uint64_t)attr->getZDim())));
+ kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs));
+ }
+
+ if (FD->hasAttr<ReqdWorkGroupSizeAttr>()) {
+ llvm::SmallVector <llvm::Value*, 5> attrMDArgs;
+ attrMDArgs.push_back(llvm::MDString::get(Context, "reqd_work_group_size"));
+ ReqdWorkGroupSizeAttr *attr = FD->getAttr<ReqdWorkGroupSizeAttr>();
+ llvm::Type *iTy = llvm::IntegerType::get(Context, 32);
+ attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+ llvm::APInt(32, (uint64_t)attr->getXDim())));
+ attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+ llvm::APInt(32, (uint64_t)attr->getYDim())));
+ attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+ llvm::APInt(32, (uint64_t)attr->getZDim())));
+ kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs));
+ }
+
+ llvm::MDNode *kernelMDNode = llvm::MDNode::get(Context, kernelMDArgs);
+ llvm::NamedMDNode *OpenCLKernelMetadata =
+ CGM.getModule().getOrInsertNamedMetadata("opencl.kernels");
+ OpenCLKernelMetadata->addOperand(kernelMDNode);
+}
+
void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
llvm::Function *Fn,
const CGFunctionInfo &FnInfo,
@@ -279,14 +355,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (getContext().getLangOpts().OpenCL) {
// Add metadata for a kernel function.
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
- if (FD->hasAttr<OpenCLKernelAttr>()) {
- llvm::LLVMContext &Context = getLLVMContext();
- llvm::NamedMDNode *OpenCLMetadata =
- CGM.getModule().getOrInsertNamedMetadata("opencl.kernels");
-
- llvm::Value *Op = Fn;
- OpenCLMetadata->addOperand(llvm::MDNode::get(Context, Op));
- }
+ EmitOpenCLKernelMetadata(FD, Fn);
}
llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn);
@@ -537,7 +606,7 @@ bool CodeGenFunction::containsBreak(const Stmt *S) {
/// constant folds return true and set the boolean result in Result.
bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
bool &ResultBool) {
- llvm::APInt ResultInt;
+ llvm::APSInt ResultInt;
if (!ConstantFoldsToSimpleInteger(Cond, ResultInt))
return false;
@@ -549,7 +618,7 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
/// to a constant, or if it does but contains a label, return false. If it
/// constant folds return true and set the folded value.
bool CodeGenFunction::
-ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APInt &ResultInt) {
+ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &ResultInt) {
// FIXME: Rename and handle conversion of other evaluatable things
// to bool.
llvm::APSInt Int;
@@ -687,10 +756,10 @@ void CodeGenFunction::ErrorUnsupported(const Stmt *S, const char *Type,
/// emitNonZeroVLAInit - Emit the "zero" initialization of a
/// variable-length array whose elements have a non-zero bit-pattern.
///
+/// \param baseType the inner-most element type of the array
/// \param src - a char* pointing to the bit-pattern for a single
/// base element of the array
/// \param sizeInChars - the total size of the VLA, in chars
-/// \param align - the total alignment of the VLA
static void emitNonZeroVLAInit(CodeGenFunction &CGF, QualType baseType,
llvm::Value *dest, llvm::Value *src,
llvm::Value *sizeInChars) {
@@ -881,33 +950,49 @@ llvm::Value *CodeGenFunction::emitArrayLength(const ArrayType *origArrayType,
llvm::ConstantInt *zero = Builder.getInt32(0);
gepIndices.push_back(zero);
- // It's more efficient to calculate the count from the LLVM
- // constant-length arrays than to re-evaluate the array bounds.
uint64_t countFromCLAs = 1;
+ QualType eltType;
llvm::ArrayType *llvmArrayType =
- cast<llvm::ArrayType>(
+ dyn_cast<llvm::ArrayType>(
cast<llvm::PointerType>(addr->getType())->getElementType());
- while (true) {
+ while (llvmArrayType) {
assert(isa<ConstantArrayType>(arrayType));
assert(cast<ConstantArrayType>(arrayType)->getSize().getZExtValue()
== llvmArrayType->getNumElements());
gepIndices.push_back(zero);
countFromCLAs *= llvmArrayType->getNumElements();
+ eltType = arrayType->getElementType();
llvmArrayType =
dyn_cast<llvm::ArrayType>(llvmArrayType->getElementType());
- if (!llvmArrayType) break;
-
arrayType = getContext().getAsArrayType(arrayType->getElementType());
- assert(arrayType && "LLVM and Clang types are out-of-synch");
+ assert((!llvmArrayType || arrayType) &&
+ "LLVM and Clang types are out-of-synch");
}
- baseType = arrayType->getElementType();
+ if (arrayType) {
+ // From this point onwards, the Clang array type has been emitted
+ // as some other type (probably a packed struct). Compute the array
+ // size, and just emit the 'begin' expression as a bitcast.
+ while (arrayType) {
+ countFromCLAs *=
+ cast<ConstantArrayType>(arrayType)->getSize().getZExtValue();
+ eltType = arrayType->getElementType();
+ arrayType = getContext().getAsArrayType(eltType);
+ }
+
+ unsigned AddressSpace =
+ cast<llvm::PointerType>(addr->getType())->getAddressSpace();
+ llvm::Type *BaseType = ConvertType(eltType)->getPointerTo(AddressSpace);
+ addr = Builder.CreateBitCast(addr, BaseType, "array.begin");
+ } else {
+ // Create the actual GEP.
+ addr = Builder.CreateInBoundsGEP(addr, gepIndices, "array.begin");
+ }
- // Create the actual GEP.
- addr = Builder.CreateInBoundsGEP(addr, gepIndices, "array.begin");
+ baseType = eltType;
llvm::Value *numElements
= llvm::ConstantInt::get(SizeTy, countFromCLAs);
@@ -1071,7 +1156,8 @@ void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E,
llvm::Constant *Init) {
assert (Init && "Invalid DeclRefExpr initializer!");
if (CGDebugInfo *Dbg = getDebugInfo())
- Dbg->EmitGlobalVariable(E->getDecl(), Init);
+ if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo)
+ Dbg->EmitGlobalVariable(E->getDecl(), Init);
}
CodeGenFunction::PeepholeProtection
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 83f1e2d..ed3e43b 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -591,6 +591,11 @@ public:
/// we prefer to insert allocas.
llvm::AssertingVH<llvm::Instruction> AllocaInsertPt;
+ /// BoundsChecking - Emit run-time bounds checks. Higher values mean
+ /// potentially higher performance penalties.
+ unsigned char BoundsChecking;
+
+ /// CatchUndefined - Emit run-time checks to catch undefined behaviors.
bool CatchUndefined;
/// In ARC, whether we should autorelease the return value.
@@ -1192,8 +1197,18 @@ private:
llvm::BasicBlock *TerminateHandler;
llvm::BasicBlock *TrapBB;
+ /// Add a kernel metadata node to the named metadata node 'opencl.kernels'.
+ /// In the kernel metadata node, reference the kernel function and metadata
+ /// nodes for its optional attribute qualifiers (OpenCL 1.1 6.7.2):
+ /// - A node for the work_group_size_hint(X,Y,Z) qualifier contains string
+ /// "work_group_size_hint", and three 32-bit integers X, Y and Z.
+ /// - A node for the reqd_work_group_size(X,Y,Z) qualifier contains string
+ /// "reqd_work_group_size", and three 32-bit integers X, Y and Z.
+ void EmitOpenCLKernelMetadata(const FunctionDecl *FD,
+ llvm::Function *Fn);
+
public:
- CodeGenFunction(CodeGenModule &cgm);
+ CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext=false);
~CodeGenFunction();
CodeGenTypes &getTypes() const { return CGM.getTypes(); }
@@ -1305,6 +1320,7 @@ public:
const ObjCPropertyImplDecl *PID);
void generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
const ObjCPropertyImplDecl *propImpl,
+ const ObjCMethodDecl *GetterMothodDecl,
llvm::Constant *AtomicHelperFn);
void GenerateObjCCtorDtorMethod(ObjCImplementationDecl *IMP,
@@ -1560,6 +1576,7 @@ public:
return LValue::MakeAddr(V, T, Alignment, getContext(),
CGM.getTBAAInfo(T));
}
+
LValue MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T) {
CharUnits Alignment;
if (!T->isIncompleteType())
@@ -1616,8 +1633,8 @@ public:
///
/// \param IgnoreResult - True if the resulting value isn't used.
RValue EmitAnyExpr(const Expr *E,
- AggValueSlot AggSlot = AggValueSlot::ignored(),
- bool IgnoreResult = false);
+ AggValueSlot aggSlot = AggValueSlot::ignored(),
+ bool ignoreResult = false);
// EmitVAListRef - Emit a "reference" to a va_list; this is either the address
// or the value of the expression, depending on how va_list is defined.
@@ -1643,7 +1660,7 @@ public:
/// volatile.
void EmitAggregateCopy(llvm::Value *DestPtr, llvm::Value *SrcPtr,
QualType EltTy, bool isVolatile=false,
- unsigned Alignment = 0);
+ CharUnits Alignment = CharUnits::Zero());
/// StartBlock - Start new block named N. If insert block is a dummy block
/// then reuse it.
@@ -1964,6 +1981,7 @@ public:
void EmitCaseStmt(const CaseStmt &S);
void EmitCaseStmtRange(const CaseStmt &S);
void EmitAsmStmt(const AsmStmt &S);
+ void EmitMSAsmStmt(const MSAsmStmt &S);
void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S);
void EmitObjCAtTryStmt(const ObjCAtTryStmt &S);
@@ -2099,6 +2117,7 @@ public:
LValue EmitMemberExpr(const MemberExpr *E);
LValue EmitObjCIsaExpr(const ObjCIsaExpr *E);
LValue EmitCompoundLiteralLValue(const CompoundLiteralExpr *E);
+ LValue EmitInitListLValue(const InitListExpr *E);
LValue EmitConditionalOperatorLValue(const AbstractConditionalOperator *E);
LValue EmitCastLValue(const CastExpr *E);
LValue EmitNullInitializationLValue(const CXXScalarValueInitExpr *E);
@@ -2143,9 +2162,6 @@ public:
llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar);
- LValue EmitLValueForAnonRecordField(llvm::Value* Base,
- const IndirectFieldDecl* Field,
- unsigned CVRQualifiers);
LValue EmitLValueForField(LValue Base, const FieldDecl* Field);
/// EmitLValueForFieldInitialization - Like EmitLValueForField, except that
@@ -2158,9 +2174,6 @@ public:
llvm::Value* Base, const ObjCIvarDecl *Ivar,
unsigned CVRQualifiers);
- LValue EmitLValueForBitfield(llvm::Value* Base, const FieldDecl* Field,
- unsigned CVRQualifiers);
-
LValue EmitCXXConstructLValue(const CXXConstructExpr *E);
LValue EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E);
LValue EmitLambdaLValue(const LambdaExpr *E);
@@ -2259,12 +2272,11 @@ public:
llvm::Value *BuildVector(ArrayRef<llvm::Value*> Ops);
llvm::Value *EmitX86BuiltinExpr(unsigned BuiltinID, const CallExpr *E);
- llvm::Value *EmitHexagonBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
llvm::Value *EmitPPCBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
llvm::Value *EmitObjCProtocolExpr(const ObjCProtocolExpr *E);
llvm::Value *EmitObjCStringLiteral(const ObjCStringLiteral *E);
- llvm::Value *EmitObjCNumericLiteral(const ObjCNumericLiteral *E);
+ llvm::Value *EmitObjCBoxedExpr(const ObjCBoxedExpr *E);
llvm::Value *EmitObjCArrayLiteral(const ObjCArrayLiteral *E);
llvm::Value *EmitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
llvm::Value *EmitObjCCollectionLiteral(const Expr *E,
@@ -2359,7 +2371,7 @@ public:
/// EmitAggExpr - Emit the computation of the specified expression
/// of aggregate type. The result is computed into the given slot,
/// which may be null to indicate that the value is not needed.
- void EmitAggExpr(const Expr *E, AggValueSlot AS, bool IgnoreResult = false);
+ void EmitAggExpr(const Expr *E, AggValueSlot AS);
/// EmitAggExprToLValue - Emit the computation of the specified expression of
/// aggregate type into a temporary LValue.
@@ -2411,10 +2423,9 @@ public:
void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr,
bool PerformInit);
- /// EmitCXXGlobalDtorRegistration - Emits a call to register the global ptr
- /// with the C++ runtime so that its destructor will be called at exit.
- void EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
- llvm::Constant *DeclPtr);
+ /// Call atexit() with a function that passes the given argument to
+ /// the given function.
+ void registerGlobalDtorWithAtExit(llvm::Constant *fn, llvm::Constant *addr);
/// Emit code in this function to perform a guarded variable
/// initialization. Guarded initializations are used when it's not
@@ -2497,7 +2508,7 @@ public:
/// ConstantFoldsToSimpleInteger - If the specified expression does not fold
/// to a constant, or if it does but contains a label, return false. If it
/// constant folds return true and set the folded value.
- bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APInt &Result);
+ bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result);
/// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an
/// if statement) to the specified blocks. Based on the condition, this might
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index 9a55c08..3ae3c52 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -102,14 +102,16 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO,
if (LangOpts.CUDA)
createCUDARuntime();
- // Enable TBAA unless it's suppressed.
- if (!CodeGenOpts.RelaxedAliasing && CodeGenOpts.OptimizationLevel > 0)
- TBAA = new CodeGenTBAA(Context, VMContext, getLangOpts(),
+ // Enable TBAA unless it's suppressed. ThreadSanitizer needs TBAA even at O0.
+ if (LangOpts.ThreadSanitizer ||
+ (!CodeGenOpts.RelaxedAliasing && CodeGenOpts.OptimizationLevel > 0))
+ TBAA = new CodeGenTBAA(Context, VMContext, CodeGenOpts, getLangOpts(),
ABI.getMangleContext());
// If debug info or coverage generation is enabled, create the CGDebugInfo
// object.
- if (CodeGenOpts.DebugInfo || CodeGenOpts.EmitGcovArcs ||
+ if (CodeGenOpts.DebugInfo != CodeGenOptions::NoDebugInfo ||
+ CodeGenOpts.EmitGcovArcs ||
CodeGenOpts.EmitGcovNotes)
DebugInfo = new CGDebugInfo(*this);
@@ -133,10 +135,22 @@ CodeGenModule::~CodeGenModule() {
}
void CodeGenModule::createObjCRuntime() {
- if (!LangOpts.NeXTRuntime)
+ // This is just isGNUFamily(), but we want to force implementors of
+ // new ABIs to decide how best to do this.
+ switch (LangOpts.ObjCRuntime.getKind()) {
+ case ObjCRuntime::GNUstep:
+ case ObjCRuntime::GCC:
+ case ObjCRuntime::ObjFW:
ObjCRuntime = CreateGNUObjCRuntime(*this);
- else
+ return;
+
+ case ObjCRuntime::FragileMacOSX:
+ case ObjCRuntime::MacOSX:
+ case ObjCRuntime::iOS:
ObjCRuntime = CreateMacObjCRuntime(*this);
+ return;
+ }
+ llvm_unreachable("bad runtime kind");
}
void CodeGenModule::createOpenCLRuntime() {
@@ -245,6 +259,45 @@ void CodeGenModule::setGlobalVisibility(llvm::GlobalValue *GV,
GV->setVisibility(GetLLVMVisibility(LV.visibility()));
}
+static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(StringRef S) {
+ return llvm::StringSwitch<llvm::GlobalVariable::ThreadLocalMode>(S)
+ .Case("global-dynamic", llvm::GlobalVariable::GeneralDynamicTLSModel)
+ .Case("local-dynamic", llvm::GlobalVariable::LocalDynamicTLSModel)
+ .Case("initial-exec", llvm::GlobalVariable::InitialExecTLSModel)
+ .Case("local-exec", llvm::GlobalVariable::LocalExecTLSModel);
+}
+
+static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(
+ CodeGenOptions::TLSModel M) {
+ switch (M) {
+ case CodeGenOptions::GeneralDynamicTLSModel:
+ return llvm::GlobalVariable::GeneralDynamicTLSModel;
+ case CodeGenOptions::LocalDynamicTLSModel:
+ return llvm::GlobalVariable::LocalDynamicTLSModel;
+ case CodeGenOptions::InitialExecTLSModel:
+ return llvm::GlobalVariable::InitialExecTLSModel;
+ case CodeGenOptions::LocalExecTLSModel:
+ return llvm::GlobalVariable::LocalExecTLSModel;
+ }
+ llvm_unreachable("Invalid TLS model!");
+}
+
+void CodeGenModule::setTLSMode(llvm::GlobalVariable *GV,
+ const VarDecl &D) const {
+ assert(D.isThreadSpecified() && "setting TLS mode on non-TLS var!");
+
+ llvm::GlobalVariable::ThreadLocalMode TLM;
+ TLM = GetLLVMTLSModel(CodeGenOpts.DefaultTLSModel);
+
+ // Override the TLS model if it is explicitly specified.
+ if (D.hasAttr<TLSModelAttr>()) {
+ const TLSModelAttr *Attr = D.getAttr<TLSModelAttr>();
+ TLM = GetLLVMTLSModel(Attr->getModel());
+ }
+
+ GV->setThreadLocalMode(TLM);
+}
+
/// Set the symbol visibility of type information (vtable and RTTI)
/// associated with the given type.
void CodeGenModule::setTypeVisibility(llvm::GlobalValue *GV,
@@ -334,7 +387,8 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
else if (const CXXDestructorDecl *D = dyn_cast<CXXDestructorDecl>(ND))
getCXXABI().getMangleContext().mangleCXXDtor(D, GD.getDtorType(), Out);
else if (const BlockDecl *BD = dyn_cast<BlockDecl>(ND))
- getCXXABI().getMangleContext().mangleBlock(BD, Out);
+ getCXXABI().getMangleContext().mangleBlock(BD, Out,
+ dyn_cast_or_null<VarDecl>(initializedGlobalDecl.getDecl()));
else
getCXXABI().getMangleContext().mangleName(ND, Out);
@@ -355,7 +409,8 @@ void CodeGenModule::getBlockMangledName(GlobalDecl GD, MangleBuffer &Buffer,
const Decl *D = GD.getDecl();
llvm::raw_svector_ostream Out(Buffer.getBuffer());
if (D == 0)
- MangleCtx.mangleGlobalBlock(BD, Out);
+ MangleCtx.mangleGlobalBlock(BD,
+ dyn_cast_or_null<VarDecl>(initializedGlobalDecl.getDecl()), Out);
else if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(D))
MangleCtx.mangleCtorBlock(CD, GD.getCtorType(), BD, Out);
else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(D))
@@ -474,8 +529,7 @@ void CodeGenModule::SetLLVMFunctionAttributes(const Decl *D,
unsigned CallingConv;
AttributeListType AttributeList;
ConstructAttributeList(Info, D, AttributeList, CallingConv);
- F->setAttributes(llvm::AttrListPtr::get(AttributeList.begin(),
- AttributeList.size()));
+ F->setAttributes(llvm::AttrListPtr::get(AttributeList));
F->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
}
@@ -493,7 +547,7 @@ static bool hasUnwindExceptions(const LangOptions &LangOpts) {
// If ObjC exceptions are enabled, this depends on the ABI.
if (LangOpts.ObjCExceptions) {
- if (!LangOpts.ObjCNonFragileABI) return false;
+ return LangOpts.ObjCRuntime.hasUnwindExceptions();
}
return true;
@@ -517,10 +571,14 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
F->addFnAttr(llvm::Attribute::NoInline);
// (noinline wins over always_inline, and we can't specify both in IR)
- if (D->hasAttr<AlwaysInlineAttr>() &&
+ if ((D->hasAttr<AlwaysInlineAttr>() || D->hasAttr<ForceInlineAttr>()) &&
!F->hasFnAttr(llvm::Attribute::NoInline))
F->addFnAttr(llvm::Attribute::AlwaysInline);
+ // FIXME: Communicate hot and cold attributes to LLVM more directly.
+ if (D->hasAttr<ColdAttr>())
+ F->addFnAttr(llvm::Attribute::OptimizeForSize);
+
if (isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D))
F->setUnnamedAddr(true);
@@ -652,7 +710,7 @@ void CodeGenModule::EmitDeferred() {
if (!DeferredVTables.empty()) {
const CXXRecordDecl *RD = DeferredVTables.back();
DeferredVTables.pop_back();
- getVTables().GenerateClassData(getVTableLinkage(RD), RD);
+ getCXXABI().EmitVTables(RD);
continue;
}
@@ -930,7 +988,7 @@ CodeGenModule::shouldEmitFunction(const FunctionDecl *F) {
if (getFunctionLinkage(F) != llvm::Function::AvailableExternallyLinkage)
return true;
if (CodeGenOpts.OptimizationLevel == 0 &&
- !F->hasAttr<AlwaysInlineAttr>())
+ !F->hasAttr<AlwaysInlineAttr>() && !F->hasAttr<ForceInlineAttr>())
return false;
// PR9614. Avoid cases where the source code is lying to us. An available
// externally function should have an equivalent function somewhere else,
@@ -1054,6 +1112,7 @@ CodeGenModule::GetOrCreateLLVMFunction(StringRef MangledName,
} else if (getLangOpts().CPlusPlus && D.getDecl()) {
// Look for a declaration that's lexically in a record.
const FunctionDecl *FD = cast<FunctionDecl>(D.getDecl());
+ FD = FD->getMostRecentDecl();
do {
if (isa<CXXRecordDecl>(FD->getLexicalDeclContext())) {
if (FD->isImplicit() && !ForVTable) {
@@ -1166,11 +1225,12 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName,
DeferredDecls.erase(DDI);
}
+ unsigned AddrSpace = GetGlobalVarAddressSpace(D, Ty->getAddressSpace());
llvm::GlobalVariable *GV =
new llvm::GlobalVariable(getModule(), Ty->getElementType(), false,
llvm::GlobalValue::ExternalLinkage,
0, MangledName, 0,
- false, Ty->getAddressSpace());
+ llvm::GlobalVariable::NotThreadLocal, AddrSpace);
// Handle things which are present even on external declarations.
if (D) {
@@ -1193,10 +1253,14 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName,
GV->setVisibility(GetLLVMVisibility(LV.visibility()));
}
- GV->setThreadLocal(D->isThreadSpecified());
+ if (D->isThreadSpecified())
+ setTLSMode(GV, *D);
}
- return GV;
+ if (AddrSpace != Ty->getAddressSpace())
+ return llvm::ConstantExpr::getBitCast(GV, Ty);
+ else
+ return GV;
}
@@ -1286,7 +1350,7 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) {
void CodeGenModule::EmitVTable(CXXRecordDecl *Class, bool DefinitionRequired) {
if (DefinitionRequired)
- getVTables().GenerateClassData(getVTableLinkage(Class), Class);
+ getCXXABI().EmitVTables(Class);
}
llvm::GlobalVariable::LinkageTypes
@@ -1481,6 +1545,20 @@ CodeGenModule::MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D,
return llvmInit;
}
+unsigned CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D,
+ unsigned AddrSpace) {
+ if (LangOpts.CUDA && CodeGenOpts.CUDAIsDevice) {
+ if (D->hasAttr<CUDAConstantAttr>())
+ AddrSpace = getContext().getTargetAddressSpace(LangAS::cuda_constant);
+ else if (D->hasAttr<CUDASharedAttr>())
+ AddrSpace = getContext().getTargetAddressSpace(LangAS::cuda_shared);
+ else
+ AddrSpace = getContext().getTargetAddressSpace(LangAS::cuda_device);
+ }
+
+ return AddrSpace;
+}
+
void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
llvm::Constant *Init = 0;
QualType ASTTy = D->getType();
@@ -1511,8 +1589,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
// FIXME: It does so in a global constructor, which is *not* what we
// want.
- if (!Init)
+ if (!Init) {
+ initializedGlobalDecl = GlobalDecl(D);
Init = EmitConstantInit(*InitDecl);
+ }
if (!Init) {
QualType T = InitExpr->getType();
if (D->getType()->isReferenceType())
@@ -1560,7 +1640,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
if (GV == 0 ||
GV->getType()->getElementType() != InitType ||
GV->getType()->getAddressSpace() !=
- getContext().getTargetAddressSpace(ASTTy)) {
+ GetGlobalVarAddressSpace(D, getContext().getTargetAddressSpace(ASTTy))) {
// Move the old entry aside so that we'll create a new one.
Entry->setName(StringRef());
@@ -1604,7 +1684,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
// Emit global variable debug information.
if (CGDebugInfo *DI = getModuleDebugInfo())
- DI->EmitGlobalVariable(GV, D);
+ if (getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo)
+ DI->EmitGlobalVariable(GV, D);
}
llvm::GlobalValue::LinkageTypes
@@ -1710,8 +1791,7 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old,
ArgList.clear();
if (!NewCall->getType()->isVoidTy())
NewCall->takeName(CI);
- NewCall->setAttributes(llvm::AttrListPtr::get(AttrVec.begin(),
- AttrVec.end()));
+ NewCall->setAttributes(llvm::AttrListPtr::get(AttrVec));
NewCall->setCallingConv(CI->getCallingConv());
// Finally, remove the old call, replacing any uses with the new one.
@@ -2059,7 +2139,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) {
std::string StringClass(getLangOpts().ObjCConstantStringClass);
llvm::Type *Ty = getTypes().ConvertType(getContext().IntTy);
llvm::Constant *GV;
- if (LangOpts.ObjCNonFragileABI) {
+ if (LangOpts.ObjCRuntime.isNonFragile()) {
std::string str =
StringClass.empty() ? "OBJC_CLASS_$_NSConstantString"
: "OBJC_CLASS_$_" + StringClass;
@@ -2104,7 +2184,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) {
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false);
+ ICIS_NoInit);
Field->setAccess(AS_public);
D->addDecl(Field);
}
@@ -2147,7 +2227,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) {
"_unnamed_nsstring_");
// FIXME. Fix section.
if (const char *Sect =
- LangOpts.ObjCNonFragileABI
+ LangOpts.ObjCRuntime.isNonFragile()
? getContext().getTargetInfo().getNSStringNonFragileABISection()
: getContext().getTargetInfo().getNSStringSection())
GV->setSection(Sect);
@@ -2179,7 +2259,7 @@ QualType CodeGenModule::getObjCFastEnumerationStateType() {
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false);
+ ICIS_NoInit);
Field->setAccess(AS_public);
D->addDecl(Field);
}
@@ -2506,14 +2586,8 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
// Forward declarations, no (immediate) code generation.
case Decl::ObjCInterface:
+ case Decl::ObjCCategory:
break;
-
- case Decl::ObjCCategory: {
- ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(D);
- if (CD->IsClassExtension() && CD->hasSynthBitfield())
- Context.ResetObjCLayout(CD->getClassInterface());
- break;
- }
case Decl::ObjCProtocol: {
ObjCProtocolDecl *Proto = cast<ObjCProtocolDecl>(D);
@@ -2530,8 +2604,6 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case Decl::ObjCImplementation: {
ObjCImplementationDecl *OMD = cast<ObjCImplementationDecl>(D);
- if (LangOpts.ObjCNonFragileABI2 && OMD->hasSynthBitfield())
- Context.ResetObjCLayout(OMD->getClassInterface());
EmitObjCPropertyImplementations(OMD);
EmitObjCIvarInitializations(OMD);
ObjCRuntime->GenerateClass(OMD);
@@ -2564,7 +2636,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
const std::string &S = getModule().getModuleInlineAsm();
if (S.empty())
getModule().setModuleInlineAsm(AsmString);
- else if (*--S.end() == '\n')
+ else if (S.end()[-1] == '\n')
getModule().setModuleInlineAsm(S + AsmString.str());
else
getModule().setModuleInlineAsm(S + '\n' + AsmString.str());
diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h
index 38f5008..d6ff50d 100644
--- a/lib/CodeGen/CodeGenModule.h
+++ b/lib/CodeGen/CodeGenModule.h
@@ -138,6 +138,7 @@ namespace CodeGen {
union {
unsigned char PointerAlignInBytes;
unsigned char PointerSizeInBytes;
+ unsigned char SizeSizeInBytes; // sizeof(size_t)
};
};
@@ -350,6 +351,8 @@ class CodeGenModule : public CodeGenTypeCache {
struct {
int GlobalUniqueCount;
} Block;
+
+ GlobalDecl initializedGlobalDecl;
/// @}
public:
@@ -471,6 +474,10 @@ public:
/// GlobalValue.
void setGlobalVisibility(llvm::GlobalValue *GV, const NamedDecl *D) const;
+ /// setTLSMode - Set the TLS mode for the given LLVM GlobalVariable
+ /// for the thread-local variable declaration D.
+ void setTLSMode(llvm::GlobalVariable *GV, const VarDecl &D) const;
+
/// TypeVisibilityKind - The kind of global variable that is passed to
/// setTypeVisibility
enum TypeVisibilityKind {
@@ -516,6 +523,12 @@ public:
CreateOrReplaceCXXRuntimeVariable(StringRef Name, llvm::Type *Ty,
llvm::GlobalValue::LinkageTypes Linkage);
+ /// GetGlobalVarAddressSpace - Return the address space of the underlying
+ /// global variable for D, as determined by its declaration. Normally this
+ /// is the same as the address space of D's type, but in CUDA, address spaces
+ /// are associated with declarations, not types.
+ unsigned GetGlobalVarAddressSpace(const VarDecl *D, unsigned AddrSpace);
+
/// GetAddrOfGlobalVar - Return the llvm::Constant for the address of the
/// given global variable. If Ty is non-null and if the global doesn't exist,
/// then it will be greated with the specified type instead of whatever the
@@ -580,7 +593,7 @@ public:
/// getUniqueBlockCount - Fetches the global unique block count.
int getUniqueBlockCount() { return ++Block.GlobalUniqueCount; }
-
+
/// getBlockDescriptorType - Fetches the type of a generic block
/// descriptor.
llvm::Type *getBlockDescriptorType();
diff --git a/lib/CodeGen/CodeGenTBAA.cpp b/lib/CodeGen/CodeGenTBAA.cpp
index a3cadcf..bab60af 100644
--- a/lib/CodeGen/CodeGenTBAA.cpp
+++ b/lib/CodeGen/CodeGenTBAA.cpp
@@ -18,6 +18,7 @@
#include "CodeGenTBAA.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
+#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/LLVMContext.h"
#include "llvm/Metadata.h"
#include "llvm/Constants.h"
@@ -26,8 +27,9 @@ using namespace clang;
using namespace CodeGen;
CodeGenTBAA::CodeGenTBAA(ASTContext &Ctx, llvm::LLVMContext& VMContext,
+ const CodeGenOptions &CGO,
const LangOptions &Features, MangleContext &MContext)
- : Context(Ctx), VMContext(VMContext), Features(Features), MContext(MContext),
+ : Context(Ctx), CodeGenOpts(CGO), Features(Features), MContext(MContext),
MDHelper(VMContext), Root(0), Char(0) {
}
@@ -74,6 +76,10 @@ static bool TypeHasMayAlias(QualType QTy) {
llvm::MDNode *
CodeGenTBAA::getTBAAInfo(QualType QTy) {
+ // At -O0 TBAA is not emitted for regular types.
+ if (CodeGenOpts.OptimizationLevel == 0 || CodeGenOpts.RelaxedAliasing)
+ return NULL;
+
// If the type has the may_alias attribute (even on a typedef), it is
// effectively in the general char alias class.
if (TypeHasMayAlias(QTy))
diff --git a/lib/CodeGen/CodeGenTBAA.h b/lib/CodeGen/CodeGenTBAA.h
index 4a97852..c17a5cf 100644
--- a/lib/CodeGen/CodeGenTBAA.h
+++ b/lib/CodeGen/CodeGenTBAA.h
@@ -16,8 +16,8 @@
#define CLANG_CODEGEN_CODEGENTBAA_H
#include "clang/Basic/LLVM.h"
+#include "llvm/MDBuilder.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/Support/MDBuilder.h"
namespace llvm {
class LLVMContext;
@@ -26,6 +26,7 @@ namespace llvm {
namespace clang {
class ASTContext;
+ class CodeGenOptions;
class LangOptions;
class MangleContext;
class QualType;
@@ -38,7 +39,7 @@ namespace CodeGen {
/// while lowering AST types to LLVM types.
class CodeGenTBAA {
ASTContext &Context;
- llvm::LLVMContext& VMContext;
+ const CodeGenOptions &CodeGenOpts;
const LangOptions &Features;
MangleContext &MContext;
@@ -61,6 +62,7 @@ class CodeGenTBAA {
public:
CodeGenTBAA(ASTContext &Ctx, llvm::LLVMContext &VMContext,
+ const CodeGenOptions &CGO,
const LangOptions &Features,
MangleContext &MContext);
~CodeGenTBAA();
diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp
index 41fd536..9a78dae 100644
--- a/lib/CodeGen/CodeGenTypes.cpp
+++ b/lib/CodeGen/CodeGenTypes.cpp
@@ -474,11 +474,11 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
// build it.
const CGFunctionInfo *FI;
if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FT)) {
- FI = &arrangeFunctionType(
+ FI = &arrangeFreeFunctionType(
CanQual<FunctionProtoType>::CreateUnsafe(QualType(FPT, 0)));
} else {
const FunctionNoProtoType *FNPT = cast<FunctionNoProtoType>(FT);
- FI = &arrangeFunctionType(
+ FI = &arrangeFreeFunctionType(
CanQual<FunctionNoProtoType>::CreateUnsafe(QualType(FNPT, 0)));
}
diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h
index ba2b3ae..3c29d2d 100644
--- a/lib/CodeGen/CodeGenTypes.h
+++ b/lib/CodeGen/CodeGenTypes.h
@@ -189,26 +189,32 @@ public:
const CGFunctionInfo &arrangeCXXDestructor(const CXXDestructorDecl *D,
CXXDtorType Type);
- const CGFunctionInfo &arrangeFunctionCall(const CallArgList &Args,
- const FunctionType *Ty);
- const CGFunctionInfo &arrangeFunctionCall(QualType ResTy,
- const CallArgList &args,
- const FunctionType::ExtInfo &info,
- RequiredArgs required);
-
- const CGFunctionInfo &arrangeFunctionType(CanQual<FunctionProtoType> Ty);
- const CGFunctionInfo &arrangeFunctionType(CanQual<FunctionNoProtoType> Ty);
+ const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args,
+ const FunctionType *Ty);
+ const CGFunctionInfo &arrangeFreeFunctionCall(QualType ResTy,
+ const CallArgList &args,
+ FunctionType::ExtInfo info,
+ RequiredArgs required);
+
+ const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args,
+ const FunctionProtoType *type,
+ RequiredArgs required);
+
+ const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionProtoType> Ty);
+ const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionNoProtoType> Ty);
const CGFunctionInfo &arrangeCXXMethodType(const CXXRecordDecl *RD,
const FunctionProtoType *FTP);
- /// Retrieves the ABI information for the given function signature.
- /// This is the "core" routine to which all the others defer.
+ /// "Arrange" the LLVM information for a call or type with the given
+ /// signature. This is largely an internal method; other clients
+ /// should use one of the above routines, which ultimately defer to
+ /// this.
///
/// \param argTypes - must all actually be canonical as params
- const CGFunctionInfo &arrangeFunctionType(CanQualType returnType,
- ArrayRef<CanQualType> argTypes,
- const FunctionType::ExtInfo &info,
- RequiredArgs args);
+ const CGFunctionInfo &arrangeLLVMFunctionInfo(CanQualType returnType,
+ ArrayRef<CanQualType> argTypes,
+ FunctionType::ExtInfo info,
+ RequiredArgs args);
/// \brief Compute a new LLVM record layout object for the given record.
CGRecordLayout *ComputeRecordLayout(const RecordDecl *D,
diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp
index 98f67f3..0b7ce36 100644
--- a/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/lib/CodeGen/ItaniumCXXABI.cpp
@@ -20,6 +20,7 @@
#include "CGCXXABI.h"
#include "CGRecordLayout.h"
+#include "CGVTables.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include <clang/AST/Mangle.h>
@@ -48,10 +49,6 @@ protected:
return PtrDiffTy;
}
- bool NeedsArrayCookie(const CXXNewExpr *expr);
- bool NeedsArrayCookie(const CXXDeleteExpr *expr,
- QualType elementType);
-
public:
ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool IsARM = false) :
CGCXXABI(CGM), PtrDiffTy(0), IsARM(IsARM) { }
@@ -111,19 +108,24 @@ public:
void EmitInstanceFunctionProlog(CodeGenFunction &CGF);
- CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
+ StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; }
+
+ CharUnits getArrayCookieSizeImpl(QualType elementType);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
- void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
- const CXXDeleteExpr *expr,
- QualType ElementType, llvm::Value *&NumElements,
- llvm::Value *&AllocPtr, CharUnits &CookieSize);
+ llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF,
+ llvm::Value *allocPtr,
+ CharUnits cookieSize);
void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::GlobalVariable *DeclPtr, bool PerformInit);
+ void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,
+ llvm::Constant *addr);
+
+ void EmitVTables(const CXXRecordDecl *Class);
};
class ARMCXXABI : public ItaniumCXXABI {
@@ -148,16 +150,14 @@ public:
void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResTy);
- CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
+ CharUnits getArrayCookieSizeImpl(QualType elementType);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
- void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
- const CXXDeleteExpr *expr,
- QualType ElementType, llvm::Value *&NumElements,
- llvm::Value *&AllocPtr, CharUnits &CookieSize);
+ llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF, llvm::Value *allocPtr,
+ CharUnits cookieSize);
private:
/// \brief Returns true if the given instance method is one of the
@@ -796,54 +796,11 @@ void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
/************************** Array allocation cookies **************************/
-bool ItaniumCXXABI::NeedsArrayCookie(const CXXNewExpr *expr) {
- // If the class's usual deallocation function takes two arguments,
- // it needs a cookie.
- if (expr->doesUsualArrayDeleteWantSize())
- return true;
-
- // Automatic Reference Counting:
- // We need an array cookie for pointers with strong or weak lifetime.
- QualType AllocatedType = expr->getAllocatedType();
- if (getContext().getLangOpts().ObjCAutoRefCount &&
- AllocatedType->isObjCLifetimeType()) {
- switch (AllocatedType.getObjCLifetime()) {
- case Qualifiers::OCL_None:
- case Qualifiers::OCL_ExplicitNone:
- case Qualifiers::OCL_Autoreleasing:
- return false;
-
- case Qualifiers::OCL_Strong:
- case Qualifiers::OCL_Weak:
- return true;
- }
- }
-
- // Otherwise, if the class has a non-trivial destructor, it always
- // needs a cookie.
- const CXXRecordDecl *record =
- AllocatedType->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
- return (record && !record->hasTrivialDestructor());
-}
-
-bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr,
- QualType elementType) {
- // If the class's usual deallocation function takes two arguments,
- // it needs a cookie.
- if (expr->doesUsualArrayDeleteWantSize())
- return true;
-
- return elementType.isDestructedType();
-}
-
-CharUnits ItaniumCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
- if (!NeedsArrayCookie(expr))
- return CharUnits::Zero();
-
- // Padding is the maximum of sizeof(size_t) and alignof(elementType)
- ASTContext &Ctx = getContext();
- return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()),
- Ctx.getTypeAlignInChars(expr->getAllocatedType()));
+CharUnits ItaniumCXXABI::getArrayCookieSizeImpl(QualType elementType) {
+ // The array cookie is a size_t; pad that up to the element alignment.
+ // The cookie is actually right-justified in that space.
+ return std::max(CharUnits::fromQuantity(CGM.SizeSizeInBytes),
+ CGM.getContext().getTypeAlignInChars(elementType));
}
llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
@@ -851,7 +808,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
- assert(NeedsArrayCookie(expr));
+ assert(requiresArrayCookie(expr));
unsigned AS = cast<llvm::PointerType>(NewPtr->getType())->getAddressSpace();
@@ -862,6 +819,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
// The size of the cookie.
CharUnits CookieSize =
std::max(SizeSize, Ctx.getTypeAlignInChars(ElementType));
+ assert(CookieSize == getArrayCookieSizeImpl(ElementType));
// Compute an offset to the cookie.
llvm::Value *CookiePtr = NewPtr;
@@ -882,53 +840,25 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
CookieSize.getQuantity());
}
-void ItaniumCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
- llvm::Value *Ptr,
- const CXXDeleteExpr *expr,
- QualType ElementType,
- llvm::Value *&NumElements,
- llvm::Value *&AllocPtr,
- CharUnits &CookieSize) {
- // Derive a char* in the same address space as the pointer.
- unsigned AS = cast<llvm::PointerType>(Ptr->getType())->getAddressSpace();
- llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS);
-
- // If we don't need an array cookie, bail out early.
- if (!NeedsArrayCookie(expr, ElementType)) {
- AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
- NumElements = 0;
- CookieSize = CharUnits::Zero();
- return;
- }
-
- QualType SizeTy = getContext().getSizeType();
- CharUnits SizeSize = getContext().getTypeSizeInChars(SizeTy);
- llvm::Type *SizeLTy = CGF.ConvertType(SizeTy);
-
- CookieSize
- = std::max(SizeSize, getContext().getTypeAlignInChars(ElementType));
-
- CharUnits NumElementsOffset = CookieSize - SizeSize;
-
- // Compute the allocated pointer.
- AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
- AllocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr,
- -CookieSize.getQuantity());
-
- llvm::Value *NumElementsPtr = AllocPtr;
- if (!NumElementsOffset.isZero())
- NumElementsPtr =
- CGF.Builder.CreateConstInBoundsGEP1_64(NumElementsPtr,
- NumElementsOffset.getQuantity());
- NumElementsPtr =
- CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo(AS));
- NumElements = CGF.Builder.CreateLoad(NumElementsPtr);
+llvm::Value *ItaniumCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
+ llvm::Value *allocPtr,
+ CharUnits cookieSize) {
+ // The element size is right-justified in the cookie.
+ llvm::Value *numElementsPtr = allocPtr;
+ CharUnits numElementsOffset =
+ cookieSize - CharUnits::fromQuantity(CGF.SizeSizeInBytes);
+ if (!numElementsOffset.isZero())
+ numElementsPtr =
+ CGF.Builder.CreateConstInBoundsGEP1_64(numElementsPtr,
+ numElementsOffset.getQuantity());
+
+ unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace();
+ numElementsPtr =
+ CGF.Builder.CreateBitCast(numElementsPtr, CGF.SizeTy->getPointerTo(AS));
+ return CGF.Builder.CreateLoad(numElementsPtr);
}
-CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
- if (!NeedsArrayCookie(expr))
- return CharUnits::Zero();
-
+CharUnits ARMCXXABI::getArrayCookieSizeImpl(QualType elementType) {
// On ARM, the cookie is always:
// struct array_cookie {
// std::size_t element_size; // element_size != 0
@@ -936,7 +866,7 @@ CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
// };
// TODO: what should we do if the allocated type actually wants
// greater alignment?
- return getContext().getTypeSizeInChars(getContext().getSizeType()) * 2;
+ return CharUnits::fromQuantity(2 * CGM.SizeSizeInBytes);
}
llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
@@ -944,7 +874,7 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
- assert(NeedsArrayCookie(expr));
+ assert(requiresArrayCookie(expr));
// NewPtr is a char*.
@@ -975,44 +905,18 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
CookieSize.getQuantity());
}
-void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
- llvm::Value *Ptr,
- const CXXDeleteExpr *expr,
- QualType ElementType,
- llvm::Value *&NumElements,
- llvm::Value *&AllocPtr,
- CharUnits &CookieSize) {
- // Derive a char* in the same address space as the pointer.
- unsigned AS = cast<llvm::PointerType>(Ptr->getType())->getAddressSpace();
- llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS);
-
- // If we don't need an array cookie, bail out early.
- if (!NeedsArrayCookie(expr, ElementType)) {
- AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
- NumElements = 0;
- CookieSize = CharUnits::Zero();
- return;
- }
-
- QualType SizeTy = getContext().getSizeType();
- CharUnits SizeSize = getContext().getTypeSizeInChars(SizeTy);
- llvm::Type *SizeLTy = CGF.ConvertType(SizeTy);
-
- // The cookie size is always 2 * sizeof(size_t).
- CookieSize = 2 * SizeSize;
-
- // The allocated pointer is the input ptr, minus that amount.
- AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
- AllocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr,
- -CookieSize.getQuantity());
-
- // The number of elements is at offset sizeof(size_t) relative to that.
- llvm::Value *NumElementsPtr
- = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr,
- SizeSize.getQuantity());
- NumElementsPtr =
- CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo(AS));
- NumElements = CGF.Builder.CreateLoad(NumElementsPtr);
+llvm::Value *ARMCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
+ llvm::Value *allocPtr,
+ CharUnits cookieSize) {
+ // The number of elements is at offset sizeof(size_t) relative to
+ // the allocated pointer.
+ llvm::Value *numElementsPtr
+ = CGF.Builder.CreateConstInBoundsGEP1_64(allocPtr, CGF.SizeSizeInBytes);
+
+ unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace();
+ numElementsPtr =
+ CGF.Builder.CreateBitCast(numElementsPtr, CGF.SizeTy->getPointerTo(AS));
+ return CGF.Builder.CreateLoad(numElementsPtr);
}
/*********************** Static local initialization **************************/
@@ -1200,3 +1104,60 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
CGF.EmitBlock(EndBlock);
}
+
+/// Register a global destructor using __cxa_atexit.
+static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
+ llvm::Constant *dtor,
+ llvm::Constant *addr) {
+ // We're assuming that the destructor function is something we can
+ // reasonably call with the default CC. Go ahead and cast it to the
+ // right prototype.
+ llvm::Type *dtorTy =
+ llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo();
+
+ // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
+ llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy };
+ llvm::FunctionType *atexitTy =
+ llvm::FunctionType::get(CGF.IntTy, paramTys, false);
+
+ // Fetch the actual function.
+ llvm::Constant *atexit =
+ CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");
+ if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))
+ fn->setDoesNotThrow();
+
+ // Create a variable that binds the atexit to this shared object.
+ llvm::Constant *handle =
+ CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle");
+
+ llvm::Value *args[] = {
+ llvm::ConstantExpr::getBitCast(dtor, dtorTy),
+ llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy),
+ handle
+ };
+ CGF.Builder.CreateCall(atexit, args)->setDoesNotThrow();
+}
+
+/// Register a global destructor as best as we know how.
+void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
+ llvm::Constant *dtor,
+ llvm::Constant *addr) {
+ // Use __cxa_atexit if available.
+ if (CGM.getCodeGenOpts().CXAAtExit) {
+ return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr);
+ }
+
+ // In Apple kexts, we want to add a global destructor entry.
+ // FIXME: shouldn't this be guarded by some variable?
+ if (CGM.getContext().getLangOpts().AppleKext) {
+ // Generate a global destructor entry.
+ return CGM.AddCXXDtorEntry(dtor, addr);
+ }
+
+ CGF.registerGlobalDtorWithAtExit(dtor, addr);
+}
+
+/// Generate and emit virtual tables for the given class.
+void ItaniumCXXABI::EmitVTables(const CXXRecordDecl *Class) {
+ CGM.getVTables().GenerateClassData(CGM.getVTableLinkage(Class), Class);
+}
diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp
index 825e041..6a2925b 100644
--- a/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -28,6 +28,8 @@ class MicrosoftCXXABI : public CGCXXABI {
public:
MicrosoftCXXABI(CodeGenModule &CGM) : CGCXXABI(CGM) {}
+ StringRef GetPureVirtualCallName() { return "_purecall"; }
+
void BuildConstructorSignature(const CXXConstructorDecl *Ctor,
CXXCtorType Type,
CanQualType &ResTy,
@@ -56,6 +58,13 @@ public:
// TODO: 'for base' flag
}
+ void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
+ llvm::GlobalVariable *DeclPtr,
+ bool PerformInit);
+
+ void EmitVTables(const CXXRecordDecl *Class);
+
+
// ==== Notes on array cookies =========
//
// MSVC seems to only use cookies when the class has a destructor; a
@@ -78,17 +87,92 @@ public:
// delete[] p;
// }
// Whereas it prints "104" and "104" if you give A a destructor.
- void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
- const CXXDeleteExpr *expr,
- QualType ElementType, llvm::Value *&NumElements,
- llvm::Value *&AllocPtr, CharUnits &CookieSize) {
- CGF.CGM.ErrorUnsupported(expr, "don't know how to handle array cookies "
- "in the Microsoft C++ ABI");
- }
+
+ bool requiresArrayCookie(const CXXDeleteExpr *expr, QualType elementType);
+ bool requiresArrayCookie(const CXXNewExpr *expr);
+ CharUnits getArrayCookieSizeImpl(QualType type);
+ llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
+ llvm::Value *NewPtr,
+ llvm::Value *NumElements,
+ const CXXNewExpr *expr,
+ QualType ElementType);
+ llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF,
+ llvm::Value *allocPtr,
+ CharUnits cookieSize);
};
}
+bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr,
+ QualType elementType) {
+ // Microsoft seems to completely ignore the possibility of a
+ // two-argument usual deallocation function.
+ return elementType.isDestructedType();
+}
+
+bool MicrosoftCXXABI::requiresArrayCookie(const CXXNewExpr *expr) {
+ // Microsoft seems to completely ignore the possibility of a
+ // two-argument usual deallocation function.
+ return expr->getAllocatedType().isDestructedType();
+}
+
+CharUnits MicrosoftCXXABI::getArrayCookieSizeImpl(QualType type) {
+ // The array cookie is always a size_t; we then pad that out to the
+ // alignment of the element type.
+ ASTContext &Ctx = getContext();
+ return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()),
+ Ctx.getTypeAlignInChars(type));
+}
+
+llvm::Value *MicrosoftCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
+ llvm::Value *allocPtr,
+ CharUnits cookieSize) {
+ unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace();
+ llvm::Value *numElementsPtr =
+ CGF.Builder.CreateBitCast(allocPtr, CGF.SizeTy->getPointerTo(AS));
+ return CGF.Builder.CreateLoad(numElementsPtr);
+}
+
+llvm::Value* MicrosoftCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
+ llvm::Value *newPtr,
+ llvm::Value *numElements,
+ const CXXNewExpr *expr,
+ QualType elementType) {
+ assert(requiresArrayCookie(expr));
+
+ // The size of the cookie.
+ CharUnits cookieSize = getArrayCookieSizeImpl(elementType);
+
+ // Compute an offset to the cookie.
+ llvm::Value *cookiePtr = newPtr;
+
+ // Write the number of elements into the appropriate slot.
+ unsigned AS = cast<llvm::PointerType>(newPtr->getType())->getAddressSpace();
+ llvm::Value *numElementsPtr
+ = CGF.Builder.CreateBitCast(cookiePtr, CGF.SizeTy->getPointerTo(AS));
+ CGF.Builder.CreateStore(numElements, numElementsPtr);
+
+ // Finally, compute a pointer to the actual data buffer by skipping
+ // over the cookie completely.
+ return CGF.Builder.CreateConstInBoundsGEP1_64(newPtr,
+ cookieSize.getQuantity());
+}
+
+void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
+ llvm::GlobalVariable *DeclPtr,
+ bool PerformInit) {
+ // FIXME: this code was only tested for global initialization.
+ // Not sure whether we want thread-safe static local variables as VS
+ // doesn't make them thread-safe.
+
+ // Emit the initializer and add a global destructor if appropriate.
+ CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit);
+}
+
+void MicrosoftCXXABI::EmitVTables(const CXXRecordDecl *Class) {
+ // FIXME: implement
+}
+
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
return new MicrosoftCXXABI(CGM);
}
diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp
index 2b71fdd..9c23ed9 100644
--- a/lib/CodeGen/TargetInfo.cpp
+++ b/lib/CodeGen/TargetInfo.cpp
@@ -413,12 +413,18 @@ static llvm::Type* X86AdjustInlineAsmType(CodeGen::CodeGenFunction &CGF,
/// X86_32ABIInfo - The X86-32 ABI information.
class X86_32ABIInfo : public ABIInfo {
+ enum Class {
+ Integer,
+ Float
+ };
+
static const unsigned MinABIStackAlignInBytes = 4;
bool IsDarwinVectorABI;
bool IsSmallStructInRegABI;
bool IsMMXDisabled;
bool IsWin32FloatStructABI;
+ unsigned DefaultNumRegisterParameters;
static bool isRegisterSize(unsigned Size) {
return (Size == 8 || Size == 16 || Size == 32 || Size == 64);
@@ -434,33 +440,31 @@ class X86_32ABIInfo : public ABIInfo {
/// \brief Return the alignment to use for the given type on the stack.
unsigned getTypeStackAlignInBytes(QualType Ty, unsigned Align) const;
-public:
-
- ABIArgInfo classifyReturnType(QualType RetTy,
+ Class classify(QualType Ty) const;
+ ABIArgInfo classifyReturnType(QualType RetTy,
unsigned callingConvention) const;
+ ABIArgInfo classifyArgumentTypeWithReg(QualType RetTy,
+ unsigned &FreeRegs) const;
ABIArgInfo classifyArgumentType(QualType RetTy) const;
- virtual void computeInfo(CGFunctionInfo &FI) const {
- FI.getReturnInfo() = classifyReturnType(FI.getReturnType(),
- FI.getCallingConvention());
- for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end();
- it != ie; ++it)
- it->info = classifyArgumentType(it->type);
- }
+public:
+ virtual void computeInfo(CGFunctionInfo &FI) const;
virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
CodeGenFunction &CGF) const;
- X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m, bool w)
+ X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m, bool w,
+ unsigned r)
: ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p),
- IsMMXDisabled(m), IsWin32FloatStructABI(w) {}
+ IsMMXDisabled(m), IsWin32FloatStructABI(w),
+ DefaultNumRegisterParameters(r) {}
};
class X86_32TargetCodeGenInfo : public TargetCodeGenInfo {
public:
X86_32TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT,
- bool d, bool p, bool m, bool w)
- :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m, w)) {}
+ bool d, bool p, bool m, bool w, unsigned r)
+ :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m, w, r)) {}
void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &CGM) const;
@@ -626,6 +630,10 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy,
ABIArgInfo::getExtend() : ABIArgInfo::getDirect());
}
+static bool isSSEVectorType(ASTContext &Context, QualType Ty) {
+ return Ty->getAs<VectorType>() && Context.getTypeSize(Ty) == 128;
+}
+
static bool isRecordWithSSEVectorType(ASTContext &Context, QualType Ty) {
const RecordType *RT = Ty->getAs<RecordType>();
if (!RT)
@@ -643,7 +651,7 @@ static bool isRecordWithSSEVectorType(ASTContext &Context, QualType Ty) {
i != e; ++i) {
QualType FT = i->getType();
- if (FT->getAs<VectorType>() && Context.getTypeSize(FT) == 128)
+ if (isSSEVectorType(Context, FT))
return true;
if (isRecordWithSSEVectorType(Context, FT))
@@ -667,7 +675,8 @@ unsigned X86_32ABIInfo::getTypeStackAlignInBytes(QualType Ty,
}
// Otherwise, if the type contains an SSE vector type, the alignment is 16.
- if (Align >= 16 && isRecordWithSSEVectorType(getContext(), Ty))
+ if (Align >= 16 && (isSSEVectorType(getContext(), Ty) ||
+ isRecordWithSSEVectorType(getContext(), Ty)))
return 16;
return MinABIStackAlignInBytes;
@@ -692,6 +701,57 @@ ABIArgInfo X86_32ABIInfo::getIndirectResult(QualType Ty, bool ByVal) const {
return ABIArgInfo::getIndirect(StackAlign);
}
+X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const {
+ const Type *T = isSingleElementStruct(Ty, getContext());
+ if (!T)
+ T = Ty.getTypePtr();
+
+ if (const BuiltinType *BT = T->getAs<BuiltinType>()) {
+ BuiltinType::Kind K = BT->getKind();
+ if (K == BuiltinType::Float || K == BuiltinType::Double)
+ return Float;
+ }
+ return Integer;
+}
+
+ABIArgInfo
+X86_32ABIInfo::classifyArgumentTypeWithReg(QualType Ty,
+ unsigned &FreeRegs) const {
+ // Common case first.
+ if (FreeRegs == 0)
+ return classifyArgumentType(Ty);
+
+ Class C = classify(Ty);
+ if (C == Float)
+ return classifyArgumentType(Ty);
+
+ unsigned SizeInRegs = (getContext().getTypeSize(Ty) + 31) / 32;
+ if (SizeInRegs == 0)
+ return classifyArgumentType(Ty);
+
+ if (SizeInRegs > FreeRegs) {
+ FreeRegs = 0;
+ return classifyArgumentType(Ty);
+ }
+ assert(SizeInRegs >= 1 && SizeInRegs <= 3);
+ FreeRegs -= SizeInRegs;
+
+ // If it is a simple scalar, keep the type so that we produce a cleaner IR.
+ ABIArgInfo Foo = classifyArgumentType(Ty);
+ if (Foo.isDirect() && !Foo.getDirectOffset() && !Foo.getPaddingType())
+ return ABIArgInfo::getDirectInReg(Foo.getCoerceToType());
+ if (Foo.isExtend())
+ return ABIArgInfo::getExtendInReg(Foo.getCoerceToType());
+
+ llvm::LLVMContext &LLVMContext = getVMContext();
+ llvm::Type *Int32 = llvm::Type::getInt32Ty(LLVMContext);
+ SmallVector<llvm::Type*, 3> Elements;
+ for (unsigned I = 0; I < SizeInRegs; ++I)
+ Elements.push_back(Int32);
+ llvm::Type *Result = llvm::StructType::get(LLVMContext, Elements);
+ return ABIArgInfo::getDirectInReg(Result);
+}
+
ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const {
// FIXME: Set alignment on indirect arguments.
if (isAggregateTypeForABI(Ty)) {
@@ -753,6 +813,28 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const {
ABIArgInfo::getExtend() : ABIArgInfo::getDirect());
}
+void X86_32ABIInfo::computeInfo(CGFunctionInfo &FI) const {
+ FI.getReturnInfo() = classifyReturnType(FI.getReturnType(),
+ FI.getCallingConvention());
+
+ unsigned FreeRegs = FI.getHasRegParm() ? FI.getRegParm() :
+ DefaultNumRegisterParameters;
+
+ // If the return value is indirect, then the hidden argument is consuming one
+ // integer register.
+ if (FI.getReturnInfo().isIndirect() && FreeRegs) {
+ --FreeRegs;
+ ABIArgInfo &Old = FI.getReturnInfo();
+ Old = ABIArgInfo::getIndirectInReg(Old.getIndirectAlign(),
+ Old.getIndirectByVal(),
+ Old.getIndirectRealign());
+ }
+
+ for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end();
+ it != ie; ++it)
+ it->info = classifyArgumentTypeWithReg(it->type, FreeRegs);
+}
+
llvm::Value *X86_32ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
CodeGenFunction &CGF) const {
llvm::Type *BPP = CGF.Int8PtrPtrTy;
@@ -1345,7 +1427,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase,
// single eightbyte, each is classified separately. Each eightbyte gets
// initialized to class NO_CLASS.
Class FieldLo, FieldHi;
- uint64_t Offset = OffsetBase + Layout.getBaseClassOffsetInBits(Base);
+ uint64_t Offset =
+ OffsetBase + getContext().toBits(Layout.getBaseClassOffset(Base));
classify(i->getType(), Offset, FieldLo, FieldHi);
Lo = merge(Lo, FieldLo);
Hi = merge(Hi, FieldHi);
@@ -1584,7 +1667,7 @@ static bool BitsContainNoUserData(QualType Ty, unsigned StartBit,
cast<CXXRecordDecl>(i->getType()->getAs<RecordType>()->getDecl());
// If the base is after the span we care about, ignore it.
- unsigned BaseOffset = (unsigned)Layout.getBaseClassOffsetInBits(Base);
+ unsigned BaseOffset = Context.toBits(Layout.getBaseClassOffset(Base));
if (BaseOffset >= EndBit) continue;
unsigned BaseStart = BaseOffset < StartBit ? StartBit-BaseOffset :0;
@@ -2411,6 +2494,64 @@ PPC32TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
return false;
}
+// PowerPC-64
+
+namespace {
+class PPC64TargetCodeGenInfo : public DefaultTargetCodeGenInfo {
+public:
+ PPC64TargetCodeGenInfo(CodeGenTypes &CGT) : DefaultTargetCodeGenInfo(CGT) {}
+
+ int getDwarfEHStackPointer(CodeGen::CodeGenModule &M) const {
+ // This is recovered from gcc output.
+ return 1; // r1 is the dedicated stack pointer
+ }
+
+ bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
+ llvm::Value *Address) const;
+};
+
+}
+
+bool
+PPC64TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
+ llvm::Value *Address) const {
+ // This is calculated from the LLVM and GCC tables and verified
+ // against gcc output. AFAIK all ABIs use the same encoding.
+
+ CodeGen::CGBuilderTy &Builder = CGF.Builder;
+
+ llvm::IntegerType *i8 = CGF.Int8Ty;
+ llvm::Value *Four8 = llvm::ConstantInt::get(i8, 4);
+ llvm::Value *Eight8 = llvm::ConstantInt::get(i8, 8);
+ llvm::Value *Sixteen8 = llvm::ConstantInt::get(i8, 16);
+
+ // 0-31: r0-31, the 8-byte general-purpose registers
+ AssignToArrayRange(Builder, Address, Eight8, 0, 31);
+
+ // 32-63: fp0-31, the 8-byte floating-point registers
+ AssignToArrayRange(Builder, Address, Eight8, 32, 63);
+
+ // 64-76 are various 4-byte special-purpose registers:
+ // 64: mq
+ // 65: lr
+ // 66: ctr
+ // 67: ap
+ // 68-75 cr0-7
+ // 76: xer
+ AssignToArrayRange(Builder, Address, Four8, 64, 76);
+
+ // 77-108: v0-31, the 16-byte vector registers
+ AssignToArrayRange(Builder, Address, Sixteen8, 77, 108);
+
+ // 109: vrsave
+ // 110: vscr
+ // 111: spe_acc
+ // 112: spefscr
+ // 113: sfp
+ AssignToArrayRange(Builder, Address, Four8, 109, 113);
+
+ return false;
+}
//===----------------------------------------------------------------------===//
// ARM ABI Implementation
@@ -2559,7 +2700,8 @@ static bool isHomogeneousAggregate(QualType Ty, const Type *&Base,
// double, or 64-bit or 128-bit vectors.
if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) {
if (BT->getKind() != BuiltinType::Float &&
- BT->getKind() != BuiltinType::Double)
+ BT->getKind() != BuiltinType::Double &&
+ BT->getKind() != BuiltinType::LongDouble)
return false;
} else if (const VectorType *VT = Ty->getAs<VectorType>()) {
unsigned VecSize = Context.getTypeSize(VT);
@@ -2615,19 +2757,23 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty) const {
}
}
+ // Support byval for ARM.
+ if (getContext().getTypeSizeInChars(Ty) > CharUnits::fromQuantity(64) ||
+ getContext().getTypeAlign(Ty) > 64) {
+ return ABIArgInfo::getIndirect(0, /*ByVal=*/true);
+ }
+
// Otherwise, pass by coercing to a structure of the appropriate size.
- //
- // FIXME: This is kind of nasty... but there isn't much choice because the ARM
- // backend doesn't support byval.
- // FIXME: This doesn't handle alignment > 64 bits.
llvm::Type* ElemTy;
unsigned SizeRegs;
- if (getContext().getTypeAlign(Ty) > 32) {
- ElemTy = llvm::Type::getInt64Ty(getVMContext());
- SizeRegs = (getContext().getTypeSize(Ty) + 63) / 64;
- } else {
+ // FIXME: Try to match the types of the arguments more accurately where
+ // we can.
+ if (getContext().getTypeAlign(Ty) <= 32) {
ElemTy = llvm::Type::getInt32Ty(getVMContext());
SizeRegs = (getContext().getTypeSize(Ty) + 31) / 32;
+ } else {
+ ElemTy = llvm::Type::getInt64Ty(getVMContext());
+ SizeRegs = (getContext().getTypeSize(Ty) + 63) / 64;
}
llvm::Type *STy =
@@ -2833,14 +2979,14 @@ llvm::Value *ARMABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
}
//===----------------------------------------------------------------------===//
-// PTX ABI Implementation
+// NVPTX ABI Implementation
//===----------------------------------------------------------------------===//
namespace {
-class PTXABIInfo : public ABIInfo {
+class NVPTXABIInfo : public ABIInfo {
public:
- PTXABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {}
+ NVPTXABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {}
ABIArgInfo classifyReturnType(QualType RetTy) const;
ABIArgInfo classifyArgumentType(QualType Ty) const;
@@ -2850,16 +2996,16 @@ public:
CodeGenFunction &CFG) const;
};
-class PTXTargetCodeGenInfo : public TargetCodeGenInfo {
+class NVPTXTargetCodeGenInfo : public TargetCodeGenInfo {
public:
- PTXTargetCodeGenInfo(CodeGenTypes &CGT)
- : TargetCodeGenInfo(new PTXABIInfo(CGT)) {}
+ NVPTXTargetCodeGenInfo(CodeGenTypes &CGT)
+ : TargetCodeGenInfo(new NVPTXABIInfo(CGT)) {}
virtual void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &M) const;
};
-ABIArgInfo PTXABIInfo::classifyReturnType(QualType RetTy) const {
+ABIArgInfo NVPTXABIInfo::classifyReturnType(QualType RetTy) const {
if (RetTy->isVoidType())
return ABIArgInfo::getIgnore();
if (isAggregateTypeForABI(RetTy))
@@ -2867,14 +3013,14 @@ ABIArgInfo PTXABIInfo::classifyReturnType(QualType RetTy) const {
return ABIArgInfo::getDirect();
}
-ABIArgInfo PTXABIInfo::classifyArgumentType(QualType Ty) const {
+ABIArgInfo NVPTXABIInfo::classifyArgumentType(QualType Ty) const {
if (isAggregateTypeForABI(Ty))
return ABIArgInfo::getIndirect(0);
return ABIArgInfo::getDirect();
}
-void PTXABIInfo::computeInfo(CGFunctionInfo &FI) const {
+void NVPTXABIInfo::computeInfo(CGFunctionInfo &FI) const {
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end();
it != ie; ++it)
@@ -2885,6 +3031,8 @@ void PTXABIInfo::computeInfo(CGFunctionInfo &FI) const {
return;
// Calling convention as default by an ABI.
+ // We're still using the PTX_Kernel/PTX_Device calling conventions here,
+ // but we should switch to NVVM metadata later on.
llvm::CallingConv::ID DefaultCC;
const LangOptions &LangOpts = getContext().getLangOpts();
if (LangOpts.OpenCL || LangOpts.CUDA) {
@@ -2903,14 +3051,14 @@ void PTXABIInfo::computeInfo(CGFunctionInfo &FI) const {
}
-llvm::Value *PTXABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
- CodeGenFunction &CFG) const {
- llvm_unreachable("PTX does not support varargs");
+llvm::Value *NVPTXABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
+ CodeGenFunction &CFG) const {
+ llvm_unreachable("NVPTX does not support varargs");
}
-void PTXTargetCodeGenInfo::SetTargetAttributes(const Decl *D,
- llvm::GlobalValue *GV,
- CodeGen::CodeGenModule &M) const{
+void NVPTXTargetCodeGenInfo::
+SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
+ CodeGen::CodeGenModule &M) const{
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
if (!FD) return;
@@ -3097,13 +3245,16 @@ void MSP430TargetCodeGenInfo::SetTargetAttributes(const Decl *D,
namespace {
class MipsABIInfo : public ABIInfo {
bool IsO32;
- unsigned MinABIStackAlignInBytes;
- llvm::Type* HandleAggregates(QualType Ty) const;
+ unsigned MinABIStackAlignInBytes, StackAlignInBytes;
+ void CoerceToIntArgs(uint64_t TySize,
+ SmallVector<llvm::Type*, 8> &ArgList) const;
+ llvm::Type* HandleAggregates(QualType Ty, uint64_t TySize) const;
llvm::Type* returnAggregateInRegs(QualType RetTy, uint64_t Size) const;
llvm::Type* getPaddingType(uint64_t Align, uint64_t Offset) const;
public:
MipsABIInfo(CodeGenTypes &CGT, bool _IsO32) :
- ABIInfo(CGT), IsO32(_IsO32), MinABIStackAlignInBytes(IsO32 ? 4 : 8) {}
+ ABIInfo(CGT), IsO32(_IsO32), MinABIStackAlignInBytes(IsO32 ? 4 : 8),
+ StackAlignInBytes(IsO32 ? 8 : 16) {}
ABIArgInfo classifyReturnType(QualType RetTy) const;
ABIArgInfo classifyArgumentType(QualType RetTy, uint64_t &Offset) const;
@@ -3132,36 +3283,56 @@ public:
};
}
+void MipsABIInfo::CoerceToIntArgs(uint64_t TySize,
+ SmallVector<llvm::Type*, 8> &ArgList) const {
+ llvm::IntegerType *IntTy =
+ llvm::IntegerType::get(getVMContext(), MinABIStackAlignInBytes * 8);
+
+ // Add (TySize / MinABIStackAlignInBytes) args of IntTy.
+ for (unsigned N = TySize / (MinABIStackAlignInBytes * 8); N; --N)
+ ArgList.push_back(IntTy);
+
+ // If necessary, add one more integer type to ArgList.
+ unsigned R = TySize % (MinABIStackAlignInBytes * 8);
+
+ if (R)
+ ArgList.push_back(llvm::IntegerType::get(getVMContext(), R));
+}
+
// In N32/64, an aligned double precision floating point field is passed in
// a register.
-llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty) const {
- if (IsO32)
- return 0;
+llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty, uint64_t TySize) const {
+ SmallVector<llvm::Type*, 8> ArgList, IntArgList;
+
+ if (IsO32) {
+ CoerceToIntArgs(TySize, ArgList);
+ return llvm::StructType::get(getVMContext(), ArgList);
+ }
if (Ty->isComplexType())
return CGT.ConvertType(Ty);
const RecordType *RT = Ty->getAs<RecordType>();
- // Unions are passed in integer registers.
- if (!RT || !RT->isStructureOrClassType())
- return 0;
+ // Unions/vectors are passed in integer registers.
+ if (!RT || !RT->isStructureOrClassType()) {
+ CoerceToIntArgs(TySize, ArgList);
+ return llvm::StructType::get(getVMContext(), ArgList);
+ }
const RecordDecl *RD = RT->getDecl();
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
- uint64_t StructSize = getContext().getTypeSize(Ty);
- assert(!(StructSize % 8) && "Size of structure must be multiple of 8.");
+ assert(!(TySize % 8) && "Size of structure must be multiple of 8.");
uint64_t LastOffset = 0;
unsigned idx = 0;
llvm::IntegerType *I64 = llvm::IntegerType::get(getVMContext(), 64);
- SmallVector<llvm::Type*, 8> ArgList;
// Iterate over fields in the struct/class and check if there are any aligned
// double fields.
for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
i != e; ++i, ++idx) {
- const QualType Ty = (*i)->getType();
+ const QualType Ty = i->getType();
const BuiltinType *BT = Ty->getAs<BuiltinType>();
if (!BT || BT->getKind() != BuiltinType::Double)
@@ -3180,43 +3351,33 @@ llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty) const {
LastOffset = Offset + 64;
}
- // This struct/class doesn't have an aligned double field.
- if (!LastOffset)
- return 0;
-
- // Add ((StructSize - LastOffset) / 64) args of type i64.
- for (unsigned N = (StructSize - LastOffset) / 64; N; --N)
- ArgList.push_back(I64);
-
- // If the size of the remainder is not zero, add one more integer type to
- // ArgList.
- unsigned R = (StructSize - LastOffset) % 64;
- if (R)
- ArgList.push_back(llvm::IntegerType::get(getVMContext(), R));
+ CoerceToIntArgs(TySize - LastOffset, IntArgList);
+ ArgList.append(IntArgList.begin(), IntArgList.end());
return llvm::StructType::get(getVMContext(), ArgList);
}
llvm::Type *MipsABIInfo::getPaddingType(uint64_t Align, uint64_t Offset) const {
- // Padding is inserted only for N32/64.
- if (IsO32)
- return 0;
+ assert((Offset % MinABIStackAlignInBytes) == 0);
+
+ if ((Align - 1) & Offset)
+ return llvm::IntegerType::get(getVMContext(), MinABIStackAlignInBytes * 8);
- assert(Align <= 16 && "Alignment larger than 16 not handled.");
- return (Align == 16 && Offset & 0xf) ?
- llvm::IntegerType::get(getVMContext(), 64) : 0;
+ return 0;
}
ABIArgInfo
MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const {
uint64_t OrigOffset = Offset;
- uint64_t TySize =
- llvm::RoundUpToAlignment(getContext().getTypeSize(Ty), 64) / 8;
+ uint64_t TySize = getContext().getTypeSize(Ty);
uint64_t Align = getContext().getTypeAlign(Ty) / 8;
- Offset = llvm::RoundUpToAlignment(Offset, std::max(Align, (uint64_t)8));
- Offset += TySize;
- if (isAggregateTypeForABI(Ty)) {
+ Align = std::min(std::max(Align, (uint64_t)MinABIStackAlignInBytes),
+ (uint64_t)StackAlignInBytes);
+ Offset = llvm::RoundUpToAlignment(Offset, Align);
+ Offset += llvm::RoundUpToAlignment(TySize, Align * 8) / 8;
+
+ if (isAggregateTypeForABI(Ty) || Ty->isVectorType()) {
// Ignore empty aggregates.
if (TySize == 0)
return ABIArgInfo::getIgnore();
@@ -3224,20 +3385,15 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const {
// Records with non trivial destructors/constructors should not be passed
// by value.
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) {
- Offset = OrigOffset + 8;
+ Offset = OrigOffset + MinABIStackAlignInBytes;
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
}
- // If we have reached here, aggregates are passed either indirectly via a
- // byval pointer or directly by coercing to another structure type. In the
- // latter case, padding is inserted if the offset of the aggregate is
- // unaligned.
- llvm::Type *ResType = HandleAggregates(Ty);
-
- if (!ResType)
- return ABIArgInfo::getIndirect(0);
-
- return ABIArgInfo::getDirect(ResType, 0, getPaddingType(Align, OrigOffset));
+ // If we have reached here, aggregates are passed directly by coercing to
+ // another structure type. Padding is inserted if the offset of the
+ // aggregate is unaligned.
+ return ABIArgInfo::getDirect(HandleAggregates(Ty, TySize), 0,
+ getPaddingType(Align, OrigOffset));
}
// Treat an enum type as its underlying type.
@@ -3253,7 +3409,7 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const {
llvm::Type*
MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const {
const RecordType *RT = RetTy->getAs<RecordType>();
- SmallVector<llvm::Type*, 2> RTList;
+ SmallVector<llvm::Type*, 8> RTList;
if (RT && RT->isStructureOrClassType()) {
const RecordDecl *RD = RT->getDecl();
@@ -3272,12 +3428,12 @@ MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const {
if (FieldCnt && (FieldCnt <= 2) && !Layout.getFieldOffset(0)) {
RecordDecl::field_iterator b = RD->field_begin(), e = RD->field_end();
for (; b != e; ++b) {
- const BuiltinType *BT = (*b)->getType()->getAs<BuiltinType>();
+ const BuiltinType *BT = b->getType()->getAs<BuiltinType>();
if (!BT || !BT->isFloatingPoint())
break;
- RTList.push_back(CGT.ConvertType((*b)->getType()));
+ RTList.push_back(CGT.ConvertType(b->getType()));
}
if (b == e)
@@ -3288,11 +3444,7 @@ MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const {
}
}
- RTList.push_back(llvm::IntegerType::get(getVMContext(),
- std::min(Size, (uint64_t)64)));
- if (Size > 64)
- RTList.push_back(llvm::IntegerType::get(getVMContext(), Size - 64));
-
+ CoerceToIntArgs(Size, RTList);
return llvm::StructType::get(getVMContext(), RTList);
}
@@ -3302,11 +3454,15 @@ ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const {
if (RetTy->isVoidType() || Size == 0)
return ABIArgInfo::getIgnore();
- if (isAggregateTypeForABI(RetTy)) {
+ if (isAggregateTypeForABI(RetTy) || RetTy->isVectorType()) {
if (Size <= 128) {
if (RetTy->isAnyComplexType())
return ABIArgInfo::getDirect();
+ // O32 returns integer vectors in registers.
+ if (IsO32 && RetTy->isVectorType() && !RetTy->hasFloatingRepresentation())
+ return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size));
+
if (!IsO32 && !isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy))
return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size));
}
@@ -3327,7 +3483,7 @@ void MipsABIInfo::computeInfo(CGFunctionInfo &FI) const {
RetInfo = classifyReturnType(FI.getReturnType());
// Check if a pointer to an aggregate is passed as a hidden argument.
- uint64_t Offset = RetInfo.isIndirect() ? 8 : 0;
+ uint64_t Offset = RetInfo.isIndirect() ? MinABIStackAlignInBytes : 0;
for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end();
it != ie; ++it)
@@ -3634,10 +3790,12 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
case llvm::Triple::ppc:
return *(TheTargetCodeGenInfo = new PPC32TargetCodeGenInfo(Types));
+ case llvm::Triple::ppc64:
+ return *(TheTargetCodeGenInfo = new PPC64TargetCodeGenInfo(Types));
- case llvm::Triple::ptx32:
- case llvm::Triple::ptx64:
- return *(TheTargetCodeGenInfo = new PTXTargetCodeGenInfo(Types));
+ case llvm::Triple::nvptx:
+ case llvm::Triple::nvptx64:
+ return *(TheTargetCodeGenInfo = new NVPTXTargetCodeGenInfo(Types));
case llvm::Triple::mblaze:
return *(TheTargetCodeGenInfo = new MBlazeTargetCodeGenInfo(Types));
@@ -3653,8 +3811,8 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
if (Triple.isOSDarwin())
return *(TheTargetCodeGenInfo =
- new X86_32TargetCodeGenInfo(
- Types, true, true, DisableMMX, false));
+ new X86_32TargetCodeGenInfo(Types, true, true, DisableMMX, false,
+ CodeGenOpts.NumRegisterParameters));
switch (Triple.getOS()) {
case llvm::Triple::Cygwin:
@@ -3663,19 +3821,22 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
case llvm::Triple::DragonFly:
case llvm::Triple::FreeBSD:
case llvm::Triple::OpenBSD:
+ case llvm::Triple::Bitrig:
return *(TheTargetCodeGenInfo =
- new X86_32TargetCodeGenInfo(
- Types, false, true, DisableMMX, false));
+ new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX,
+ false,
+ CodeGenOpts.NumRegisterParameters));
case llvm::Triple::Win32:
return *(TheTargetCodeGenInfo =
- new X86_32TargetCodeGenInfo(
- Types, false, true, DisableMMX, true));
+ new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX, true,
+ CodeGenOpts.NumRegisterParameters));
default:
return *(TheTargetCodeGenInfo =
- new X86_32TargetCodeGenInfo(
- Types, false, false, DisableMMX, false));
+ new X86_32TargetCodeGenInfo(Types, false, false, DisableMMX,
+ false,
+ CodeGenOpts.NumRegisterParameters));
}
}
diff --git a/lib/Driver/ArgList.cpp b/lib/Driver/ArgList.cpp
index 55a0ddf..7fd439e 100644
--- a/lib/Driver/ArgList.cpp
+++ b/lib/Driver/ArgList.cpp
@@ -140,6 +140,68 @@ Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1,
return Res;
}
+Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1,
+ OptSpecifier Id2, OptSpecifier Id3,
+ OptSpecifier Id4, OptSpecifier Id5) const {
+ Arg *Res = 0;
+ for (const_iterator it = begin(), ie = end(); it != ie; ++it) {
+ if ((*it)->getOption().matches(Id0) ||
+ (*it)->getOption().matches(Id1) ||
+ (*it)->getOption().matches(Id2) ||
+ (*it)->getOption().matches(Id3) ||
+ (*it)->getOption().matches(Id4) ||
+ (*it)->getOption().matches(Id5)) {
+ Res = *it;
+ Res->claim();
+ }
+ }
+
+ return Res;
+}
+
+Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1,
+ OptSpecifier Id2, OptSpecifier Id3,
+ OptSpecifier Id4, OptSpecifier Id5,
+ OptSpecifier Id6) const {
+ Arg *Res = 0;
+ for (const_iterator it = begin(), ie = end(); it != ie; ++it) {
+ if ((*it)->getOption().matches(Id0) ||
+ (*it)->getOption().matches(Id1) ||
+ (*it)->getOption().matches(Id2) ||
+ (*it)->getOption().matches(Id3) ||
+ (*it)->getOption().matches(Id4) ||
+ (*it)->getOption().matches(Id5) ||
+ (*it)->getOption().matches(Id6)) {
+ Res = *it;
+ Res->claim();
+ }
+ }
+
+ return Res;
+}
+
+Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1,
+ OptSpecifier Id2, OptSpecifier Id3,
+ OptSpecifier Id4, OptSpecifier Id5,
+ OptSpecifier Id6, OptSpecifier Id7) const {
+ Arg *Res = 0;
+ for (const_iterator it = begin(), ie = end(); it != ie; ++it) {
+ if ((*it)->getOption().matches(Id0) ||
+ (*it)->getOption().matches(Id1) ||
+ (*it)->getOption().matches(Id2) ||
+ (*it)->getOption().matches(Id3) ||
+ (*it)->getOption().matches(Id4) ||
+ (*it)->getOption().matches(Id5) ||
+ (*it)->getOption().matches(Id6) ||
+ (*it)->getOption().matches(Id7)) {
+ Res = *it;
+ Res->claim();
+ }
+ }
+
+ return Res;
+}
+
bool ArgList::hasFlag(OptSpecifier Pos, OptSpecifier Neg, bool Default) const {
if (Arg *A = getLastArg(Pos, Neg))
return A->getOption().matches(Pos);
diff --git a/lib/Driver/CC1Options.cpp b/lib/Driver/CC1Options.cpp
deleted file mode 100644
index 884b363..0000000
--- a/lib/Driver/CC1Options.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-//===--- CC1Options.cpp - Clang CC1 Options Table -------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Driver/CC1Options.h"
-#include "clang/Driver/Option.h"
-#include "clang/Driver/OptTable.h"
-using namespace clang;
-using namespace clang::driver;
-using namespace clang::driver::options;
-using namespace clang::driver::cc1options;
-
-static const OptTable::Info CC1InfoTable[] = {
-#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
- HELPTEXT, METAVAR) \
- { NAME, HELPTEXT, METAVAR, Option::KIND##Class, PARAM, FLAGS, \
- OPT_##GROUP, OPT_##ALIAS },
-#include "clang/Driver/CC1Options.inc"
-};
-
-namespace {
-
-class CC1OptTable : public OptTable {
-public:
- CC1OptTable()
- : OptTable(CC1InfoTable, sizeof(CC1InfoTable) / sizeof(CC1InfoTable[0])) {}
-};
-
-}
-
-OptTable *clang::driver::createCC1OptTable() {
- return new CC1OptTable();
-}
diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt
index a798e20..4ada7d9 100644
--- a/lib/Driver/CMakeLists.txt
+++ b/lib/Driver/CMakeLists.txt
@@ -1,10 +1,7 @@
-set(LLVM_USED_LIBS clangBasic clangAST clangParse)
-
add_clang_library(clangDriver
Action.cpp
Arg.cpp
ArgList.cpp
- CC1Options.cpp
CC1AsOptions.cpp
Compilation.cpp
Driver.cpp
@@ -21,13 +18,14 @@ add_clang_library(clangDriver
Types.cpp
)
-IF(MSVC)
- get_target_property(NON_ANSI_COMPILE_FLAGS clangDriver COMPILE_FLAGS)
- string(REPLACE /Za
- "" NON_ANSI_COMPILE_FLAGS
- ${NON_ANSI_COMPILE_FLAGS})
- set_target_properties(clangDriver PROPERTIES COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS})
-ENDIF(MSVC)
+add_dependencies(clangDriver
+ ClangAttrList
+ ClangCC1AsOptions
+ ClangDiagnosticCommon
+ ClangDiagnosticDriver
+ ClangDriverOptions
+ )
-add_dependencies(clangDriver ClangAttrList ClangDiagnosticDriver
- ClangDriverOptions ClangCC1Options ClangCC1AsOptions)
+target_link_libraries(clangDriver
+ clangBasic
+ )
diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp
index 5553fc9..c962fca 100644
--- a/lib/Driver/Compilation.cpp
+++ b/lib/Driver/Compilation.cpp
@@ -219,7 +219,7 @@ void Compilation::initCompilationForDiagnostics(void) {
// to avoid emitting warnings about unused args.
OptSpecifier OutputOpts[] = { options::OPT_o, options::OPT_MD,
options::OPT_MMD };
- for (unsigned i = 0; i != sizeof(OutputOpts)/sizeof(OutputOpts[0]); ++i) {
+ for (unsigned i = 0, e = llvm::array_lengthof(OutputOpts); i != e; ++i) {
if (TranslatedArgs->hasArg(OutputOpts[i]))
TranslatedArgs->eraseArg(OutputOpts[i]);
}
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
index 3ddac69..87d533d 100644
--- a/lib/Driver/Driver.cpp
+++ b/lib/Driver/Driver.cpp
@@ -59,7 +59,7 @@ Driver::Driver(StringRef ClangExecutable,
CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false),
CCGenDiagnostics(false), CCCGenericGCCName(""), CheckInputsExist(true),
CCCUseClang(true), CCCUseClangCXX(true), CCCUseClangCPP(true),
- CCCUsePCH(true), SuppressMissingInputWarning(false) {
+ ForcedClangUse(false), CCCUsePCH(true), SuppressMissingInputWarning(false) {
if (IsProduction) {
// In a "production" build, only use clang on architectures we expect to
// work.
@@ -115,9 +115,10 @@ InputArgList *Driver::ParseArgStrings(ArrayRef<const char *> ArgList) {
}
// Warn about -mcpu= without an argument.
- if (A->getOption().matches(options::OPT_mcpu_EQ) &&
+ if (A->getOption().matches(options::OPT_mcpu_EQ) &&
A->containsValue("")) {
- Diag(clang::diag::warn_drv_empty_joined_argument) << A->getAsString(*Args);
+ Diag(clang::diag::warn_drv_empty_joined_argument) <<
+ A->getAsString(*Args);
}
}
@@ -253,7 +254,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
if (char *env = ::getenv("COMPILER_PATH")) {
StringRef CompilerPath = env;
while (!CompilerPath.empty()) {
- std::pair<StringRef, StringRef> Split = CompilerPath.split(':');
+ std::pair<StringRef, StringRef> Split = CompilerPath.split(':');
PrefixDirs.push_back(Split.first);
CompilerPath = Split.second;
}
@@ -376,24 +377,33 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
void Driver::generateCompilationDiagnostics(Compilation &C,
const Command *FailingCommand) {
if (C.getArgs().hasArg(options::OPT_fno_crash_diagnostics))
- return;
+ return;
// Don't try to generate diagnostics for link jobs.
- if (FailingCommand->getCreator().isLinkJob())
+ if (FailingCommand && FailingCommand->getCreator().isLinkJob())
return;
+ // Print the version of the compiler.
+ PrintVersion(C, llvm::errs());
+
Diag(clang::diag::note_drv_command_failed_diag_msg)
- << "Please submit a bug report to " BUG_REPORT_URL " and include command"
- " line arguments and all diagnostic information.";
+ << "PLEASE submit a bug report to " BUG_REPORT_URL " and include the "
+ "crash backtrace, preprocessed source, and associated run script.";
// Suppress driver output and emit preprocessor output to temp file.
CCCIsCPP = true;
CCGenDiagnostics = true;
+ C.getArgs().AddFlagArg(0, Opts->getOption(options::OPT_frewrite_includes));
// Save the original job command(s).
std::string Cmd;
llvm::raw_string_ostream OS(Cmd);
- C.PrintJob(OS, C.getJobs(), "\n", false);
+ if (FailingCommand)
+ C.PrintJob(OS, *FailingCommand, "\n", false);
+ else
+ // Crash triggered by FORCE_CLANG_DIAGNOSTICS_CRASH, which doesn't have an
+ // associated FailingCommand, so just pass all jobs.
+ C.PrintJob(OS, C.getJobs(), "\n", false);
OS.flush();
// Clear stale state and suppress tool output.
@@ -473,7 +483,9 @@ void Driver::generateCompilationDiagnostics(Compilation &C,
// If the command succeeded, we are done.
if (Res == 0) {
Diag(clang::diag::note_drv_command_failed_diag_msg)
- << "Preprocessed source(s) and associated run script(s) are located at:";
+ << "\n********************\n\n"
+ "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n"
+ "Preprocessed source(s) and associated run script(s) are located at:";
ArgStringList Files = C.getTempFiles();
for (ArgStringList::const_iterator it = Files.begin(), ie = Files.end();
it != ie; ++it) {
@@ -489,10 +501,76 @@ void Driver::generateCompilationDiagnostics(Compilation &C,
Diag(clang::diag::note_drv_command_failed_diag_msg)
<< "Error generating run script: " + Script + " " + Err;
} else {
+ // Strip away options not necessary to reproduce the crash.
+ // FIXME: This doesn't work with quotes (e.g., -D "foo bar").
+ SmallVector<std::string, 16> Flag;
+ Flag.push_back("-D ");
+ Flag.push_back("-F");
+ Flag.push_back("-I ");
+ Flag.push_back("-M ");
+ Flag.push_back("-MD ");
+ Flag.push_back("-MF ");
+ Flag.push_back("-MG ");
+ Flag.push_back("-MM ");
+ Flag.push_back("-MMD ");
+ Flag.push_back("-MP ");
+ Flag.push_back("-MQ ");
+ Flag.push_back("-MT ");
+ Flag.push_back("-o ");
+ Flag.push_back("-coverage-file ");
+ Flag.push_back("-dependency-file ");
+ Flag.push_back("-fdebug-compilation-dir ");
+ Flag.push_back("-fmodule-cache-path ");
+ Flag.push_back("-idirafter ");
+ Flag.push_back("-include ");
+ Flag.push_back("-include-pch ");
+ Flag.push_back("-internal-isystem ");
+ Flag.push_back("-internal-externc-isystem ");
+ Flag.push_back("-iprefix ");
+ Flag.push_back("-iwithprefix ");
+ Flag.push_back("-iwithprefixbefore ");
+ Flag.push_back("-isysroot ");
+ Flag.push_back("-isystem ");
+ Flag.push_back("-iquote ");
+ Flag.push_back("-resource-dir ");
+ Flag.push_back("-serialize-diagnostic-file ");
+ for (unsigned i = 0, e = Flag.size(); i < e; ++i) {
+ size_t I = 0, E = 0;
+ do {
+ I = Cmd.find(Flag[i], I);
+ if (I == std::string::npos) break;
+
+ E = Cmd.find(" ", I + Flag[i].length());
+ if (E == std::string::npos) break;
+ // The -D option is not removed. Instead, the argument is quoted.
+ if (Flag[i] != "-D ") {
+ Cmd.erase(I, E - I + 1);
+ } else {
+ Cmd.insert(I+3, "\"");
+ Cmd.insert(++E, "\"");
+ I = E;
+ }
+ } while(1);
+ }
+ // Append the new filename with correct preprocessed suffix.
+ size_t I, E;
+ I = Cmd.find("-main-file-name ");
+ assert (I != std::string::npos && "Expected to find -main-file-name");
+ I += 16;
+ E = Cmd.find(" ", I);
+ assert (E != std::string::npos && "-main-file-name missing argument?");
+ StringRef OldFilename = StringRef(Cmd).slice(I, E);
+ StringRef NewFilename = llvm::sys::path::filename(*it);
+ I = StringRef(Cmd).rfind(OldFilename);
+ E = I + OldFilename.size();
+ I = Cmd.rfind(" ", I) + 1;
+ Cmd.replace(I, E - I, NewFilename.data(), NewFilename.size());
ScriptOS << Cmd;
Diag(clang::diag::note_drv_command_failed_diag_msg) << Script;
}
}
+ Diag(clang::diag::note_drv_command_failed_diag_msg)
+ << "\n\n********************";
} else {
// Failure, remove preprocessed files.
if (!C.getArgs().hasArg(options::OPT_save_temps))
@@ -529,14 +607,8 @@ int Driver::ExecuteCompilation(const Compilation &C,
C.CleanupFileList(C.getResultFiles(), true);
// Failure result files are valid unless we crashed.
- if (Res < 0) {
+ if (Res < 0)
C.CleanupFileList(C.getFailureResultFiles(), true);
-#ifdef _WIN32
- // Exit status should not be negative on Win32,
- // unless abnormal termination.
- Res = 1;
-#endif
- }
}
// Print extra information about abnormal failures, if possible.
@@ -630,7 +702,7 @@ bool Driver::HandleImmediateArgs(const Compilation &C) {
return false;
}
- if (C.getArgs().hasArg(options::OPT__help) ||
+ if (C.getArgs().hasArg(options::OPT_help) ||
C.getArgs().hasArg(options::OPT__help_hidden)) {
PrintHelp(C.getArgs().hasArg(options::OPT__help_hidden));
return false;
@@ -748,8 +820,7 @@ static unsigned PrintActions1(const Compilation &C, Action *A,
if (InputAction *IA = dyn_cast<InputAction>(A)) {
os << "\"" << IA->getInputArg().getValue(C.getArgs()) << "\"";
} else if (BindArchAction *BIA = dyn_cast<BindArchAction>(A)) {
- os << '"' << (BIA->getArchName() ? BIA->getArchName() :
- C.getDefaultToolChain().getArchName()) << '"'
+ os << '"' << BIA->getArchName() << '"'
<< ", {" << PrintActions1(C, *BIA->begin(), Ids) << "}";
} else {
os << "{";
@@ -823,7 +894,7 @@ void Driver::BuildUniversalActions(const ToolChain &TC,
// When there is no explicit arch for this platform, make sure we still bind
// the architecture (to the default) so that -Xarch_ is handled correctly.
if (!Archs.size())
- Archs.push_back(0);
+ Archs.push_back(Args.MakeArgString(TC.getArchName()));
// FIXME: We killed off some others but these aren't yet detected in a
// functional manner. If we added information to jobs about which "auxiliary"
@@ -873,7 +944,7 @@ void Driver::BuildUniversalActions(const ToolChain &TC,
if (A && !A->getOption().matches(options::OPT_g0) &&
!A->getOption().matches(options::OPT_gstabs) &&
ContainsCompileOrAssembleAction(Actions.back())) {
-
+
// Add a 'dsymutil' step if necessary, when debug info is enabled and we
// have a compile input. We need to run 'dsymutil' ourselves in such cases
// because the debug info will refer to a temporary object file which is
@@ -1060,18 +1131,27 @@ void Driver::BuildActions(const ToolChain &TC, const DerivedArgList &Args,
if (Args.hasArg(options::OPT_Qunused_arguments))
continue;
+ // Special case when final phase determined by binary name, rather than
+ // by a command-line argument with a corresponding Arg.
+ if (CCCIsCPP)
+ Diag(clang::diag::warn_drv_input_file_unused_by_cpp)
+ << InputArg->getAsString(Args)
+ << getPhaseName(InitialPhase);
// Special case '-E' warning on a previously preprocessed file to make
// more sense.
- if (InitialPhase == phases::Compile && FinalPhase == phases::Preprocess &&
- getPreprocessedType(InputType) == types::TY_INVALID)
+ else if (InitialPhase == phases::Compile &&
+ FinalPhase == phases::Preprocess &&
+ getPreprocessedType(InputType) == types::TY_INVALID)
Diag(clang::diag::warn_drv_preprocessed_input_file_unused)
<< InputArg->getAsString(Args)
- << FinalPhaseArg->getOption().getName();
+ << !!FinalPhaseArg
+ << FinalPhaseArg ? FinalPhaseArg->getOption().getName() : "";
else
Diag(clang::diag::warn_drv_input_file_unused)
<< InputArg->getAsString(Args)
<< getPhaseName(InitialPhase)
- << FinalPhaseArg->getOption().getName();
+ << !!FinalPhaseArg
+ << FinalPhaseArg ? FinalPhaseArg->getOption().getName() : "";
continue;
}
@@ -1130,14 +1210,23 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase,
if (Args.hasArg(options::OPT_M, options::OPT_MM)) {
OutputTy = types::TY_Dependencies;
} else {
- OutputTy = types::getPreprocessedType(Input->getType());
+ OutputTy = Input->getType();
+ if (!Args.hasFlag(options::OPT_frewrite_includes,
+ options::OPT_fno_rewrite_includes, false))
+ OutputTy = types::getPreprocessedType(OutputTy);
assert(OutputTy != types::TY_INVALID &&
"Cannot preprocess this input type!");
}
return new PreprocessJobAction(Input, OutputTy);
}
- case phases::Precompile:
- return new PrecompileJobAction(Input, types::TY_PCH);
+ case phases::Precompile: {
+ types::ID OutputTy = types::TY_PCH;
+ if (Args.hasArg(options::OPT_fsyntax_only)) {
+ // Syntax checks should not emit a PCH file
+ OutputTy = types::TY_Nothing;
+ }
+ return new PrecompileJobAction(Input, OutputTy);
+ }
case phases::Compile: {
if (Args.hasArg(options::OPT_fsyntax_only)) {
return new CompileJobAction(Input, types::TY_Nothing);
@@ -1332,10 +1421,13 @@ void Driver::BuildJobsForAction(Compilation &C,
}
if (const BindArchAction *BAA = dyn_cast<BindArchAction>(A)) {
- const ToolChain *TC = &C.getDefaultToolChain();
+ const ToolChain *TC;
+ const char *ArchName = BAA->getArchName();
- if (BAA->getArchName())
- TC = &getToolChain(C.getArgs(), BAA->getArchName());
+ if (ArchName)
+ TC = &getToolChain(C.getArgs(), ArchName);
+ else
+ TC = &C.getDefaultToolChain();
BuildJobsForAction(C, *BAA->begin(), TC, BAA->getArchName(),
AtTopLevel, LinkingOutput, Result);
@@ -1453,15 +1545,24 @@ const char *Driver::GetNamedOutputPath(Compilation &C,
NamedOutput = C.getArgs().MakeArgString(Suffixed.c_str());
}
- // If we're saving temps and the temp filename conflicts with the input
- // filename, then avoid overwriting input file.
+ // If we're saving temps and the temp file conflicts with the input file,
+ // then avoid overwriting input file.
if (!AtTopLevel && C.getArgs().hasArg(options::OPT_save_temps) &&
NamedOutput == BaseName) {
- StringRef Name = llvm::sys::path::filename(BaseInput);
- std::pair<StringRef, StringRef> Split = Name.split('.');
- std::string TmpName =
- GetTemporaryPath(Split.first, types::getTypeTempSuffix(JA.getType()));
- return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str()));
+
+ bool SameFile = false;
+ SmallString<256> Result;
+ llvm::sys::fs::current_path(Result);
+ llvm::sys::path::append(Result, BaseName);
+ llvm::sys::fs::equivalent(BaseInput, Result.c_str(), SameFile);
+ // Must share the same path to conflict.
+ if (SameFile) {
+ StringRef Name = llvm::sys::path::filename(BaseInput);
+ std::pair<StringRef, StringRef> Split = Name.split('.');
+ std::string TmpName =
+ GetTemporaryPath(Split.first, types::getTypeTempSuffix(JA.getType()));
+ return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str()));
+ }
}
// As an annoying special case, PCH generation doesn't strip the pathname.
@@ -1564,7 +1665,7 @@ std::string Driver::GetProgramPath(const char *Name, const ToolChain &TC,
return Name;
}
-std::string Driver::GetTemporaryPath(StringRef Prefix, const char *Suffix)
+std::string Driver::GetTemporaryPath(StringRef Prefix, const char *Suffix)
const {
// FIXME: This is lame; sys::Path should provide this function (in particular,
// it should know how to find the temporary files dir).
@@ -1579,14 +1680,15 @@ std::string Driver::GetTemporaryPath(StringRef Prefix, const char *Suffix)
llvm::sys::Path P(TmpDir);
P.appendComponent(Prefix);
if (P.makeUnique(false, &Error)) {
- Diag(clang::diag::err_drv_unable_to_make_temp) << Error;
+ Diag(clang::diag::err_unable_to_make_temp) << Error;
return "";
}
// FIXME: Grumble, makeUnique sometimes leaves the file around!? PR3837.
P.eraseFromDisk(false, 0);
- P.appendSuffix(Suffix);
+ if (Suffix)
+ P.appendSuffix(Suffix);
return P.str();
}
@@ -1674,6 +1776,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
case llvm::Triple::OpenBSD:
TC = new toolchains::OpenBSD(*this, Target, Args);
break;
+ case llvm::Triple::Bitrig:
+ TC = new toolchains::Bitrig(*this, Target, Args);
+ break;
case llvm::Triple::NetBSD:
TC = new toolchains::NetBSD(*this, Target, Args);
break;
diff --git a/lib/Driver/OptTable.cpp b/lib/Driver/OptTable.cpp
index 4f5390b..a3e38b2 100644
--- a/lib/Driver/OptTable.cpp
+++ b/lib/Driver/OptTable.cpp
@@ -181,6 +181,8 @@ Option *OptTable::CreateOption(unsigned id) const {
}
if (info.Flags & Unsupported)
Opt->setUnsupported(true);
+ if (info.Flags & CC1Option)
+ Opt->setIsCC1Option(true);
return Opt;
}
diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp
index db4d2a8..48ed044 100644
--- a/lib/Driver/ToolChain.cpp
+++ b/lib/Driver/ToolChain.cpp
@@ -14,10 +14,10 @@
#include "clang/Driver/ArgList.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
-#include "clang/Driver/ObjCRuntime.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
+#include "clang/Basic/ObjCRuntime.h"
using namespace clang::driver;
using namespace clang;
@@ -49,25 +49,9 @@ bool ToolChain::HasNativeLLVMSupport() const {
return false;
}
-void ToolChain::configureObjCRuntime(ObjCRuntime &runtime) const {
- switch (runtime.getKind()) {
- case ObjCRuntime::NeXT:
- // Assume a minimal NeXT runtime.
- runtime.HasARC = false;
- runtime.HasWeak = false;
- runtime.HasSubscripting = false;
- runtime.HasTerminate = false;
- return;
-
- case ObjCRuntime::GNU:
- // Assume a maximal GNU runtime.
- runtime.HasARC = true;
- runtime.HasWeak = true;
- runtime.HasSubscripting = false; // to be added
- runtime.HasTerminate = false; // to be added
- return;
- }
- llvm_unreachable("invalid runtime kind!");
+ObjCRuntime ToolChain::getDefaultObjCRuntime(bool isNonFragile) const {
+ return ObjCRuntime(isNonFragile ? ObjCRuntime::GNUstep : ObjCRuntime::GCC,
+ VersionTuple());
}
/// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting.
@@ -189,6 +173,9 @@ void ToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
// Each toolchain should provide the appropriate include flags.
}
+void ToolChain::addClangTargetOptions(ArgStringList &CC1Args) const {
+}
+
ToolChain::RuntimeLibType ToolChain::GetRuntimeLibType(
const ArgList &Args) const
{
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index 7f9ed9a..01c6623 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -14,10 +14,10 @@
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
-#include "clang/Driver/ObjCRuntime.h"
#include "clang/Driver/OptTable.h"
#include "clang/Driver/Option.h"
#include "clang/Driver/Options.h"
+#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/SmallString.h"
@@ -42,9 +42,7 @@ using namespace clang;
/// Darwin - Darwin tool chain for i386 and x86_64.
Darwin::Darwin(const Driver &D, const llvm::Triple& Triple)
- : ToolChain(D, Triple), TargetInitialized(false),
- ARCRuntimeForSimulator(ARCSimulator_None),
- LibCXXForSimulator(LibCXXSimulator_None)
+ : ToolChain(D, Triple), TargetInitialized(false)
{
// Compute the initial Darwin version from the triple
unsigned Major, Minor, Micro;
@@ -59,6 +57,11 @@ Darwin::Darwin(const Driver &D, const llvm::Triple& Triple)
DarwinVersion[0] = Minor + 4;
DarwinVersion[1] = Micro;
DarwinVersion[2] = 0;
+
+ // Compute the initial iOS version from the triple
+ Triple.getiOSVersion(Major, Minor, Micro);
+ llvm::raw_string_ostream(iOSVersionMin)
+ << Major << '.' << Minor << '.' << Micro;
}
types::ID Darwin::LookupTypeForExtension(const char *Ext) const {
@@ -75,42 +78,19 @@ bool Darwin::HasNativeLLVMSupport() const {
return true;
}
-bool Darwin::hasARCRuntime() const {
- // FIXME: Remove this once there is a proper way to detect an ARC runtime
- // for the simulator.
- switch (ARCRuntimeForSimulator) {
- case ARCSimulator_None:
- break;
- case ARCSimulator_HasARCRuntime:
- return true;
- case ARCSimulator_NoARCRuntime:
- return false;
- }
-
- if (isTargetIPhoneOS())
- return !isIPhoneOSVersionLT(5);
- else
- return !isMacosxVersionLT(10, 7);
-}
-
-bool Darwin::hasSubscriptingRuntime() const {
- return !isTargetIPhoneOS() && !isMacosxVersionLT(10, 8);
-}
-
/// Darwin provides an ARC runtime starting in MacOS X 10.7 and iOS 5.0.
-void Darwin::configureObjCRuntime(ObjCRuntime &runtime) const {
- if (runtime.getKind() != ObjCRuntime::NeXT)
- return ToolChain::configureObjCRuntime(runtime);
-
- runtime.HasARC = runtime.HasWeak = hasARCRuntime();
- runtime.HasSubscripting = hasSubscriptingRuntime();
-
- // So far, objc_terminate is only available in iOS 5.
- // FIXME: do the simulator logic properly.
- if (!ARCRuntimeForSimulator && isTargetIPhoneOS())
- runtime.HasTerminate = !isIPhoneOSVersionLT(5);
- else
- runtime.HasTerminate = false;
+ObjCRuntime Darwin::getDefaultObjCRuntime(bool isNonFragile) const {
+ if (isTargetIPhoneOS()) {
+ return ObjCRuntime(ObjCRuntime::iOS, TargetVersion);
+ } else if (TargetSimulatorVersionFromDefines != VersionTuple()) {
+ return ObjCRuntime(ObjCRuntime::iOS, TargetSimulatorVersionFromDefines);
+ } else {
+ if (isNonFragile) {
+ return ObjCRuntime(ObjCRuntime::MacOSX, TargetVersion);
+ } else {
+ return ObjCRuntime(ObjCRuntime::FragileMacOSX, TargetVersion);
+ }
+ }
}
/// Darwin provides a blocks runtime starting in MacOS X 10.6 and iOS 3.2.
@@ -194,21 +174,25 @@ void Generic_ELF::anchor() {}
Tool &Darwin::SelectTool(const Compilation &C, const JobAction &JA,
const ActionList &Inputs) const {
- Action::ActionClass Key;
+ Action::ActionClass Key = JA.getKind();
+ bool useClang = false;
if (getDriver().ShouldUseClangCompiler(C, JA, getTriple())) {
+ useClang = true;
// Fallback to llvm-gcc for i386 kext compiles, we don't support that ABI.
- if (Inputs.size() == 1 &&
+ if (!getDriver().shouldForceClangUse() &&
+ Inputs.size() == 1 &&
types::isCXX(Inputs[0]->getType()) &&
getTriple().isOSDarwin() &&
getTriple().getArch() == llvm::Triple::x86 &&
(C.getArgs().getLastArg(options::OPT_fapple_kext) ||
C.getArgs().getLastArg(options::OPT_mkernel)))
- Key = JA.getKind();
- else
- Key = Action::AnalyzeJobClass;
- } else
- Key = JA.getKind();
+ useClang = false;
+ }
+
+ // FIXME: This seems like a hacky way to choose clang frontend.
+ if (useClang)
+ Key = Action::AnalyzeJobClass;
bool UseIntegratedAs = C.getArgs().hasFlag(options::OPT_integrated_as,
options::OPT_no_integrated_as,
@@ -287,76 +271,6 @@ void DarwinClang::AddGCCLibexecPath(unsigned darwinVersion) {
getProgramPaths().push_back(Path);
}
-void DarwinClang::AddLinkSearchPathArgs(const ArgList &Args,
- ArgStringList &CmdArgs) const {
- // The Clang toolchain uses explicit paths for internal libraries.
-
- // Unfortunately, we still might depend on a few of the libraries that are
- // only available in the gcc library directory (in particular
- // libstdc++.dylib). For now, hardcode the path to the known install location.
- // FIXME: This should get ripped out someday. However, when building on
- // 10.6 (darwin10), we're still relying on this to find libstdc++.dylib.
- llvm::sys::Path P(getDriver().Dir);
- P.eraseComponent(); // .../usr/bin -> ../usr
- P.appendComponent("llvm-gcc-4.2");
- P.appendComponent("lib");
- P.appendComponent("gcc");
- switch (getTriple().getArch()) {
- default:
- llvm_unreachable("Invalid Darwin arch!");
- case llvm::Triple::x86:
- case llvm::Triple::x86_64:
- P.appendComponent("i686-apple-darwin10");
- break;
- case llvm::Triple::arm:
- case llvm::Triple::thumb:
- P.appendComponent("arm-apple-darwin10");
- break;
- case llvm::Triple::ppc:
- case llvm::Triple::ppc64:
- P.appendComponent("powerpc-apple-darwin10");
- break;
- }
- P.appendComponent("4.2.1");
-
- // Determine the arch specific GCC subdirectory.
- const char *ArchSpecificDir = 0;
- switch (getTriple().getArch()) {
- default:
- break;
- case llvm::Triple::arm:
- case llvm::Triple::thumb: {
- std::string Triple = ComputeLLVMTriple(Args);
- StringRef TripleStr = Triple;
- if (TripleStr.startswith("armv5") || TripleStr.startswith("thumbv5"))
- ArchSpecificDir = "v5";
- else if (TripleStr.startswith("armv6") || TripleStr.startswith("thumbv6"))
- ArchSpecificDir = "v6";
- else if (TripleStr.startswith("armv7") || TripleStr.startswith("thumbv7"))
- ArchSpecificDir = "v7";
- break;
- }
- case llvm::Triple::ppc64:
- ArchSpecificDir = "ppc64";
- break;
- case llvm::Triple::x86_64:
- ArchSpecificDir = "x86_64";
- break;
- }
-
- if (ArchSpecificDir) {
- P.appendComponent(ArchSpecificDir);
- bool Exists;
- if (!llvm::sys::fs::exists(P.str(), Exists) && Exists)
- CmdArgs.push_back(Args.MakeArgString("-L" + P.str()));
- P.eraseComponent();
- }
-
- bool Exists;
- if (!llvm::sys::fs::exists(P.str(), Exists) && Exists)
- CmdArgs.push_back(Args.MakeArgString("-L" + P.str()));
-}
-
void DarwinClang::AddLinkARCArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
@@ -374,7 +288,7 @@ void DarwinClang::AddLinkARCArgs(const ArgList &Args,
else if (isTargetIPhoneOS())
s += "iphoneos";
// FIXME: Remove this once we depend fully on -mios-simulator-version-min.
- else if (ARCRuntimeForSimulator != ARCSimulator_None)
+ else if (TargetSimulatorVersionFromDefines != VersionTuple())
s += "iphonesimulator";
else
s += "macosx";
@@ -545,11 +459,13 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const {
unsigned Major = 0, Minor = 0, Micro = 0;
if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) &&
Major < 10 && Minor < 100 && Micro < 100) {
- ARCRuntimeForSimulator = Major < 5 ? ARCSimulator_NoARCRuntime
- : ARCSimulator_HasARCRuntime;
- LibCXXForSimulator = Major < 5 ? LibCXXSimulator_NotAvailable
- : LibCXXSimulator_Available;
+ TargetSimulatorVersionFromDefines = VersionTuple(Major, Minor, Micro);
}
+ // When using the define to indicate the simulator, we force
+ // 10.6 macosx target.
+ const Option *O = Opts.getOption(options::OPT_mmacosx_version_min_EQ);
+ OSXVersion = Args.MakeJoinedArg(0, O, "10.6");
+ Args.append(OSXVersion);
break;
}
}
@@ -593,9 +509,9 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const {
// If no OSX or iOS target has been specified and we're compiling for armv7,
// go ahead as assume we're targeting iOS.
- if (OSXTarget.empty() && iOSTarget.empty())
- if (getDarwinArchName(Args) == "armv7")
- iOSTarget = "0.0";
+ if (OSXTarget.empty() && iOSTarget.empty() &&
+ getDarwinArchName(Args) == "armv7")
+ iOSTarget = iOSVersionMin;
// Handle conflicting deployment targets
//
@@ -956,27 +872,27 @@ DerivedArgList *Darwin::TranslateArgs(const DerivedArgList &Args,
// Add an explicit version min argument for the deployment target. We do this
// after argument translation because -Xarch_ arguments may add a version min
// argument.
- AddDeploymentTarget(*DAL);
+ if (BoundArch)
+ AddDeploymentTarget(*DAL);
// Validate the C++ standard library choice.
CXXStdlibType Type = GetCXXStdlibType(*DAL);
if (Type == ToolChain::CST_Libcxx) {
- switch (LibCXXForSimulator) {
- case LibCXXSimulator_None:
- // Handle non-simulator cases.
- if (isTargetIPhoneOS()) {
- if (isIPhoneOSVersionLT(5, 0)) {
- getDriver().Diag(clang::diag::err_drv_invalid_libcxx_deployment)
- << "iOS 5.0";
- }
- }
- break;
- case LibCXXSimulator_NotAvailable:
+ // Check whether the target provides libc++.
+ StringRef where;
+
+ // Complain about targetting iOS < 5.0 in any way.
+ if (TargetSimulatorVersionFromDefines != VersionTuple()) {
+ if (TargetSimulatorVersionFromDefines < VersionTuple(5, 0))
+ where = "iOS 5.0";
+ } else if (isTargetIPhoneOS()) {
+ if (isIPhoneOSVersionLT(5, 0))
+ where = "iOS 5.0";
+ }
+
+ if (where != StringRef()) {
getDriver().Diag(clang::diag::err_drv_invalid_libcxx_deployment)
- << "iOS 5.0";
- break;
- case LibCXXSimulator_Available:
- break;
+ << where;
}
}
@@ -1187,6 +1103,9 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(
"arm-linux-gnueabi",
"arm-linux-androideabi"
};
+ static const char *const ARMHFTriples[] = {
+ "arm-linux-gnueabihf",
+ };
static const char *const X86_64LibDirs[] = { "/lib64", "/lib" };
static const char *const X86_64Triples[] = {
@@ -1210,7 +1129,8 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(
"i586-redhat-linux",
"i386-redhat-linux",
"i586-suse-linux",
- "i486-slackware-linux"
+ "i486-slackware-linux",
+ "i686-montavista-linux"
};
static const char *const MIPSLibDirs[] = { "/lib" };
@@ -1218,11 +1138,17 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(
static const char *const MIPSELLibDirs[] = { "/lib" };
static const char *const MIPSELTriples[] = { "mipsel-linux-gnu" };
+ static const char *const MIPS64LibDirs[] = { "/lib64", "/lib" };
+ static const char *const MIPS64Triples[] = { "mips64-linux-gnu" };
+ static const char *const MIPS64ELLibDirs[] = { "/lib64", "/lib" };
+ static const char *const MIPS64ELTriples[] = { "mips64el-linux-gnu" };
+
static const char *const PPCLibDirs[] = { "/lib32", "/lib" };
static const char *const PPCTriples[] = {
"powerpc-linux-gnu",
"powerpc-unknown-linux-gnu",
- "powerpc-suse-linux"
+ "powerpc-suse-linux",
+ "powerpc-montavista-linuxspe"
};
static const char *const PPC64LibDirs[] = { "/lib64", "/lib" };
static const char *const PPC64Triples[] = {
@@ -1236,8 +1162,13 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(
case llvm::Triple::arm:
case llvm::Triple::thumb:
LibDirs.append(ARMLibDirs, ARMLibDirs + llvm::array_lengthof(ARMLibDirs));
- TripleAliases.append(
- ARMTriples, ARMTriples + llvm::array_lengthof(ARMTriples));
+ if (TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHF) {
+ TripleAliases.append(
+ ARMHFTriples, ARMHFTriples + llvm::array_lengthof(ARMHFTriples));
+ } else {
+ TripleAliases.append(
+ ARMTriples, ARMTriples + llvm::array_lengthof(ARMTriples));
+ }
break;
case llvm::Triple::x86_64:
LibDirs.append(
@@ -1263,12 +1194,40 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector(
MIPSLibDirs, MIPSLibDirs + llvm::array_lengthof(MIPSLibDirs));
TripleAliases.append(
MIPSTriples, MIPSTriples + llvm::array_lengthof(MIPSTriples));
+ MultiarchLibDirs.append(
+ MIPS64LibDirs, MIPS64LibDirs + llvm::array_lengthof(MIPS64LibDirs));
+ MultiarchTripleAliases.append(
+ MIPS64Triples, MIPS64Triples + llvm::array_lengthof(MIPS64Triples));
break;
case llvm::Triple::mipsel:
LibDirs.append(
MIPSELLibDirs, MIPSELLibDirs + llvm::array_lengthof(MIPSELLibDirs));
TripleAliases.append(
MIPSELTriples, MIPSELTriples + llvm::array_lengthof(MIPSELTriples));
+ MultiarchLibDirs.append(
+ MIPS64ELLibDirs, MIPS64ELLibDirs + llvm::array_lengthof(MIPS64ELLibDirs));
+ MultiarchTripleAliases.append(
+ MIPS64ELTriples, MIPS64ELTriples + llvm::array_lengthof(MIPS64ELTriples));
+ break;
+ case llvm::Triple::mips64:
+ LibDirs.append(
+ MIPS64LibDirs, MIPS64LibDirs + llvm::array_lengthof(MIPS64LibDirs));
+ TripleAliases.append(
+ MIPS64Triples, MIPS64Triples + llvm::array_lengthof(MIPS64Triples));
+ MultiarchLibDirs.append(
+ MIPSLibDirs, MIPSLibDirs + llvm::array_lengthof(MIPSLibDirs));
+ MultiarchTripleAliases.append(
+ MIPSTriples, MIPSTriples + llvm::array_lengthof(MIPSTriples));
+ break;
+ case llvm::Triple::mips64el:
+ LibDirs.append(
+ MIPS64ELLibDirs, MIPS64ELLibDirs + llvm::array_lengthof(MIPS64ELLibDirs));
+ TripleAliases.append(
+ MIPS64ELTriples, MIPS64ELTriples + llvm::array_lengthof(MIPS64ELTriples));
+ MultiarchLibDirs.append(
+ MIPSELLibDirs, MIPSELLibDirs + llvm::array_lengthof(MIPSELLibDirs));
+ MultiarchTripleAliases.append(
+ MIPSELTriples, MIPSELTriples + llvm::array_lengthof(MIPSELTriples));
break;
case llvm::Triple::ppc:
LibDirs.append(PPCLibDirs, PPCLibDirs + llvm::array_lengthof(PPCLibDirs));
@@ -1350,7 +1309,9 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple(
// crtbegin.o without the subdirectory.
StringRef MultiarchSuffix
= (TargetArch == llvm::Triple::x86_64 ||
- TargetArch == llvm::Triple::ppc64) ? "/64" : "/32";
+ TargetArch == llvm::Triple::ppc64 ||
+ TargetArch == llvm::Triple::mips64 ||
+ TargetArch == llvm::Triple::mips64el) ? "/64" : "/32";
if (llvm::sys::fs::exists(LI->path() + MultiarchSuffix + "/crtbegin.o")) {
GCCMultiarchSuffix = MultiarchSuffix.str();
} else {
@@ -1606,6 +1567,67 @@ Tool &OpenBSD::SelectTool(const Compilation &C, const JobAction &JA,
return *T;
}
+/// Bitrig - Bitrig tool chain which can call as(1) and ld(1) directly.
+
+Bitrig::Bitrig(const Driver &D, const llvm::Triple& Triple, const ArgList &Args)
+ : Generic_ELF(D, Triple, Args) {
+ getFilePaths().push_back(getDriver().Dir + "/../lib");
+ getFilePaths().push_back("/usr/lib");
+}
+
+Tool &Bitrig::SelectTool(const Compilation &C, const JobAction &JA,
+ const ActionList &Inputs) const {
+ Action::ActionClass Key;
+ if (getDriver().ShouldUseClangCompiler(C, JA, getTriple()))
+ Key = Action::AnalyzeJobClass;
+ else
+ Key = JA.getKind();
+
+ bool UseIntegratedAs = C.getArgs().hasFlag(options::OPT_integrated_as,
+ options::OPT_no_integrated_as,
+ IsIntegratedAssemblerDefault());
+
+ Tool *&T = Tools[Key];
+ if (!T) {
+ switch (Key) {
+ case Action::AssembleJobClass: {
+ if (UseIntegratedAs)
+ T = new tools::ClangAs(*this);
+ else
+ T = new tools::bitrig::Assemble(*this);
+ break;
+ }
+ case Action::LinkJobClass:
+ T = new tools::bitrig::Link(*this); break;
+ default:
+ T = &Generic_GCC::SelectTool(C, JA, Inputs);
+ }
+ }
+
+ return *T;
+}
+
+void Bitrig::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
+ ArgStringList &CC1Args) const {
+ if (DriverArgs.hasArg(options::OPT_nostdlibinc) ||
+ DriverArgs.hasArg(options::OPT_nostdincxx))
+ return;
+
+ std::string Triple = getTriple().str();
+ if (Triple.substr(0, 5) == "amd64")
+ Triple.replace(0, 5, "x86_64");
+
+ addSystemInclude(DriverArgs, CC1Args, "/usr/include/c++/4.6.2");
+ addSystemInclude(DriverArgs, CC1Args, "/usr/include/c++/4.6.2/backward");
+ addSystemInclude(DriverArgs, CC1Args, "/usr/include/c++/4.6.2/" + Triple);
+
+}
+
+void Bitrig::AddCXXStdlibLibArgs(const ArgList &Args,
+ ArgStringList &CmdArgs) const {
+ CmdArgs.push_back("-lstdc++");
+}
+
/// FreeBSD - FreeBSD tool chain which can call as(1) and ld(1) directly.
FreeBSD::FreeBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args)
@@ -1957,6 +1979,16 @@ static std::string getMultiarchTriple(const llvm::Triple TargetTriple,
// common linux triples that don't quite match the Clang triple for both
// 32-bit and 64-bit targets. Multiarch fixes its install triples to these
// regardless of what the actual target triple is.
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHF) {
+ if (llvm::sys::fs::exists(SysRoot + "/lib/arm-linux-gnueabihf"))
+ return "arm-linux-gnueabihf";
+ } else {
+ if (llvm::sys::fs::exists(SysRoot + "/lib/arm-linux-gnueabi"))
+ return "arm-linux-gnueabi";
+ }
+ return TargetTriple.str();
case llvm::Triple::x86:
if (llvm::sys::fs::exists(SysRoot + "/lib/i386-linux-gnu"))
return "i386-linux-gnu";
@@ -2139,6 +2171,12 @@ Tool &Linux::SelectTool(const Compilation &C, const JobAction &JA,
return *T;
}
+void Linux::addClangTargetOptions(ArgStringList &CC1Args) const {
+ const Generic_GCC::GCCVersion &V = GCCInstallation.getVersion();
+ if (V >= Generic_GCC::GCCVersion::Parse("4.7.0"))
+ CC1Args.push_back("-fuse-init-array");
+}
+
void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const {
const Driver &D = getDriver();
@@ -2197,6 +2235,9 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
const StringRef ARMMultiarchIncludeDirs[] = {
"/usr/include/arm-linux-gnueabi"
};
+ const StringRef ARMHFMultiarchIncludeDirs[] = {
+ "/usr/include/arm-linux-gnueabihf"
+ };
const StringRef MIPSMultiarchIncludeDirs[] = {
"/usr/include/mips-linux-gnu"
};
@@ -2215,7 +2256,10 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
} else if (getTriple().getArch() == llvm::Triple::x86) {
MultiarchIncludeDirs = X86MultiarchIncludeDirs;
} else if (getTriple().getArch() == llvm::Triple::arm) {
- MultiarchIncludeDirs = ARMMultiarchIncludeDirs;
+ if (getTriple().getEnvironment() == llvm::Triple::GNUEABIHF)
+ MultiarchIncludeDirs = ARMHFMultiarchIncludeDirs;
+ else
+ MultiarchIncludeDirs = ARMMultiarchIncludeDirs;
} else if (getTriple().getArch() == llvm::Triple::mips) {
MultiarchIncludeDirs = MIPSMultiarchIncludeDirs;
} else if (getTriple().getArch() == llvm::Triple::mipsel) {
@@ -2281,7 +2325,7 @@ void Linux::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
// equivalent to '/usr/include/c++/X.Y' in almost all cases.
StringRef LibDir = GCCInstallation.getParentLibPath();
StringRef InstallDir = GCCInstallation.getInstallPath();
- StringRef Version = GCCInstallation.getVersion();
+ StringRef Version = GCCInstallation.getVersion().Text;
if (!addLibStdCXXIncludePaths(LibDir + "/../include/c++/" + Version,
(GCCInstallation.getTriple().str() +
GCCInstallation.getMultiarchSuffix()),
diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h
index eaa6be1..95a11be 100644
--- a/lib/Driver/ToolChains.h
+++ b/lib/Driver/ToolChains.h
@@ -99,7 +99,7 @@ protected:
StringRef getParentLibPath() const { return GCCParentLibPath; }
/// \brief Get the detected GCC version string.
- StringRef getVersion() const { return Version.Text; }
+ const GCCVersion &getVersion() const { return Version; }
private:
static void CollectLibDirsAndTriples(
@@ -176,22 +176,6 @@ private:
// the argument translation business.
mutable bool TargetInitialized;
- // FIXME: Remove this once there is a proper way to detect an ARC runtime
- // for the simulator.
- public:
- mutable enum {
- ARCSimulator_None,
- ARCSimulator_HasARCRuntime,
- ARCSimulator_NoARCRuntime
- } ARCRuntimeForSimulator;
-
- mutable enum {
- LibCXXSimulator_None,
- LibCXXSimulator_NotAvailable,
- LibCXXSimulator_Available
- } LibCXXForSimulator;
-
-private:
/// Whether we are targeting iPhoneOS target.
mutable bool TargetIsIPhoneOS;
@@ -201,12 +185,19 @@ private:
/// The OS version we are targeting.
mutable VersionTuple TargetVersion;
+protected:
+ // FIXME: Remove this once there is a proper way to detect an ARC runtime
+ // for the simulator.
+ mutable VersionTuple TargetSimulatorVersionFromDefines;
+
+private:
/// The default macosx-version-min of this tool chain; empty until
/// initialized.
std::string MacosxVersionMin;
- bool hasARCRuntime() const;
- bool hasSubscriptingRuntime() const;
+ /// The default ios-version-min of this tool chain; empty until
+ /// initialized.
+ std::string iOSVersionMin;
private:
void AddDeploymentTarget(DerivedArgList &Args) const;
@@ -254,7 +245,7 @@ public:
bool isTargetMacOS() const {
return !isTargetIOSSimulator() &&
!isTargetIPhoneOS() &&
- ARCRuntimeForSimulator == ARCSimulator_None;
+ TargetSimulatorVersionFromDefines == VersionTuple();
}
bool isTargetInitialized() const { return TargetInitialized; }
@@ -279,14 +270,6 @@ public:
return TargetVersion < VersionTuple(V0, V1, V2);
}
- /// AddLinkSearchPathArgs - Add the linker search paths to \arg CmdArgs.
- ///
- /// \param Args - The input argument list.
- /// \param CmdArgs [out] - The command argument list to append the paths
- /// (prefixed by -L) to.
- virtual void AddLinkSearchPathArgs(const ArgList &Args,
- ArgStringList &CmdArgs) const = 0;
-
/// AddLinkARCArgs - Add the linker arguments to link the ARC runtime library.
virtual void AddLinkARCArgs(const ArgList &Args,
ArgStringList &CmdArgs) const = 0;
@@ -304,7 +287,7 @@ public:
virtual bool HasNativeLLVMSupport() const;
- virtual void configureObjCRuntime(ObjCRuntime &runtime) const;
+ virtual ObjCRuntime getDefaultObjCRuntime(bool isNonFragile) const;
virtual bool hasBlocksRuntime() const;
virtual DerivedArgList *TranslateArgs(const DerivedArgList &Args,
@@ -333,7 +316,11 @@ public:
return ToolChain::IsStrictAliasingDefault();
#endif
}
-
+
+ virtual bool IsMathErrnoDefault() const {
+ return false;
+ }
+
virtual bool IsObjCDefaultSynthPropertiesDefault() const {
return true;
}
@@ -342,12 +329,7 @@ public:
// Non-fragile ABI is default for everything but i386.
return getTriple().getArch() != llvm::Triple::x86;
}
- virtual bool IsObjCLegacyDispatchDefault() const {
- // This is only used with the non-fragile ABI.
- // Legacy dispatch is used everywhere except on x86_64.
- return getTriple().getArch() != llvm::Triple::x86_64;
- }
virtual bool UseObjCMixedDispatch() const {
// This is only used with the non-fragile ABI and non-legacy dispatch.
@@ -392,9 +374,6 @@ public:
/// @name Darwin ToolChain Implementation
/// {
- virtual void AddLinkSearchPathArgs(const ArgList &Args,
- ArgStringList &CmdArgs) const;
-
virtual void AddLinkRuntimeLibArgs(const ArgList &Args,
ArgStringList &CmdArgs) const;
void AddLinkRuntimeLib(const ArgList &Args, ArgStringList &CmdArgs,
@@ -459,33 +438,39 @@ class LLVM_LIBRARY_VISIBILITY OpenBSD : public Generic_ELF {
public:
OpenBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args);
+ virtual bool IsMathErrnoDefault() const { return false; }
virtual bool IsObjCNonFragileABIDefault() const { return true; }
- virtual bool IsObjCLegacyDispatchDefault() const {
- llvm::Triple::ArchType Arch = getTriple().getArch();
- if (Arch == llvm::Triple::arm ||
- Arch == llvm::Triple::x86 ||
- Arch == llvm::Triple::x86_64)
- return false;
- return true;
- }
virtual Tool &SelectTool(const Compilation &C, const JobAction &JA,
const ActionList &Inputs) const;
};
+class LLVM_LIBRARY_VISIBILITY Bitrig : public Generic_ELF {
+public:
+ Bitrig(const Driver &D, const llvm::Triple& Triple, const ArgList &Args);
+
+ virtual bool IsMathErrnoDefault() const { return false; }
+ virtual bool IsObjCNonFragileABIDefault() const { return true; }
+ virtual bool IsObjCLegacyDispatchDefault() const { return false; }
+
+ virtual Tool &SelectTool(const Compilation &C, const JobAction &JA,
+ const ActionList &Inputs) const;
+
+ virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
+ ArgStringList &CC1Args) const;
+ virtual void AddCXXStdlibLibArgs(const ArgList &Args,
+ ArgStringList &CmdArgs) const;
+ virtual unsigned GetDefaultStackProtectorLevel(bool KernelOrKext) const {
+ return 1;
+ }
+};
+
class LLVM_LIBRARY_VISIBILITY FreeBSD : public Generic_ELF {
public:
FreeBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args);
+ virtual bool IsMathErrnoDefault() const { return false; }
virtual bool IsObjCNonFragileABIDefault() const { return true; }
- virtual bool IsObjCLegacyDispatchDefault() const {
- llvm::Triple::ArchType Arch = getTriple().getArch();
- if (Arch == llvm::Triple::arm ||
- Arch == llvm::Triple::x86 ||
- Arch == llvm::Triple::x86_64)
- return false;
- return true;
- }
virtual Tool &SelectTool(const Compilation &C, const JobAction &JA,
const ActionList &Inputs) const;
@@ -495,15 +480,8 @@ class LLVM_LIBRARY_VISIBILITY NetBSD : public Generic_ELF {
public:
NetBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args);
+ virtual bool IsMathErrnoDefault() const { return false; }
virtual bool IsObjCNonFragileABIDefault() const { return true; }
- virtual bool IsObjCLegacyDispatchDefault() const {
- llvm::Triple::ArchType Arch = getTriple().getArch();
- if (Arch == llvm::Triple::arm ||
- Arch == llvm::Triple::x86 ||
- Arch == llvm::Triple::x86_64)
- return false;
- return true;
- }
virtual Tool &SelectTool(const Compilation &C, const JobAction &JA,
const ActionList &Inputs) const;
@@ -521,6 +499,8 @@ class LLVM_LIBRARY_VISIBILITY DragonFly : public Generic_ELF {
public:
DragonFly(const Driver &D, const llvm::Triple& Triple, const ArgList &Args);
+ virtual bool IsMathErrnoDefault() const { return false; }
+
virtual Tool &SelectTool(const Compilation &C, const JobAction &JA,
const ActionList &Inputs) const;
};
@@ -536,6 +516,7 @@ public:
virtual void AddClangSystemIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const;
+ virtual void addClangTargetOptions(ArgStringList &CC1Args) const;
virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
ArgStringList &CC1Args) const;
@@ -577,6 +558,10 @@ public:
virtual Tool &SelectTool(const Compilation &C, const JobAction &JA,
const ActionList &Inputs) const;
+ virtual bool IsObjCDefaultSynthPropertiesDefault() const {
+ return true;
+ }
+
virtual bool IsIntegratedAssemblerDefault() const;
virtual bool IsUnwindTablesDefault() const;
virtual const char *GetDefaultRelocationModel() const;
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 47b5294..b4234cf 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -16,11 +16,11 @@
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Job.h"
-#include "clang/Driver/ObjCRuntime.h"
#include "clang/Driver/Option.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/ToolChain.h"
#include "clang/Driver/Util.h"
+#include "clang/Basic/ObjCRuntime.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
@@ -174,8 +174,10 @@ static bool isObjCAutoRefCount(const ArgList &Args) {
/// \brief Determine whether we are linking the ObjC runtime.
static bool isObjCRuntimeLinked(const ArgList &Args) {
- if (isObjCAutoRefCount(Args))
+ if (isObjCAutoRefCount(Args)) {
+ Args.ClaimAllArgs(options::OPT_fobjc_link_runtime);
return true;
+ }
return Args.hasArg(options::OPT_fobjc_link_runtime);
}
@@ -422,16 +424,47 @@ void Clang::AddPreprocessingOptions(Compilation &C,
getToolChain().AddClangSystemIncludeArgs(Args, CmdArgs);
}
+/// getLLVMArchSuffixForARM - Get the LLVM arch name to use for a particular
+/// CPU.
+//
+// FIXME: This is redundant with -mcpu, why does LLVM use this.
+// FIXME: tblgen this, or kill it!
+static const char *getLLVMArchSuffixForARM(StringRef CPU) {
+ return llvm::StringSwitch<const char *>(CPU)
+ .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t")
+ .Cases("arm720t", "arm9", "arm9tdmi", "v4t")
+ .Cases("arm920", "arm920t", "arm922t", "v4t")
+ .Cases("arm940t", "ep9312","v4t")
+ .Cases("arm10tdmi", "arm1020t", "v5")
+ .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e")
+ .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e")
+ .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e")
+ .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6")
+ .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6")
+ .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2")
+ .Cases("cortex-a8", "cortex-a9", "v7")
+ .Case("cortex-m3", "v7m")
+ .Case("cortex-m4", "v7m")
+ .Case("cortex-m0", "v6m")
+ .Default("");
+}
+
/// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting.
//
// FIXME: tblgen this.
-static const char *getARMTargetCPU(const ArgList &Args,
+static std::string getARMTargetCPU(const ArgList &Args,
const llvm::Triple &Triple) {
// FIXME: Warn on inconsistent use of -mcpu and -march.
// If we have -mcpu=, use that.
- if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ))
- return A->getValue(Args);
+ if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) {
+ StringRef MCPU = A->getValue(Args);
+ // Handle -mcpu=native.
+ if (MCPU == "native")
+ return llvm::sys::getHostCPUName();
+ else
+ return MCPU;
+ }
StringRef MArch;
if (Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
@@ -442,13 +475,25 @@ static const char *getARMTargetCPU(const ArgList &Args,
MArch = Triple.getArchName();
}
+ // Handle -march=native.
+ std::string NativeMArch;
+ if (MArch == "native") {
+ std::string CPU = llvm::sys::getHostCPUName();
+ if (CPU != "generic") {
+ // Translate the native cpu into the architecture. The switch below will
+ // then chose the minimum cpu for that arch.
+ NativeMArch = std::string("arm") + getLLVMArchSuffixForARM(CPU);
+ MArch = NativeMArch;
+ }
+ }
+
return llvm::StringSwitch<const char *>(MArch)
.Cases("armv2", "armv2a","arm2")
.Case("armv3", "arm6")
.Case("armv3m", "arm7m")
.Cases("armv4", "armv4t", "arm7tdmi")
.Cases("armv5", "armv5t", "arm10tdmi")
- .Cases("armv5e", "armv5te", "arm1026ejs")
+ .Cases("armv5e", "armv5te", "arm1022e")
.Case("armv5tej", "arm926ej-s")
.Cases("armv6", "armv6k", "arm1136jf-s")
.Case("armv6j", "arm1136j-s")
@@ -465,31 +510,6 @@ static const char *getARMTargetCPU(const ArgList &Args,
.Default("arm7tdmi");
}
-/// getLLVMArchSuffixForARM - Get the LLVM arch name to use for a particular
-/// CPU.
-//
-// FIXME: This is redundant with -mcpu, why does LLVM use this.
-// FIXME: tblgen this, or kill it!
-static const char *getLLVMArchSuffixForARM(StringRef CPU) {
- return llvm::StringSwitch<const char *>(CPU)
- .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t")
- .Cases("arm720t", "arm9", "arm9tdmi", "v4t")
- .Cases("arm920", "arm920t", "arm922t", "v4t")
- .Cases("arm940t", "ep9312","v4t")
- .Cases("arm10tdmi", "arm1020t", "v5")
- .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e")
- .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e")
- .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e")
- .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6")
- .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6")
- .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2")
- .Cases("cortex-a8", "cortex-a9", "v7")
- .Case("cortex-m3", "v7m")
- .Case("cortex-m4", "v7m")
- .Case("cortex-m0", "v6m")
- .Default("");
-}
-
// FIXME: Move to target hook.
static bool isSignedCharDefault(const llvm::Triple &Triple) {
switch (Triple.getArch()) {
@@ -601,25 +621,21 @@ static StringRef getARMFloatABI(const Driver &D,
// Darwin defaults to "softfp" for v6 and v7.
//
// FIXME: Factor out an ARM class so we can cache the arch somewhere.
- StringRef ArchName =
+ std::string ArchName =
getLLVMArchSuffixForARM(getARMTargetCPU(Args, Triple));
- if (ArchName.startswith("v6") || ArchName.startswith("v7"))
+ if (StringRef(ArchName).startswith("v6") ||
+ StringRef(ArchName).startswith("v7"))
FloatABI = "softfp";
else
FloatABI = "soft";
break;
}
- case llvm::Triple::Linux: {
- if (Triple.getEnvironment() == llvm::Triple::GNUEABI) {
- FloatABI = "softfp";
- break;
- }
- }
- // fall through
-
default:
switch(Triple.getEnvironment()) {
+ case llvm::Triple::GNUEABIHF:
+ FloatABI = "hard";
+ break;
case llvm::Triple::GNUEABI:
FloatABI = "softfp";
break;
@@ -628,9 +644,9 @@ static StringRef getARMFloatABI(const Driver &D,
FloatABI = "softfp";
break;
case llvm::Triple::ANDROIDEABI: {
- StringRef ArchName =
+ std::string ArchName =
getLLVMArchSuffixForARM(getARMTargetCPU(Args, Triple));
- if (ArchName.startswith("v7"))
+ if (StringRef(ArchName).startswith("v7"))
FloatABI = "softfp";
else
FloatABI = "soft";
@@ -666,6 +682,7 @@ void Clang::AddARMTargetArgs(const ArgList &Args,
switch(Triple.getEnvironment()) {
case llvm::Triple::ANDROIDEABI:
case llvm::Triple::GNUEABI:
+ case llvm::Triple::GNUEABIHF:
ABIName = "aapcs-linux";
break;
case llvm::Triple::EABI:
@@ -680,7 +697,7 @@ void Clang::AddARMTargetArgs(const ArgList &Args,
// Set the CPU based on -march= and -mcpu=.
CmdArgs.push_back("-target-cpu");
- CmdArgs.push_back(getARMTargetCPU(Args, Triple));
+ CmdArgs.push_back(Args.MakeArgString(getARMTargetCPU(Args, Triple)));
// Determine floating point ABI from the options & target defaults.
StringRef FloatABI = getARMFloatABI(D, Args, Triple);
@@ -755,6 +772,9 @@ void Clang::AddARMTargetArgs(const ArgList &Args,
if (A->getOption().matches(options::OPT_mno_global_merge))
CmdArgs.push_back("-mno-global-merge");
}
+
+ if (Args.hasArg(options::OPT_mno_implicit_float))
+ CmdArgs.push_back("-no-implicit-float");
}
// Get default architecture.
@@ -825,19 +845,9 @@ static void getMipsCPUAndABI(const ArgList &Args,
ABIName = getMipsABIFromArch(ArchName);
}
-void Clang::AddMIPSTargetArgs(const ArgList &Args,
- ArgStringList &CmdArgs) const {
- const Driver &D = getToolChain().getDriver();
- StringRef CPUName;
- StringRef ABIName;
- getMipsCPUAndABI(Args, getToolChain(), CPUName, ABIName);
-
- CmdArgs.push_back("-target-cpu");
- CmdArgs.push_back(CPUName.data());
-
- CmdArgs.push_back("-target-abi");
- CmdArgs.push_back(ABIName.data());
-
+// Select the MIPS float ABI as determined by -msoft-float, -mhard-float,
+// and -mfloat-abi=.
+static StringRef getMipsFloatABI(const Driver &D, const ArgList &Args) {
// Select the float ABI as determined by -msoft-float, -mhard-float,
// and -mfloat-abi=.
StringRef FloatABI;
@@ -851,8 +861,7 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args,
else {
FloatABI = A->getValue(Args);
if (FloatABI != "soft" && FloatABI != "single" && FloatABI != "hard") {
- D.Diag(diag::err_drv_invalid_mfloat_abi)
- << A->getAsString(Args);
+ D.Diag(diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args);
FloatABI = "hard";
}
}
@@ -866,6 +875,38 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args,
FloatABI = "hard";
}
+ return FloatABI;
+}
+
+static void AddTargetFeature(const ArgList &Args,
+ ArgStringList &CmdArgs,
+ OptSpecifier OnOpt,
+ OptSpecifier OffOpt,
+ StringRef FeatureName) {
+ if (Arg *A = Args.getLastArg(OnOpt, OffOpt)) {
+ CmdArgs.push_back("-target-feature");
+ if (A->getOption().matches(OnOpt))
+ CmdArgs.push_back(Args.MakeArgString("+" + FeatureName));
+ else
+ CmdArgs.push_back(Args.MakeArgString("-" + FeatureName));
+ }
+}
+
+void Clang::AddMIPSTargetArgs(const ArgList &Args,
+ ArgStringList &CmdArgs) const {
+ const Driver &D = getToolChain().getDriver();
+ StringRef CPUName;
+ StringRef ABIName;
+ getMipsCPUAndABI(Args, getToolChain(), CPUName, ABIName);
+
+ CmdArgs.push_back("-target-cpu");
+ CmdArgs.push_back(CPUName.data());
+
+ CmdArgs.push_back("-target-abi");
+ CmdArgs.push_back(ABIName.data());
+
+ StringRef FloatABI = getMipsFloatABI(D, Args);
+
if (FloatABI == "soft") {
// Floating point operations and argument passing are soft.
CmdArgs.push_back("-msoft-float");
@@ -890,6 +931,82 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args,
CmdArgs.push_back("-mfloat-abi");
CmdArgs.push_back("hard");
}
+
+ AddTargetFeature(Args, CmdArgs,
+ options::OPT_mips16, options::OPT_mno_mips16,
+ "mips16");
+ AddTargetFeature(Args, CmdArgs,
+ options::OPT_mdsp, options::OPT_mno_dsp,
+ "dsp");
+ AddTargetFeature(Args, CmdArgs,
+ options::OPT_mdspr2, options::OPT_mno_dspr2,
+ "dspr2");
+}
+
+/// getPPCTargetCPU - Get the (LLVM) name of the PowerPC cpu we are targeting.
+static std::string getPPCTargetCPU(const ArgList &Args) {
+ if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) {
+ StringRef CPUName = A->getValue(Args);
+
+ if (CPUName == "native") {
+ std::string CPU = llvm::sys::getHostCPUName();
+ if (!CPU.empty() && CPU != "generic")
+ return CPU;
+ else
+ return "";
+ }
+
+ return llvm::StringSwitch<const char *>(CPUName)
+ .Case("common", "generic")
+ .Case("440", "440")
+ .Case("440fp", "440")
+ .Case("450", "450")
+ .Case("601", "601")
+ .Case("602", "602")
+ .Case("603", "603")
+ .Case("603e", "603e")
+ .Case("603ev", "603ev")
+ .Case("604", "604")
+ .Case("604e", "604e")
+ .Case("620", "620")
+ .Case("G3", "g3")
+ .Case("7400", "7400")
+ .Case("G4", "g4")
+ .Case("7450", "7450")
+ .Case("G4+", "g4+")
+ .Case("750", "750")
+ .Case("970", "970")
+ .Case("G5", "g5")
+ .Case("a2", "a2")
+ .Case("power6", "pwr6")
+ .Case("power7", "pwr7")
+ .Case("powerpc", "ppc")
+ .Case("powerpc64", "ppc64")
+ .Default("");
+ }
+
+ return "";
+}
+
+void Clang::AddPPCTargetArgs(const ArgList &Args,
+ ArgStringList &CmdArgs) const {
+ std::string TargetCPUName = getPPCTargetCPU(Args);
+
+ // LLVM may default to generating code for the native CPU,
+ // but, like gcc, we default to a more generic option for
+ // each architecture. (except on Darwin)
+ llvm::Triple Triple = getToolChain().getTriple();
+ if (TargetCPUName.empty() && !Triple.isOSDarwin()) {
+ if (Triple.getArch() == llvm::Triple::ppc64)
+ TargetCPUName = "ppc64";
+ else
+ TargetCPUName = "ppc";
+ }
+
+ if (!TargetCPUName.empty()) {
+ CmdArgs.push_back("-target-cpu");
+ CmdArgs.push_back(Args.MakeArgString(TargetCPUName.c_str()));
+ }
}
void Clang::AddSparcTargetArgs(const ArgList &Args,
@@ -958,7 +1075,7 @@ void Clang::AddX86TargetArgs(const ArgList &Args,
// FIXME: We should also incorporate the detected target features for use
// with -native.
std::string CPU = llvm::sys::getHostCPUName();
- if (!CPU.empty())
+ if (!CPU.empty() && CPU != "generic")
CPUName = Args.MakeArgString(CPU);
} else
CPUName = A->getValue(Args);
@@ -982,6 +1099,11 @@ void Clang::AddX86TargetArgs(const ArgList &Args,
CPUName = "x86-64";
else if (getToolChain().getArch() == llvm::Triple::x86)
CPUName = "i486";
+ } else if (getToolChain().getOS().startswith("bitrig")) {
+ if (getToolChain().getArch() == llvm::Triple::x86_64)
+ CPUName = "x86-64";
+ else if (getToolChain().getArch() == llvm::Triple::x86)
+ CPUName = "i686";
} else if (getToolChain().getOS().startswith("freebsd")) {
if (getToolChain().getArch() == llvm::Triple::x86_64)
CPUName = "x86-64";
@@ -1088,7 +1210,7 @@ void Clang::AddHexagonTargetArgs(const ArgList &Args,
CmdArgs.push_back("-fno-signed-char");
CmdArgs.push_back("-nobuiltininc");
- if (Args.hasArg(options::OPT_mqdsp6_compat))
+ if (Args.hasArg(options::OPT_mqdsp6_compat))
CmdArgs.push_back("-mqdsp6-compat");
if (Arg *A = Args.getLastArg(options::OPT_G,
@@ -1100,18 +1222,23 @@ void Clang::AddHexagonTargetArgs(const ArgList &Args,
A->claim();
}
+ if (!Args.hasArg(options::OPT_fno_short_enums))
+ CmdArgs.push_back("-fshort-enums");
+ if (Args.getLastArg(options::OPT_mieee_rnd_near)) {
+ CmdArgs.push_back ("-mllvm");
+ CmdArgs.push_back ("-enable-hexagon-ieee-rnd-near");
+ }
CmdArgs.push_back ("-mllvm");
CmdArgs.push_back ("-machine-sink-split=0");
}
static bool
-shouldUseExceptionTablesForObjCExceptions(unsigned objcABIVersion,
+shouldUseExceptionTablesForObjCExceptions(const ObjCRuntime &runtime,
const llvm::Triple &Triple) {
// We use the zero-cost exception tables for Objective-C if the non-fragile
// ABI is enabled or when compiling for x86_64 and ARM on Snow Leopard and
// later.
-
- if (objcABIVersion >= 2)
+ if (runtime.isNonFragile())
return true;
if (!Triple.isOSDarwin())
@@ -1130,7 +1257,7 @@ shouldUseExceptionTablesForObjCExceptions(unsigned objcABIVersion,
static void addExceptionArgs(const ArgList &Args, types::ID InputType,
const llvm::Triple &Triple,
bool KernelOrKext,
- unsigned objcABIVersion,
+ const ObjCRuntime &objcRuntime,
ArgStringList &CmdArgs) {
if (KernelOrKext) {
// -mkernel and -fapple-kext imply no exceptions, so claim exception related
@@ -1176,7 +1303,7 @@ static void addExceptionArgs(const ArgList &Args, types::ID InputType,
CmdArgs.push_back("-fobjc-exceptions");
ShouldUseExceptionTables |=
- shouldUseExceptionTablesForObjCExceptions(objcABIVersion, Triple);
+ shouldUseExceptionTablesForObjCExceptions(objcRuntime, Triple);
}
if (types::isCXX(InputType)) {
@@ -1269,22 +1396,56 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) {
/// This needs to be called before we add the C run-time (malloc, etc).
static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
- // Add asan linker flags when linking an executable, but not a shared object.
- if (Args.hasArg(options::OPT_shared) ||
- !Args.hasFlag(options::OPT_faddress_sanitizer,
+ if (!Args.hasFlag(options::OPT_faddress_sanitizer,
options::OPT_fno_address_sanitizer, false))
return;
+ if(TC.getTriple().getEnvironment() == llvm::Triple::ANDROIDEABI) {
+ if (!Args.hasArg(options::OPT_shared)) {
+ if (!Args.hasArg(options::OPT_pie))
+ TC.getDriver().Diag(diag::err_drv_asan_android_requires_pie);
+ // For an executable, we add a .preinit_array stub.
+ CmdArgs.push_back("-u");
+ CmdArgs.push_back("__asan_preinit");
+ CmdArgs.push_back("-lasan");
+ }
- // LibAsan is "libclang_rt.asan-<ArchName>.a" in the Linux library resource
- // directory.
- SmallString<128> LibAsan(TC.getDriver().ResourceDir);
- llvm::sys::path::append(LibAsan, "lib", "linux",
- (Twine("libclang_rt.asan-") +
- TC.getArchName() + ".a"));
- CmdArgs.push_back(Args.MakeArgString(LibAsan));
- CmdArgs.push_back("-lpthread");
- CmdArgs.push_back("-ldl");
- CmdArgs.push_back("-export-dynamic");
+ CmdArgs.push_back("-lasan_preload");
+ CmdArgs.push_back("-ldl");
+ } else {
+ if (!Args.hasArg(options::OPT_shared)) {
+ // LibAsan is "libclang_rt.asan-<ArchName>.a" in the Linux library
+ // resource directory.
+ SmallString<128> LibAsan(TC.getDriver().ResourceDir);
+ llvm::sys::path::append(LibAsan, "lib", "linux",
+ (Twine("libclang_rt.asan-") +
+ TC.getArchName() + ".a"));
+ CmdArgs.push_back(Args.MakeArgString(LibAsan));
+ CmdArgs.push_back("-lpthread");
+ CmdArgs.push_back("-ldl");
+ CmdArgs.push_back("-export-dynamic");
+ }
+ }
+}
+
+/// If ThreadSanitizer is enabled, add appropriate linker flags (Linux).
+/// This needs to be called before we add the C run-time (malloc, etc).
+static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args,
+ ArgStringList &CmdArgs) {
+ if (!Args.hasFlag(options::OPT_fthread_sanitizer,
+ options::OPT_fno_thread_sanitizer, false))
+ return;
+ if (!Args.hasArg(options::OPT_shared)) {
+ // LibTsan is "libclang_rt.tsan-<ArchName>.a" in the Linux library
+ // resource directory.
+ SmallString<128> LibTsan(TC.getDriver().ResourceDir);
+ llvm::sys::path::append(LibTsan, "lib", "linux",
+ (Twine("libclang_rt.tsan-") +
+ TC.getArchName() + ".a"));
+ CmdArgs.push_back(Args.MakeArgString(LibTsan));
+ CmdArgs.push_back("-lpthread");
+ CmdArgs.push_back("-ldl");
+ CmdArgs.push_back("-export-dynamic");
+ }
}
static bool shouldUseFramePointer(const ArgList &Args,
@@ -1328,8 +1489,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString(TripleStr));
// Select the appropriate action.
- bool IsRewriter = false;
- bool IsModernRewriter = false;
+ RewriteKind rewriteKind = RK_None;
if (isa<AnalyzeJobAction>(JA)) {
assert(JA.getType() == types::TY_Plist && "Invalid output type.");
@@ -1380,7 +1540,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// Use PCH if the user requested it.
bool UsePCH = D.CCCUsePCH;
- if (UsePCH)
+ if (JA.getType() == types::TY_Nothing)
+ CmdArgs.push_back("-fsyntax-only");
+ else if (UsePCH)
CmdArgs.push_back("-emit-pch");
else
CmdArgs.push_back("-emit-pth");
@@ -1401,10 +1563,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-emit-pch");
} else if (JA.getType() == types::TY_RewrittenObjC) {
CmdArgs.push_back("-rewrite-objc");
- IsModernRewriter = true;
+ rewriteKind = RK_NonFragile;
} else if (JA.getType() == types::TY_RewrittenLegacyObjC) {
CmdArgs.push_back("-rewrite-objc");
- IsRewriter = true;
+ rewriteKind = RK_Fragile;
} else {
assert(JA.getType() == types::TY_PP_Asm &&
"Unexpected output type!");
@@ -1488,22 +1650,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// This comes from the default translation the driver + cc1
// would do to enable flag_pic.
- //
- // FIXME: Centralize this code.
- Arg *LastPICArg = 0;
- for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) {
- if ((*I)->getOption().matches(options::OPT_fPIC) ||
- (*I)->getOption().matches(options::OPT_fno_PIC) ||
- (*I)->getOption().matches(options::OPT_fpic) ||
- (*I)->getOption().matches(options::OPT_fno_pic) ||
- (*I)->getOption().matches(options::OPT_fPIE) ||
- (*I)->getOption().matches(options::OPT_fno_PIE) ||
- (*I)->getOption().matches(options::OPT_fpie) ||
- (*I)->getOption().matches(options::OPT_fno_pie)) {
- LastPICArg = *I;
- (*I)->claim();
- }
- }
+
+ Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC,
+ options::OPT_fpic, options::OPT_fno_pic,
+ options::OPT_fPIE, options::OPT_fno_PIE,
+ options::OPT_fpie, options::OPT_fno_pie);
bool PICDisabled = false;
bool PICEnabled = false;
bool PICForPIE = false;
@@ -1606,16 +1757,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
A->getOption().getID() != options::OPT_fhonor_nans)
CmdArgs.push_back("-menable-no-nans");
- // -fno-math-errno is default.
- bool MathErrno = false;
+ // -fmath-errno is the default on some platforms, e.g. BSD-derived OSes.
+ bool MathErrno = getToolChain().IsMathErrnoDefault();
if (Arg *A = Args.getLastArg(options::OPT_ffast_math,
options::OPT_fmath_errno,
- options::OPT_fno_math_errno)) {
- if (A->getOption().getID() == options::OPT_fmath_errno) {
- CmdArgs.push_back("-fmath-errno");
- MathErrno = true;
- }
- }
+ options::OPT_fno_math_errno))
+ MathErrno = A->getOption().getID() == options::OPT_fmath_errno;
+ if (MathErrno)
+ CmdArgs.push_back("-fmath-errno");
// There are several flags which require disabling very specific
// optimizations. Any of these being disabled forces us to turn off the
@@ -1661,12 +1810,33 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
!TrappingMath)
CmdArgs.push_back("-menable-unsafe-fp-math");
- // We separately look for the '-ffast-math' flag, and if we find it, tell the
- // frontend to provide the appropriate preprocessor macros. This is distinct
- // from enabling any optimizations as it induces a language change which must
- // survive serialization and deserialization, etc.
+
+ // Validate and pass through -fp-contract option.
+ if (Arg *A = Args.getLastArg(options::OPT_ffast_math,
+ options::OPT_ffp_contract)) {
+ if (A->getOption().getID() == options::OPT_ffp_contract) {
+ StringRef Val = A->getValue(Args);
+ if (Val == "fast" || Val == "on" || Val == "off") {
+ CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + Val));
+ } else {
+ D.Diag(diag::err_drv_unsupported_option_argument)
+ << A->getOption().getName() << Val;
+ }
+ } else { // A is OPT_ffast_math
+ // If fast-math is set then set the fp-contract mode to fast.
+ CmdArgs.push_back(Args.MakeArgString("-ffp-contract=fast"));
+ }
+ }
+
+ // We separately look for the '-ffast-math' and '-ffinite-math-only' flags,
+ // and if we find them, tell the frontend to provide the appropriate
+ // preprocessor macros. This is distinct from enabling any optimizations as
+ // these options induce language changes which must survive serialization
+ // and deserialization, etc.
if (Args.hasArg(options::OPT_ffast_math))
CmdArgs.push_back("-ffast-math");
+ if (Args.hasArg(options::OPT_ffinite_math_only))
+ CmdArgs.push_back("-ffinite-math-only");
// Decide whether to use verbose asm. Verbose assembly is the default on
// toolchains which have the integrated assembler on by default.
@@ -1711,6 +1881,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
AsynchronousUnwindTables))
CmdArgs.push_back("-munwind-tables");
+ getToolChain().addClangTargetOptions(CmdArgs);
+
if (Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) {
CmdArgs.push_back("-mlimit-float-precision");
CmdArgs.push_back(A->getValue(Args));
@@ -1741,6 +1913,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
AddMIPSTargetArgs(Args, CmdArgs);
break;
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ AddPPCTargetArgs(Args, CmdArgs);
+ break;
+
case llvm::Triple::sparc:
AddSparcTargetArgs(Args, CmdArgs);
break;
@@ -1800,13 +1977,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
D.CCLogDiagnosticsFilename : "-");
}
- // Special case debug options to only pass -g to clang. This is
- // wrong.
+ // Use the last option from "-g" group. "-gline-tables-only" is
+ // preserved, all other debug options are substituted with "-g".
Args.ClaimAllArgs(options::OPT_g_Group);
- if (Arg *A = Args.getLastArg(options::OPT_g_Group))
- if (!A->getOption().matches(options::OPT_g0)) {
+ if (Arg *A = Args.getLastArg(options::OPT_g_Group)) {
+ if (A->getOption().matches(options::OPT_gline_tables_only)) {
+ CmdArgs.push_back("-gline-tables-only");
+ } else if (!A->getOption().matches(options::OPT_g0) &&
+ !A->getOption().matches(options::OPT_ggdb0)) {
CmdArgs.push_back("-g");
}
+ }
+
+ // We ignore flags -gstrict-dwarf and -grecord-gcc-switches for now.
+ Args.ClaimAllArgs(options::OPT_g_flags_Group);
Args.AddAllArgs(CmdArgs, options::OPT_ffunction_sections);
Args.AddAllArgs(CmdArgs, options::OPT_fdata_sections);
@@ -1917,7 +2101,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}
Args.AddAllArgs(CmdArgs, options::OPT_W_Group);
- Args.AddLastArg(CmdArgs, options::OPT_pedantic);
+ if (Args.hasFlag(options::OPT_pedantic, options::OPT_no_pedantic, false))
+ CmdArgs.push_back("-pedantic");
Args.AddLastArg(CmdArgs, options::OPT_pedantic_errors);
Args.AddLastArg(CmdArgs, options::OPT_w);
@@ -2007,11 +2192,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Arg *A = Args.getLastArg(options::OPT_Wlarge_by_value_copy_EQ,
options::OPT_Wlarge_by_value_copy_def)) {
- CmdArgs.push_back("-Wlarge-by-value-copy");
- if (A->getNumValues())
- CmdArgs.push_back(A->getValue(Args));
- else
- CmdArgs.push_back("64"); // default value for -Wlarge-by-value-copy.
+ if (A->getNumValues()) {
+ StringRef bytes = A->getValue(Args);
+ CmdArgs.push_back(Args.MakeArgString("-Wlarge-by-value-copy=" + bytes));
+ } else
+ CmdArgs.push_back("-Wlarge-by-value-copy=64"); // default value
+ }
+
+ if (Arg *A = Args.getLastArg(options::OPT_fbounds_checking,
+ options::OPT_fbounds_checking_EQ)) {
+ if (A->getNumValues()) {
+ StringRef val = A->getValue(Args);
+ CmdArgs.push_back(Args.MakeArgString("-fbounds-checking=" + val));
+ } else
+ CmdArgs.push_back("-fbounds-checking=1");
}
if (Args.hasArg(options::OPT__relocatable_pch))
@@ -2066,6 +2260,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fvisibility_inlines_hidden);
+ Args.AddLastArg(CmdArgs, options::OPT_ftlsmodel_EQ);
+
// -fhosted is default.
if (Args.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, false) ||
KernelOrKext)
@@ -2079,6 +2275,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fno_limit_debug_info);
Args.AddLastArg(CmdArgs, options::OPT_fno_operator_names);
Args.AddLastArg(CmdArgs, options::OPT_faltivec);
+ Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree);
+ Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type);
// Report and error for -faltivec on anything other then PowerPC.
if (const Arg *A = Args.getLastArg(options::OPT_faltivec))
@@ -2106,6 +2304,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.getLastArg(options::OPT_fapple_kext))
CmdArgs.push_back("-fapple-kext");
+ if (Args.hasFlag(options::OPT_frewrite_includes,
+ options::OPT_fno_rewrite_includes, false))
+ CmdArgs.push_back("-frewrite-includes");
+
Args.AddLastArg(CmdArgs, options::OPT_fobjc_sender_dependent_dispatch);
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_print_source_range_info);
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits);
@@ -2259,6 +2461,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
getToolChain().getTriple().getOS() == llvm::Triple::Win32))
CmdArgs.push_back("-fms-extensions");
+ // -fms-inline-asm.
+ if (Args.hasArg(options::OPT_fenable_experimental_ms_inline_asm))
+ CmdArgs.push_back("-fenable-experimental-ms-inline-asm");
+
// -fms-compatibility=0 is default.
if (Args.hasFlag(options::OPT_fms_compatibility,
options::OPT_fno_ms_compatibility,
@@ -2309,83 +2515,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.hasArg(options::OPT_fno_inline_functions))
CmdArgs.push_back("-fno-inline-functions");
- // -fobjc-nonfragile-abi=0 is default.
- ObjCRuntime objCRuntime;
- unsigned objcABIVersion = 0;
- bool NeXTRuntimeIsDefault
- = (IsRewriter || IsModernRewriter ||
- getToolChain().getTriple().isOSDarwin());
- if (Args.hasFlag(options::OPT_fnext_runtime, options::OPT_fgnu_runtime,
- NeXTRuntimeIsDefault)) {
- objCRuntime.setKind(ObjCRuntime::NeXT);
- } else {
- CmdArgs.push_back("-fgnu-runtime");
- objCRuntime.setKind(ObjCRuntime::GNU);
- }
- getToolChain().configureObjCRuntime(objCRuntime);
- if (objCRuntime.HasARC)
- CmdArgs.push_back("-fobjc-runtime-has-arc");
- if (objCRuntime.HasWeak)
- CmdArgs.push_back("-fobjc-runtime-has-weak");
- if (objCRuntime.HasTerminate)
- CmdArgs.push_back("-fobjc-runtime-has-terminate");
-
- // Compute the Objective-C ABI "version" to use. Version numbers are
- // slightly confusing for historical reasons:
- // 1 - Traditional "fragile" ABI
- // 2 - Non-fragile ABI, version 1
- // 3 - Non-fragile ABI, version 2
- objcABIVersion = 1;
- // If -fobjc-abi-version= is present, use that to set the version.
- if (Arg *A = Args.getLastArg(options::OPT_fobjc_abi_version_EQ)) {
- if (StringRef(A->getValue(Args)) == "1")
- objcABIVersion = 1;
- else if (StringRef(A->getValue(Args)) == "2")
- objcABIVersion = 2;
- else if (StringRef(A->getValue(Args)) == "3")
- objcABIVersion = 3;
- else
- D.Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args);
- } else {
- // Otherwise, determine if we are using the non-fragile ABI.
- bool NonFragileABIIsDefault =
- (IsModernRewriter ||
- (!IsRewriter && getToolChain().IsObjCNonFragileABIDefault()));
- if (Args.hasFlag(options::OPT_fobjc_nonfragile_abi,
- options::OPT_fno_objc_nonfragile_abi,
- NonFragileABIIsDefault)) {
- // Determine the non-fragile ABI version to use.
-#ifdef DISABLE_DEFAULT_NONFRAGILEABI_TWO
- unsigned NonFragileABIVersion = 1;
-#else
- unsigned NonFragileABIVersion = 2;
-#endif
-
- if (Arg *A = Args.getLastArg(
- options::OPT_fobjc_nonfragile_abi_version_EQ)) {
- if (StringRef(A->getValue(Args)) == "1")
- NonFragileABIVersion = 1;
- else if (StringRef(A->getValue(Args)) == "2")
- NonFragileABIVersion = 2;
- else
- D.Diag(diag::err_drv_clang_unsupported)
- << A->getAsString(Args);
- }
-
- objcABIVersion = 1 + NonFragileABIVersion;
- } else {
- objcABIVersion = 1;
- }
- }
+ ObjCRuntime objcRuntime = AddObjCRuntimeArgs(Args, CmdArgs, rewriteKind);
- if (objcABIVersion == 1) {
- CmdArgs.push_back("-fobjc-fragile-abi");
- } else {
- // -fobjc-dispatch-method is only relevant with the nonfragile-abi, and
- // legacy is the default.
+ // -fobjc-dispatch-method is only relevant with the nonfragile-abi, and
+ // legacy is the default.
+ if (objcRuntime.isNonFragile()) {
if (!Args.hasFlag(options::OPT_fobjc_legacy_dispatch,
options::OPT_fno_objc_legacy_dispatch,
- getToolChain().IsObjCLegacyDispatchDefault())) {
+ objcRuntime.isLegacyDispatchDefaultForArch(
+ getToolChain().getTriple().getArch()))) {
if (getToolChain().UseObjCMixedDispatch())
CmdArgs.push_back("-fobjc-dispatch-method=mixed");
else
@@ -2428,7 +2566,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// -fobjc-infer-related-result-type is the default, except in the Objective-C
// rewriter.
- if (IsRewriter || IsModernRewriter)
+ if (rewriteKind != RK_None)
CmdArgs.push_back("-fno-objc-infer-related-result-type");
// Handle -fobjc-gc and -fobjc-gc-only. They are exclusive, and -fobjc-gc-only
@@ -2451,7 +2589,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// Add exception args.
addExceptionArgs(Args, InputType, getToolChain().getTriple(),
- KernelOrKext, objcABIVersion, CmdArgs);
+ KernelOrKext, objcRuntime, CmdArgs);
if (getToolChain().UseSjLjExceptions())
CmdArgs.push_back("-fsjlj-exceptions");
@@ -2490,12 +2628,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// Honor -fpack-struct= and -fpack-struct, if given. Note that
// -fno-pack-struct doesn't apply to -fpack-struct=.
if (Arg *A = Args.getLastArg(options::OPT_fpack_struct_EQ)) {
- CmdArgs.push_back("-fpack-struct");
- CmdArgs.push_back(A->getValue(Args));
+ std::string PackStructStr = "-fpack-struct=";
+ PackStructStr += A->getValue(Args);
+ CmdArgs.push_back(Args.MakeArgString(PackStructStr));
} else if (Args.hasFlag(options::OPT_fpack_struct,
options::OPT_fno_pack_struct, false)) {
- CmdArgs.push_back("-fpack-struct");
- CmdArgs.push_back("1");
+ CmdArgs.push_back("-fpack-struct=1");
}
if (Args.hasArg(options::OPT_mkernel) ||
@@ -2729,7 +2867,7 @@ void ClangAs::AddARMTargetArgs(const ArgList &Args,
// Set the CPU based on -march= and -mcpu=.
CmdArgs.push_back("-target-cpu");
- CmdArgs.push_back(getARMTargetCPU(Args, Triple));
+ CmdArgs.push_back(Args.MakeArgString(getARMTargetCPU(Args, Triple)));
// Honor -mfpu=.
if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ))
@@ -2740,6 +2878,131 @@ void ClangAs::AddARMTargetArgs(const ArgList &Args,
addFPMathArgs(D, A, Args, CmdArgs, getARMTargetCPU(Args, Triple));
}
+/// Add options related to the Objective-C runtime/ABI.
+///
+/// Returns true if the runtime is non-fragile.
+ObjCRuntime Clang::AddObjCRuntimeArgs(const ArgList &args,
+ ArgStringList &cmdArgs,
+ RewriteKind rewriteKind) const {
+ // Look for the controlling runtime option.
+ Arg *runtimeArg = args.getLastArg(options::OPT_fnext_runtime,
+ options::OPT_fgnu_runtime,
+ options::OPT_fobjc_runtime_EQ);
+
+ // Just forward -fobjc-runtime= to the frontend. This supercedes
+ // options about fragility.
+ if (runtimeArg &&
+ runtimeArg->getOption().matches(options::OPT_fobjc_runtime_EQ)) {
+ ObjCRuntime runtime;
+ StringRef value = runtimeArg->getValue(args);
+ if (runtime.tryParse(value)) {
+ getToolChain().getDriver().Diag(diag::err_drv_unknown_objc_runtime)
+ << value;
+ }
+
+ runtimeArg->render(args, cmdArgs);
+ return runtime;
+ }
+
+ // Otherwise, we'll need the ABI "version". Version numbers are
+ // slightly confusing for historical reasons:
+ // 1 - Traditional "fragile" ABI
+ // 2 - Non-fragile ABI, version 1
+ // 3 - Non-fragile ABI, version 2
+ unsigned objcABIVersion = 1;
+ // If -fobjc-abi-version= is present, use that to set the version.
+ if (Arg *abiArg = args.getLastArg(options::OPT_fobjc_abi_version_EQ)) {
+ StringRef value = abiArg->getValue(args);
+ if (value == "1")
+ objcABIVersion = 1;
+ else if (value == "2")
+ objcABIVersion = 2;
+ else if (value == "3")
+ objcABIVersion = 3;
+ else
+ getToolChain().getDriver().Diag(diag::err_drv_clang_unsupported)
+ << value;
+ } else {
+ // Otherwise, determine if we are using the non-fragile ABI.
+ bool nonFragileABIIsDefault =
+ (rewriteKind == RK_NonFragile ||
+ (rewriteKind == RK_None &&
+ getToolChain().IsObjCNonFragileABIDefault()));
+ if (args.hasFlag(options::OPT_fobjc_nonfragile_abi,
+ options::OPT_fno_objc_nonfragile_abi,
+ nonFragileABIIsDefault)) {
+ // Determine the non-fragile ABI version to use.
+#ifdef DISABLE_DEFAULT_NONFRAGILEABI_TWO
+ unsigned nonFragileABIVersion = 1;
+#else
+ unsigned nonFragileABIVersion = 2;
+#endif
+
+ if (Arg *abiArg = args.getLastArg(
+ options::OPT_fobjc_nonfragile_abi_version_EQ)) {
+ StringRef value = abiArg->getValue(args);
+ if (value == "1")
+ nonFragileABIVersion = 1;
+ else if (value == "2")
+ nonFragileABIVersion = 2;
+ else
+ getToolChain().getDriver().Diag(diag::err_drv_clang_unsupported)
+ << value;
+ }
+
+ objcABIVersion = 1 + nonFragileABIVersion;
+ } else {
+ objcABIVersion = 1;
+ }
+ }
+
+ // We don't actually care about the ABI version other than whether
+ // it's non-fragile.
+ bool isNonFragile = objcABIVersion != 1;
+
+ // If we have no runtime argument, ask the toolchain for its default runtime.
+ // However, the rewriter only really supports the Mac runtime, so assume that.
+ ObjCRuntime runtime;
+ if (!runtimeArg) {
+ switch (rewriteKind) {
+ case RK_None:
+ runtime = getToolChain().getDefaultObjCRuntime(isNonFragile);
+ break;
+ case RK_Fragile:
+ runtime = ObjCRuntime(ObjCRuntime::FragileMacOSX, VersionTuple());
+ break;
+ case RK_NonFragile:
+ runtime = ObjCRuntime(ObjCRuntime::MacOSX, VersionTuple());
+ break;
+ }
+
+ // -fnext-runtime
+ } else if (runtimeArg->getOption().matches(options::OPT_fnext_runtime)) {
+ // On Darwin, make this use the default behavior for the toolchain.
+ if (getToolChain().getTriple().isOSDarwin()) {
+ runtime = getToolChain().getDefaultObjCRuntime(isNonFragile);
+
+ // Otherwise, build for a generic macosx port.
+ } else {
+ runtime = ObjCRuntime(ObjCRuntime::MacOSX, VersionTuple());
+ }
+
+ // -fgnu-runtime
+ } else {
+ assert(runtimeArg->getOption().matches(options::OPT_fgnu_runtime));
+ // Legacy behaviour is to target the gnustep runtime if we are i
+ // non-fragile mode or the GCC runtime in fragile mode.
+ if (isNonFragile)
+ runtime = ObjCRuntime(ObjCRuntime::GNUstep, VersionTuple());
+ else
+ runtime = ObjCRuntime(ObjCRuntime::GCC, VersionTuple());
+ }
+
+ cmdArgs.push_back(args.MakeArgString(
+ "-fobjc-runtime=" + runtime.getAsString()));
+ return runtime;
+}
+
void ClangAs::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -4024,9 +4287,6 @@ void darwin::Link::AddLinkArgs(Compilation &C,
} else if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) {
CmdArgs.push_back("-syslibroot");
CmdArgs.push_back(A->getValue(Args));
- } else if (getDarwinToolChain().isTargetIPhoneOS()) {
- CmdArgs.push_back("-syslibroot");
- CmdArgs.push_back("/Developer/SDKs/Extra");
}
Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace);
@@ -4085,7 +4345,6 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddAllArgs(CmdArgs, options::OPT_t);
Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag);
Args.AddAllArgs(CmdArgs, options::OPT_u_Group);
- Args.AddAllArgs(CmdArgs, options::OPT_A);
Args.AddLastArg(CmdArgs, options::OPT_e);
Args.AddAllArgs(CmdArgs, options::OPT_m_Separate);
Args.AddAllArgs(CmdArgs, options::OPT_r);
@@ -4099,8 +4358,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
- if (!Args.hasArg(options::OPT_A) &&
- !Args.hasArg(options::OPT_nostdlib) &&
+ if (!Args.hasArg(options::OPT_nostdlib) &&
!Args.hasArg(options::OPT_nostartfiles)) {
// Derived from startfile spec.
if (Args.hasArg(options::OPT_dynamiclib)) {
@@ -4144,6 +4402,14 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
// darwin_crt2 spec is empty.
}
+ // By default on OS X 10.8 and later, we don't link with a crt1.o
+ // file and the linker knows to use _main as the entry point. But,
+ // when compiling with -pg, we need to link with the gcrt1.o file,
+ // so pass the -no_new_main option to tell the linker to use the
+ // "start" symbol as the entry point.
+ if (getDarwinToolChain().isTargetMacOS() &&
+ !getDarwinToolChain().isMacosxVersionLT(10, 8))
+ CmdArgs.push_back("-no_new_main");
} else {
if (Args.hasArg(options::OPT_static) ||
Args.hasArg(options::OPT_object) ||
@@ -4201,30 +4467,30 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
// This is more complicated in gcc...
CmdArgs.push_back("-lgomp");
- getDarwinToolChain().AddLinkSearchPathArgs(Args, CmdArgs);
-
- if (isObjCRuntimeLinked(Args)) {
+ AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs);
+
+ if (isObjCRuntimeLinked(Args) &&
+ !Args.hasArg(options::OPT_nostdlib) &&
+ !Args.hasArg(options::OPT_nodefaultlibs)) {
// Avoid linking compatibility stubs on i386 mac.
if (!getDarwinToolChain().isTargetMacOS() ||
getDarwinToolChain().getArchName() != "i386") {
// If we don't have ARC or subscripting runtime support, link in the
// runtime stubs. We have to do this *before* adding any of the normal
// linker inputs so that its initializer gets run first.
- ObjCRuntime runtime;
- getDarwinToolChain().configureObjCRuntime(runtime);
+ ObjCRuntime runtime =
+ getDarwinToolChain().getDefaultObjCRuntime(/*nonfragile*/ true);
// We use arclite library for both ARC and subscripting support.
- if ((!runtime.HasARC && isObjCAutoRefCount(Args)) ||
- !runtime.HasSubscripting)
+ if ((!runtime.hasARC() && isObjCAutoRefCount(Args)) ||
+ !runtime.hasSubscripting())
getDarwinToolChain().AddLinkARCArgs(Args, CmdArgs);
- CmdArgs.push_back("-framework");
- CmdArgs.push_back("Foundation");
}
+ CmdArgs.push_back("-framework");
+ CmdArgs.push_back("Foundation");
// Link libobj.
CmdArgs.push_back("-lobjc");
}
- AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs);
-
if (LinkingOutput) {
CmdArgs.push_back("-arch_multiple");
CmdArgs.push_back("-final_output");
@@ -4245,8 +4511,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
getDarwinToolChain().AddLinkRuntimeLibArgs(Args, CmdArgs);
}
- if (!Args.hasArg(options::OPT_A) &&
- !Args.hasArg(options::OPT_nostdlib) &&
+ if (!Args.hasArg(options::OPT_nostdlib) &&
!Args.hasArg(options::OPT_nostartfiles)) {
// endfile_spec is empty.
}
@@ -4698,6 +4963,142 @@ void openbsd::Link::ConstructJob(Compilation &C, const JobAction &JA,
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}
+void bitrig::Assemble::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ ArgStringList CmdArgs;
+
+ Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA,
+ options::OPT_Xassembler);
+
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(Output.getFilename());
+
+ for (InputInfoList::const_iterator
+ it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) {
+ const InputInfo &II = *it;
+ CmdArgs.push_back(II.getFilename());
+ }
+
+ const char *Exec =
+ Args.MakeArgString(getToolChain().GetProgramPath("as"));
+ C.addCommand(new Command(JA, *this, Exec, CmdArgs));
+}
+
+void bitrig::Link::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ const Driver &D = getToolChain().getDriver();
+ ArgStringList CmdArgs;
+
+ if ((!Args.hasArg(options::OPT_nostdlib)) &&
+ (!Args.hasArg(options::OPT_shared))) {
+ CmdArgs.push_back("-e");
+ CmdArgs.push_back("__start");
+ }
+
+ if (Args.hasArg(options::OPT_static)) {
+ CmdArgs.push_back("-Bstatic");
+ } else {
+ if (Args.hasArg(options::OPT_rdynamic))
+ CmdArgs.push_back("-export-dynamic");
+ CmdArgs.push_back("--eh-frame-hdr");
+ CmdArgs.push_back("-Bdynamic");
+ if (Args.hasArg(options::OPT_shared)) {
+ CmdArgs.push_back("-shared");
+ } else {
+ CmdArgs.push_back("-dynamic-linker");
+ CmdArgs.push_back("/usr/libexec/ld.so");
+ }
+ }
+
+ if (Output.isFilename()) {
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(Output.getFilename());
+ } else {
+ assert(Output.isNothing() && "Invalid output.");
+ }
+
+ if (!Args.hasArg(options::OPT_nostdlib) &&
+ !Args.hasArg(options::OPT_nostartfiles)) {
+ if (!Args.hasArg(options::OPT_shared)) {
+ if (Args.hasArg(options::OPT_pg))
+ CmdArgs.push_back(Args.MakeArgString(
+ getToolChain().GetFilePath("gcrt0.o")));
+ else
+ CmdArgs.push_back(Args.MakeArgString(
+ getToolChain().GetFilePath("crt0.o")));
+ CmdArgs.push_back(Args.MakeArgString(
+ getToolChain().GetFilePath("crtbegin.o")));
+ } else {
+ CmdArgs.push_back(Args.MakeArgString(
+ getToolChain().GetFilePath("crtbeginS.o")));
+ }
+ }
+
+ Args.AddAllArgs(CmdArgs, options::OPT_L);
+ Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
+ Args.AddAllArgs(CmdArgs, options::OPT_e);
+
+ AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs);
+
+ if (!Args.hasArg(options::OPT_nostdlib) &&
+ !Args.hasArg(options::OPT_nodefaultlibs)) {
+ if (D.CCCIsCXX) {
+ getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (Args.hasArg(options::OPT_pg))
+ CmdArgs.push_back("-lm_p");
+ else
+ CmdArgs.push_back("-lm");
+ }
+
+ if (Args.hasArg(options::OPT_pthread))
+ CmdArgs.push_back("-lpthread");
+ if (!Args.hasArg(options::OPT_shared)) {
+ if (Args.hasArg(options::OPT_pg))
+ CmdArgs.push_back("-lc_p");
+ else
+ CmdArgs.push_back("-lc");
+ }
+
+ std::string myarch = "-lclang_rt.";
+ const llvm::Triple &T = getToolChain().getTriple();
+ llvm::Triple::ArchType Arch = T.getArch();
+ switch (Arch) {
+ case llvm::Triple::arm:
+ myarch += ("arm");
+ break;
+ case llvm::Triple::x86:
+ myarch += ("i386");
+ break;
+ case llvm::Triple::x86_64:
+ myarch += ("amd64");
+ break;
+ default:
+ assert(0 && "Unsupported architecture");
+ }
+ CmdArgs.push_back(Args.MakeArgString(myarch));
+ }
+
+ if (!Args.hasArg(options::OPT_nostdlib) &&
+ !Args.hasArg(options::OPT_nostartfiles)) {
+ if (!Args.hasArg(options::OPT_shared))
+ CmdArgs.push_back(Args.MakeArgString(
+ getToolChain().GetFilePath("crtend.o")));
+ else
+ CmdArgs.push_back(Args.MakeArgString(
+ getToolChain().GetFilePath("crtendS.o")));
+ }
+
+ const char *Exec =
+ Args.MakeArgString(getToolChain().GetProgramPath("ld"));
+ C.addCommand(new Command(JA, *this, Exec, CmdArgs));
+}
+
void freebsd::Assemble::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -4744,6 +5145,14 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA,
const Driver &D = getToolChain().getDriver();
ArgStringList CmdArgs;
+ // Silence warning for "clang -g foo.o -o foo"
+ Args.ClaimAllArgs(options::OPT_g_Group);
+ // and "clang -emit-llvm foo.o -o foo"
+ Args.ClaimAllArgs(options::OPT_emit_llvm);
+ // and for "clang -w foo.o -o foo". Other warning options are already
+ // handled somewhere else.
+ Args.ClaimAllArgs(options::OPT_w);
+
if (!D.SysRoot.empty())
CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot));
@@ -4759,6 +5168,14 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-dynamic-linker");
CmdArgs.push_back("/libexec/ld-elf.so.1");
}
+ if (getToolChain().getTriple().getOSMajorVersion() >= 9) {
+ llvm::Triple::ArchType Arch = getToolChain().getArch();
+ if (Arch == llvm::Triple::arm || Arch == llvm::Triple::sparc ||
+ Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64) {
+ CmdArgs.push_back("--hash-style=both");
+ }
+ }
+ CmdArgs.push_back("--enable-new-dtags");
}
// When building 32-bit code on FreeBSD/amd64, we have to explicitly
@@ -5064,6 +5481,14 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA,
StringRef MArch = getToolChain().getArchName();
if (MArch == "armv7" || MArch == "armv7a" || MArch == "armv7-a")
CmdArgs.push_back("-mfpu=neon");
+
+ StringRef ARMFloatABI = getARMFloatABI(getToolChain().getDriver(), Args,
+ getToolChain().getTriple());
+ CmdArgs.push_back(Args.MakeArgString("-mfloat-abi=" + ARMFloatABI));
+
+ Args.AddLastArg(CmdArgs, options::OPT_march_EQ);
+ Args.AddLastArg(CmdArgs, options::OPT_mcpu_EQ);
+ Args.AddLastArg(CmdArgs, options::OPT_mfpu_EQ);
} else if (getToolChain().getArch() == llvm::Triple::mips ||
getToolChain().getArch() == llvm::Triple::mipsel ||
getToolChain().getArch() == llvm::Triple::mips64 ||
@@ -5089,11 +5514,19 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-EB");
else
CmdArgs.push_back("-EL");
- }
- Args.AddLastArg(CmdArgs, options::OPT_march_EQ);
- Args.AddLastArg(CmdArgs, options::OPT_mcpu_EQ);
- Args.AddLastArg(CmdArgs, options::OPT_mfpu_EQ);
+ Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC,
+ options::OPT_fpic, options::OPT_fno_pic,
+ options::OPT_fPIE, options::OPT_fno_PIE,
+ options::OPT_fpie, options::OPT_fno_pie);
+ if (LastPICArg &&
+ (LastPICArg->getOption().matches(options::OPT_fPIC) ||
+ LastPICArg->getOption().matches(options::OPT_fpic) ||
+ LastPICArg->getOption().matches(options::OPT_fPIE) ||
+ LastPICArg->getOption().matches(options::OPT_fpie))) {
+ CmdArgs.push_back("-KPIC");
+ }
+ }
Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA,
options::OPT_Xassembler);
@@ -5112,9 +5545,10 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA,
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}
-static void AddLibgcc(const Driver &D, ArgStringList &CmdArgs,
- const ArgList &Args) {
- bool StaticLibgcc = Args.hasArg(options::OPT_static) ||
+static void AddLibgcc(llvm::Triple Triple, const Driver &D,
+ ArgStringList &CmdArgs, const ArgList &Args) {
+ bool isAndroid = Triple.getEnvironment() == llvm::Triple::ANDROIDEABI;
+ bool StaticLibgcc = isAndroid || Args.hasArg(options::OPT_static) ||
Args.hasArg(options::OPT_static_libgcc);
if (!D.CCCIsCXX)
CmdArgs.push_back("-lgcc");
@@ -5130,7 +5564,7 @@ static void AddLibgcc(const Driver &D, ArgStringList &CmdArgs,
CmdArgs.push_back("--no-as-needed");
}
- if (StaticLibgcc)
+ if (StaticLibgcc && !isAndroid)
CmdArgs.push_back("-lgcc_eh");
else if (!Args.hasArg(options::OPT_shared) && D.CCCIsCXX)
CmdArgs.push_back("-lgcc");
@@ -5144,13 +5578,16 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
const toolchains::Linux& ToolChain =
static_cast<const toolchains::Linux&>(getToolChain());
const Driver &D = ToolChain.getDriver();
+ const bool isAndroid = ToolChain.getTriple().getEnvironment() ==
+ llvm::Triple::ANDROIDEABI;
+
ArgStringList CmdArgs;
// Silence warning for "clang -g foo.o -o foo"
Args.ClaimAllArgs(options::OPT_g_Group);
// and "clang -emit-llvm foo.o -o foo"
Args.ClaimAllArgs(options::OPT_emit_llvm);
- // and for "clang -g foo.o -o foo". Other warning options are already
+ // and for "clang -w foo.o -o foo". Other warning options are already
// handled somewhere else.
Args.ClaimAllArgs(options::OPT_w);
@@ -5204,6 +5641,10 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-static");
} else if (Args.hasArg(options::OPT_shared)) {
CmdArgs.push_back("-shared");
+ if ((ToolChain.getArch() == llvm::Triple::arm
+ || ToolChain.getArch() == llvm::Triple::thumb) && isAndroid) {
+ CmdArgs.push_back("-Bsymbolic");
+ }
}
if (ToolChain.getArch() == llvm::Triple::arm ||
@@ -5211,11 +5652,17 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
(!Args.hasArg(options::OPT_static) &&
!Args.hasArg(options::OPT_shared))) {
CmdArgs.push_back("-dynamic-linker");
- if (ToolChain.getArch() == llvm::Triple::x86)
+ if (isAndroid)
+ CmdArgs.push_back("/system/bin/linker");
+ else if (ToolChain.getArch() == llvm::Triple::x86)
CmdArgs.push_back("/lib/ld-linux.so.2");
else if (ToolChain.getArch() == llvm::Triple::arm ||
- ToolChain.getArch() == llvm::Triple::thumb)
- CmdArgs.push_back("/lib/ld-linux.so.3");
+ ToolChain.getArch() == llvm::Triple::thumb) {
+ if (ToolChain.getTriple().getEnvironment() == llvm::Triple::GNUEABIHF)
+ CmdArgs.push_back("/lib/ld-linux-armhf.so.3");
+ else
+ CmdArgs.push_back("/lib/ld-linux.so.3");
+ }
else if (ToolChain.getArch() == llvm::Triple::mips ||
ToolChain.getArch() == llvm::Triple::mipsel)
CmdArgs.push_back("/lib/ld.so.1");
@@ -5235,25 +5682,27 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
if (!Args.hasArg(options::OPT_nostdlib) &&
!Args.hasArg(options::OPT_nostartfiles)) {
- const char *crt1 = NULL;
- if (!Args.hasArg(options::OPT_shared)){
- if (Args.hasArg(options::OPT_pie))
- crt1 = "Scrt1.o";
- else
- crt1 = "crt1.o";
- }
- if (crt1)
- CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1)));
+ if (!isAndroid) {
+ const char *crt1 = NULL;
+ if (!Args.hasArg(options::OPT_shared)){
+ if (Args.hasArg(options::OPT_pie))
+ crt1 = "Scrt1.o";
+ else
+ crt1 = "crt1.o";
+ }
+ if (crt1)
+ CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1)));
- CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o")));
+ CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o")));
+ }
const char *crtbegin;
if (Args.hasArg(options::OPT_static))
- crtbegin = "crtbeginT.o";
+ crtbegin = isAndroid ? "crtbegin_static.o" : "crtbeginT.o";
else if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie))
- crtbegin = "crtbeginS.o";
+ crtbegin = isAndroid ? "crtbegin_so.o" : "crtbeginS.o";
else
- crtbegin = "crtbegin.o";
+ crtbegin = isAndroid ? "crtbegin_dynamic.o" : "crtbegin.o";
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin)));
}
@@ -5276,7 +5725,9 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs);
- if (D.CCCIsCXX && !Args.hasArg(options::OPT_nostdlib)) {
+ if (D.CCCIsCXX &&
+ !Args.hasArg(options::OPT_nostdlib) &&
+ !Args.hasArg(options::OPT_nodefaultlibs)) {
bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
!Args.hasArg(options::OPT_static);
if (OnlyLibstdcxxStatic)
@@ -5289,34 +5740,37 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
// Call this before we add the C run-time.
addAsanRTLinux(getToolChain(), Args, CmdArgs);
+ addTsanRTLinux(getToolChain(), Args, CmdArgs);
if (!Args.hasArg(options::OPT_nostdlib)) {
- if (Args.hasArg(options::OPT_static))
- CmdArgs.push_back("--start-group");
+ if (!Args.hasArg(options::OPT_nodefaultlibs)) {
+ if (Args.hasArg(options::OPT_static))
+ CmdArgs.push_back("--start-group");
- AddLibgcc(D, CmdArgs, Args);
+ AddLibgcc(ToolChain.getTriple(), D, CmdArgs, Args);
- if (Args.hasArg(options::OPT_pthread) ||
- Args.hasArg(options::OPT_pthreads))
- CmdArgs.push_back("-lpthread");
-
- CmdArgs.push_back("-lc");
+ if (Args.hasArg(options::OPT_pthread) ||
+ Args.hasArg(options::OPT_pthreads))
+ CmdArgs.push_back("-lpthread");
- if (Args.hasArg(options::OPT_static))
- CmdArgs.push_back("--end-group");
- else
- AddLibgcc(D, CmdArgs, Args);
+ CmdArgs.push_back("-lc");
+ if (Args.hasArg(options::OPT_static))
+ CmdArgs.push_back("--end-group");
+ else
+ AddLibgcc(ToolChain.getTriple(), D, CmdArgs, Args);
+ }
if (!Args.hasArg(options::OPT_nostartfiles)) {
const char *crtend;
if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie))
- crtend = "crtendS.o";
+ crtend = isAndroid ? "crtend_so.o" : "crtendS.o";
else
- crtend = "crtend.o";
+ crtend = isAndroid ? "crtend_android.o" : "crtend.o";
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtend)));
- CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o")));
+ if (!isAndroid)
+ CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o")));
}
}
@@ -5581,7 +6035,14 @@ void visualstudio::Link::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-nologo");
- AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs);
+ Args.AddAllArgValues(CmdArgs, options::OPT_l);
+
+ // Add filenames immediately.
+ for (InputInfoList::const_iterator
+ it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) {
+ if (it->isFilename())
+ CmdArgs.push_back(it->getFilename());
+ }
const char *Exec =
Args.MakeArgString(getToolChain().GetProgramPath("link.exe"));
diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h
index 651a8f2..999c57a 100644
--- a/lib/Driver/Tools.h
+++ b/lib/Driver/Tools.h
@@ -18,6 +18,8 @@
#include "llvm/Support/Compiler.h"
namespace clang {
+ class ObjCRuntime;
+
namespace driver {
class Driver;
@@ -39,10 +41,16 @@ namespace tools {
void AddARMTargetArgs(const ArgList &Args, ArgStringList &CmdArgs,
bool KernelOrKext) const;
void AddMIPSTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const;
+ void AddPPCTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const;
void AddSparcTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const;
void AddX86TargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const;
void AddHexagonTargetArgs (const ArgList &Args, ArgStringList &CmdArgs) const;
+ enum RewriteKind { RK_None, RK_Fragile, RK_NonFragile };
+
+ ObjCRuntime AddObjCRuntimeArgs(const ArgList &args, ArgStringList &cmdArgs,
+ RewriteKind rewrite) const;
+
public:
Clang(const ToolChain &TC) : Tool("clang", "clang frontend", TC) {}
@@ -369,6 +377,36 @@ namespace openbsd {
};
} // end namespace openbsd
+ /// bitrig -- Directly call GNU Binutils assembler and linker
+namespace bitrig {
+ class LLVM_LIBRARY_VISIBILITY Assemble : public Tool {
+ public:
+ Assemble(const ToolChain &TC) : Tool("bitrig::Assemble", "assembler",
+ TC) {}
+
+ virtual bool hasIntegratedCPP() const { return false; }
+
+ virtual void ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &TCArgs,
+ const char *LinkingOutput) const;
+ };
+ class LLVM_LIBRARY_VISIBILITY Link : public Tool {
+ public:
+ Link(const ToolChain &TC) : Tool("bitrig::Link", "linker", TC) {}
+
+ virtual bool hasIntegratedCPP() const { return false; }
+ virtual bool isLinkJob() const { return true; }
+
+ virtual void ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &TCArgs,
+ const char *LinkingOutput) const;
+ };
+} // end namespace bitrig
+
/// freebsd -- Directly call GNU Binutils assembler and linker
namespace freebsd {
class LLVM_LIBRARY_VISIBILITY Assemble : public Tool {
diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp
index 50742fe..9d8fcfd 100644
--- a/lib/Driver/Types.cpp
+++ b/lib/Driver/Types.cpp
@@ -67,7 +67,8 @@ bool types::appendSuffixForType(ID Id) {
bool types::canLipoType(ID Id) {
return (Id == TY_Nothing ||
Id == TY_Image ||
- Id == TY_Object);
+ Id == TY_Object ||
+ Id == TY_LTO_BC);
}
bool types::isAcceptedByClang(ID Id) {
@@ -129,6 +130,7 @@ bool types::isCXX(ID Id) {
case TY_ObjCXX: case TY_PP_ObjCXX:
case TY_CXXHeader: case TY_PP_CXXHeader:
case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader:
+ case TY_CUDA:
return true;
}
}
diff --git a/lib/Edit/CMakeLists.txt b/lib/Edit/CMakeLists.txt
index c87478c..cce1c19 100644
--- a/lib/Edit/CMakeLists.txt
+++ b/lib/Edit/CMakeLists.txt
@@ -1,7 +1,20 @@
-set(LLVM_USED_LIBS clangBasic clangAST clangLex)
-
add_clang_library(clangEdit
Commit.cpp
EditedSource.cpp
RewriteObjCFoundationAPI.cpp
)
+
+add_dependencies(clangEdit
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangEdit
+ clangBasic
+ clangAST
+ clangLex
+ )
diff --git a/lib/Edit/Commit.cpp b/lib/Edit/Commit.cpp
index c45ee1f..41c72e4 100644
--- a/lib/Edit/Commit.cpp
+++ b/lib/Edit/Commit.cpp
@@ -332,6 +332,7 @@ bool Commit::canReplaceText(SourceLocation loc, StringRef text,
if (invalidTemp)
return false;
+ Len = text.size();
return file.substr(Offs.getOffset()).startswith(text);
}
diff --git a/lib/Edit/EditedSource.cpp b/lib/Edit/EditedSource.cpp
index 5b7fa4a..b2a1663 100644
--- a/lib/Edit/EditedSource.cpp
+++ b/lib/Edit/EditedSource.cpp
@@ -100,8 +100,11 @@ bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
FileOffset B = I->first;
FileOffset E = B.getWithOffset(FA.RemoveLen);
+ if (BeginOffs == B)
+ break;
+
if (BeginOffs < E) {
- if (BeginOffs >= B) {
+ if (BeginOffs > B) {
BeginOffs = E;
++I;
}
diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp
index 24a0db1..d15b7a7 100644
--- a/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -14,6 +14,7 @@
#include "clang/Edit/Rewriters.h"
#include "clang/Edit/Commit.h"
#include "clang/Lex/Lexer.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NSAPI.h"
@@ -22,7 +23,8 @@ using namespace clang;
using namespace edit;
static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
- IdentifierInfo *&ClassId) {
+ IdentifierInfo *&ClassId,
+ const LangOptions &LangOpts) {
if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
return false;
@@ -34,6 +36,18 @@ static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
return true;
+ // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
+ // since the change from +1 to +0 will be handled fine by ARC.
+ if (LangOpts.ObjCAutoRefCount) {
+ if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
+ if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
+ Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
+ if (Rec->getMethodFamily() == OMF_alloc)
+ return true;
+ }
+ }
+ }
+
return false;
}
@@ -44,7 +58,7 @@ static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
const NSAPI &NS, Commit &commit) {
IdentifierInfo *II = 0;
- if (!checkForLiteralCreation(Msg, II))
+ if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
return false;
if (Msg->getNumArgs() != 1)
return false;
@@ -54,16 +68,19 @@ bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
if ((isa<ObjCStringLiteral>(Arg) &&
NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
- NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel) ||
+ (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
+ NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
(isa<ObjCArrayLiteral>(Arg) &&
NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
- NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel) ||
+ (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
+ NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
(isa<ObjCDictionaryLiteral>(Arg) &&
NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
- NS.getNSDictionarySelector(
- NSAPI::NSDict_dictionaryWithDictionary) == Sel)) {
+ (NS.getNSDictionarySelector(
+ NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
+ NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
commit.replaceWithInner(Msg->getSourceRange(),
Msg->getArg(0)->getSourceRange());
@@ -77,15 +94,91 @@ bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
// rewriteToObjCSubscriptSyntax.
//===----------------------------------------------------------------------===//
+/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
+/// that the migrator handles) but return their instances as 'id', resulting
+/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
+///
+/// When checking if we can convert to subscripting syntax, check whether
+/// the receiver is a result of a class method from a hardcoded list of
+/// such classes. In such a case return the specific class as the interface
+/// of the receiver.
+///
+/// FIXME: Remove this when these classes start using 'instancetype'.
+static const ObjCInterfaceDecl *
+maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
+ const Expr *Receiver,
+ ASTContext &Ctx) {
+ assert(IFace && Receiver);
+
+ // If the receiver has type 'id'...
+ if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
+ return IFace;
+
+ const ObjCMessageExpr *
+ InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
+ if (!InnerMsg)
+ return IFace;
+
+ QualType ClassRec;
+ switch (InnerMsg->getReceiverKind()) {
+ case ObjCMessageExpr::Instance:
+ case ObjCMessageExpr::SuperInstance:
+ return IFace;
+
+ case ObjCMessageExpr::Class:
+ ClassRec = InnerMsg->getClassReceiver();
+ break;
+ case ObjCMessageExpr::SuperClass:
+ ClassRec = InnerMsg->getSuperType();
+ break;
+ }
+
+ if (ClassRec.isNull())
+ return IFace;
+
+ // ...and it is the result of a class message...
+
+ const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
+ if (!ObjTy)
+ return IFace;
+ const ObjCInterfaceDecl *OID = ObjTy->getInterface();
+
+ // ...and the receiving class is NSMapTable or NSLocale, return that
+ // class as the receiving interface.
+ if (OID->getName() == "NSMapTable" ||
+ OID->getName() == "NSLocale")
+ return OID;
+
+ return IFace;
+}
+
+static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
+ const ObjCMessageExpr *Msg,
+ ASTContext &Ctx,
+ Selector subscriptSel) {
+ const Expr *Rec = Msg->getInstanceReceiver();
+ if (!Rec)
+ return false;
+ IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
+
+ if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
+ if (!MD->isUnavailable())
+ return true;
+ }
+ return false;
+}
+
+static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
+
static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
- Receiver = Receiver->IgnoreImpCasts();
- if (isa<BinaryOperator>(Receiver) || isa<UnaryOperator>(Receiver)) {
+ if (subscriptOperatorNeedsParens(Receiver)) {
SourceRange RecRange = Receiver->getSourceRange();
commit.insertWrap("(", RecRange, ")");
}
}
-static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) {
+static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
+ Commit &commit) {
if (Msg->getNumArgs() != 1)
return false;
const Expr *Rec = Msg->getInstanceReceiver();
@@ -106,8 +199,34 @@ static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) {
return true;
}
-static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg,
+static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
+ const ObjCMessageExpr *Msg,
+ const NSAPI &NS,
+ Commit &commit) {
+ if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
+ NS.getObjectAtIndexedSubscriptSelector()))
+ return false;
+ return rewriteToSubscriptGetCommon(Msg, commit);
+}
+
+static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
+ const ObjCMessageExpr *Msg,
+ const NSAPI &NS,
+ Commit &commit) {
+ if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
+ NS.getObjectForKeyedSubscriptSelector()))
+ return false;
+ return rewriteToSubscriptGetCommon(Msg, commit);
+}
+
+static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
+ const ObjCMessageExpr *Msg,
+ const NSAPI &NS,
Commit &commit) {
+ if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
+ NS.getSetObjectAtIndexedSubscriptSelector()))
+ return false;
+
if (Msg->getNumArgs() != 2)
return false;
const Expr *Rec = Msg->getInstanceReceiver();
@@ -134,8 +253,14 @@ static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg,
return true;
}
-static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg,
+static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
+ const ObjCMessageExpr *Msg,
+ const NSAPI &NS,
Commit &commit) {
+ if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
+ NS.getSetObjectForKeyedSubscriptSelector()))
+ return false;
+
if (Msg->getNumArgs() != 2)
return false;
const Expr *Rec = Msg->getInstanceReceiver();
@@ -162,7 +287,7 @@ static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg,
}
bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
- const NSAPI &NS, Commit &commit) {
+ const NSAPI &NS, Commit &commit) {
if (!Msg || Msg->isImplicit() ||
Msg->getReceiverKind() != ObjCMessageExpr::Instance)
return false;
@@ -175,25 +300,22 @@ bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
const_cast<ObjCMethodDecl *>(Method));
if (!IFace)
return false;
- IdentifierInfo *II = IFace->getIdentifier();
Selector Sel = Msg->getSelector();
- if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
- Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) ||
- (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
- Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)))
- return rewriteToSubscriptGet(Msg, commit);
+ if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
+ return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
+
+ if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
+ return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
if (Msg->getNumArgs() != 2)
return false;
- if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
- Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
- return rewriteToArraySubscriptSet(Msg, commit);
+ if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
+ return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
- if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
- Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
- return rewriteToDictionarySubscriptSet(Msg, commit);
+ if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
+ return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
return false;
}
@@ -208,11 +330,15 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
const NSAPI &NS, Commit &commit);
static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
const NSAPI &NS, Commit &commit);
+static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit);
+static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit);
bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
const NSAPI &NS, Commit &commit) {
IdentifierInfo *II = 0;
- if (!checkForLiteralCreation(Msg, II))
+ if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
return false;
if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
@@ -221,6 +347,8 @@ bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
return rewriteToDictionaryLiteral(Msg, NS, commit);
if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
return rewriteToNumberLiteral(Msg, NS, commit);
+ if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
+ return rewriteToStringBoxedExpression(Msg, NS, commit);
return false;
}
@@ -229,6 +357,9 @@ bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
// rewriteToArrayLiteral.
//===----------------------------------------------------------------------===//
+/// \brief Adds an explicit cast to 'id' if the type is not objc object.
+static void objectifyExpr(const Expr *E, Commit &commit);
+
static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
const NSAPI &NS, Commit &commit) {
Selector Sel = Msg->getSelector();
@@ -244,19 +375,24 @@ static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
if (Msg->getNumArgs() != 1)
return false;
+ objectifyExpr(Msg->getArg(0), commit);
SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
commit.replaceWithInner(MsgRange, ArgRange);
commit.insertWrap("@[", ArgRange, "]");
return true;
}
- if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) {
+ if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
+ Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
if (Msg->getNumArgs() == 0)
return false;
const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
return false;
+ for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
+ objectifyExpr(Msg->getArg(i), commit);
+
if (Msg->getNumArgs() == 1) {
commit.replace(MsgRange, "@[]");
return true;
@@ -291,6 +427,10 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
NSAPI::NSDict_dictionaryWithObjectForKey)) {
if (Msg->getNumArgs() != 2)
return false;
+
+ objectifyExpr(Msg->getArg(0), commit);
+ objectifyExpr(Msg->getArg(1), commit);
+
SourceRange ValRange = Msg->getArg(0)->getSourceRange();
SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
// Insert key before the value.
@@ -305,7 +445,8 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
}
if (Sel == NS.getNSDictionarySelector(
- NSAPI::NSDict_dictionaryWithObjectsAndKeys)) {
+ NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
+ Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
if (Msg->getNumArgs() % 2 != 1)
return false;
unsigned SentinelIdx = Msg->getNumArgs() - 1;
@@ -319,6 +460,9 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
}
for (unsigned i = 0; i < SentinelIdx; i += 2) {
+ objectifyExpr(Msg->getArg(i), commit);
+ objectifyExpr(Msg->getArg(i+1), commit);
+
SourceRange ValRange = Msg->getArg(i)->getSourceRange();
SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
// Insert value after key.
@@ -357,7 +501,7 @@ static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
return true;
}
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
}
static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
@@ -371,7 +515,7 @@ static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
return true;
}
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
}
namespace {
@@ -473,10 +617,10 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
literalE = UOE->getSubExpr();
}
- // Only integer and floating literals; non-literals or imaginary literal
- // cannot be rewritten.
+ // Only integer and floating literals, otherwise try to rewrite to boxed
+ // expression.
if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
ASTContext &Ctx = NS.getASTContext();
Selector Sel = Msg->getSelector();
@@ -496,7 +640,7 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
case NSAPI::NSNumberWithShort:
case NSAPI::NSNumberWithUnsignedShort:
case NSAPI::NSNumberWithBool:
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
case NSAPI::NSNumberWithUnsignedInt:
case NSAPI::NSNumberWithUnsignedInteger:
@@ -536,15 +680,16 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
}
// We will need to modify the literal suffix to get the same type as the call.
- // Don't even try if it came from a macro.
+ // Try with boxed expression if it came from a macro.
if (ArgRange.getBegin().isMacroID())
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
bool LitIsFloat = ArgTy->isFloatingType();
- // For a float passed to integer call, don't try rewriting. It is difficult
- // and a very uncommon case anyway.
+ // For a float passed to integer call, don't try rewriting to objc literal.
+ // It is difficult and a very uncommon case anyway.
+ // But try with boxed expression.
if (LitIsFloat && !CallIsFloating)
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
// Try to modify the literal make it the same type as the method call.
// -Modify the suffix, and/or
@@ -555,11 +700,11 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
isIntZero = !IntE->getValue().getBoolValue();
if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
// Not easy to do int -> float with hex/octal and uncommon anyway.
if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
- return false;
+ return rewriteToNumericBoxedExpression(Msg, NS, commit);
SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
@@ -585,3 +730,284 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
}
return true;
}
+
+// FIXME: Make determination of operator precedence more general and
+// make it broadly available.
+static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
+ const Expr* Expr = FullExpr->IgnoreImpCasts();
+ if (isa<ArraySubscriptExpr>(Expr) ||
+ isa<CallExpr>(Expr) ||
+ isa<DeclRefExpr>(Expr) ||
+ isa<CXXNamedCastExpr>(Expr) ||
+ isa<CXXConstructExpr>(Expr) ||
+ isa<CXXThisExpr>(Expr) ||
+ isa<CXXTypeidExpr>(Expr) ||
+ isa<CXXUnresolvedConstructExpr>(Expr) ||
+ isa<ObjCMessageExpr>(Expr) ||
+ isa<ObjCPropertyRefExpr>(Expr) ||
+ isa<ObjCProtocolExpr>(Expr) ||
+ isa<MemberExpr>(Expr) ||
+ isa<ObjCIvarRefExpr>(Expr) ||
+ isa<ParenExpr>(FullExpr) ||
+ isa<ParenListExpr>(Expr) ||
+ isa<SizeOfPackExpr>(Expr))
+ return false;
+
+ return true;
+}
+static bool castOperatorNeedsParens(const Expr *FullExpr) {
+ const Expr* Expr = FullExpr->IgnoreImpCasts();
+ if (isa<ArraySubscriptExpr>(Expr) ||
+ isa<CallExpr>(Expr) ||
+ isa<DeclRefExpr>(Expr) ||
+ isa<CastExpr>(Expr) ||
+ isa<CXXNewExpr>(Expr) ||
+ isa<CXXConstructExpr>(Expr) ||
+ isa<CXXDeleteExpr>(Expr) ||
+ isa<CXXNoexceptExpr>(Expr) ||
+ isa<CXXPseudoDestructorExpr>(Expr) ||
+ isa<CXXScalarValueInitExpr>(Expr) ||
+ isa<CXXThisExpr>(Expr) ||
+ isa<CXXTypeidExpr>(Expr) ||
+ isa<CXXUnresolvedConstructExpr>(Expr) ||
+ isa<ObjCMessageExpr>(Expr) ||
+ isa<ObjCPropertyRefExpr>(Expr) ||
+ isa<ObjCProtocolExpr>(Expr) ||
+ isa<MemberExpr>(Expr) ||
+ isa<ObjCIvarRefExpr>(Expr) ||
+ isa<ParenExpr>(FullExpr) ||
+ isa<ParenListExpr>(Expr) ||
+ isa<SizeOfPackExpr>(Expr) ||
+ isa<UnaryOperator>(Expr))
+ return false;
+
+ return true;
+}
+
+static void objectifyExpr(const Expr *E, Commit &commit) {
+ if (!E) return;
+
+ QualType T = E->getType();
+ if (T->isObjCObjectPointerType()) {
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
+ return;
+ } else {
+ return;
+ }
+ } else if (!T->isPointerType()) {
+ return;
+ }
+
+ SourceRange Range = E->getSourceRange();
+ if (castOperatorNeedsParens(E))
+ commit.insertWrap("(", Range, ")");
+ commit.insertBefore(Range.getBegin(), "(id)");
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteToNumericBoxedExpression.
+//===----------------------------------------------------------------------===//
+
+static bool isEnumConstant(const Expr *E) {
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
+ if (const ValueDecl *VD = DRE->getDecl())
+ return isa<EnumConstantDecl>(VD);
+
+ return false;
+}
+
+static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ if (Msg->getNumArgs() != 1)
+ return false;
+
+ const Expr *Arg = Msg->getArg(0);
+ if (Arg->isTypeDependent())
+ return false;
+
+ ASTContext &Ctx = NS.getASTContext();
+ Selector Sel = Msg->getSelector();
+ llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
+ MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
+ if (!MKOpt)
+ return false;
+ NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
+
+ const Expr *OrigArg = Arg->IgnoreImpCasts();
+ QualType FinalTy = Arg->getType();
+ QualType OrigTy = OrigArg->getType();
+ uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
+ uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
+
+ bool isTruncated = FinalTySize < OrigTySize;
+ bool needsCast = false;
+
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
+ switch (ICE->getCastKind()) {
+ case CK_LValueToRValue:
+ case CK_NoOp:
+ case CK_UserDefinedConversion:
+ break;
+
+ case CK_IntegralCast: {
+ if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
+ break;
+ // Be more liberal with Integer/UnsignedInteger which are very commonly
+ // used.
+ if ((MK == NSAPI::NSNumberWithInteger ||
+ MK == NSAPI::NSNumberWithUnsignedInteger) &&
+ !isTruncated) {
+ if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
+ break;
+ if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
+ OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
+ break;
+ }
+
+ needsCast = true;
+ break;
+ }
+
+ case CK_PointerToBoolean:
+ case CK_IntegralToBoolean:
+ case CK_IntegralToFloating:
+ case CK_FloatingToIntegral:
+ case CK_FloatingToBoolean:
+ case CK_FloatingCast:
+ case CK_FloatingComplexToReal:
+ case CK_FloatingComplexToBoolean:
+ case CK_IntegralComplexToReal:
+ case CK_IntegralComplexToBoolean:
+ case CK_AtomicToNonAtomic:
+ needsCast = true;
+ break;
+
+ case CK_Dependent:
+ case CK_BitCast:
+ case CK_LValueBitCast:
+ case CK_BaseToDerived:
+ case CK_DerivedToBase:
+ case CK_UncheckedDerivedToBase:
+ case CK_Dynamic:
+ case CK_ToUnion:
+ case CK_ArrayToPointerDecay:
+ case CK_FunctionToPointerDecay:
+ case CK_NullToPointer:
+ case CK_NullToMemberPointer:
+ case CK_BaseToDerivedMemberPointer:
+ case CK_DerivedToBaseMemberPointer:
+ case CK_MemberPointerToBoolean:
+ case CK_ReinterpretMemberPointer:
+ case CK_ConstructorConversion:
+ case CK_IntegralToPointer:
+ case CK_PointerToIntegral:
+ case CK_ToVoid:
+ case CK_VectorSplat:
+ case CK_CPointerToObjCPointerCast:
+ case CK_BlockPointerToObjCPointerCast:
+ case CK_AnyPointerToBlockPointerCast:
+ case CK_ObjCObjectLValueCast:
+ case CK_FloatingRealToComplex:
+ case CK_FloatingComplexCast:
+ case CK_FloatingComplexToIntegralComplex:
+ case CK_IntegralRealToComplex:
+ case CK_IntegralComplexCast:
+ case CK_IntegralComplexToFloatingComplex:
+ case CK_ARCProduceObject:
+ case CK_ARCConsumeObject:
+ case CK_ARCReclaimReturnedObject:
+ case CK_ARCExtendBlockObject:
+ case CK_NonAtomicToAtomic:
+ case CK_CopyAndAutoreleaseBlockObject:
+ return false;
+ }
+ }
+
+ if (needsCast) {
+ DiagnosticsEngine &Diags = Ctx.getDiagnostics();
+ // FIXME: Use a custom category name to distinguish migration diagnostics.
+ unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
+ "converting to boxing syntax requires casting %0 to %1");
+ Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
+ << Msg->getSourceRange();
+ return false;
+ }
+
+ SourceRange ArgRange = OrigArg->getSourceRange();
+ commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
+
+ if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
+ commit.insertBefore(ArgRange.getBegin(), "@");
+ else
+ commit.insertWrap("@(", ArgRange, ")");
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteToStringBoxedExpression.
+//===----------------------------------------------------------------------===//
+
+static bool doRewriteToUTF8StringBoxedExpressionHelper(
+ const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ const Expr *Arg = Msg->getArg(0);
+ if (Arg->isTypeDependent())
+ return false;
+
+ ASTContext &Ctx = NS.getASTContext();
+
+ const Expr *OrigArg = Arg->IgnoreImpCasts();
+ QualType OrigTy = OrigArg->getType();
+ if (OrigTy->isArrayType())
+ OrigTy = Ctx.getArrayDecayedType(OrigTy);
+
+ if (const StringLiteral *
+ StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
+ commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
+ commit.insert(StrE->getLocStart(), "@");
+ return true;
+ }
+
+ if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
+ QualType PointeeType = PT->getPointeeType();
+ if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
+ SourceRange ArgRange = OrigArg->getSourceRange();
+ commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
+
+ if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
+ commit.insertBefore(ArgRange.getBegin(), "@");
+ else
+ commit.insertWrap("@(", ArgRange, ")");
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ Selector Sel = Msg->getSelector();
+
+ if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
+ Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
+ if (Msg->getNumArgs() != 1)
+ return false;
+ return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
+ }
+
+ if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
+ if (Msg->getNumArgs() != 2)
+ return false;
+
+ const Expr *encodingArg = Msg->getArg(1);
+ if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
+ NS.isNSASCIIStringEncodingConstant(encodingArg))
+ return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
+ }
+
+ return false;
+}
diff --git a/lib/Frontend/ASTConsumers.cpp b/lib/Frontend/ASTConsumers.cpp
index 390ae09..bb1a4e6 100644
--- a/lib/Frontend/ASTConsumers.cpp
+++ b/lib/Frontend/ASTConsumers.cpp
@@ -12,47 +12,116 @@
//===----------------------------------------------------------------------===//
#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Basic/FileManager.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/FileManager.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/RecordLayout.h"
#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "llvm/Module.h"
-#include "llvm/Support/Timer.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Timer.h"
using namespace clang;
//===----------------------------------------------------------------------===//
/// ASTPrinter - Pretty-printer and dumper of ASTs
namespace {
- class ASTPrinter : public ASTConsumer {
+ class ASTPrinter : public ASTConsumer,
+ public RecursiveASTVisitor<ASTPrinter> {
+ typedef RecursiveASTVisitor<ASTPrinter> base;
+
+ public:
+ ASTPrinter(raw_ostream *Out = NULL, bool Dump = false,
+ StringRef FilterString = "")
+ : Out(Out ? *Out : llvm::outs()), Dump(Dump),
+ FilterString(FilterString) {}
+
+ virtual void HandleTranslationUnit(ASTContext &Context) {
+ TranslationUnitDecl *D = Context.getTranslationUnitDecl();
+
+ if (FilterString.empty()) {
+ if (Dump)
+ D->dump(Out);
+ else
+ D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true);
+ return;
+ }
+
+ TraverseDecl(D);
+ }
+
+ bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+ bool TraverseDecl(Decl *D) {
+ if (filterMatches(D)) {
+ Out.changeColor(llvm::raw_ostream::BLUE) <<
+ (Dump ? "Dumping " : "Printing ") << getName(D) << ":\n";
+ Out.resetColor();
+ if (Dump)
+ D->dump(Out);
+ else
+ D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true);
+ // Don't traverse child nodes to avoid output duplication.
+ return true;
+ }
+ return base::TraverseDecl(D);
+ }
+
+ private:
+ std::string getName(Decl *D) {
+ if (isa<NamedDecl>(D))
+ return cast<NamedDecl>(D)->getQualifiedNameAsString();
+ return "";
+ }
+ bool filterMatches(Decl *D) {
+ return getName(D).find(FilterString) != std::string::npos;
+ }
+
raw_ostream &Out;
bool Dump;
+ std::string FilterString;
+ };
+
+ class ASTDeclNodeLister : public ASTConsumer,
+ public RecursiveASTVisitor<ASTDeclNodeLister> {
+ typedef RecursiveASTVisitor<ASTPrinter> base;
public:
- ASTPrinter(raw_ostream* o = NULL, bool Dump = false)
- : Out(o? *o : llvm::outs()), Dump(Dump) { }
+ ASTDeclNodeLister(raw_ostream *Out = NULL)
+ : Out(Out ? *Out : llvm::outs()) {}
virtual void HandleTranslationUnit(ASTContext &Context) {
- PrintingPolicy Policy = Context.getPrintingPolicy();
- Policy.Dump = Dump;
- Context.getTranslationUnitDecl()->print(Out, Policy, /*Indentation=*/0,
- /*PrintInstantiation=*/true);
+ TraverseDecl(Context.getTranslationUnitDecl());
}
+
+ bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+ virtual bool VisitNamedDecl(NamedDecl *D) {
+ Out << D->getQualifiedNameAsString() << "\n";
+ return true;
+ }
+
+ private:
+ raw_ostream &Out;
};
} // end anonymous namespace
-ASTConsumer *clang::CreateASTPrinter(raw_ostream* out) {
- return new ASTPrinter(out);
+ASTConsumer *clang::CreateASTPrinter(raw_ostream *Out,
+ StringRef FilterString) {
+ return new ASTPrinter(Out, /*Dump=*/ false, FilterString);
+}
+
+ASTConsumer *clang::CreateASTDumper(StringRef FilterString) {
+ return new ASTPrinter(0, /*Dump=*/ true, FilterString);
}
-ASTConsumer *clang::CreateASTDumper() {
- return new ASTPrinter(0, true);
+ASTConsumer *clang::CreateASTDeclNodeLister() {
+ return new ASTDeclNodeLister(0);
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index 7aa9603..42a6772 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -17,12 +17,6 @@
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/AST/StmtVisitor.h"
-#include "clang/Driver/Compilation.h"
-#include "clang/Driver/Driver.h"
-#include "clang/Driver/Job.h"
-#include "clang/Driver/ArgList.h"
-#include "clang/Driver/Options.h"
-#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
@@ -122,7 +116,8 @@ static OnDiskDataMap &getOnDiskDataMap() {
}
static void cleanupOnDiskMapAtExit(void) {
- // No mutex required here since we are leaving the program.
+ // Use the mutex because there can be an alive thread destroying an ASTUnit.
+ llvm::MutexGuard Guard(getOnDiskMutex());
OnDiskDataMap &M = getOnDiskDataMap();
for (OnDiskDataMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
// We don't worry about freeing the memory associated with OnDiskDataMap.
@@ -220,6 +215,7 @@ ASTUnit::ASTUnit(bool _MainFileIsAST)
PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0),
NumWarningsInPreamble(0),
ShouldCacheCodeCompletionResults(false),
+ IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false),
CompletionCacheTopLevelHashValue(0),
PreambleTopLevelHashValue(0),
CurrentTopLevelHashValue(0),
@@ -275,43 +271,43 @@ static unsigned getDeclShowContexts(NamedDecl *ND,
if (!ND)
return 0;
- unsigned Contexts = 0;
+ uint64_t Contexts = 0;
if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND) ||
isa<ClassTemplateDecl>(ND) || isa<TemplateTemplateParmDecl>(ND)) {
// Types can appear in these contexts.
if (LangOpts.CPlusPlus || !isa<TagDecl>(ND))
- Contexts |= (1 << (CodeCompletionContext::CCC_TopLevel - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1))
- | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1))
- | (1 << (CodeCompletionContext::CCC_Statement - 1))
- | (1 << (CodeCompletionContext::CCC_Type - 1))
- | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_TopLevel)
+ | (1LL << CodeCompletionContext::CCC_ObjCIvarList)
+ | (1LL << CodeCompletionContext::CCC_ClassStructUnion)
+ | (1LL << CodeCompletionContext::CCC_Statement)
+ | (1LL << CodeCompletionContext::CCC_Type)
+ | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression);
// In C++, types can appear in expressions contexts (for functional casts).
if (LangOpts.CPlusPlus)
- Contexts |= (1 << (CodeCompletionContext::CCC_Expression - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_Expression);
// In Objective-C, message sends can send interfaces. In Objective-C++,
// all types are available due to functional casts.
if (LangOpts.CPlusPlus || isa<ObjCInterfaceDecl>(ND))
- Contexts |= (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver);
// In Objective-C, you can only be a subclass of another Objective-C class
if (isa<ObjCInterfaceDecl>(ND))
- Contexts |= (1 << (CodeCompletionContext::CCC_ObjCInterfaceName - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_ObjCInterfaceName);
// Deal with tag names.
if (isa<EnumDecl>(ND)) {
- Contexts |= (1 << (CodeCompletionContext::CCC_EnumTag - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_EnumTag);
// Part of the nested-name-specifier in C++0x.
if (LangOpts.CPlusPlus0x)
IsNestedNameSpecifier = true;
} else if (RecordDecl *Record = dyn_cast<RecordDecl>(ND)) {
if (Record->isUnion())
- Contexts |= (1 << (CodeCompletionContext::CCC_UnionTag - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_UnionTag);
else
- Contexts |= (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1));
+ Contexts |= (1LL << CodeCompletionContext::CCC_ClassOrStructTag);
if (LangOpts.CPlusPlus)
IsNestedNameSpecifier = true;
@@ -319,16 +315,16 @@ static unsigned getDeclShowContexts(NamedDecl *ND,
IsNestedNameSpecifier = true;
} else if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) {
// Values can appear in these contexts.
- Contexts = (1 << (CodeCompletionContext::CCC_Statement - 1))
- | (1 << (CodeCompletionContext::CCC_Expression - 1))
- | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1));
+ Contexts = (1LL << CodeCompletionContext::CCC_Statement)
+ | (1LL << CodeCompletionContext::CCC_Expression)
+ | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression)
+ | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver);
} else if (isa<ObjCProtocolDecl>(ND)) {
- Contexts = (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1));
+ Contexts = (1LL << CodeCompletionContext::CCC_ObjCProtocolName);
} else if (isa<ObjCCategoryDecl>(ND)) {
- Contexts = (1 << (CodeCompletionContext::CCC_ObjCCategoryName - 1));
+ Contexts = (1LL << CodeCompletionContext::CCC_ObjCCategoryName);
} else if (isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND)) {
- Contexts = (1 << (CodeCompletionContext::CCC_Namespace - 1));
+ Contexts = (1LL << CodeCompletionContext::CCC_Namespace);
// Part of the nested-name-specifier.
IsNestedNameSpecifier = true;
@@ -364,7 +360,8 @@ void ASTUnit::CacheCodeCompletionResults() {
CachedCodeCompletionResult CachedResult;
CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema,
*CachedCompletionAllocator,
- getCodeCompletionTUInfo());
+ getCodeCompletionTUInfo(),
+ IncludeBriefCommentsInCodeCompletion);
CachedResult.ShowInContexts = getDeclShowContexts(Results[I].Declaration,
Ctx->getLangOpts(),
IsNestedNameSpecifier);
@@ -402,23 +399,23 @@ void ASTUnit::CacheCodeCompletionResults() {
if (TheSema->Context.getLangOpts().CPlusPlus &&
IsNestedNameSpecifier && !Results[I].StartsNestedNameSpecifier) {
// The contexts in which a nested-name-specifier can appear in C++.
- unsigned NNSContexts
- = (1 << (CodeCompletionContext::CCC_TopLevel - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1))
- | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1))
- | (1 << (CodeCompletionContext::CCC_Statement - 1))
- | (1 << (CodeCompletionContext::CCC_Expression - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1))
- | (1 << (CodeCompletionContext::CCC_EnumTag - 1))
- | (1 << (CodeCompletionContext::CCC_UnionTag - 1))
- | (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1))
- | (1 << (CodeCompletionContext::CCC_Type - 1))
- | (1 << (CodeCompletionContext::CCC_PotentiallyQualifiedName - 1))
- | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1));
+ uint64_t NNSContexts
+ = (1LL << CodeCompletionContext::CCC_TopLevel)
+ | (1LL << CodeCompletionContext::CCC_ObjCIvarList)
+ | (1LL << CodeCompletionContext::CCC_ClassStructUnion)
+ | (1LL << CodeCompletionContext::CCC_Statement)
+ | (1LL << CodeCompletionContext::CCC_Expression)
+ | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver)
+ | (1LL << CodeCompletionContext::CCC_EnumTag)
+ | (1LL << CodeCompletionContext::CCC_UnionTag)
+ | (1LL << CodeCompletionContext::CCC_ClassOrStructTag)
+ | (1LL << CodeCompletionContext::CCC_Type)
+ | (1LL << CodeCompletionContext::CCC_PotentiallyQualifiedName)
+ | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression);
if (isa<NamespaceDecl>(Results[I].Declaration) ||
isa<NamespaceAliasDecl>(Results[I].Declaration))
- NNSContexts |= (1 << (CodeCompletionContext::CCC_Namespace - 1));
+ NNSContexts |= (1LL << CodeCompletionContext::CCC_Namespace);
if (unsigned RemainingContexts
= NNSContexts & ~CachedResult.ShowInContexts) {
@@ -429,7 +426,8 @@ void ASTUnit::CacheCodeCompletionResults() {
CachedResult.Completion
= Results[I].CreateCodeCompletionString(*TheSema,
*CachedCompletionAllocator,
- getCodeCompletionTUInfo());
+ getCodeCompletionTUInfo(),
+ IncludeBriefCommentsInCodeCompletion);
CachedResult.ShowInContexts = RemainingContexts;
CachedResult.Priority = CCP_NestedNameSpecifier;
CachedResult.TypeClass = STC_Void;
@@ -451,20 +449,21 @@ void ASTUnit::CacheCodeCompletionResults() {
CachedResult.Completion
= Results[I].CreateCodeCompletionString(*TheSema,
*CachedCompletionAllocator,
- getCodeCompletionTUInfo());
+ getCodeCompletionTUInfo(),
+ IncludeBriefCommentsInCodeCompletion);
CachedResult.ShowInContexts
- = (1 << (CodeCompletionContext::CCC_TopLevel - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1))
- | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1))
- | (1 << (CodeCompletionContext::CCC_Statement - 1))
- | (1 << (CodeCompletionContext::CCC_Expression - 1))
- | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1))
- | (1 << (CodeCompletionContext::CCC_MacroNameUse - 1))
- | (1 << (CodeCompletionContext::CCC_PreprocessorExpression - 1))
- | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1))
- | (1 << (CodeCompletionContext::CCC_OtherWithMacros - 1));
+ = (1LL << CodeCompletionContext::CCC_TopLevel)
+ | (1LL << CodeCompletionContext::CCC_ObjCInterface)
+ | (1LL << CodeCompletionContext::CCC_ObjCImplementation)
+ | (1LL << CodeCompletionContext::CCC_ObjCIvarList)
+ | (1LL << CodeCompletionContext::CCC_ClassStructUnion)
+ | (1LL << CodeCompletionContext::CCC_Statement)
+ | (1LL << CodeCompletionContext::CCC_Expression)
+ | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver)
+ | (1LL << CodeCompletionContext::CCC_MacroNameUse)
+ | (1LL << CodeCompletionContext::CCC_PreprocessorExpression)
+ | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression)
+ | (1LL << CodeCompletionContext::CCC_OtherWithMacros);
CachedResult.Priority = Results[I].Priority;
CachedResult.Kind = Results[I].CursorKind;
@@ -659,7 +658,8 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename,
RemappedFile *RemappedFiles,
unsigned NumRemappedFiles,
bool CaptureDiagnostics,
- bool AllowPCHWithCompilerErrors) {
+ bool AllowPCHWithCompilerErrors,
+ bool UserFilesAreVolatile) {
OwningPtr<ASTUnit> AST(new ASTUnit(true));
// Recover resources if we crash before exiting this method.
@@ -675,8 +675,10 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename,
AST->CaptureDiagnostics = CaptureDiagnostics;
AST->Diagnostics = Diags;
AST->FileMgr = new FileManager(FileSystemOpts);
+ AST->UserFilesAreVolatile = UserFilesAreVolatile;
AST->SourceMgr = new SourceManager(AST->getDiagnostics(),
- AST->getFileManager());
+ AST->getFileManager(),
+ UserFilesAreVolatile);
AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager(),
AST->getDiagnostics(),
AST->ASTFileLangOpts,
@@ -1078,7 +1080,8 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
LangOpts = &Clang->getLangOpts();
FileSystemOpts = Clang->getFileSystemOpts();
FileMgr = new FileManager(FileSystemOpts);
- SourceMgr = new SourceManager(getDiagnostics(), *FileMgr);
+ SourceMgr = new SourceManager(getDiagnostics(), *FileMgr,
+ UserFilesAreVolatile);
TheSema.reset();
Ctx = 0;
PP = 0;
@@ -1139,7 +1142,8 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
StoredDiagnostics);
}
- Act->Execute();
+ if (!Act->Execute())
+ goto error;
transferASTDataFromCompilerInstance(*Clang);
@@ -1665,7 +1669,8 @@ StringRef ASTUnit::getMainFileName() const {
ASTUnit *ASTUnit::create(CompilerInvocation *CI,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
- bool CaptureDiagnostics) {
+ bool CaptureDiagnostics,
+ bool UserFilesAreVolatile) {
OwningPtr<ASTUnit> AST;
AST.reset(new ASTUnit(false));
ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics);
@@ -1673,7 +1678,9 @@ ASTUnit *ASTUnit::create(CompilerInvocation *CI,
AST->Invocation = CI;
AST->FileSystemOpts = CI->getFileSystemOpts();
AST->FileMgr = new FileManager(AST->FileSystemOpts);
- AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr);
+ AST->UserFilesAreVolatile = UserFilesAreVolatile;
+ AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr,
+ UserFilesAreVolatile);
return AST.take();
}
@@ -1688,6 +1695,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI,
bool CaptureDiagnostics,
bool PrecompilePreamble,
bool CacheCodeCompletionResults,
+ bool IncludeBriefCommentsInCodeCompletion,
+ bool UserFilesAreVolatile,
OwningPtr<ASTUnit> *ErrAST) {
assert(CI && "A CompilerInvocation is required");
@@ -1695,7 +1704,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI,
ASTUnit *AST = Unit;
if (!AST) {
// Create the AST unit.
- OwnAST.reset(create(CI, Diags, CaptureDiagnostics));
+ OwnAST.reset(create(CI, Diags, CaptureDiagnostics, UserFilesAreVolatile));
AST = OwnAST.get();
}
@@ -1709,6 +1718,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI,
AST->PreambleRebuildCounter = 2;
AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete;
AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults;
+ AST->IncludeBriefCommentsInCodeCompletion
+ = IncludeBriefCommentsInCodeCompletion;
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit>
@@ -1801,7 +1812,13 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI,
AST->getCurrentTopLevelHashValue()));
Clang->setASTConsumer(new MultiplexConsumer(Consumers));
}
- Act->Execute();
+ if (!Act->Execute()) {
+ AST->transferASTDataFromCompilerInstance(*Clang);
+ if (OwnAST && ErrAST)
+ ErrAST->swap(OwnAST);
+
+ return 0;
+ }
// Steal the created target, context, and preprocessor.
AST->transferASTDataFromCompilerInstance(*Clang);
@@ -1849,7 +1866,9 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
bool CaptureDiagnostics,
bool PrecompilePreamble,
TranslationUnitKind TUKind,
- bool CacheCodeCompletionResults) {
+ bool CacheCodeCompletionResults,
+ bool IncludeBriefCommentsInCodeCompletion,
+ bool UserFilesAreVolatile) {
// Create the AST unit.
OwningPtr<ASTUnit> AST;
AST.reset(new ASTUnit(false));
@@ -1859,7 +1878,10 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
AST->CaptureDiagnostics = CaptureDiagnostics;
AST->TUKind = TUKind;
AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults;
+ AST->IncludeBriefCommentsInCodeCompletion
+ = IncludeBriefCommentsInCodeCompletion;
AST->Invocation = CI;
+ AST->UserFilesAreVolatile = UserFilesAreVolatile;
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit>
@@ -1883,8 +1905,10 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
bool PrecompilePreamble,
TranslationUnitKind TUKind,
bool CacheCodeCompletionResults,
+ bool IncludeBriefCommentsInCodeCompletion,
bool AllowPCHWithCompilerErrors,
bool SkipFunctionBodies,
+ bool UserFilesAreVolatile,
OwningPtr<ASTUnit> *ErrAST) {
if (!Diags.getPtr()) {
// No diagnostics engine was provided, so create our own diagnostics object
@@ -1942,6 +1966,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
AST->CaptureDiagnostics = CaptureDiagnostics;
AST->TUKind = TUKind;
AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults;
+ AST->IncludeBriefCommentsInCodeCompletion
+ = IncludeBriefCommentsInCodeCompletion;
+ AST->UserFilesAreVolatile = UserFilesAreVolatile;
AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size();
AST->StoredDiagnostics.swap(StoredDiagnostics);
AST->Invocation = CI;
@@ -2034,38 +2061,37 @@ namespace {
/// results from an ASTUnit with the code-completion results provided to it,
/// then passes the result on to
class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer {
- unsigned long long NormalContexts;
+ uint64_t NormalContexts;
ASTUnit &AST;
CodeCompleteConsumer &Next;
public:
AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next,
- bool IncludeMacros, bool IncludeCodePatterns,
- bool IncludeGlobals)
- : CodeCompleteConsumer(IncludeMacros, IncludeCodePatterns, IncludeGlobals,
- Next.isOutputBinary()), AST(AST), Next(Next)
+ const CodeCompleteOptions &CodeCompleteOpts)
+ : CodeCompleteConsumer(CodeCompleteOpts, Next.isOutputBinary()),
+ AST(AST), Next(Next)
{
// Compute the set of contexts in which we will look when we don't have
// any information about the specific context.
NormalContexts
- = (1LL << (CodeCompletionContext::CCC_TopLevel - 1))
- | (1LL << (CodeCompletionContext::CCC_ObjCInterface - 1))
- | (1LL << (CodeCompletionContext::CCC_ObjCImplementation - 1))
- | (1LL << (CodeCompletionContext::CCC_ObjCIvarList - 1))
- | (1LL << (CodeCompletionContext::CCC_Statement - 1))
- | (1LL << (CodeCompletionContext::CCC_Expression - 1))
- | (1LL << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1))
- | (1LL << (CodeCompletionContext::CCC_DotMemberAccess - 1))
- | (1LL << (CodeCompletionContext::CCC_ArrowMemberAccess - 1))
- | (1LL << (CodeCompletionContext::CCC_ObjCPropertyAccess - 1))
- | (1LL << (CodeCompletionContext::CCC_ObjCProtocolName - 1))
- | (1LL << (CodeCompletionContext::CCC_ParenthesizedExpression - 1))
- | (1LL << (CodeCompletionContext::CCC_Recovery - 1));
+ = (1LL << CodeCompletionContext::CCC_TopLevel)
+ | (1LL << CodeCompletionContext::CCC_ObjCInterface)
+ | (1LL << CodeCompletionContext::CCC_ObjCImplementation)
+ | (1LL << CodeCompletionContext::CCC_ObjCIvarList)
+ | (1LL << CodeCompletionContext::CCC_Statement)
+ | (1LL << CodeCompletionContext::CCC_Expression)
+ | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver)
+ | (1LL << CodeCompletionContext::CCC_DotMemberAccess)
+ | (1LL << CodeCompletionContext::CCC_ArrowMemberAccess)
+ | (1LL << CodeCompletionContext::CCC_ObjCPropertyAccess)
+ | (1LL << CodeCompletionContext::CCC_ObjCProtocolName)
+ | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression)
+ | (1LL << CodeCompletionContext::CCC_Recovery);
if (AST.getASTContext().getLangOpts().CPlusPlus)
- NormalContexts |= (1LL << (CodeCompletionContext::CCC_EnumTag - 1))
- | (1LL << (CodeCompletionContext::CCC_UnionTag - 1))
- | (1LL << (CodeCompletionContext::CCC_ClassOrStructTag - 1));
+ NormalContexts |= (1LL << CodeCompletionContext::CCC_EnumTag)
+ | (1LL << CodeCompletionContext::CCC_UnionTag)
+ | (1LL << CodeCompletionContext::CCC_ClassOrStructTag);
}
virtual void ProcessCodeCompleteResults(Sema &S,
@@ -2180,9 +2206,9 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S,
unsigned NumResults) {
// Merge the results we were given with the results we cached.
bool AddedResult = false;
- unsigned InContexts
- = (Context.getKind() == CodeCompletionContext::CCC_Recovery? NormalContexts
- : (1ULL << (Context.getKind() - 1)));
+ uint64_t InContexts =
+ Context.getKind() == CodeCompletionContext::CCC_Recovery
+ ? NormalContexts : (1LL << Context.getKind());
// Contains the set of names that are hidden by "local" completion results.
llvm::StringSet<llvm::BumpPtrAllocator> HiddenNames;
typedef CodeCompletionResult Result;
@@ -2273,6 +2299,7 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column,
unsigned NumRemappedFiles,
bool IncludeMacros,
bool IncludeCodePatterns,
+ bool IncludeBriefComments,
CodeCompleteConsumer &Consumer,
DiagnosticsEngine &Diag, LangOptions &LangOpts,
SourceManager &SourceMgr, FileManager &FileMgr,
@@ -2289,13 +2316,17 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column,
CCInvocation(new CompilerInvocation(*Invocation));
FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts();
+ CodeCompleteOptions &CodeCompleteOpts = FrontendOpts.CodeCompleteOpts;
PreprocessorOptions &PreprocessorOpts = CCInvocation->getPreprocessorOpts();
- FrontendOpts.ShowMacrosInCodeCompletion
- = IncludeMacros && CachedCompletionResults.empty();
- FrontendOpts.ShowCodePatternsInCodeCompletion = IncludeCodePatterns;
- FrontendOpts.ShowGlobalSymbolsInCodeCompletion
- = CachedCompletionResults.empty();
+ CodeCompleteOpts.IncludeMacros = IncludeMacros &&
+ CachedCompletionResults.empty();
+ CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns;
+ CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty();
+ CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments;
+
+ assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion);
+
FrontendOpts.CodeCompletionAt.FileName = File;
FrontendOpts.CodeCompletionAt.Line = Line;
FrontendOpts.CodeCompletionAt.Column = Column;
@@ -2364,10 +2395,7 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column,
// Use the code completion consumer we were given, but adding any cached
// code-completion results.
AugmentedCodeCompleteConsumer *AugmentedConsumer
- = new AugmentedCodeCompleteConsumer(*this, Consumer,
- FrontendOpts.ShowMacrosInCodeCompletion,
- FrontendOpts.ShowCodePatternsInCodeCompletion,
- FrontendOpts.ShowGlobalSymbolsInCodeCompletion);
+ = new AugmentedCodeCompleteConsumer(*this, Consumer, CodeCompleteOpts);
Clang->setCodeCompletionConsumer(AugmentedConsumer);
Clang->getFrontendOpts().SkipFunctionBodies = true;
diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt
index 2bee240..0566d54 100644
--- a/lib/Frontend/CMakeLists.txt
+++ b/lib/Frontend/CMakeLists.txt
@@ -1,14 +1,3 @@
-set( LLVM_USED_LIBS
- clangAST
- clangBasic
- clangDriver
- clangEdit
- clangLex
- clangParse
- clangSema
- clangSerialization
- )
-
add_clang_library(clangFrontend
ASTConsumers.cpp
ASTMerge.cpp
@@ -41,21 +30,29 @@ add_clang_library(clangFrontend
Warnings.cpp
)
-IF(MSVC)
- get_target_property(NON_ANSI_COMPILE_FLAGS clangFrontend COMPILE_FLAGS)
- string(REPLACE /Za
- "" NON_ANSI_COMPILE_FLAGS
- ${NON_ANSI_COMPILE_FLAGS})
- set_target_properties(clangFrontend PROPERTIES COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS})
-ENDIF(MSVC)
-
-add_dependencies(clangFrontend
+add_dependencies(clangFrontend
ClangAttrClasses
ClangAttrList
- ClangCC1Options
- ClangDiagnosticFrontend
+ ClangAttrParsedAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticAST
+ ClangDiagnosticCommon
+ ClangDiagnosticDriver
+ ClangDiagnosticFrontend
ClangDiagnosticLex
ClangDiagnosticSema
ClangDriverOptions
- ClangDeclNodes
- ClangStmtNodes)
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangFrontend
+ clangAST
+ clangBasic
+ clangDriver
+ clangEdit
+ clangLex
+ clangParse
+ clangSema
+ clangSerialization
+ )
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index 803e418..6de1531 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -387,9 +387,7 @@ void CompilerInstance::createCodeCompletionConsumer() {
setCodeCompletionConsumer(
createCodeCompletionConsumer(getPreprocessor(),
Loc.FileName, Loc.Line, Loc.Column,
- getFrontendOpts().ShowMacrosInCodeCompletion,
- getFrontendOpts().ShowCodePatternsInCodeCompletion,
- getFrontendOpts().ShowGlobalSymbolsInCodeCompletion,
+ getFrontendOpts().CodeCompleteOpts,
llvm::outs()));
if (!CompletionConsumer)
return;
@@ -415,16 +413,13 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP,
const std::string &Filename,
unsigned Line,
unsigned Column,
- bool ShowMacros,
- bool ShowCodePatterns,
- bool ShowGlobals,
+ const CodeCompleteOptions &Opts,
raw_ostream &OS) {
if (EnableCodeCompletion(PP, Filename, Line, Column))
return 0;
// Set up the creation routine for code-completion.
- return new PrintingCodeCompleteConsumer(ShowMacros, ShowCodePatterns,
- ShowGlobals, OS);
+ return new PrintingCodeCompleteConsumer(Opts, OS);
}
void CompilerInstance::createSema(TranslationUnitKind TUKind,
@@ -456,7 +451,7 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) {
FileMgr->FixupRelativePath(NewOutFile);
if (llvm::error_code ec = llvm::sys::fs::rename(it->TempFilename,
NewOutFile.str())) {
- getDiagnostics().Report(diag::err_fe_unable_to_rename_temp)
+ getDiagnostics().Report(diag::err_unable_to_rename_temp)
<< it->TempFilename << it->Filename << ec.message();
bool existed;
@@ -560,7 +555,8 @@ CompilerInstance::createOutputFile(StringRef OutputPath,
TempPath += "-%%%%%%%%";
int fd;
if (llvm::sys::fs::unique_file(TempPath.str(), fd, TempPath,
- /*makeAbsolute=*/false) == llvm::errc::success) {
+ /*makeAbsolute=*/false, 0664)
+ == llvm::errc::success) {
OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
OSFile = TempFile = TempPath.str();
}
@@ -859,13 +855,6 @@ Module *CompilerInstance::loadModule(SourceLocation ImportLoc,
}
// Determine what file we're searching from.
- SourceManager &SourceMgr = getSourceManager();
- SourceLocation ExpandedImportLoc = SourceMgr.getExpansionLoc(ImportLoc);
- const FileEntry *CurFile
- = SourceMgr.getFileEntryForID(SourceMgr.getFileID(ExpandedImportLoc));
- if (!CurFile)
- CurFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
-
StringRef ModuleName = Path[0].first->getName();
SourceLocation ModuleNameLoc = Path[0].second;
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 4c5b063..d39679c 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -13,7 +13,7 @@
#include "clang/Basic/FileManager.h"
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
-#include "clang/Driver/CC1Options.h"
+#include "clang/Driver/Options.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/OptTable.h"
#include "clang/Driver/Option.h"
@@ -181,8 +181,21 @@ static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, ToArgsList &Res) {
}
static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) {
- if (Opts.DebugInfo)
- Res.push_back("-g");
+ switch (Opts.DebugInfo) {
+ case CodeGenOptions::NoDebugInfo:
+ break;
+ case CodeGenOptions::DebugLineTablesOnly:
+ Res.push_back("-gline-tables-only");
+ break;
+ case CodeGenOptions::LimitedDebugInfo:
+ Res.push_back("-g");
+ Res.push_back("-flimit-debug-info");
+ break;
+ case CodeGenOptions::FullDebugInfo:
+ Res.push_back("-g");
+ Res.push_back("-fno-limit-debug-info");
+ break;
+ }
if (Opts.DisableLLVMOpts)
Res.push_back("-disable-llvm-optzns");
if (Opts.DisableRedZone)
@@ -193,14 +206,12 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) {
Res.push_back("-fdebug-compilation-dir", Opts.DebugCompilationDir);
if (!Opts.DwarfDebugFlags.empty())
Res.push_back("-dwarf-debug-flags", Opts.DwarfDebugFlags);
- if (Opts.ObjCRuntimeHasARC)
- Res.push_back("-fobjc-runtime-has-arc");
- if (Opts.ObjCRuntimeHasTerminate)
- Res.push_back("-fobjc-runtime-has-terminate");
if (Opts.EmitGcovArcs)
Res.push_back("-femit-coverage-data");
if (Opts.EmitGcovNotes)
Res.push_back("-femit-coverage-notes");
+ if (Opts.EmitOpenCLArgMetadata)
+ Res.push_back("-cl-kernel-arg-info");
if (!Opts.MergeAllConstants)
Res.push_back("-fno-merge-all-constants");
if (Opts.NoCommon)
@@ -270,6 +281,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) {
Res.push_back("-fobjc-dispatch-method=non-legacy");
break;
}
+ if (Opts.BoundsChecking > 0)
+ Res.push_back("-fbounds-checking=" + llvm::utostr(Opts.BoundsChecking));
if (Opts.NumRegisterParameters)
Res.push_back("-mregparm", llvm::utostr(Opts.NumRegisterParameters));
if (Opts.NoGlobalMerge)
@@ -296,6 +309,20 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) {
Res.push_back("-disable-llvm-verifier");
for (unsigned i = 0, e = Opts.BackendOptions.size(); i != e; ++i)
Res.push_back("-backend-option", Opts.BackendOptions[i]);
+
+ switch (Opts.DefaultTLSModel) {
+ case CodeGenOptions::GeneralDynamicTLSModel:
+ break;
+ case CodeGenOptions::LocalDynamicTLSModel:
+ Res.push_back("-ftls-model=local-dynamic");
+ break;
+ case CodeGenOptions::InitialExecTLSModel:
+ Res.push_back("-ftls-model=initial-exec");
+ break;
+ case CodeGenOptions::LocalExecTLSModel:
+ Res.push_back("-ftls-model=local-exec");
+ break;
+ }
}
static void DependencyOutputOptsToArgs(const DependencyOutputOptions &Opts,
@@ -407,6 +434,7 @@ static const char *getActionName(frontend::ActionKind Kind) {
case frontend::PluginAction:
llvm_unreachable("Invalid kind!");
+ case frontend::ASTDeclList: return "-ast-list";
case frontend::ASTDump: return "-ast-dump";
case frontend::ASTDumpXML: return "-ast-dump-xml";
case frontend::ASTPrint: return "-ast-print";
@@ -445,6 +473,18 @@ static void FileSystemOptsToArgs(const FileSystemOptions &Opts, ToArgsList &Res)
Res.push_back("-working-directory", Opts.WorkingDir);
}
+static void CodeCompleteOptionsToArgs(const CodeCompleteOptions &Opts,
+ ToArgsList &Res) {
+ if (Opts.IncludeMacros)
+ Res.push_back("-code-completion-macros");
+ if (Opts.IncludeCodePatterns)
+ Res.push_back("-code-completion-patterns");
+ if (!Opts.IncludeGlobals)
+ Res.push_back("-no-code-completion-globals");
+ if (Opts.IncludeBriefComments)
+ Res.push_back("-code-completion-brief-comments");
+}
+
static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) {
if (Opts.DisableFree)
Res.push_back("-disable-free");
@@ -452,12 +492,6 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) {
Res.push_back("-relocatable-pch");
if (Opts.ShowHelp)
Res.push_back("-help");
- if (Opts.ShowMacrosInCodeCompletion)
- Res.push_back("-code-completion-macros");
- if (Opts.ShowCodePatternsInCodeCompletion)
- Res.push_back("-code-completion-patterns");
- if (!Opts.ShowGlobalSymbolsInCodeCompletion)
- Res.push_back("-no-code-completion-globals");
if (Opts.ShowStats)
Res.push_back("-print-stats");
if (Opts.ShowTimers)
@@ -485,6 +519,7 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) {
Res.push_back("-arcmt-migrate");
break;
}
+ CodeCompleteOptionsToArgs(Opts.CodeCompleteOpts, Res);
if (!Opts.MTMigrateDir.empty())
Res.push_back("-mt-migrate-directory", Opts.MTMigrateDir);
if (!Opts.ARCMTMigrateReportOut.empty())
@@ -524,6 +559,8 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) {
for(unsigned i = 0, e = Opts.PluginArgs.size(); i != e; ++i)
Res.push_back("-plugin-arg-" + Opts.ActionName, Opts.PluginArgs[i]);
}
+ if (!Opts.ASTDumpFilter.empty())
+ Res.push_back("-ast-dump-filter", Opts.ASTDumpFilter);
for (unsigned i = 0, e = Opts.Plugins.size(); i != e; ++i)
Res.push_back("-load", Opts.Plugins[i]);
for (unsigned i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) {
@@ -608,6 +645,16 @@ static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts,
Res.push_back(E.Path);
}
+ /// User-specified system header prefixes.
+ for (unsigned i = 0, e = Opts.SystemHeaderPrefixes.size(); i != e; ++i) {
+ if (Opts.SystemHeaderPrefixes[i].IsSystemHeader)
+ Res.push_back("-isystem-prefix");
+ else
+ Res.push_back("-ino-system-prefix");
+
+ Res.push_back(Opts.SystemHeaderPrefixes[i].Prefix);
+ }
+
if (!Opts.ResourceDir.empty())
Res.push_back("-resource-dir", Opts.ResourceDir);
if (!Opts.ModuleCachePath.empty())
@@ -653,8 +700,6 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) {
Res.push_back("-fmsc-version=" + llvm::utostr(Opts.MSCVersion));
if (Opts.Borland)
Res.push_back("-fborland-extensions");
- if (!Opts.ObjCNonFragileABI)
- Res.push_back("-fobjc-fragile-abi");
if (Opts.ObjCDefaultSynthProperties)
Res.push_back("-fobjc-default-synthesize-properties");
// NoInline is implicit.
@@ -690,8 +735,6 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) {
Res.push_back("-fno-rtti");
if (Opts.MSBitfields)
Res.push_back("-mms-bitfields");
- if (!Opts.NeXTRuntime)
- Res.push_back("-fgnu-runtime");
if (Opts.Freestanding)
Res.push_back("-ffreestanding");
if (Opts.NoBuiltin)
@@ -721,6 +764,11 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) {
Res.push_back("-ftrapv-handler", Opts.OverflowHandler);
break;
}
+ switch (Opts.getFPContractMode()) {
+ case LangOptions::FPC_Off: Res.push_back("-ffp-contract=off"); break;
+ case LangOptions::FPC_On: Res.push_back("-ffp-contract=on"); break;
+ case LangOptions::FPC_Fast: Res.push_back("-ffp-contract=fast"); break;
+ }
if (Opts.HeinousExtensions)
Res.push_back("-fheinous-gnu-extensions");
// Optimize is implicit.
@@ -761,6 +809,7 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) {
Res.push_back("-fobjc-gc-only");
}
}
+ Res.push_back("-fobjc-runtime=" + Opts.ObjCRuntime.getAsString());
if (Opts.ObjCAutoRefCount)
Res.push_back("-fobjc-arc");
if (Opts.ObjCRuntimeHasWeak)
@@ -770,7 +819,7 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) {
if (Opts.AppleKext)
Res.push_back("-fapple-kext");
-
+
if (Opts.getVisibilityMode() != DefaultVisibility) {
Res.push_back("-fvisibility");
if (Opts.getVisibilityMode() == HiddenVisibility) {
@@ -880,7 +929,7 @@ static void TargetOptsToArgs(const TargetOptions &Opts,
Res.push_back("-target-feature", Opts.Features[i]);
}
-void CompilerInvocation::toArgs(std::vector<std::string> &Res) {
+void CompilerInvocation::toArgs(std::vector<std::string> &Res) const {
ToArgsList List(Res);
AnalyzerOptsToArgs(getAnalyzerOpts(), List);
CodeGenOptsToArgs(getCodeGenOpts(), List);
@@ -900,7 +949,7 @@ void CompilerInvocation::toArgs(std::vector<std::string> &Res) {
//===----------------------------------------------------------------------===//
using namespace clang::driver;
-using namespace clang::driver::cc1options;
+using namespace clang::driver::options;
//
@@ -909,14 +958,66 @@ static unsigned getOptimizationLevel(ArgList &Args, InputKind IK,
unsigned DefaultOpt = 0;
if (IK == IK_OpenCL && !Args.hasArg(OPT_cl_opt_disable))
DefaultOpt = 2;
- // -Os/-Oz implies -O2
- return (Args.hasArg(OPT_Os) || Args.hasArg (OPT_Oz)) ? 2 :
- Args.getLastArgIntValue(OPT_O, DefaultOpt, Diags);
+
+ if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
+ if (A->getOption().matches(options::OPT_O0))
+ return 0;
+
+ assert (A->getOption().matches(options::OPT_O));
+
+ llvm::StringRef S(A->getValue(Args));
+ if (S == "s" || S == "z" || S.empty())
+ return 2;
+
+ return Args.getLastArgIntValue(OPT_O, DefaultOpt, Diags);
+ }
+
+ return DefaultOpt;
+}
+
+static unsigned getOptimizationLevelSize(ArgList &Args, InputKind IK,
+ DiagnosticsEngine &Diags) {
+ if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
+ if (A->getOption().matches(options::OPT_O)) {
+ switch (A->getValue(Args)[0]) {
+ default:
+ return 0;
+ case 's':
+ return 1;
+ case 'z':
+ return 2;
+ }
+ }
+ }
+ return 0;
+}
+
+static void addWarningArgs(ArgList &Args, std::vector<std::string> &Warnings) {
+ for (arg_iterator I = Args.filtered_begin(OPT_W_Group),
+ E = Args.filtered_end(); I != E; ++I) {
+ Arg *A = *I;
+ // If the argument is a pure flag, add its name (minus the "-W" at the beginning)
+ // to the warning list. Else, add its value (for the OPT_W case).
+ if (A->getOption().getKind() == Option::FlagClass) {
+ Warnings.push_back(A->getOption().getName().substr(2));
+ } else {
+ for (unsigned Idx = 0, End = A->getNumValues();
+ Idx < End; ++Idx) {
+ StringRef V = A->getValue(Args, Idx);
+ // "-Wl," and such are not warning options.
+ // FIXME: Should be handled by putting these in separate flags.
+ if (V.startswith("l,") || V.startswith("a,") || V.startswith("p,"))
+ continue;
+
+ Warnings.push_back(V);
+ }
+ }
+ }
}
static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags) {
- using namespace cc1options;
+ using namespace options;
bool Success = true;
if (Arg *A = Args.getLastArg(OPT_analyzer_store)) {
StringRef Name = A->getValue(Args);
@@ -1026,7 +1127,6 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function);
Opts.UnoptimizedCFG = Args.hasArg(OPT_analysis_UnoptimizedCFG);
Opts.CFGAddImplicitDtors = Args.hasArg(OPT_analysis_CFGAddImplicitDtors);
- Opts.CFGAddInitializers = Args.hasArg(OPT_analysis_CFGAddInitializers);
Opts.TrimGraph = Args.hasArg(OPT_trim_egraph);
Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes, 150000,Diags);
Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 4, Diags);
@@ -1066,7 +1166,7 @@ static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) {
static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
DiagnosticsEngine &Diags) {
- using namespace cc1options;
+ using namespace options;
bool Success = true;
unsigned OptLevel = getOptimizationLevel(Args, IK, Diags);
@@ -1083,12 +1183,18 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
: CodeGenOptions::OnlyAlwaysInlining;
// -fno-inline-functions overrides OptimizationLevel > 1.
Opts.NoInline = Args.hasArg(OPT_fno_inline);
- Opts.Inlining = Args.hasArg(OPT_fno_inline_functions) ?
+ Opts.Inlining = Args.hasArg(OPT_fno_inline_functions) ?
CodeGenOptions::OnlyAlwaysInlining : Opts.Inlining;
- Opts.DebugInfo = Args.hasArg(OPT_g);
- Opts.LimitDebugInfo = !Args.hasArg(OPT_fno_limit_debug_info)
- || Args.hasArg(OPT_flimit_debug_info);
+ if (Args.hasArg(OPT_gline_tables_only)) {
+ Opts.DebugInfo = CodeGenOptions::DebugLineTablesOnly;
+ } else if (Args.hasArg(OPT_g_Flag)) {
+ if (Args.hasFlag(OPT_flimit_debug_info, OPT_fno_limit_debug_info, true))
+ Opts.DebugInfo = CodeGenOptions::LimitedDebugInfo;
+ else
+ Opts.DebugInfo = CodeGenOptions::FullDebugInfo;
+ }
+
Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns);
Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone);
Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables);
@@ -1099,8 +1205,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.MergeAllConstants = !Args.hasArg(OPT_fno_merge_all_constants);
Opts.NoCommon = Args.hasArg(OPT_fno_common);
Opts.NoImplicitFloat = Args.hasArg(OPT_no_implicit_float);
- Opts.OptimizeSize = Args.hasArg(OPT_Os);
- Opts.OptimizeSize = Args.hasArg(OPT_Oz) ? 2 : Opts.OptimizeSize;
+ Opts.OptimizeSize = getOptimizationLevelSize(Args, IK, Diags);
Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) ||
Args.hasArg(OPT_ffreestanding));
Opts.UnrollLoops = Args.hasArg(OPT_funroll_loops) ||
@@ -1108,8 +1213,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose);
Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions);
- Opts.ObjCRuntimeHasARC = Args.hasArg(OPT_fobjc_runtime_has_arc);
- Opts.ObjCRuntimeHasTerminate = Args.hasArg(OPT_fobjc_runtime_has_terminate);
Opts.CUDAIsDevice = Args.hasArg(OPT_fcuda_is_device);
Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit);
Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases);
@@ -1145,6 +1248,9 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.UnwindTables = Args.hasArg(OPT_munwind_tables);
Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
Opts.TrapFuncName = Args.getLastArgValue(OPT_ftrap_function_EQ);
+ Opts.BoundsChecking = Args.getLastArgIntValue(OPT_fbounds_checking_EQ, 0,
+ Diags);
+ Opts.UseInitArray = Args.hasArg(OPT_fuse_init_array);
Opts.FunctionSections = Args.hasArg(OPT_ffunction_sections);
Opts.DataSections = Args.hasArg(OPT_fdata_sections);
@@ -1156,6 +1262,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.InstrumentForProfiling = Args.hasArg(OPT_pg);
Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data);
Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes);
+ Opts.EmitOpenCLArgMetadata = Args.hasArg(OPT_cl_kernel_arg_info);
+ Opts.EmitMicrosoftInlineAsm = Args.hasArg(OPT_fenable_experimental_ms_inline_asm);
Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file);
Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
Opts.LinkBitcodeFile = Args.getLastArgValue(OPT_mlink_bitcode_file);
@@ -1180,12 +1288,28 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
}
}
+ if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) {
+ StringRef Name = A->getValue(Args);
+ unsigned Model = llvm::StringSwitch<unsigned>(Name)
+ .Case("global-dynamic", CodeGenOptions::GeneralDynamicTLSModel)
+ .Case("local-dynamic", CodeGenOptions::LocalDynamicTLSModel)
+ .Case("initial-exec", CodeGenOptions::InitialExecTLSModel)
+ .Case("local-exec", CodeGenOptions::LocalExecTLSModel)
+ .Default(~0U);
+ if (Model == ~0U) {
+ Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
+ Success = false;
+ } else {
+ Opts.DefaultTLSModel = static_cast<CodeGenOptions::TLSModel>(Model);
+ }
+ }
+
return Success;
}
static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts,
ArgList &Args) {
- using namespace cc1options;
+ using namespace options;
Opts.OutputFile = Args.getLastArgValue(OPT_dependency_file);
Opts.Targets = Args.getAllArgValues(OPT_MT);
Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps);
@@ -1198,7 +1322,7 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts,
bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
DiagnosticsEngine *Diags) {
- using namespace cc1options;
+ using namespace options;
bool Success = true;
Opts.DiagnosticLogFile = Args.getLastArgValue(OPT_diagnostic_log_file);
@@ -1273,6 +1397,8 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info);
Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits);
Opts.VerifyDiagnostics = Args.hasArg(OPT_verify);
+ Opts.ElideType = !Args.hasArg(OPT_fno_elide_type);
+ Opts.ShowTemplateTree = Args.hasArg(OPT_fdiagnostics_show_template_tree);
Opts.ErrorLimit = Args.getLastArgIntValue(OPT_ferror_limit, 0, Diags);
Opts.MacroBacktraceLimit
= Args.getLastArgIntValue(OPT_fmacro_backtrace_limit,
@@ -1295,16 +1421,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
}
Opts.MessageLength = Args.getLastArgIntValue(OPT_fmessage_length, 0, Diags);
Opts.DumpBuildInformation = Args.getLastArgValue(OPT_dump_build_information);
-
- for (arg_iterator it = Args.filtered_begin(OPT_W),
- ie = Args.filtered_end(); it != ie; ++it) {
- StringRef V = (*it)->getValue(Args);
- // "-Wl," and such are not warnings options.
- if (V.startswith("l,") || V.startswith("a,") || V.startswith("p,"))
- continue;
-
- Opts.Warnings.push_back(V);
- }
+ addWarningArgs(Args, Opts.Warnings);
return Success;
}
@@ -1315,12 +1432,14 @@ static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) {
static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
DiagnosticsEngine &Diags) {
- using namespace cc1options;
+ using namespace options;
Opts.ProgramAction = frontend::ParseSyntaxOnly;
if (const Arg *A = Args.getLastArg(OPT_Action_Group)) {
switch (A->getOption().getID()) {
default:
llvm_unreachable("Invalid option in group!");
+ case OPT_ast_list:
+ Opts.ProgramAction = frontend::ASTDeclList; break;
case OPT_ast_dump:
Opts.ProgramAction = frontend::ASTDump; break;
case OPT_ast_dump_xml:
@@ -1418,11 +1537,6 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.Plugins = Args.getAllArgValues(OPT_load);
Opts.RelocatablePCH = Args.hasArg(OPT_relocatable_pch);
Opts.ShowHelp = Args.hasArg(OPT_help);
- Opts.ShowMacrosInCodeCompletion = Args.hasArg(OPT_code_completion_macros);
- Opts.ShowCodePatternsInCodeCompletion
- = Args.hasArg(OPT_code_completion_patterns);
- Opts.ShowGlobalSymbolsInCodeCompletion
- = !Args.hasArg(OPT_no_code_completion_globals);
Opts.ShowStats = Args.hasArg(OPT_print_stats);
Opts.ShowTimers = Args.hasArg(OPT_ftime_report);
Opts.ShowVersion = Args.hasArg(OPT_version);
@@ -1432,6 +1546,17 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings);
Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile);
Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp);
+ Opts.ASTDumpFilter = Args.getLastArgValue(OPT_ast_dump_filter);
+
+ Opts.CodeCompleteOpts.IncludeMacros
+ = Args.hasArg(OPT_code_completion_macros);
+ Opts.CodeCompleteOpts.IncludeCodePatterns
+ = Args.hasArg(OPT_code_completion_patterns);
+ Opts.CodeCompleteOpts.IncludeGlobals
+ = !Args.hasArg(OPT_no_code_completion_globals);
+ Opts.CodeCompleteOpts.IncludeBriefComments
+ = Args.hasArg(OPT_code_completion_brief_comments);
+
Opts.OverrideRecordLayoutsFile
= Args.getLastArgValue(OPT_foverride_record_layout_EQ);
if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
@@ -1535,7 +1660,7 @@ std::string CompilerInvocation::GetResourcesPath(const char *Argv0,
}
static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) {
- using namespace cc1options;
+ using namespace options;
Opts.Sysroot = Args.getLastArgValue(OPT_isysroot, "/");
Opts.Verbose = Args.hasArg(OPT_v);
Opts.UseBuiltinIncludes = !Args.hasArg(OPT_nobuiltininc);
@@ -1620,6 +1745,14 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) {
Opts.AddPath((*I)->getValue(Args), frontend::System,
false, false, /*IgnoreSysRoot=*/true, /*IsInternal=*/true,
(*I)->getOption().matches(OPT_internal_externc_isystem));
+
+ // Add the path prefixes which are implicitly treated as being system headers.
+ for (arg_iterator I = Args.filtered_begin(OPT_isystem_prefix,
+ OPT_ino_system_prefix),
+ E = Args.filtered_end();
+ I != E; ++I)
+ Opts.AddSystemHeaderPrefix((*I)->getValue(Args),
+ (*I)->getOption().matches(OPT_isystem_prefix));
}
void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
@@ -1677,9 +1810,22 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
Opts.HexFloats = Std.hasHexFloats();
Opts.ImplicitInt = Std.hasImplicitInt();
- // OpenCL has some additional defaults.
+ // Set OpenCL Version.
if (LangStd == LangStandard::lang_opencl) {
Opts.OpenCL = 1;
+ Opts.OpenCLVersion = 100;
+ }
+ else if (LangStd == LangStandard::lang_opencl11) {
+ Opts.OpenCL = 1;
+ Opts.OpenCLVersion = 110;
+ }
+ else if (LangStd == LangStandard::lang_opencl12) {
+ Opts.OpenCL = 1;
+ Opts.OpenCLVersion = 120;
+ }
+
+ // OpenCL has some additional defaults.
+ if (Opts.OpenCL) {
Opts.AltiVec = 0;
Opts.CXXOperatorNames = 1;
Opts.LaxVectorConversions = 0;
@@ -1751,13 +1897,24 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
}
}
+ // -cl-std only applies for OpenCL language standards.
+ // Override the -std option in this case.
if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) {
- if (strcmp(A->getValue(Args), "CL1.1") != 0) {
+ LangStandard::Kind OpenCLLangStd
+ = llvm::StringSwitch<LangStandard::Kind>(A->getValue(Args))
+ .Case("CL", LangStandard::lang_opencl)
+ .Case("CL1.1", LangStandard::lang_opencl11)
+ .Case("CL1.2", LangStandard::lang_opencl12)
+ .Default(LangStandard::lang_unspecified);
+
+ if (OpenCLLangStd == LangStandard::lang_unspecified) {
Diags.Report(diag::err_drv_invalid_value)
- << A->getAsString(Args) << A->getValue(Args);
+ << A->getAsString(Args) << A->getValue(Args);
}
+ else
+ LangStd = OpenCLLangStd;
}
-
+
CompilerInvocation::setLangDefaults(Opts, IK, LangStd);
// We abuse '-f[no-]gnu-keywords' to force overriding all GNU-extension
@@ -1772,16 +1929,23 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Opts.CXXOperatorNames = 0;
if (Opts.ObjC1) {
+ if (Arg *arg = Args.getLastArg(OPT_fobjc_runtime_EQ)) {
+ StringRef value = arg->getValue(Args);
+ if (Opts.ObjCRuntime.tryParse(value))
+ Diags.Report(diag::err_drv_unknown_objc_runtime) << value;
+ }
+
if (Args.hasArg(OPT_fobjc_gc_only))
Opts.setGC(LangOptions::GCOnly);
else if (Args.hasArg(OPT_fobjc_gc))
Opts.setGC(LangOptions::HybridGC);
else if (Args.hasArg(OPT_fobjc_arc)) {
Opts.ObjCAutoRefCount = 1;
- if (Args.hasArg(OPT_fobjc_fragile_abi))
+ if (!Opts.ObjCRuntime.isNonFragile())
Diags.Report(diag::err_arc_nonfragile_abi);
}
+ Opts.ObjCRuntimeHasWeak = Opts.ObjCRuntime.hasWeak();
if (Args.hasArg(OPT_fobjc_runtime_has_weak))
Opts.ObjCRuntimeHasWeak = 1;
@@ -1825,6 +1989,18 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Diags.Report(diag::err_drv_invalid_value)
<< Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis;
+ if (Arg *A = Args.getLastArg(OPT_ffp_contract)) {
+ StringRef Val = A->getValue(Args);
+ if (Val == "fast")
+ Opts.setFPContractMode(LangOptions::FPC_Fast);
+ else if (Val == "on")
+ Opts.setFPContractMode(LangOptions::FPC_On);
+ else if (Val == "off")
+ Opts.setFPContractMode(LangOptions::FPC_Off);
+ else
+ Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
+ }
+
if (Args.hasArg(OPT_fvisibility_inlines_hidden))
Opts.InlineVisibilityHidden = 1;
@@ -1876,25 +2052,21 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Opts.AccessControl = !Args.hasArg(OPT_fno_access_control);
Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors);
Opts.MathErrno = Args.hasArg(OPT_fmath_errno);
- Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 1024,
+ Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 512,
Diags);
Opts.ConstexprCallDepth = Args.getLastArgIntValue(OPT_fconstexpr_depth, 512,
Diags);
Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing);
- Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy,
+ Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy_EQ,
0, Diags);
Opts.MSBitfields = Args.hasArg(OPT_mms_bitfields);
- Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime);
Opts.ObjCConstantStringClass =
Args.getLastArgValue(OPT_fconstant_string_class);
- Opts.ObjCNonFragileABI = !Args.hasArg(OPT_fobjc_fragile_abi);
- if (Opts.ObjCNonFragileABI)
- Opts.ObjCNonFragileABI2 = true;
Opts.ObjCDefaultSynthProperties =
Args.hasArg(OPT_fobjc_default_synthesize_properties);
Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior);
Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls);
- Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct, 0, Diags);
+ Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct_EQ, 0, Diags);
Opts.PICLevel = Args.getLastArgIntValue(OPT_pic_level, 0, Diags);
Opts.PIELevel = Args.getLastArgIntValue(OPT_pie_level, 0, Diags);
Opts.Static = Args.hasArg(OPT_static_define);
@@ -1924,9 +2096,10 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Opts.Deprecated);
// FIXME: Eliminate this dependency.
- unsigned Opt = getOptimizationLevel(Args, IK, Diags);
+ unsigned Opt = getOptimizationLevel(Args, IK, Diags),
+ OptSize = getOptimizationLevelSize(Args, IK, Diags);
Opts.Optimize = Opt != 0;
- Opts.OptimizeSize = Args.hasArg(OPT_Os) || Args.hasArg(OPT_Oz);
+ Opts.OptimizeSize = OptSize != 0;
// This is the __NO_INLINE__ define, which just depends on things like the
// optimization level and -fno-inline, not actually whether the backend has
@@ -1934,6 +2107,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Opts.NoInlineDefine = !Opt || Args.hasArg(OPT_fno_inline);
Opts.FastMath = Args.hasArg(OPT_ffast_math);
+ Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only);
unsigned SSP = Args.getLastArgIntValue(OPT_stack_protector, 0, Diags);
switch (SSP) {
@@ -1950,7 +2124,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,
FileManager &FileMgr,
DiagnosticsEngine &Diags) {
- using namespace cc1options;
+ using namespace options;
Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch);
Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth);
if (const Arg *A = Args.getLastArg(OPT_token_cache))
@@ -2052,16 +2226,17 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,
static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts,
ArgList &Args) {
- using namespace cc1options;
+ using namespace options;
Opts.ShowCPP = !Args.hasArg(OPT_dM);
Opts.ShowComments = Args.hasArg(OPT_C);
Opts.ShowLineMarkers = !Args.hasArg(OPT_P);
Opts.ShowMacroComments = Args.hasArg(OPT_CC);
Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD);
+ Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes);
}
static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) {
- using namespace cc1options;
+ using namespace options;
Opts.ABI = Args.getLastArgValue(OPT_target_abi);
Opts.CXXABI = Args.getLastArgValue(OPT_cxx_abi);
Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
@@ -2083,7 +2258,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
bool Success = true;
// Parse the arguments.
- OwningPtr<OptTable> Opts(createCC1OptTable());
+ OwningPtr<OptTable> Opts(createDriverOptTable());
unsigned MissingArgIndex, MissingArgCount;
OwningPtr<InputArgList> Args(
Opts->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount));
@@ -2102,6 +2277,15 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
Success = false;
}
+ // Issue errors on arguments that are not valid for CC1.
+ for (ArgList::iterator I = Args->begin(), E = Args->end();
+ I != E; ++I) {
+ if (!(*I)->getOption().isCC1Option()) {
+ Diags.Report(diag::err_drv_unknown_argument) << (*I)->getAsString(*Args);
+ Success = false;
+ }
+ }
+
Success = ParseAnalyzerArgs(Res.getAnalyzerOpts(), *Args, Diags) && Success;
Success = ParseMigratorArgs(Res.getMigratorOpts(), *Args) && Success;
ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), *Args);
diff --git a/lib/Frontend/CreateInvocationFromCommandLine.cpp b/lib/Frontend/CreateInvocationFromCommandLine.cpp
index b477ade..0aca86e 100644
--- a/lib/Frontend/CreateInvocationFromCommandLine.cpp
+++ b/lib/Frontend/CreateInvocationFromCommandLine.cpp
@@ -43,13 +43,17 @@ clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList,
Args.push_back("<clang>"); // FIXME: Remove dummy argument.
Args.insert(Args.end(), ArgList.begin(), ArgList.end());
- // FIXME: Find a cleaner way to force the driver into restricted modes. We
- // also want to force it to use clang.
+ // FIXME: Find a cleaner way to force the driver into restricted modes.
Args.push_back("-fsyntax-only");
// FIXME: We shouldn't have to pass in the path info.
driver::Driver TheDriver("clang", llvm::sys::getDefaultTargetTriple(),
"a.out", false, *Diags);
+ // Force driver to use clang.
+ // FIXME: This seems like a hack. Maybe the "Clang" tool subclass should be
+ // available for using it to get the arguments, thus avoiding the overkill
+ // of using the driver.
+ TheDriver.setForcedClangUse();
// Don't check that inputs exist, they may have been remapped.
TheDriver.setCheckInputsExist(false);
diff --git a/lib/Frontend/DiagnosticRenderer.cpp b/lib/Frontend/DiagnosticRenderer.cpp
index 6c3bb1d..f052f90 100644
--- a/lib/Frontend/DiagnosticRenderer.cpp
+++ b/lib/Frontend/DiagnosticRenderer.cpp
@@ -22,56 +22,6 @@
#include <algorithm>
using namespace clang;
-/// Look through spelling locations for a macro argument expansion, and
-/// if found skip to it so that we can trace the argument rather than the macros
-/// in which that argument is used. If no macro argument expansion is found,
-/// don't skip anything and return the starting location.
-static SourceLocation skipToMacroArgExpansion(const SourceManager &SM,
- SourceLocation StartLoc) {
- for (SourceLocation L = StartLoc; L.isMacroID();
- L = SM.getImmediateSpellingLoc(L)) {
- if (SM.isMacroArgExpansion(L))
- return L;
- }
-
- // Otherwise just return initial location, there's nothing to skip.
- return StartLoc;
-}
-
-/// Gets the location of the immediate macro caller, one level up the stack
-/// toward the initial macro typed into the source.
-static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM,
- SourceLocation Loc) {
- if (!Loc.isMacroID()) return Loc;
-
- // When we have the location of (part of) an expanded parameter, its spelling
- // location points to the argument as typed into the macro call, and
- // therefore is used to locate the macro caller.
- if (SM.isMacroArgExpansion(Loc))
- return SM.getImmediateSpellingLoc(Loc);
-
- // Otherwise, the caller of the macro is located where this macro is
- // expanded (while the spelling is part of the macro definition).
- return SM.getImmediateExpansionRange(Loc).first;
-}
-
-/// Gets the location of the immediate macro callee, one level down the stack
-/// toward the leaf macro.
-static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM,
- SourceLocation Loc) {
- if (!Loc.isMacroID()) return Loc;
-
- // When we have the location of (part of) an expanded parameter, its
- // expansion location points to the unexpanded paramater reference within
- // the macro definition (or callee).
- if (SM.isMacroArgExpansion(Loc))
- return SM.getImmediateExpansionRange(Loc).first;
-
- // Otherwise, the callee of the macro is located where this location was
- // spelled inside the macro definition.
- return SM.getImmediateSpellingLoc(Loc);
-}
-
/// \brief Retrieve the name of the immediate macro expansion.
///
/// This routine starts from a source location, and finds the name of the macro
@@ -109,24 +59,9 @@ static StringRef getImmediateMacroName(SourceLocation Loc,
return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength);
}
-/// Get the presumed location of a diagnostic message. This computes the
-/// presumed location for the top of any macro backtrace when present.
-static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM,
- SourceLocation Loc) {
- // This is a condensed form of the algorithm used by emitCaretDiagnostic to
- // walk to the top of the macro call stack.
- while (Loc.isMacroID()) {
- Loc = skipToMacroArgExpansion(SM, Loc);
- Loc = getImmediateMacroCallerLoc(SM, Loc);
- }
-
- return SM.getPresumedLoc(Loc);
-}
-
-DiagnosticRenderer::DiagnosticRenderer(const SourceManager &SM,
- const LangOptions &LangOpts,
+DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts)
-: SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
+: LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
DiagnosticRenderer::~DiagnosticRenderer() {}
@@ -184,18 +119,23 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc,
StringRef Message,
ArrayRef<CharSourceRange> Ranges,
ArrayRef<FixItHint> FixItHints,
+ const SourceManager *SM,
DiagOrStoredDiag D) {
+ assert(SM || Loc.isInvalid());
beginDiagnostic(D, Level);
- PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Loc);
+ PresumedLoc PLoc;
+ if (Loc.isValid()) {
+ PLoc = SM->getPresumedLocForDisplay(Loc);
- // First, if this diagnostic is not in the main file, print out the
- // "included from" lines.
- emitIncludeStack(PLoc.getIncludeLoc(), Level);
+ // First, if this diagnostic is not in the main file, print out the
+ // "included from" lines.
+ emitIncludeStack(PLoc.getIncludeLoc(), Level, *SM);
+ }
// Next, emit the actual diagnostic message.
- emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D);
+ emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D);
// Only recurse if we have a valid location.
if (Loc.isValid()) {
@@ -205,7 +145,7 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc,
llvm::SmallVector<FixItHint, 8> MergedFixits;
if (!FixItHints.empty()) {
- mergeFixits(FixItHints, SM, LangOpts, MergedFixits);
+ mergeFixits(FixItHints, *SM, LangOpts, MergedFixits);
FixItHints = MergedFixits;
}
@@ -216,7 +156,7 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc,
MutableRanges.push_back(I->RemoveRange);
unsigned MacroDepth = 0;
- emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints,
+ emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints, *SM,
MacroDepth);
}
@@ -230,6 +170,8 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc,
void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) {
emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(),
Diag.getRanges(), Diag.getFixIts(),
+ Diag.getLocation().isValid() ? &Diag.getLocation().getManager()
+ : 0,
&Diag);
}
@@ -245,7 +187,8 @@ void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) {
/// \param Loc The include location of the current file (not the diagnostic
/// location).
void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc,
- DiagnosticsEngine::Level Level) {
+ DiagnosticsEngine::Level Level,
+ const SourceManager &SM) {
// Skip redundant include stacks altogether.
if (LastIncludeLoc == Loc)
return;
@@ -254,12 +197,13 @@ void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc,
if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
return;
- emitIncludeStackRecursively(Loc);
+ emitIncludeStackRecursively(Loc, SM);
}
/// \brief Helper to recursivly walk up the include stack and print each layer
/// on the way back down.
-void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc) {
+void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc,
+ const SourceManager &SM) {
if (Loc.isInvalid())
return;
@@ -268,10 +212,10 @@ void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc) {
return;
// Emit the other include frames first.
- emitIncludeStackRecursively(PLoc.getIncludeLoc());
+ emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM);
// Emit the inclusion text/note.
- emitIncludeLocation(Loc, PLoc);
+ emitIncludeLocation(Loc, PLoc, SM);
}
/// \brief Recursively emit notes for each macro expansion and caret
@@ -292,6 +236,7 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets(
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> Hints,
+ const SourceManager &SM,
unsigned &MacroDepth,
unsigned OnMacroInst)
{
@@ -302,26 +247,26 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets(
if (Loc.isFileID()) {
assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!");
MacroDepth = OnMacroInst;
- emitCodeContext(Loc, Level, Ranges, Hints);
+ emitCodeContext(Loc, Level, Ranges, Hints, SM);
return;
}
// Otherwise recurse through each macro expansion layer.
// When processing macros, skip over the expansions leading up to
// a macro argument, and trace the argument's expansion stack instead.
- Loc = skipToMacroArgExpansion(SM, Loc);
+ Loc = SM.skipToMacroArgExpansion(Loc);
- SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc);
+ SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc);
// FIXME: Map ranges?
- emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, MacroDepth,
+ emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, SM, MacroDepth,
OnMacroInst + 1);
// Save the original location so we can find the spelling of the macro call.
SourceLocation MacroLoc = Loc;
// Map the location.
- Loc = getImmediateMacroCalleeLoc(SM, Loc);
+ Loc = SM.getImmediateMacroCalleeLoc(Loc);
unsigned MacroSkipStart = 0, MacroSkipEnd = 0;
if (MacroDepth > DiagOpts.MacroBacktraceLimit &&
@@ -341,9 +286,9 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets(
I != E; ++I) {
SourceLocation Start = I->getBegin(), End = I->getEnd();
if (Start.isMacroID())
- I->setBegin(getImmediateMacroCalleeLoc(SM, Start));
+ I->setBegin(SM.getImmediateMacroCalleeLoc(Start));
if (End.isMacroID())
- I->setEnd(getImmediateMacroCalleeLoc(SM, End));
+ I->setEnd(SM.getImmediateMacroCalleeLoc(End));
}
if (Suppressed) {
@@ -365,22 +310,22 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets(
<< getImmediateMacroName(MacroLoc, SM, LangOpts) << "'";
emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note,
Message.str(),
- Ranges, ArrayRef<FixItHint>());
+ Ranges, ArrayRef<FixItHint>(), &SM);
}
DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {}
void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc,
- PresumedLoc PLoc) {
+ PresumedLoc PLoc,
+ const SourceManager &SM) {
// Generate a note indicating the include location.
SmallString<200> MessageStorage;
llvm::raw_svector_ostream Message(MessageStorage);
Message << "in file included from " << PLoc.getFilename() << ':'
<< PLoc.getLine() << ":";
- emitNote(Loc, Message.str());
+ emitNote(Loc, Message.str(), &SM);
}
void DiagnosticNoteRenderer::emitBasicNote(StringRef Message) {
- emitNote(SourceLocation(), Message);
+ emitNote(SourceLocation(), Message, 0);
}
-
diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
index da4bdfa..a4321e7 100644
--- a/lib/Frontend/FrontendAction.cpp
+++ b/lib/Frontend/FrontendAction.cpp
@@ -83,31 +83,31 @@ public:
}
};
- /// \brief Checks deserialized declarations and emits error if a name
- /// matches one given in command-line using -error-on-deserialized-decl.
- class DeserializedDeclsChecker : public DelegatingDeserializationListener {
- ASTContext &Ctx;
- std::set<std::string> NamesToCheck;
-
- public:
- DeserializedDeclsChecker(ASTContext &Ctx,
- const std::set<std::string> &NamesToCheck,
- ASTDeserializationListener *Previous)
- : DelegatingDeserializationListener(Previous),
- Ctx(Ctx), NamesToCheck(NamesToCheck) { }
-
- virtual void DeclRead(serialization::DeclID ID, const Decl *D) {
- if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
- if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) {
- unsigned DiagID
- = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error,
- "%0 was deserialized");
- Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID)
- << ND->getNameAsString();
- }
-
- DelegatingDeserializationListener::DeclRead(ID, D);
- }
+/// \brief Checks deserialized declarations and emits error if a name
+/// matches one given in command-line using -error-on-deserialized-decl.
+class DeserializedDeclsChecker : public DelegatingDeserializationListener {
+ ASTContext &Ctx;
+ std::set<std::string> NamesToCheck;
+
+public:
+ DeserializedDeclsChecker(ASTContext &Ctx,
+ const std::set<std::string> &NamesToCheck,
+ ASTDeserializationListener *Previous)
+ : DelegatingDeserializationListener(Previous),
+ Ctx(Ctx), NamesToCheck(NamesToCheck) { }
+
+ virtual void DeclRead(serialization::DeclID ID, const Decl *D) {
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
+ if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) {
+ unsigned DiagID
+ = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error,
+ "%0 was deserialized");
+ Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID)
+ << ND->getNameAsString();
+ }
+
+ DelegatingDeserializationListener::DeclRead(ID, D);
+ }
};
} // end anonymous namespace
@@ -162,6 +162,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
setCurrentInput(Input);
setCompilerInstance(&CI);
+ bool HasBegunSourceFile = false;
if (!BeginInvocation(CI))
goto failure;
@@ -214,6 +215,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
// Inform the diagnostic client we are processing a source file.
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0);
+ HasBegunSourceFile = true;
// Initialize the action.
if (!BeginSourceFileAction(CI, Input.File))
@@ -228,6 +230,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
// Inform the diagnostic client we are processing a source file.
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
&CI.getPreprocessor());
+ HasBegunSourceFile = true;
// Initialize the action.
if (!BeginSourceFileAction(CI, Input.File))
@@ -309,13 +312,14 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
CI.setFileManager(0);
}
- CI.getDiagnosticClient().EndSourceFile();
+ if (HasBegunSourceFile)
+ CI.getDiagnosticClient().EndSourceFile();
setCurrentInput(FrontendInputFile());
setCompilerInstance(0);
return false;
}
-void FrontendAction::Execute() {
+bool FrontendAction::Execute() {
CompilerInstance &CI = getCompilerInstance();
// Initialize the main file entry. This needs to be delayed until after PCH
@@ -325,7 +329,7 @@ void FrontendAction::Execute() {
getCurrentInput().IsSystem
? SrcMgr::C_System
: SrcMgr::C_User))
- return;
+ return false;
}
if (CI.hasFrontendTimer()) {
@@ -333,6 +337,8 @@ void FrontendAction::Execute() {
ExecuteAction();
}
else ExecuteAction();
+
+ return true;
}
void FrontendAction::EndSourceFile() {
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index 737ee4a..24960cf 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -47,13 +47,18 @@ void InitOnlyAction::ExecuteAction() {
ASTConsumer *ASTPrintAction::CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) {
if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile))
- return CreateASTPrinter(OS);
+ return CreateASTPrinter(OS, CI.getFrontendOpts().ASTDumpFilter);
return 0;
}
ASTConsumer *ASTDumpAction::CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) {
- return CreateASTDumper();
+ return CreateASTDumper(CI.getFrontendOpts().ASTDumpFilter);
+}
+
+ASTConsumer *ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ return CreateASTDeclNodeLister();
}
ASTConsumer *ASTDumpXMLAction::CreateASTConsumer(CompilerInstance &CI,
@@ -131,7 +136,7 @@ ASTConsumer *GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
///
/// \param Module The module we're collecting includes from.
///
-/// \param Includes Will be augmented with the set of #includes or #imports
+/// \param Includes Will be augmented with the set of \#includes or \#imports
/// needed to load all of the named headers.
static void collectModuleHeaderIncludes(const LangOptions &LangOpts,
FileManager &FileMgr,
diff --git a/lib/Frontend/InitHeaderSearch.cpp b/lib/Frontend/InitHeaderSearch.cpp
index 3f7e682..8178f7a 100644
--- a/lib/Frontend/InitHeaderSearch.cpp
+++ b/lib/Frontend/InitHeaderSearch.cpp
@@ -40,6 +40,7 @@ class InitHeaderSearch {
std::vector<std::pair<IncludeDirGroup, DirectoryLookup> > IncludePath;
typedef std::vector<std::pair<IncludeDirGroup,
DirectoryLookup> >::const_iterator path_iterator;
+ std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes;
HeaderSearch &Headers;
bool Verbose;
std::string IncludeSysroot;
@@ -57,6 +58,12 @@ public:
bool isCXXAware, bool isUserSupplied,
bool isFramework, bool IgnoreSysRoot = false);
+ /// AddSystemHeaderPrefix - Add the specified prefix to the system header
+ /// prefix list.
+ void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) {
+ SystemHeaderPrefixes.push_back(std::make_pair(Prefix, IsSystemHeader));
+ }
+
/// AddGnuCPlusPlusIncludePaths - Add the necessary paths to support a gnu
/// libstdc++.
void AddGnuCPlusPlusIncludePaths(StringRef Base,
@@ -210,6 +217,8 @@ void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple,
switch (os) {
case llvm::Triple::FreeBSD:
case llvm::Triple::NetBSD:
+ case llvm::Triple::OpenBSD:
+ case llvm::Triple::Bitrig:
break;
default:
// FIXME: temporary hack: hard-coded paths.
@@ -623,6 +632,8 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
bool DontSearchCurDir = false; // TODO: set to true if -I- is set?
Headers.SetSearchPaths(SearchList, NumQuoted, NumAngled, DontSearchCurDir);
+ Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);
+
// If verbose, print the list of directories that will be searched.
if (Verbose) {
llvm::errs() << "#include \"...\" search starts here:\n";
@@ -660,6 +671,10 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,
Init.AddDefaultIncludePaths(Lang, Triple, HSOpts);
+ for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i)
+ Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix,
+ HSOpts.SystemHeaderPrefixes[i].IsSystemHeader);
+
if (HSOpts.UseBuiltinIncludes) {
// Set up the builtin include directory in the module map.
llvm::sys::Path P(HSOpts.ResourceDir);
diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp
index 93d49b0..1440da6 100644
--- a/lib/Frontend/InitPreprocessor.cpp
+++ b/lib/Frontend/InitPreprocessor.cpp
@@ -49,7 +49,7 @@ static void DefineBuiltinMacro(MacroBuilder &Builder, StringRef Macro,
}
}
-/// AddImplicitInclude - Add an implicit #include of the specified file to the
+/// AddImplicitInclude - Add an implicit \#include of the specified file to the
/// predefines buffer.
static void AddImplicitInclude(MacroBuilder &Builder, StringRef File,
FileManager &FileMgr) {
@@ -66,8 +66,8 @@ static void AddImplicitIncludeMacros(MacroBuilder &Builder,
Builder.append("##"); // ##?
}
-/// AddImplicitIncludePTH - Add an implicit #include using the original file
-/// used to generate a PTH cache.
+/// AddImplicitIncludePTH - Add an implicit \#include using the original file
+/// used to generate a PTH cache.
static void AddImplicitIncludePTH(MacroBuilder &Builder, Preprocessor &PP,
StringRef ImplicitIncludePTH) {
PTHManager *P = PP.getPTHManager();
@@ -288,20 +288,16 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
else if (!LangOpts.GNUMode && LangOpts.Digraphs)
Builder.defineMacro("__STDC_VERSION__", "199409L");
} else {
- if (LangOpts.GNUMode)
- Builder.defineMacro("__cplusplus");
- else {
- // C++0x [cpp.predefined]p1:
- // The name_ _cplusplus is defined to the value 201103L when compiling a
- // C++ translation unit.
- if (LangOpts.CPlusPlus0x)
- Builder.defineMacro("__cplusplus", "201103L");
- // C++03 [cpp.predefined]p1:
- // The name_ _cplusplus is defined to the value 199711L when compiling a
- // C++ translation unit.
- else
- Builder.defineMacro("__cplusplus", "199711L");
- }
+ // C++11 [cpp.predefined]p1:
+ // The name __cplusplus is defined to the value 201103L when compiling a
+ // C++ translation unit.
+ if (LangOpts.CPlusPlus0x)
+ Builder.defineMacro("__cplusplus", "201103L");
+ // C++03 [cpp.predefined]p1:
+ // The name __cplusplus is defined to the value 199711L when compiling a
+ // C++ translation unit.
+ else
+ Builder.defineMacro("__cplusplus", "199711L");
}
if (LangOpts.ObjC1)
@@ -369,7 +365,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__");
if (LangOpts.ObjC1) {
- if (LangOpts.ObjCNonFragileABI) {
+ if (LangOpts.ObjCRuntime.isNonFragile()) {
Builder.defineMacro("__OBJC2__");
if (LangOpts.ObjCExceptions)
@@ -379,8 +375,13 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (LangOpts.getGC() != LangOptions::NonGC)
Builder.defineMacro("__OBJC_GC__");
- if (LangOpts.NeXTRuntime)
+ if (LangOpts.ObjCRuntime.isNeXTFamily())
Builder.defineMacro("__NEXT_RUNTIME__");
+
+ Builder.defineMacro("IBOutlet", "__attribute__((iboutlet))");
+ Builder.defineMacro("IBOutletCollection(ClassName)",
+ "__attribute__((iboutletcollection(ClassName)))");
+ Builder.defineMacro("IBAction", "void)__attribute__((ibaction)");
}
// darwin_constant_cfstrings controls this. This is also dependent
@@ -444,6 +445,26 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
// Initialize target-specific preprocessor defines.
+ // __BYTE_ORDER__ was added in GCC 4.6. It's analogous
+ // to the macro __BYTE_ORDER (no trailing underscores)
+ // from glibc's <endian.h> header.
+ // We don't support the PDP-11 as a target, but include
+ // the define so it can still be compared against.
+ Builder.defineMacro("__ORDER_LITTLE_ENDIAN__", "1234");
+ Builder.defineMacro("__ORDER_BIG_ENDIAN__", "4321");
+ Builder.defineMacro("__ORDER_PDP_ENDIAN__", "3412");
+ if (TI.isBigEndian())
+ Builder.defineMacro("__BYTE_ORDER__", "__ORDER_BIG_ENDIAN__");
+ else
+ Builder.defineMacro("__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__");
+
+
+ if (TI.getPointerWidth(0) == 64 && TI.getLongWidth() == 64
+ && TI.getIntWidth() == 32) {
+ Builder.defineMacro("_LP64");
+ Builder.defineMacro("__LP64__");
+ }
+
// Define type sizing macros based on the target properties.
assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far");
Builder.defineMacro("__CHAR_BIT__", "8");
@@ -501,6 +522,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (!LangOpts.CharIsSigned)
Builder.defineMacro("__CHAR_UNSIGNED__");
+ if (!TargetInfo::isTypeSigned(TI.getWCharType()))
+ Builder.defineMacro("__WCHAR_UNSIGNED__");
+
if (!TargetInfo::isTypeSigned(TI.getWIntType()))
Builder.defineMacro("__WINT_UNSIGNED__");
@@ -520,15 +544,13 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (TI.getLongLongWidth() > TI.getLongWidth())
DefineExactWidthIntType(TargetInfo::SignedLongLong, TI, Builder);
- // Add __builtin_va_list typedef.
- Builder.append(TI.getVAListDeclaration());
-
if (const char *Prefix = TI.getUserLabelPrefix())
Builder.defineMacro("__USER_LABEL_PREFIX__", Prefix);
- // Build configuration options. FIXME: these should be controlled by
- // command line options or something.
- Builder.defineMacro("__FINITE_MATH_ONLY__", "0");
+ if (LangOpts.FastMath || LangOpts.FiniteMathOnly)
+ Builder.defineMacro("__FINITE_MATH_ONLY__", "1");
+ else
+ Builder.defineMacro("__FINITE_MATH_ONLY__", "0");
if (LangOpts.GNUInline)
Builder.defineMacro("__GNUC_GNU_INLINE__");
diff --git a/lib/Frontend/LayoutOverrideSource.cpp b/lib/Frontend/LayoutOverrideSource.cpp
index eb7865e..e023250 100644
--- a/lib/Frontend/LayoutOverrideSource.cpp
+++ b/lib/Frontend/LayoutOverrideSource.cpp
@@ -9,6 +9,7 @@
#include "clang/Frontend/LayoutOverrideSource.h"
#include "clang/AST/Decl.h"
#include "llvm/Support/raw_ostream.h"
+#include <cctype>
#include <fstream>
#include <string>
diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp
index 9e1587c..5311ed5 100644
--- a/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -26,6 +26,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cctype>
#include <cstdio>
using namespace clang;
@@ -87,7 +88,7 @@ private:
unsigned CurLine;
bool EmittedTokensOnThisLine;
- bool EmittedMacroOnThisLine;
+ bool EmittedDirectiveOnThisLine;
SrcMgr::CharacteristicKind FileType;
SmallString<512> CurFilename;
bool Initialized;
@@ -103,7 +104,7 @@ public:
CurLine = 0;
CurFilename += "<uninit>";
EmittedTokensOnThisLine = false;
- EmittedMacroOnThisLine = false;
+ EmittedDirectiveOnThisLine = false;
FileType = SrcMgr::C_User;
Initialized = false;
@@ -111,10 +112,15 @@ public:
UseLineDirective = PP.getLangOpts().MicrosoftExt;
}
- void SetEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; }
+ void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; }
bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; }
- bool StartNewLineIfNeeded();
+ void setEmittedDirectiveOnThisLine() { EmittedDirectiveOnThisLine = true; }
+ bool hasEmittedDirectiveOnThisLine() const {
+ return EmittedDirectiveOnThisLine;
+ }
+
+ bool startNewLineIfNeeded(bool ShouldUpdateCurrentLine = true);
virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
@@ -158,11 +164,7 @@ public:
void PrintPPOutputPPCallbacks::WriteLineInfo(unsigned LineNo,
const char *Extra,
unsigned ExtraLen) {
- if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) {
- OS << '\n';
- EmittedTokensOnThisLine = false;
- EmittedMacroOnThisLine = false;
- }
+ startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false);
// Emit #line directives or GNU line markers depending on what mode we're in.
if (UseLineDirective) {
@@ -207,23 +209,21 @@ bool PrintPPOutputPPCallbacks::MoveToLine(unsigned LineNo) {
} else {
// Okay, we're in -P mode, which turns off line markers. However, we still
// need to emit a newline between tokens on different lines.
- if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) {
- OS << '\n';
- EmittedTokensOnThisLine = false;
- EmittedMacroOnThisLine = false;
- }
+ startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false);
}
CurLine = LineNo;
return true;
}
-bool PrintPPOutputPPCallbacks::StartNewLineIfNeeded() {
- if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) {
+bool
+PrintPPOutputPPCallbacks::startNewLineIfNeeded(bool ShouldUpdateCurrentLine) {
+ if (EmittedTokensOnThisLine || EmittedDirectiveOnThisLine) {
OS << '\n';
EmittedTokensOnThisLine = false;
- EmittedMacroOnThisLine = false;
- ++CurLine;
+ EmittedDirectiveOnThisLine = false;
+ if (ShouldUpdateCurrentLine)
+ ++CurLine;
return true;
}
@@ -307,7 +307,7 @@ void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok,
MoveToLine(MI->getDefinitionLoc());
PrintMacroDefinition(*MacroNameTok.getIdentifierInfo(), *MI, PP, OS);
- EmittedMacroOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok,
@@ -317,12 +317,13 @@ void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok,
MoveToLine(MacroNameTok.getLocation());
OS << "#undef " << MacroNameTok.getIdentifierInfo()->getName();
- EmittedMacroOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc,
const IdentifierInfo *Kind,
const std::string &Str) {
+ startNewLineIfNeeded();
MoveToLine(Loc);
OS << "#pragma comment(" << Kind->getName();
@@ -343,11 +344,12 @@ void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc,
}
OS << ')';
- EmittedTokensOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc,
StringRef Str) {
+ startNewLineIfNeeded();
MoveToLine(Loc);
OS << "#pragma message(";
@@ -366,26 +368,29 @@ void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc,
OS << '"';
OS << ')';
- EmittedTokensOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
void PrintPPOutputPPCallbacks::
PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) {
+ startNewLineIfNeeded();
MoveToLine(Loc);
OS << "#pragma " << Namespace << " diagnostic push";
- EmittedTokensOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
void PrintPPOutputPPCallbacks::
PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) {
+ startNewLineIfNeeded();
MoveToLine(Loc);
OS << "#pragma " << Namespace << " diagnostic pop";
- EmittedTokensOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
void PrintPPOutputPPCallbacks::
PragmaDiagnostic(SourceLocation Loc, StringRef Namespace,
diag::Mapping Map, StringRef Str) {
+ startNewLineIfNeeded();
MoveToLine(Loc);
OS << "#pragma " << Namespace << " diagnostic ";
switch (Map) {
@@ -403,7 +408,7 @@ PragmaDiagnostic(SourceLocation Loc, StringRef Namespace,
break;
}
OS << " \"" << Str << '"';
- EmittedTokensOnThisLine = true;
+ setEmittedDirectiveOnThisLine();
}
/// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this
@@ -471,10 +476,9 @@ struct UnknownPragmaHandler : public PragmaHandler {
Token &PragmaTok) {
// Figure out what line we went to and insert the appropriate number of
// newline characters.
- Callbacks->StartNewLineIfNeeded();
+ Callbacks->startNewLineIfNeeded();
Callbacks->MoveToLine(PragmaTok.getLocation());
Callbacks->OS.write(Prefix, strlen(Prefix));
- Callbacks->SetEmittedTokensOnThisLine();
// Read and print all of the pragma tokens.
while (PragmaTok.isNot(tok::eod)) {
if (PragmaTok.hasLeadingSpace())
@@ -483,7 +487,7 @@ struct UnknownPragmaHandler : public PragmaHandler {
Callbacks->OS.write(&TokSpell[0], TokSpell.size());
PP.LexUnexpandedToken(PragmaTok);
}
- Callbacks->StartNewLineIfNeeded();
+ Callbacks->setEmittedDirectiveOnThisLine();
}
};
} // end anonymous namespace
@@ -497,6 +501,10 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
PrevPrevTok.startToken();
PrevTok.startToken();
while (1) {
+ if (Callbacks->hasEmittedDirectiveOnThisLine()) {
+ Callbacks->startNewLineIfNeeded();
+ Callbacks->MoveToLine(Tok.getLocation());
+ }
// If this token is at the start of a line, emit newlines if needed.
if (Tok.isAtStartOfLine() && Callbacks->HandleFirstTokOnLine(Tok)) {
@@ -533,7 +541,7 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
if (Tok.getKind() == tok::comment)
Callbacks->HandleNewlinesInToken(&S[0], S.size());
}
- Callbacks->SetEmittedTokensOnThisLine();
+ Callbacks->setEmittedTokensOnThisLine();
if (Tok.is(tok::eof)) break;
diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp
index 7bf8742..a20f30d 100644
--- a/lib/Frontend/SerializedDiagnosticPrinter.cpp
+++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp
@@ -53,10 +53,9 @@ class SDiagsRenderer : public DiagnosticNoteRenderer {
RecordData &Record;
public:
SDiagsRenderer(SDiagsWriter &Writer, RecordData &Record,
- const SourceManager &SM,
const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts)
- : DiagnosticNoteRenderer(SM, LangOpts, DiagOpts),
+ : DiagnosticNoteRenderer(LangOpts, DiagOpts),
Writer(Writer), Record(Record){}
virtual ~SDiagsRenderer() {}
@@ -67,18 +66,21 @@ protected:
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag D);
virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges) {}
+ ArrayRef<CharSourceRange> Ranges,
+ const SourceManager &SM) {}
- void emitNote(SourceLocation Loc, StringRef Message);
+ void emitNote(SourceLocation Loc, StringRef Message, const SourceManager *SM);
virtual void emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints);
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM);
virtual void beginDiagnostic(DiagOrStoredDiag D,
DiagnosticsEngine::Level Level);
@@ -137,15 +139,16 @@ private:
unsigned getEmitFile(const char *Filename);
/// \brief Add SourceLocation information the specified record.
- void AddLocToRecord(SourceLocation Loc, const SourceManager &SM,
+ void AddLocToRecord(SourceLocation Loc, const SourceManager *SM,
PresumedLoc PLoc, RecordDataImpl &Record,
unsigned TokSize = 0);
/// \brief Add SourceLocation information the specified record.
void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record,
- const SourceManager &SM,
+ const SourceManager *SM,
unsigned TokSize = 0) {
- AddLocToRecord(Loc, SM, SM.getPresumedLoc(Loc), Record, TokSize);
+ AddLocToRecord(Loc, SM, SM ? SM->getPresumedLoc(Loc) : PresumedLoc(),
+ Record, TokSize);
}
/// \brief Add CharSourceRange information the specified record.
@@ -241,7 +244,7 @@ static void EmitRecordID(unsigned ID, const char *Name,
}
void SDiagsWriter::AddLocToRecord(SourceLocation Loc,
- const SourceManager &SM,
+ const SourceManager *SM,
PresumedLoc PLoc,
RecordDataImpl &Record,
unsigned TokSize) {
@@ -257,19 +260,19 @@ void SDiagsWriter::AddLocToRecord(SourceLocation Loc,
Record.push_back(getEmitFile(PLoc.getFilename()));
Record.push_back(PLoc.getLine());
Record.push_back(PLoc.getColumn()+TokSize);
- Record.push_back(SM.getFileOffset(Loc));
+ Record.push_back(SM->getFileOffset(Loc));
}
void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range,
RecordDataImpl &Record,
const SourceManager &SM) {
- AddLocToRecord(Range.getBegin(), Record, SM);
+ AddLocToRecord(Range.getBegin(), Record, &SM);
unsigned TokSize = 0;
if (Range.isTokenRange())
TokSize = Lexer::MeasureTokenLength(Range.getEnd(),
SM, *LangOpts);
- AddLocToRecord(Range.getEnd(), Record, SM, TokSize);
+ AddLocToRecord(Range.getEnd(), Record, &SM, TokSize);
}
unsigned SDiagsWriter::getEmitFile(const char *FileName){
@@ -484,13 +487,15 @@ void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
diagBuf.clear();
Info.FormatDiagnostic(diagBuf);
- SourceManager &SM = Info.getSourceManager();
- SDiagsRenderer Renderer(*this, Record, SM, *LangOpts, DiagOpts);
+ const SourceManager *
+ SM = Info.hasSourceManager() ? &Info.getSourceManager() : 0;
+ SDiagsRenderer Renderer(*this, Record, *LangOpts, DiagOpts);
Renderer.emitDiagnostic(Info.getLocation(), DiagLevel,
diagBuf.str(),
Info.getRanges(),
llvm::makeArrayRef(Info.getFixItHints(),
Info.getNumFixItHints()),
+ SM,
&Info);
}
@@ -500,6 +505,7 @@ SDiagsRenderer::emitDiagnosticMessage(SourceLocation Loc,
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<clang::CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag D) {
// Emit the RECORD_DIAG record.
Writer.Record.clear();
@@ -539,7 +545,8 @@ void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D,
void SDiagsRenderer::emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange> &Ranges,
- ArrayRef<FixItHint> Hints) {
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
// Emit Source Ranges.
for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end();
it != ei; ++it) {
@@ -562,7 +569,8 @@ void SDiagsRenderer::emitCodeContext(SourceLocation Loc,
}
}
-void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message) {
+void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message,
+ const SourceManager *SM) {
Writer.Stream.EnterSubblock(BLOCK_DIAG, 4);
RecordData Record;
Record.push_back(RECORD_DIAG);
diff --git a/lib/Frontend/TextDiagnostic.cpp b/lib/Frontend/TextDiagnostic.cpp
index 65fb1ae..9bb3e1d 100644
--- a/lib/Frontend/TextDiagnostic.cpp
+++ b/lib/Frontend/TextDiagnostic.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include <algorithm>
+#include <cctype>
using namespace clang;
@@ -31,16 +32,36 @@ static const enum raw_ostream::Colors caretColor =
raw_ostream::GREEN;
static const enum raw_ostream::Colors warningColor =
raw_ostream::MAGENTA;
+static const enum raw_ostream::Colors templateColor =
+ raw_ostream::CYAN;
static const enum raw_ostream::Colors errorColor = raw_ostream::RED;
static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;
// Used for changing only the bold attribute.
static const enum raw_ostream::Colors savedColor =
raw_ostream::SAVEDCOLOR;
+/// \brief Add highlights to differences in template strings.
+static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
+ bool &Normal, bool Bold) {
+ for (unsigned i = 0, e = Str.size(); i < e; ++i)
+ if (Str[i] != ToggleHighlight) {
+ OS << Str[i];
+ } else {
+ if (Normal)
+ OS.changeColor(templateColor, true);
+ else {
+ OS.resetColor();
+ if (Bold)
+ OS.changeColor(savedColor, true);
+ }
+ Normal = !Normal;
+ }
+}
+
/// \brief Number of spaces to indent when word-wrapping.
const unsigned WordWrapIndentation = 6;
-int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
+static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
int bytes = 0;
while (0<i) {
if (SourceLine[--i]=='\t')
@@ -69,7 +90,7 @@ int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
/// \param TabStop used to expand tabs
/// \return pair(printable text, 'true' iff original text was printable)
///
-std::pair<SmallString<16>,bool>
+static std::pair<SmallString<16>, bool>
printableTextForNextCharacter(StringRef SourceLine, size_t *i,
unsigned TabStop) {
assert(i && "i must not be null");
@@ -146,7 +167,7 @@ printableTextForNextCharacter(StringRef SourceLine, size_t *i,
return std::make_pair(expandedByte, false);
}
-void expandTabs(std::string &SourceLine, unsigned TabStop) {
+static void expandTabs(std::string &SourceLine, unsigned TabStop) {
size_t i = SourceLine.size();
while (i>0) {
i--;
@@ -164,7 +185,7 @@ void expandTabs(std::string &SourceLine, unsigned TabStop) {
/// characters will appear at (numbering the first column as 0).
///
/// If a byte 'i' corresponds to muliple columns (e.g. the byte contains a tab
-/// character) then the the array will map that byte to the first column the
+/// character) then the array will map that byte to the first column the
/// tab appears at and the next value in the map will have been incremented
/// more than once.
///
@@ -179,9 +200,9 @@ void expandTabs(std::string &SourceLine, unsigned TabStop) {
///
/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11}
///
-/// (\u3042 is represented in UTF-8 by three bytes and takes two columns to
+/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
/// display)
-void byteToColumn(StringRef SourceLine, unsigned TabStop,
+static void byteToColumn(StringRef SourceLine, unsigned TabStop,
SmallVectorImpl<int> &out) {
out.clear();
@@ -213,9 +234,9 @@ void byteToColumn(StringRef SourceLine, unsigned TabStop,
///
/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7}
///
-/// (\u3042 is represented in UTF-8 by three bytes and takes two columns to
+/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
/// display)
-void columnToByte(StringRef SourceLine, unsigned TabStop,
+static void columnToByte(StringRef SourceLine, unsigned TabStop,
SmallVectorImpl<int> &out) {
out.clear();
@@ -307,11 +328,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
// correctly.
unsigned CaretStart = 0, CaretEnd = CaretLine.size();
for (; CaretStart != CaretEnd; ++CaretStart)
- if (!isspace(CaretLine[CaretStart]))
+ if (!isspace(static_cast<unsigned char>(CaretLine[CaretStart])))
break;
for (; CaretEnd != CaretStart; --CaretEnd)
- if (!isspace(CaretLine[CaretEnd - 1]))
+ if (!isspace(static_cast<unsigned char>(CaretLine[CaretEnd - 1])))
break;
// caret has already been inserted into CaretLine so the above whitespace
@@ -322,17 +343,33 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
if (!FixItInsertionLine.empty()) {
unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
for (; FixItStart != FixItEnd; ++FixItStart)
- if (!isspace(FixItInsertionLine[FixItStart]))
+ if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItStart])))
break;
for (; FixItEnd != FixItStart; --FixItEnd)
- if (!isspace(FixItInsertionLine[FixItEnd - 1]))
+ if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItEnd - 1])))
break;
CaretStart = std::min(FixItStart, CaretStart);
CaretEnd = std::max(FixItEnd, CaretEnd);
}
+ // CaretEnd may have been set at the middle of a character
+ // If it's not at a character's first column then advance it past the current
+ // character.
+ while (static_cast<int>(CaretEnd) < map.columns() &&
+ -1 == map.columnToByte(CaretEnd))
+ ++CaretEnd;
+
+ assert((static_cast<int>(CaretStart) > map.columns() ||
+ -1!=map.columnToByte(CaretStart)) &&
+ "CaretStart must not point to a column in the middle of a source"
+ " line character");
+ assert((static_cast<int>(CaretEnd) > map.columns() ||
+ -1!=map.columnToByte(CaretEnd)) &&
+ "CaretEnd must not point to a column in the middle of a source line"
+ " character");
+
// CaretLine[CaretStart, CaretEnd) contains all of the interesting
// parts of the caret line. While this slice is smaller than the
// number of columns we have, try to grow the slice to encompass
@@ -366,12 +403,14 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
// Skip over any whitespace we see here; we're looking for
// another bit of interesting text.
while (NewStart &&
- (map.byteToColumn(NewStart)==-1 || isspace(SourceLine[NewStart])))
+ (map.byteToColumn(NewStart)==-1 ||
+ isspace(static_cast<unsigned char>(SourceLine[NewStart]))))
--NewStart;
// Skip over this bit of "interesting" text.
while (NewStart &&
- (map.byteToColumn(NewStart)!=-1 && !isspace(SourceLine[NewStart])))
+ (map.byteToColumn(NewStart)!=-1 &&
+ !isspace(static_cast<unsigned char>(SourceLine[NewStart]))))
--NewStart;
// Move up to the non-whitespace character we just saw.
@@ -392,12 +431,14 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
// Skip over any whitespace we see here; we're looking for
// another bit of interesting text.
while (NewEnd<SourceLine.size() &&
- (map.byteToColumn(NewEnd)==-1 || isspace(SourceLine[NewEnd])))
+ (map.byteToColumn(NewEnd)==-1 ||
+ isspace(static_cast<unsigned char>(SourceLine[NewEnd]))))
++NewEnd;
// Skip over this bit of "interesting" text.
while (NewEnd<SourceLine.size() &&
- (map.byteToColumn(NewEnd)!=-1 && !isspace(SourceLine[NewEnd])))
+ (map.byteToColumn(NewEnd)!=-1 &&
+ !isspace(static_cast<unsigned char>(SourceLine[NewEnd]))))
++NewEnd;
unsigned NewColumns = map.byteToColumn(NewEnd) -
@@ -549,6 +590,7 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str,
/// \param Column the column number at which the first character of \p
/// Str will be printed. This will be non-zero when part of the first
/// line has already been printed.
+/// \param Bold if the current text should be bold
/// \param Indentation the number of spaces to indent any lines beyond
/// the first line.
/// \returns true if word-wrapping was required, or false if the
@@ -556,8 +598,10 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str,
static bool printWordWrapped(raw_ostream &OS, StringRef Str,
unsigned Columns,
unsigned Column = 0,
+ bool Bold = false,
unsigned Indentation = WordWrapIndentation) {
const unsigned Length = std::min(Str.find('\n'), Str.size());
+ bool TextNormal = true;
// The string used to indent each line.
SmallString<16> IndentStr;
@@ -581,7 +625,8 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str,
OS << ' ';
Column += 1;
}
- OS << Str.substr(WordStart, WordLength);
+ applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
+ TextNormal, Bold);
Column += WordLength;
continue;
}
@@ -590,22 +635,24 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str,
// line.
OS << '\n';
OS.write(&IndentStr[0], Indentation);
- OS << Str.substr(WordStart, WordLength);
+ applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
+ TextNormal, Bold);
Column = Indentation + WordLength;
Wrapped = true;
}
// Append any remaning text from the message with its existing formatting.
- OS << Str.substr(Length);
+ applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold);
+
+ assert(TextNormal && "Text highlighted at end of diagnostic message.");
return Wrapped;
}
TextDiagnostic::TextDiagnostic(raw_ostream &OS,
- const SourceManager &SM,
const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts)
- : DiagnosticRenderer(SM, LangOpts, DiagOpts), OS(OS) {}
+ : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {}
TextDiagnostic::~TextDiagnostic() {}
@@ -615,11 +662,13 @@ TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc,
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<clang::CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag D) {
uint64_t StartOfLocationInfo = OS.tell();
// Emit the location of this particular diagnostic.
- emitDiagnosticLoc(Loc, PLoc, Level, Ranges);
+ if (Loc.isValid())
+ emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM);
if (DiagOpts.ShowColors)
OS.resetColor();
@@ -665,20 +714,27 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
StringRef Message,
unsigned CurrentColumn, unsigned Columns,
bool ShowColors) {
+ bool Bold = false;
if (ShowColors) {
// Print warnings, errors and fatal errors in bold, no color
switch (Level) {
- case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break;
- case DiagnosticsEngine::Error: OS.changeColor(savedColor, true); break;
- case DiagnosticsEngine::Fatal: OS.changeColor(savedColor, true); break;
+ case DiagnosticsEngine::Warning:
+ case DiagnosticsEngine::Error:
+ case DiagnosticsEngine::Fatal:
+ OS.changeColor(savedColor, true);
+ Bold = true;
+ break;
default: break; //don't bold notes
}
}
if (Columns)
- printWordWrapped(OS, Message, Columns, CurrentColumn);
- else
- OS << Message;
+ printWordWrapped(OS, Message, Columns, CurrentColumn, Bold);
+ else {
+ bool Normal = true;
+ applyTemplateHighlighting(OS, Message, Normal, Bold);
+ assert(Normal && "Formatting should have returned to normal");
+ }
if (ShowColors)
OS.resetColor();
@@ -693,7 +749,8 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
/// ranges necessary.
void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges) {
+ ArrayRef<CharSourceRange> Ranges,
+ const SourceManager &SM) {
if (PLoc.isInvalid()) {
// At least print the file name if available:
FileID FID = SM.getFileID(Loc);
@@ -799,7 +856,8 @@ void TextDiagnostic::emitBasicNote(StringRef Message) {
}
void TextDiagnostic::emitIncludeLocation(SourceLocation Loc,
- PresumedLoc PLoc) {
+ PresumedLoc PLoc,
+ const SourceManager &SM) {
if (DiagOpts.ShowLocation)
OS << "In file included from " << PLoc.getFilename() << ':'
<< PLoc.getLine() << ":\n";
@@ -817,7 +875,8 @@ void TextDiagnostic::emitIncludeLocation(SourceLocation Loc,
void TextDiagnostic::emitSnippetAndCaret(
SourceLocation Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints) {
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
assert(!Loc.isInvalid() && "must have a valid source location here");
assert(Loc.isFileID() && "must have a file location here");
@@ -840,17 +899,12 @@ void TextDiagnostic::emitSnippetAndCaret(
// Get information about the buffer it points into.
bool Invalid = false;
- StringRef BufData = SM.getBufferData(FID, &Invalid);
+ const char *BufStart = SM.getBufferData(FID, &Invalid).data();
if (Invalid)
return;
- const char *BufStart = BufData.data();
- const char *BufEnd = BufStart + BufData.size();
-
unsigned LineNo = SM.getLineNumber(FID, FileOffset);
unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
- unsigned CaretEndColNo
- = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts);
// Rewind from the current position to the start of the line.
const char *TokPtr = BufStart+FileOffset;
@@ -860,14 +914,9 @@ void TextDiagnostic::emitSnippetAndCaret(
// Compute the line end. Scan forward from the error position to the end of
// the line.
const char *LineEnd = TokPtr;
- while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd!=BufEnd)
+ while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0')
++LineEnd;
- // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past
- // the source line length as currently being computed. See
- // test/Misc/message-length.c.
- CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart));
-
// Copy the line of code into an std::string for ease of manipulation.
std::string SourceLine(LineStart, LineEnd);
@@ -881,7 +930,7 @@ void TextDiagnostic::emitSnippetAndCaret(
for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
E = Ranges.end();
I != E; ++I)
- highlightRange(*I, LineNo, FID, sourceColMap, CaretLine);
+ highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM);
// Next, insert the caret itself.
ColNo = sourceColMap.byteToColumn(ColNo-1);
@@ -891,7 +940,7 @@ void TextDiagnostic::emitSnippetAndCaret(
std::string FixItInsertionLine = buildFixItInsertionLine(LineNo,
sourceColMap,
- Hints);
+ Hints, SM);
// If the source line is too long for our terminal, select only the
// "interesting" source region within that line.
@@ -934,11 +983,10 @@ void TextDiagnostic::emitSnippetAndCaret(
}
// Print out any parseable fixit information requested by the options.
- emitParseableFixits(Hints);
+ emitParseableFixits(Hints, SM);
}
-void TextDiagnostic::emitSnippet(StringRef line)
-{
+void TextDiagnostic::emitSnippet(StringRef line) {
if (line.empty())
return;
@@ -952,8 +1000,7 @@ void TextDiagnostic::emitSnippet(StringRef line)
= printableTextForNextCharacter(line, &i, DiagOpts.TabStop);
bool was_printable = res.second;
- if (DiagOpts.ShowColors
- && was_printable==print_reversed) {
+ if (DiagOpts.ShowColors && was_printable == print_reversed) {
if (print_reversed)
OS.reverseColor();
OS << to_print;
@@ -979,7 +1026,8 @@ void TextDiagnostic::emitSnippet(StringRef line)
void TextDiagnostic::highlightRange(const CharSourceRange &R,
unsigned LineNo, FileID FID,
const SourceColumnMap &map,
- std::string &CaretLine) {
+ std::string &CaretLine,
+ const SourceManager &SM) {
if (!R.isValid()) return;
SourceLocation Begin = SM.getExpansionLoc(R.getBegin());
@@ -1064,49 +1112,63 @@ void TextDiagnostic::highlightRange(const CharSourceRange &R,
std::string TextDiagnostic::buildFixItInsertionLine(
unsigned LineNo,
const SourceColumnMap &map,
- ArrayRef<FixItHint> Hints) {
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
std::string FixItInsertionLine;
if (Hints.empty() || !DiagOpts.ShowFixits)
return FixItInsertionLine;
+ unsigned PrevHintEndCol = 0;
for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
I != E; ++I) {
if (!I->CodeToInsert.empty()) {
// We have an insertion hint. Determine whether the inserted
- // code is on the same line as the caret.
+ // code contains no newlines and is on the same line as the caret.
std::pair<FileID, unsigned> HintLocInfo
= SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
- if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
+ if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
+ StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) {
// Insert the new code into the line just below the code
// that the user wrote.
- unsigned HintColNo
+ // Note: When modifying this function, be very careful about what is a
+ // "column" (printed width, platform-dependent) and what is a
+ // "byte offset" (SourceManager "column").
+ unsigned HintByteOffset
= SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
- // hint must start inside the source or right at the end
- assert(HintColNo<static_cast<unsigned>(map.bytes())+1);
- HintColNo = map.byteToColumn(HintColNo);
-
- // FIXME: if the fixit includes tabs or other characters that do not
- // take up a single column per byte when displayed then
- // I->CodeToInsert.size() is not a column number and we're mixing
- // units (columns + bytes). We should get printable versions
- // of each fixit before using them.
- unsigned LastColumnModified
- = HintColNo + I->CodeToInsert.size();
-
- if (LastColumnModified > static_cast<unsigned>(map.bytes())) {
- unsigned LastExistingColumn = map.byteToColumn(map.bytes());
- unsigned AddedColumns = LastColumnModified-LastExistingColumn;
- LastColumnModified = LastExistingColumn + AddedColumns;
- } else {
- LastColumnModified = map.byteToColumn(LastColumnModified);
- }
+ // The hint must start inside the source or right at the end
+ assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1);
+ unsigned HintCol = map.byteToColumn(HintByteOffset);
+
+ // If we inserted a long previous hint, push this one forwards, and add
+ // an extra space to show that this is not part of the previous
+ // completion. This is sort of the best we can do when two hints appear
+ // to overlap.
+ //
+ // Note that if this hint is located immediately after the previous
+ // hint, no space will be added, since the location is more important.
+ if (HintCol < PrevHintEndCol)
+ HintCol = PrevHintEndCol + 1;
+
+ // FIXME: This function handles multibyte characters in the source, but
+ // not in the fixits. This assertion is intended to catch unintended
+ // use of multibyte characters in fixits. If we decide to do this, we'll
+ // have to track separate byte widths for the source and fixit lines.
+ assert((size_t)llvm::sys::locale::columnWidth(I->CodeToInsert) ==
+ I->CodeToInsert.size());
+
+ // This relies on one byte per column in our fixit hints.
+ // This should NOT use HintByteOffset, because the source might have
+ // Unicode characters in earlier columns.
+ unsigned LastColumnModified = HintCol + I->CodeToInsert.size();
if (LastColumnModified > FixItInsertionLine.size())
FixItInsertionLine.resize(LastColumnModified, ' ');
- assert(HintColNo+I->CodeToInsert.size() <= FixItInsertionLine.size());
+
std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
- FixItInsertionLine.begin() + HintColNo);
+ FixItInsertionLine.begin() + HintCol);
+
+ PrevHintEndCol = LastColumnModified;
} else {
FixItInsertionLine.clear();
break;
@@ -1119,7 +1181,8 @@ std::string TextDiagnostic::buildFixItInsertionLine(
return FixItInsertionLine;
}
-void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints) {
+void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
if (!DiagOpts.ShowParseableFixits)
return;
diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp
index 6445a0c..382e156 100644
--- a/lib/Frontend/TextDiagnosticPrinter.cpp
+++ b/lib/Frontend/TextDiagnosticPrinter.cpp
@@ -27,7 +27,7 @@ using namespace clang;
TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
const DiagnosticOptions &diags,
bool _OwnsOutputStream)
- : OS(os), LangOpts(0), DiagOpts(&diags), SM(0),
+ : OS(os), DiagOpts(&diags),
OwnsOutputStream(_OwnsOutputStream) {
}
@@ -38,11 +38,11 @@ TextDiagnosticPrinter::~TextDiagnosticPrinter() {
void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
const Preprocessor *PP) {
- LangOpts = &LO;
+ // Build the TextDiagnostic utility.
+ TextDiag.reset(new TextDiagnostic(OS, LO, *DiagOpts));
}
void TextDiagnosticPrinter::EndSourceFile() {
- LangOpts = 0;
TextDiag.reset(0);
}
@@ -79,16 +79,6 @@ static void printDiagnosticOptions(raw_ostream &OS,
Started = true;
}
- // If the diagnostic is an extension diagnostic and not enabled by default
- // then it must have been turned on with -pedantic.
- bool EnabledByDefault;
- if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(),
- EnabledByDefault) &&
- !EnabledByDefault) {
- OS << (Started ? "," : " [") << "-pedantic";
- Started = true;
- }
-
StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
if (!Opt.empty()) {
OS << (Started ? "," : " [") << "-W" << Opt;
@@ -128,7 +118,7 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
llvm::raw_svector_ostream DiagMessageStream(OutStr);
printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
- // Keeps track of the the starting position of the location
+ // Keeps track of the starting position of the location
// information (e.g., "foo.c:10:4:") that precedes the error
// message. We use this information to determine how long the
// file+line+column number prefix is.
@@ -152,22 +142,16 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
}
// Assert that the rest of our infrastructure is setup properly.
- assert(LangOpts && "Unexpected diagnostic outside source file processing");
assert(DiagOpts && "Unexpected diagnostic without options set");
assert(Info.hasSourceManager() &&
"Unexpected diagnostic with no source manager");
-
- // Rebuild the TextDiagnostic utility if missing or the source manager has
- // changed.
- if (!TextDiag || SM != &Info.getSourceManager()) {
- SM = &Info.getSourceManager();
- TextDiag.reset(new TextDiagnostic(OS, *SM, *LangOpts, *DiagOpts));
- }
+ assert(TextDiag && "Unexpected diagnostic outside source file processing");
TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
Info.getRanges(),
llvm::makeArrayRef(Info.getFixItHints(),
- Info.getNumFixItHints()));
+ Info.getNumFixItHints()),
+ &Info.getSourceManager());
OS.flush();
}
diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 552282d..a9378a1 100644
--- a/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -11,58 +11,108 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Basic/FileManager.h"
#include "clang/Frontend/VerifyDiagnosticConsumer.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/raw_ostream.h"
-#include <climits>
+#include <cctype>
using namespace clang;
+typedef VerifyDiagnosticConsumer::Directive Directive;
+typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
+typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
- : Diags(_Diags), PrimaryClient(Diags.getClient()),
- OwnsPrimaryClient(Diags.ownsClient()),
- Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0)
+ : Diags(_Diags),
+ PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()),
+ Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0),
+ ActiveSourceFiles(0)
{
Diags.takeClient();
}
VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
+ assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
+ assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
CheckDiagnostics();
Diags.takeClient();
if (OwnsPrimaryClient)
delete PrimaryClient;
}
+#ifndef NDEBUG
+namespace {
+class VerifyFileTracker : public PPCallbacks {
+ typedef VerifyDiagnosticConsumer::FilesParsedForDirectivesSet ListType;
+ ListType &FilesList;
+ SourceManager &SM;
+
+public:
+ VerifyFileTracker(ListType &FilesList, SourceManager &SM)
+ : FilesList(FilesList), SM(SM) { }
+
+ /// \brief Hook into the preprocessor and update the list of parsed
+ /// files when the preprocessor indicates a new file is entered.
+ virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+ SrcMgr::CharacteristicKind FileType,
+ FileID PrevFID) {
+ if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(Loc)))
+ FilesList.insert(E);
+ }
+};
+} // End anonymous namespace.
+#endif
+
// DiagnosticConsumer interface.
void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
const Preprocessor *PP) {
- // FIXME: Const hack, we screw up the preprocessor but in practice its ok
- // because it doesn't get reused. It would be better if we could make a copy
- // though.
- CurrentPreprocessor = const_cast<Preprocessor*>(PP);
+ // Attach comment handler on first invocation.
+ if (++ActiveSourceFiles == 1) {
+ if (PP) {
+ CurrentPreprocessor = PP;
+ const_cast<Preprocessor*>(PP)->addCommentHandler(this);
+#ifndef NDEBUG
+ VerifyFileTracker *V = new VerifyFileTracker(FilesParsedForDirectives,
+ PP->getSourceManager());
+ const_cast<Preprocessor*>(PP)->addPPCallbacks(V);
+#endif
+ }
+ }
+ assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
PrimaryClient->BeginSourceFile(LangOpts, PP);
}
void VerifyDiagnosticConsumer::EndSourceFile() {
- CheckDiagnostics();
-
+ assert(ActiveSourceFiles && "No active source files!");
PrimaryClient->EndSourceFile();
- CurrentPreprocessor = 0;
+ // Detach comment handler once last active source file completed.
+ if (--ActiveSourceFiles == 0) {
+ if (CurrentPreprocessor)
+ const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
+
+ // Check diagnostics once last file completed.
+ CheckDiagnostics();
+ CurrentPreprocessor = 0;
+ }
}
void VerifyDiagnosticConsumer::HandleDiagnostic(
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
- if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) {
- const SourceManager &SM = Info.getSourceManager();
- FirstErrorFID = SM.getFileID(Info.getLocation());
+#ifndef NDEBUG
+ if (Info.hasSourceManager()) {
+ FileID FID = Info.getSourceManager().getFileID(Info.getLocation());
+ if (!FID.isInvalid())
+ FilesWithDiagnostics.insert(FID);
}
+#endif
// Send the diagnostic to the buffer, we will check it once we reach the end
// of the source file (or are destructed).
Buffer->HandleDiagnostic(DiagLevel, Info);
@@ -77,54 +127,21 @@ typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
namespace {
-/// Directive - Abstract class representing a parsed verify directive.
-///
-class Directive {
-public:
- static Directive* Create(bool RegexKind, const SourceLocation &Location,
- const std::string &Text, unsigned Count);
-public:
- /// Constant representing one or more matches aka regex "+".
- static const unsigned OneOrMoreCount = UINT_MAX;
-
- SourceLocation Location;
- const std::string Text;
- unsigned Count;
-
- virtual ~Directive() { }
-
- // Returns true if directive text is valid.
- // Otherwise returns false and populates E.
- virtual bool isValid(std::string &Error) = 0;
-
- // Returns true on match.
- virtual bool Match(const std::string &S) = 0;
-
-protected:
- Directive(const SourceLocation &Location, const std::string &Text,
- unsigned Count)
- : Location(Location), Text(Text), Count(Count) { }
-
-private:
- Directive(const Directive&); // DO NOT IMPLEMENT
- void operator=(const Directive&); // DO NOT IMPLEMENT
-};
-
/// StandardDirective - Directive with string matching.
///
class StandardDirective : public Directive {
public:
- StandardDirective(const SourceLocation &Location, const std::string &Text,
- unsigned Count)
- : Directive(Location, Text, Count) { }
+ StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
+ StringRef Text, unsigned Min, unsigned Max)
+ : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
virtual bool isValid(std::string &Error) {
// all strings are considered valid; even empty ones
return true;
}
- virtual bool Match(const std::string &S) {
- return S.find(Text) != std::string::npos;
+ virtual bool match(StringRef S) {
+ return S.find(Text) != StringRef::npos;
}
};
@@ -132,9 +149,9 @@ public:
///
class RegexDirective : public Directive {
public:
- RegexDirective(const SourceLocation &Location, const std::string &Text,
- unsigned Count)
- : Directive(Location, Text, Count), Regex(Text) { }
+ RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
+ StringRef Text, unsigned Min, unsigned Max)
+ : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { }
virtual bool isValid(std::string &Error) {
if (Regex.isValid(Error))
@@ -142,7 +159,7 @@ public:
return false;
}
- virtual bool Match(const std::string &S) {
+ virtual bool match(StringRef S) {
return Regex.match(S);
}
@@ -150,30 +167,11 @@ private:
llvm::Regex Regex;
};
-typedef std::vector<Directive*> DirectiveList;
-
-/// ExpectedData - owns directive objects and deletes on destructor.
-///
-struct ExpectedData {
- DirectiveList Errors;
- DirectiveList Warnings;
- DirectiveList Notes;
-
- ~ExpectedData() {
- DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 };
- for (DirectiveList **PL = Lists; *PL; ++PL) {
- DirectiveList * const L = *PL;
- for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I)
- delete *I;
- }
- }
-};
-
class ParseHelper
{
public:
- ParseHelper(const char *Begin, const char *End)
- : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { }
+ ParseHelper(StringRef S)
+ : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { }
// Return true if string literal is next.
bool Next(StringRef S) {
@@ -240,78 +238,134 @@ private:
/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
-static void ParseDirective(const char *CommentStart, unsigned CommentLen,
- ExpectedData &ED, Preprocessor &PP,
- SourceLocation Pos) {
+/// Returns true if any valid directives were found.
+static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
+ SourceLocation Pos, DiagnosticsEngine &Diags) {
// A single comment may contain multiple directives.
- for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
- // search for token: expected
+ bool FoundDirective = false;
+ for (ParseHelper PH(S); !PH.Done();) {
+ // Search for token: expected
if (!PH.Search("expected"))
break;
PH.Advance();
- // next token: -
+ // Next token: -
if (!PH.Next("-"))
continue;
PH.Advance();
- // next token: { error | warning | note }
+ // Next token: { error | warning | note }
DirectiveList* DL = NULL;
if (PH.Next("error"))
- DL = &ED.Errors;
+ DL = ED ? &ED->Errors : NULL;
else if (PH.Next("warning"))
- DL = &ED.Warnings;
+ DL = ED ? &ED->Warnings : NULL;
else if (PH.Next("note"))
- DL = &ED.Notes;
+ DL = ED ? &ED->Notes : NULL;
else
continue;
PH.Advance();
- // default directive kind
+ // If a directive has been found but we're not interested
+ // in storing the directive information, return now.
+ if (!DL)
+ return true;
+
+ // Default directive kind.
bool RegexKind = false;
const char* KindStr = "string";
- // next optional token: -
+ // Next optional token: -
if (PH.Next("-re")) {
PH.Advance();
RegexKind = true;
KindStr = "regex";
}
- // skip optional whitespace
+ // Next optional token: @
+ SourceLocation ExpectedLoc;
+ if (!PH.Next("@")) {
+ ExpectedLoc = Pos;
+ } else {
+ PH.Advance();
+ unsigned Line = 0;
+ bool FoundPlus = PH.Next("+");
+ if (FoundPlus || PH.Next("-")) {
+ // Relative to current line.
+ PH.Advance();
+ bool Invalid = false;
+ unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
+ if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
+ if (FoundPlus) ExpectedLine += Line;
+ else ExpectedLine -= Line;
+ ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
+ }
+ } else {
+ // Absolute line number.
+ if (PH.Next(Line) && Line > 0)
+ ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
+ }
+
+ if (ExpectedLoc.isInvalid()) {
+ Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
+ diag::err_verify_missing_line) << KindStr;
+ continue;
+ }
+ PH.Advance();
+ }
+
+ // Skip optional whitespace.
PH.SkipWhitespace();
- // next optional token: positive integer or a '+'.
- unsigned Count = 1;
- if (PH.Next(Count))
+ // Next optional token: positive integer or a '+'.
+ unsigned Min = 1;
+ unsigned Max = 1;
+ if (PH.Next(Min)) {
PH.Advance();
- else if (PH.Next("+")) {
- Count = Directive::OneOrMoreCount;
+ // A positive integer can be followed by a '+' meaning min
+ // or more, or by a '-' meaning a range from min to max.
+ if (PH.Next("+")) {
+ Max = Directive::MaxCount;
+ PH.Advance();
+ } else if (PH.Next("-")) {
+ PH.Advance();
+ if (!PH.Next(Max) || Max < Min) {
+ Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
+ diag::err_verify_invalid_range) << KindStr;
+ continue;
+ }
+ PH.Advance();
+ } else {
+ Max = Min;
+ }
+ } else if (PH.Next("+")) {
+ // '+' on its own means "1 or more".
+ Max = Directive::MaxCount;
PH.Advance();
}
- // skip optional whitespace
+ // Skip optional whitespace.
PH.SkipWhitespace();
- // next token: {{
+ // Next token: {{
if (!PH.Next("{{")) {
- PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin),
- diag::err_verify_missing_start) << KindStr;
+ Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
+ diag::err_verify_missing_start) << KindStr;
continue;
}
PH.Advance();
const char* const ContentBegin = PH.C; // mark content begin
- // search for token: }}
+ // Search for token: }}
if (!PH.Search("}}")) {
- PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin),
- diag::err_verify_missing_end) << KindStr;
+ Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
+ diag::err_verify_missing_end) << KindStr;
continue;
}
const char* const ContentEnd = PH.P; // mark content end
PH.Advance();
- // build directive text; convert \n to newlines
+ // Build directive text; convert \n to newlines.
std::string Text;
StringRef NewlineStr = "\\n";
StringRef Content(ContentBegin, ContentEnd-ContentBegin);
@@ -325,25 +379,83 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen,
if (Text.empty())
Text.assign(ContentBegin, ContentEnd);
- // construct new directive
- Directive *D = Directive::Create(RegexKind, Pos, Text, Count);
+ // Construct new directive.
+ Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
+ Min, Max);
std::string Error;
- if (D->isValid(Error))
+ if (D->isValid(Error)) {
DL->push_back(D);
- else {
- PP.Diag(Pos.getLocWithOffset(ContentBegin-PH.Begin),
- diag::err_verify_invalid_content)
+ FoundDirective = true;
+ } else {
+ Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
+ diag::err_verify_invalid_content)
<< KindStr << Error;
}
}
+
+ return FoundDirective;
}
-/// FindExpectedDiags - Lex the main source file to find all of the
-// expected errors and warnings.
-static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
+/// HandleComment - Hook into the preprocessor and extract comments containing
+/// expected errors and warnings.
+bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
+ SourceRange Comment) {
+ SourceManager &SM = PP.getSourceManager();
+ SourceLocation CommentBegin = Comment.getBegin();
+
+ const char *CommentRaw = SM.getCharacterData(CommentBegin);
+ StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
+
+ if (C.empty())
+ return false;
+
+ // Fold any "\<EOL>" sequences
+ size_t loc = C.find('\\');
+ if (loc == StringRef::npos) {
+ ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics());
+ return false;
+ }
+
+ std::string C2;
+ C2.reserve(C.size());
+
+ for (size_t last = 0;; loc = C.find('\\', last)) {
+ if (loc == StringRef::npos || loc == C.size()) {
+ C2 += C.substr(last);
+ break;
+ }
+ C2 += C.substr(last, loc-last);
+ last = loc + 1;
+
+ if (C[last] == '\n' || C[last] == '\r') {
+ ++last;
+
+ // Escape \r\n or \n\r, but not \n\n.
+ if (last < C.size())
+ if (C[last] == '\n' || C[last] == '\r')
+ if (C[last] != C[last-1])
+ ++last;
+ } else {
+ // This was just a normal backslash.
+ C2 += '\\';
+ }
+ }
+
+ if (!C2.empty())
+ ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics());
+ return false;
+}
+
+#ifndef NDEBUG
+/// \brief Lex the specified source file to determine whether it contains
+/// any expected-* directives. As a Lexer is used rather than a full-blown
+/// Preprocessor, directives inside skipped #if blocks will still be found.
+///
+/// \return true if any directives were found.
+static bool findDirectives(const Preprocessor &PP, FileID FID) {
// Create a raw lexer to pull all the comments out of FID.
if (FID.isInvalid())
- return;
+ return false;
SourceManager& SM = PP.getSourceManager();
// Create a lexer to lex all the tokens of the main file in raw mode.
@@ -355,6 +467,7 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
Token Tok;
Tok.setKind(tok::comment);
+ bool Found = false;
while (Tok.isNot(tok::eof)) {
RawLex.Lex(Tok);
if (!Tok.is(tok::comment)) continue;
@@ -363,19 +476,19 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
if (Comment.empty()) continue;
// Find all expected errors/warnings/notes.
- ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation());
- };
+ Found |= ParseDirective(Comment, 0, SM, Tok.getLocation(),
+ PP.getDiagnostics());
+ }
+ return Found;
}
-
-/// PrintProblem - This takes a diagnostic map of the delta between expected and
-/// seen diagnostics. If there's anything in it, then something unexpected
-/// happened. Print the map out in a nice format and return "true". If the map
-/// is empty and we're not going to print things, then return "false".
-///
-static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
- const_diag_iterator diag_begin,
- const_diag_iterator diag_end,
- const char *Kind, bool Expected) {
+#endif // !NDEBUG
+
+/// \brief Takes a list of diagnostics that have been generated but not matched
+/// by an expected-* directive and produces a diagnostic to the user from this.
+static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
+ const_diag_iterator diag_begin,
+ const_diag_iterator diag_end,
+ const char *Kind) {
if (diag_begin == diag_end) return 0;
SmallString<256> Fmt;
@@ -388,30 +501,32 @@ static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
OS << ": " << I->second;
}
- Diags.Report(diag::err_verify_inconsistent_diags)
- << Kind << !Expected << OS.str();
+ Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
+ << Kind << /*Unexpected=*/true << OS.str();
return std::distance(diag_begin, diag_end);
}
-static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
- DirectiveList &DL, const char *Kind,
- bool Expected) {
+/// \brief Takes a list of diagnostics that were expected to have been generated
+/// but were not and produces a diagnostic to the user from this.
+static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
+ DirectiveList &DL, const char *Kind) {
if (DL.empty())
return 0;
SmallString<256> Fmt;
llvm::raw_svector_ostream OS(Fmt);
for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
- Directive& D = **I;
- if (D.Location.isInvalid() || !SourceMgr)
- OS << "\n (frontend)";
- else
- OS << "\n Line " << SourceMgr->getPresumedLineNumber(D.Location);
+ Directive &D = **I;
+ OS << "\n Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
+ if (D.DirectiveLoc != D.DiagnosticLoc)
+ OS << " (directive at "
+ << SourceMgr.getFilename(D.DirectiveLoc) << ":"
+ << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")";
OS << ": " << D.Text;
}
- Diags.Report(diag::err_verify_inconsistent_diags)
- << Kind << !Expected << OS.str();
+ Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
+ << Kind << /*Unexpected=*/false << OS.str();
return DL.size();
}
@@ -428,10 +543,9 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
Directive& D = **I;
- unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location);
- bool FoundOnce = false;
+ unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
- for (unsigned i = 0; i < D.Count; ++i) {
+ for (unsigned i = 0; i < D.Max; ++i) {
DiagList::iterator II, IE;
for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
@@ -439,29 +553,22 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
continue;
const std::string &RightText = II->second;
- if (D.Match(RightText))
+ if (D.match(RightText))
break;
}
if (II == IE) {
- if (D.Count == D.OneOrMoreCount) {
- if (!FoundOnce)
- LeftOnly.push_back(*I);
- // We are only interested in at least one match, so exit the loop.
- break;
- }
// Not found.
+ if (i >= D.Min) break;
LeftOnly.push_back(*I);
} else {
// Found. The same cannot be found twice.
Right.erase(II);
- FoundOnce = true;
}
}
}
// Now all that's left in Right are those that were not matched.
- unsigned num = PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true);
- num += PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(),
- Label, false);
+ unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
+ num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
return num;
}
@@ -495,8 +602,6 @@ static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
}
void VerifyDiagnosticConsumer::CheckDiagnostics() {
- ExpectedData ED;
-
// Ensure any diagnostics go to the primary client.
bool OwnsCurClient = Diags.ownsClient();
DiagnosticConsumer *CurClient = Diags.takeClient();
@@ -506,32 +611,38 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
// markers. If not then any diagnostics are unexpected.
if (CurrentPreprocessor) {
SourceManager &SM = CurrentPreprocessor->getSourceManager();
- // Extract expected-error strings from main file.
- FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID());
- // Only check for expectations in other diagnostic locations
- // if they are not the main file (via ID or FileEntry) - the main
- // file has already been looked at, and its expectations must not
- // be added twice.
- if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID()
- && (!SM.getFileEntryForID(FirstErrorFID)
- || (SM.getFileEntryForID(FirstErrorFID) !=
- SM.getFileEntryForID(SM.getMainFileID())))) {
- FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID);
- FirstErrorFID = FileID();
+
+#ifndef NDEBUG
+ // In a debug build, scan through any files that may have been missed
+ // during parsing and issue a fatal error if directives are contained
+ // within these files. If a fatal error occurs, this suggests that
+ // this file is being parsed separately from the main file.
+ HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
+ for (FilesWithDiagnosticsSet::iterator I = FilesWithDiagnostics.begin(),
+ End = FilesWithDiagnostics.end();
+ I != End; ++I) {
+ const FileEntry *E = SM.getFileEntryForID(*I);
+ // Don't check files already parsed or those handled as modules.
+ if (E && (FilesParsedForDirectives.count(E)
+ || HS.findModuleForHeader(E)))
+ continue;
+
+ if (findDirectives(*CurrentPreprocessor, *I))
+ llvm::report_fatal_error(Twine("-verify directives found after rather"
+ " than during normal parsing of ",
+ StringRef(E ? E->getName() : "(unknown)")));
}
+#endif
// Check that the expected diagnostics occurred.
NumErrors += CheckResults(Diags, SM, *Buffer, ED);
} else {
- NumErrors += (PrintProblem(Diags, 0,
- Buffer->err_begin(), Buffer->err_end(),
- "error", false) +
- PrintProblem(Diags, 0,
- Buffer->warn_begin(), Buffer->warn_end(),
- "warn", false) +
- PrintProblem(Diags, 0,
- Buffer->note_begin(), Buffer->note_end(),
- "note", false));
+ NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(),
+ Buffer->err_end(), "error") +
+ PrintUnexpected(Diags, 0, Buffer->warn_begin(),
+ Buffer->warn_end(), "warn") +
+ PrintUnexpected(Diags, 0, Buffer->note_begin(),
+ Buffer->note_end(), "note"));
}
Diags.takeClient();
@@ -539,6 +650,9 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
// Reset the buffer, we have processed all the diagnostics in it.
Buffer.reset(new TextDiagnosticBuffer());
+ ED.Errors.clear();
+ ED.Warnings.clear();
+ ED.Notes.clear();
}
DiagnosticConsumer *
@@ -549,9 +663,10 @@ VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const {
return new VerifyDiagnosticConsumer(Diags);
}
-Directive* Directive::Create(bool RegexKind, const SourceLocation &Location,
- const std::string &Text, unsigned Count) {
+Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
+ SourceLocation DiagnosticLoc, StringRef Text,
+ unsigned Min, unsigned Max) {
if (RegexKind)
- return new RegexDirective(Location, Text, Count);
- return new StandardDirective(Location, Text, Count);
+ return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
+ return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
}
diff --git a/lib/Frontend/Warnings.cpp b/lib/Frontend/Warnings.cpp
index ec5fde0..b7d4a3b 100644
--- a/lib/Frontend/Warnings.cpp
+++ b/lib/Frontend/Warnings.cpp
@@ -53,7 +53,11 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings);
Diags.setShowOverloads(
static_cast<DiagnosticsEngine::OverloadsShown>(Opts.ShowOverloads));
-
+
+ Diags.setElideType(Opts.ElideType);
+ Diags.setPrintTemplateTree(Opts.ShowTemplateTree);
+ Diags.setShowColors(Opts.ShowColors);
+
// Handle -ferror-limit
if (Opts.ErrorLimit)
Diags.setErrorLimit(Opts.ErrorLimit);
@@ -83,6 +87,7 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
bool SetDiagnostic = (Report == 0);
for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) {
StringRef Opt = Opts.Warnings[i];
+ StringRef OrigOpt = Opts.Warnings[i];
// Treat -Wformat=0 as an alias for -Wno-format.
if (Opt == "format=0")
@@ -130,7 +135,7 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) {
if (Report)
Diags.Report(diag::warn_unknown_warning_specifier)
- << "-Werror" << ("-W" + Opt.str());
+ << "-Werror" << ("-W" + OrigOpt.str());
continue;
}
Specifier = Opt.substr(6);
@@ -158,7 +163,7 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) {
if (Report)
Diags.Report(diag::warn_unknown_warning_specifier)
- << "-Wfatal-errors" << ("-W" + Opt.str());
+ << "-Wfatal-errors" << ("-W" + OrigOpt.str());
continue;
}
Specifier = Opt.substr(13);
@@ -182,7 +187,8 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
if (Report) {
if (DiagIDs->getDiagnosticsInGroup(Opt, _Diags))
- EmitUnknownDiagWarning(Diags, "-W", Opt, isPositive);
+ EmitUnknownDiagWarning(Diags, isPositive ? "-W" : "-Wno-", Opt,
+ isPositive);
} else {
Diags.setDiagnosticGroupMapping(Opt, Mapping);
}
diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt
index 5270b1b..fe9d589 100644
--- a/lib/FrontendTool/CMakeLists.txt
+++ b/lib/FrontendTool/CMakeLists.txt
@@ -1,11 +1,20 @@
-set(LLVM_USED_LIBS clangDriver clangFrontend clangRewrite clangCodeGen
- clangStaticAnalyzerFrontend clangStaticAnalyzerCheckers clangStaticAnalyzerCore
- clangARCMigrate)
-
add_clang_library(clangFrontendTool
ExecuteCompilerInvocation.cpp
)
add_dependencies(clangFrontendTool
- ClangCC1Options
- ClangDiagnosticFrontend)
+ ClangDiagnosticCommon
+ ClangDiagnosticFrontend
+ ClangDriverOptions
+ )
+
+target_link_libraries(clangFrontendTool
+ clangDriver
+ clangFrontend
+ clangRewrite
+ clangCodeGen
+ clangStaticAnalyzerFrontend
+ clangStaticAnalyzerCheckers
+ clangStaticAnalyzerCore
+ clangARCMigrate
+ )
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 07d2b8d..bd50083 100644
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -16,7 +16,7 @@
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "clang/ARCMigrate/ARCMTActions.h"
#include "clang/CodeGen/CodeGenAction.h"
-#include "clang/Driver/CC1Options.h"
+#include "clang/Driver/Options.h"
#include "clang/Driver/OptTable.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -32,6 +32,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
using namespace clang::frontend;
switch (CI.getFrontendOpts().ProgramAction) {
+ case ASTDeclList: return new ASTDeclListAction();
case ASTDump: return new ASTDumpAction();
case ASTDumpXML: return new ASTDumpXMLAction();
case ASTPrint: return new ASTPrintAction();
@@ -71,7 +72,12 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
case PrintDeclContext: return new DeclContextPrintAction();
case PrintPreamble: return new PrintPreambleAction();
- case PrintPreprocessedInput: return new PrintPreprocessedAction();
+ case PrintPreprocessedInput: {
+ if (CI.getPreprocessorOutputOpts().RewriteIncludes)
+ return new RewriteIncludesAction();
+ return new PrintPreprocessedAction();
+ }
+
case RewriteMacros: return new RewriteMacrosAction();
case RewriteObjC: return new RewriteObjCAction();
case RewriteTest: return new RewriteTestAction();
@@ -129,7 +135,7 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) {
bool clang::ExecuteCompilerInvocation(CompilerInstance *Clang) {
// Honor -help.
if (Clang->getFrontendOpts().ShowHelp) {
- OwningPtr<driver::OptTable> Opts(driver::createCC1OptTable());
+ OwningPtr<driver::OptTable> Opts(driver::createDriverOptTable());
Opts->PrintHelp(llvm::outs(), "clang -cc1",
"LLVM 'Clang' Compiler: http://clang.llvm.org");
return 0;
diff --git a/lib/Headers/CMakeLists.txt b/lib/Headers/CMakeLists.txt
index 78141a3..6e9cc68 100644
--- a/lib/Headers/CMakeLists.txt
+++ b/lib/Headers/CMakeLists.txt
@@ -1,5 +1,6 @@
set(files
altivec.h
+ ammintrin.h
avxintrin.h
avx2intrin.h
bmiintrin.h
@@ -7,6 +8,7 @@ set(files
emmintrin.h
float.h
fma4intrin.h
+ fmaintrin.h
immintrin.h
iso646.h
limits.h
@@ -29,6 +31,7 @@ set(files
wmmintrin.h
x86intrin.h
xmmintrin.h
+ xopintrin.h
cpuid.h
unwind.h
module.map
diff --git a/lib/Headers/ammintrin.h b/lib/Headers/ammintrin.h
new file mode 100644
index 0000000..d87b9cd
--- /dev/null
+++ b/lib/Headers/ammintrin.h
@@ -0,0 +1,68 @@
+/*===---- ammintrin.h - SSE4a intrinsics -----------------------------------===
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __AMMINTRIN_H
+#define __AMMINTRIN_H
+
+#ifndef __SSE4A__
+#error "SSE4A instruction set not enabled"
+#else
+
+#include <pmmintrin.h>
+
+#define _mm_extracti_si64(x, len, idx) \
+ ((__m128i)__builtin_ia32_extrqi((__v2di)(__m128i)(x), \
+ (char)(len), (char)(idx)))
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_extract_si64(__m128i __x, __m128i __y)
+{
+ return (__m128i)__builtin_ia32_extrq((__v2di)__x, (__v16qi)__y);
+}
+
+#define _mm_inserti_si64(x, y, len, idx) \
+ ((__m128i)__builtin_ia32_insertqi((__v2di)(__m128i)(x), \
+ (__v2di)(__m128i)(y), \
+ (char)(len), (char)(idx)))
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_insert_si64(__m128i __x, __m128i __y)
+{
+ return (__m128i)__builtin_ia32_insertq((__v2di)__x, (__v2di)__y);
+}
+
+static __inline__ void __attribute__((__always_inline__, __nodebug__))
+_mm_stream_sd(double *__p, __m128d __a)
+{
+ __builtin_ia32_movntsd(__p, (__v2df)__a);
+}
+
+static __inline__ void __attribute__((__always_inline__, __nodebug__))
+_mm_stream_ss(float *__p, __m128 __a)
+{
+ __builtin_ia32_movntss(__p, (__v4sf)__a);
+}
+
+#endif /* __SSE4A__ */
+
+#endif /* __AMMINTRIN_H */
diff --git a/lib/Headers/avx2intrin.h b/lib/Headers/avx2intrin.h
index 884c46d..2c53aed 100644
--- a/lib/Headers/avx2intrin.h
+++ b/lib/Headers/avx2intrin.h
@@ -959,3 +959,243 @@ _mm_srlv_epi64(__m128i __X, __m128i __Y)
{
return (__m128i)__builtin_ia32_psrlv2di(__X, __Y);
}
+
+#define _mm_mask_i32gather_pd(a, m, i, mask, s) __extension__ ({ \
+ __m128d __a = (a); \
+ double const *__m = (m); \
+ __m128i __i = (i); \
+ __m128d __mask = (mask); \
+ (__m128d)__builtin_ia32_gatherd_pd((__v2df)__a, (const __v2df *)__m, \
+ (__v4si)__i, (__v2df)__mask, (s)); })
+
+#define _mm256_mask_i32gather_pd(a, m, i, mask, s) __extension__ ({ \
+ __m256d __a = (a); \
+ double const *__m = (m); \
+ __m128i __i = (i); \
+ __m256d __mask = (mask); \
+ (__m256d)__builtin_ia32_gatherd_pd256((__v4df)__a, (const __v4df *)__m, \
+ (__v4si)__i, (__v4df)__mask, (s)); })
+
+#define _mm_mask_i64gather_pd(a, m, i, mask, s) __extension__ ({ \
+ __m128d __a = (a); \
+ double const *__m = (m); \
+ __m128i __i = (i); \
+ __m128d __mask = (mask); \
+ (__m128d)__builtin_ia32_gatherq_pd((__v2df)__a, (const __v2df *)__m, \
+ (__v2di)__i, (__v2df)__mask, (s)); })
+
+#define _mm256_mask_i64gather_pd(a, m, i, mask, s) __extension__ ({ \
+ __m256d __a = (a); \
+ double const *__m = (m); \
+ __m256i __i = (i); \
+ __m256d __mask = (mask); \
+ (__m256d)__builtin_ia32_gatherq_pd256((__v4df)__a, (const __v4df *)__m, \
+ (__v4di)__i, (__v4df)__mask, (s)); })
+
+#define _mm_mask_i32gather_ps(a, m, i, mask, s) __extension__ ({ \
+ __m128 __a = (a); \
+ float const *__m = (m); \
+ __m128i __i = (i); \
+ __m128 __mask = (mask); \
+ (__m128)__builtin_ia32_gatherd_ps((__v4sf)__a, (const __v4sf *)__m, \
+ (__v4si)__i, (__v4sf)__mask, (s)); })
+
+#define _mm256_mask_i32gather_ps(a, m, i, mask, s) __extension__ ({ \
+ __m256 __a = (a); \
+ float const *__m = (m); \
+ __m256i __i = (i); \
+ __m256 __mask = (mask); \
+ (__m256)__builtin_ia32_gatherd_ps256((__v8sf)__a, (const __v8sf *)__m, \
+ (__v8si)__i, (__v8sf)__mask, (s)); })
+
+#define _mm_mask_i64gather_ps(a, m, i, mask, s) __extension__ ({ \
+ __m128 __a = (a); \
+ float const *__m = (m); \
+ __m128i __i = (i); \
+ __m128 __mask = (mask); \
+ (__m128)__builtin_ia32_gatherq_ps((__v4sf)__a, (const __v4sf *)__m, \
+ (__v2di)__i, (__v4sf)__mask, (s)); })
+
+#define _mm256_mask_i64gather_ps(a, m, i, mask, s) __extension__ ({ \
+ __m128 __a = (a); \
+ float const *__m = (m); \
+ __m256i __i = (i); \
+ __m128 __mask = (mask); \
+ (__m128)__builtin_ia32_gatherq_ps256((__v4sf)__a, (const __v4sf *)__m, \
+ (__v4di)__i, (__v4sf)__mask, (s)); })
+
+#define _mm_mask_i32gather_epi32(a, m, i, mask, s) __extension__ ({ \
+ __m128i __a = (a); \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ __m128i __mask = (mask); \
+ (__m128i)__builtin_ia32_gatherd_d((__v4si)__a, (const __v4si *)__m, \
+ (__v4si)__i, (__v4si)__mask, (s)); })
+
+#define _mm256_mask_i32gather_epi32(a, m, i, mask, s) __extension__ ({ \
+ __m256i __a = (a); \
+ int const *__m = (m); \
+ __m256i __i = (i); \
+ __m256i __mask = (mask); \
+ (__m256i)__builtin_ia32_gatherd_d256((__v8si)__a, (const __v8si *)__m, \
+ (__v8si)__i, (__v8si)__mask, (s)); })
+
+#define _mm_mask_i64gather_epi32(a, m, i, mask, s) __extension__ ({ \
+ __m128i __a = (a); \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ __m128i __mask = (mask); \
+ (__m128i)__builtin_ia32_gatherq_d((__v4si)__a, (const __v4si *)__m, \
+ (__v2di)__i, (__v4si)__mask, (s)); })
+
+#define _mm256_mask_i64gather_epi32(a, m, i, mask, s) __extension__ ({ \
+ __m128i __a = (a); \
+ int const *__m = (m); \
+ __m256i __i = (i); \
+ __m128i __mask = (mask); \
+ (__m128i)__builtin_ia32_gatherq_d256((__v4si)__a, (const __v4si *)__m, \
+ (__v4di)__i, (__v4si)__mask, (s)); })
+
+#define _mm_mask_i32gather_epi64(a, m, i, mask, s) __extension__ ({ \
+ __m128i __a = (a); \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ __m128i __mask = (mask); \
+ (__m128i)__builtin_ia32_gatherd_q((__v2di)__a, (const __v2di *)__m, \
+ (__v4si)__i, (__v2di)__mask, (s)); })
+
+#define _mm256_mask_i32gather_epi64(a, m, i, mask, s) __extension__ ({ \
+ __m256i __a = (a); \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ __m256i __mask = (mask); \
+ (__m256i)__builtin_ia32_gatherd_q256((__v4di)__a, (const __v4di *)__m, \
+ (__v4si)__i, (__v4di)__mask, (s)); })
+
+#define _mm_mask_i64gather_epi64(a, m, i, mask, s) __extension__ ({ \
+ __m128i __a = (a); \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ __m128i __mask = (mask); \
+ (__m128i)__builtin_ia32_gatherq_q((__v2di)__a, (const __v2di *)__m, \
+ (__v2di)__i, (__v2di)__mask, (s)); })
+
+#define _mm256_mask_i64gather_epi64(a, m, i, mask, s) __extension__ ({ \
+ __m256i __a = (a); \
+ int const *__m = (m); \
+ __m256i __i = (i); \
+ __m256i __mask = (mask); \
+ (__m256i)__builtin_ia32_gatherq_q256((__v4di)__a, (const __v4di *)__m, \
+ (__v4di)__i, (__v4di)__mask, (s)); })
+
+#define _mm_i32gather_pd(m, i, s) __extension__ ({ \
+ double const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128d)__builtin_ia32_gatherd_pd((__v2df)_mm_setzero_pd(), \
+ (const __v2df *)__m, (__v4si)__i, \
+ (__v2df)_mm_set1_pd((double)(long long int)-1), (s)); })
+
+#define _mm256_i32gather_pd(m, i, s) __extension__ ({ \
+ double const *__m = (m); \
+ __m128i __i = (i); \
+ (__m256d)__builtin_ia32_gatherd_pd256((__v4df)_mm256_setzero_pd(), \
+ (const __v4df *)__m, (__v4si)__i, \
+ (__v4df)_mm256_set1_pd((double)(long long int)-1), (s)); })
+
+#define _mm_i64gather_pd(m, i, s) __extension__ ({ \
+ double const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128d)__builtin_ia32_gatherq_pd((__v2df)_mm_setzero_pd(), \
+ (const __v2df *)__m, (__v2di)__i, \
+ (__v2df)_mm_set1_pd((double)(long long int)-1), (s)); })
+
+#define _mm256_i64gather_pd(m, i, s) __extension__ ({ \
+ double const *__m = (m); \
+ __m256i __i = (i); \
+ (__m256d)__builtin_ia32_gatherq_pd256((__v4df)_mm256_setzero_pd(), \
+ (const __v4df *)__m, (__v4di)__i, \
+ (__v4df)_mm256_set1_pd((double)(long long int)-1), (s)); })
+
+#define _mm_i32gather_ps(m, i, s) __extension__ ({ \
+ float const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128)__builtin_ia32_gatherd_ps((__v4sf)_mm_setzero_ps(), \
+ (const __v4sf *)__m, (__v4si)__i, \
+ (__v4sf)_mm_set1_ps((float)(int)-1), (s)); })
+
+#define _mm256_i32gather_ps(m, i, s) __extension__ ({ \
+ float const *__m = (m); \
+ __m256i __i = (i); \
+ (__m256)__builtin_ia32_gatherd_ps256((__v8sf)_mm256_setzero_ps(), \
+ (const __v8sf *)__m, (__v8si)__i, \
+ (__v8sf)_mm256_set1_ps((float)(int)-1), (s)); })
+
+#define _mm_i64gather_ps(m, i, s) __extension__ ({ \
+ float const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128)__builtin_ia32_gatherq_ps((__v4sf)_mm_setzero_ps(), \
+ (const __v4sf *)__m, (__v2di)__i, \
+ (__v4sf)_mm_set1_ps((float)(int)-1), (s)); })
+
+#define _mm256_i64gather_ps(m, i, s) __extension__ ({ \
+ float const *__m = (m); \
+ __m256i __i = (i); \
+ (__m128)__builtin_ia32_gatherq_ps256((__v4sf)_mm_setzero_ps(), \
+ (const __v4sf *)__m, (__v4di)__i, \
+ (__v4sf)_mm_set1_ps((float)(int)-1), (s)); })
+
+#define _mm_i32gather_epi32(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128i)__builtin_ia32_gatherd_d((__v4si)_mm_setzero_si128(), \
+ (const __v4si *)__m, (__v4si)__i, \
+ (__v4si)_mm_set1_epi32(-1), (s)); })
+
+#define _mm256_i32gather_epi32(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m256i __i = (i); \
+ (__m256i)__builtin_ia32_gatherd_d256((__v8si)_mm256_setzero_si256(), \
+ (const __v8si *)__m, (__v8si)__i, \
+ (__v8si)_mm256_set1_epi32(-1), (s)); })
+
+#define _mm_i64gather_epi32(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128i)__builtin_ia32_gatherq_d((__v4si)_mm_setzero_si128(), \
+ (const __v4si *)__m, (__v2di)__i, \
+ (__v4si)_mm_set1_epi32(-1), (s)); })
+
+#define _mm256_i64gather_epi32(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m256i __i = (i); \
+ (__m128i)__builtin_ia32_gatherq_d256((__v4si)_mm_setzero_si128(), \
+ (const __v4si *)__m, (__v4di)__i, \
+ (__v4si)_mm_set1_epi32(-1), (s)); })
+
+#define _mm_i32gather_epi64(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128i)__builtin_ia32_gatherd_q((__v2di)_mm_setzero_si128(), \
+ (const __v2di *)__m, (__v4si)__i, \
+ (__v2di)_mm_set1_epi64x(-1), (s)); })
+
+#define _mm256_i32gather_epi64(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ (__m256i)__builtin_ia32_gatherd_q256((__v4di)_mm256_setzero_si256(), \
+ (const __v4di *)__m, (__v4si)__i, \
+ (__v4di)_mm256_set1_epi64x(-1), (s)); })
+
+#define _mm_i64gather_epi64(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m128i __i = (i); \
+ (__m128i)__builtin_ia32_gatherq_q((__v2di)_mm_setzero_si128(), \
+ (const __v2di *)__m, (__v2di)__i, \
+ (__v2di)_mm_set1_epi64x(-1), (s)); })
+
+#define _mm256_i64gather_epi64(m, i, s) __extension__ ({ \
+ int const *__m = (m); \
+ __m256i __i = (i); \
+ (__m256i)__builtin_ia32_gatherq_q256((__v4di)_mm256_setzero_si256(), \
+ (const __v4di *)__m, (__v4di)__i, \
+ (__v4di)_mm256_set1_epi64x(-1), (s)); })
diff --git a/lib/Headers/bmiintrin.h b/lib/Headers/bmiintrin.h
index 2f7db73..8cb00f5 100644
--- a/lib/Headers/bmiintrin.h
+++ b/lib/Headers/bmiintrin.h
@@ -33,7 +33,7 @@
#define __BMIINTRIN_H
static __inline__ unsigned short __attribute__((__always_inline__, __nodebug__))
-__tzcnt16(unsigned short __X)
+__tzcnt_u16(unsigned short __X)
{
return __builtin_ctzs(__X);
}
@@ -69,7 +69,7 @@ __blsr_u32(unsigned int __X)
}
static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__))
-__tzcnt32(unsigned int __X)
+__tzcnt_u32(unsigned int __X)
{
return __builtin_ctz(__X);
}
@@ -106,7 +106,7 @@ __blsr_u64(unsigned long long __X)
}
static __inline__ unsigned long long __attribute__((__always_inline__, __nodebug__))
-__tzcnt64(unsigned long long __X)
+__tzcnt_u64(unsigned long long __X)
{
return __builtin_ctzll(__X);
}
diff --git a/lib/Headers/emmintrin.h b/lib/Headers/emmintrin.h
index e10b77d..91395ed 100644
--- a/lib/Headers/emmintrin.h
+++ b/lib/Headers/emmintrin.h
@@ -1186,7 +1186,10 @@ _mm_maskmoveu_si128(__m128i d, __m128i n, char *p)
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_mm_storel_epi64(__m128i *p, __m128i a)
{
- __builtin_ia32_storelv4si((__v2si *)p, a);
+ struct __mm_storel_epi64_struct {
+ long long u;
+ } __attribute__((__packed__, __may_alias__));
+ ((struct __mm_storel_epi64_struct*)p)->u = a[0];
}
static __inline__ void __attribute__((__always_inline__, __nodebug__))
diff --git a/lib/Headers/float.h b/lib/Headers/float.h
index 65b517d..2cb13d3 100644
--- a/lib/Headers/float.h
+++ b/lib/Headers/float.h
@@ -28,7 +28,7 @@
* additional definitions provided for Windows.
* For more details see http://msdn.microsoft.com/en-us/library/y0ybw9fy.aspx
*/
-#if defined(__MINGW32__) && \
+#if (defined(__MINGW32__) || defined(_MSC_VER)) && \
defined(__has_include_next) && __has_include_next(<float.h>)
# include_next <float.h>
diff --git a/lib/Headers/fmaintrin.h b/lib/Headers/fmaintrin.h
new file mode 100644
index 0000000..6bfd5a8
--- /dev/null
+++ b/lib/Headers/fmaintrin.h
@@ -0,0 +1,229 @@
+/*===---- fma4intrin.h - FMA4 intrinsics -----------------------------------===
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __IMMINTRIN_H
+#error "Never use <fmaintrin.h> directly; include <immintrin.h> instead."
+#endif
+
+#ifndef __FMAINTRIN_H
+#define __FMAINTRIN_H
+
+#ifndef __FMA__
+# error "FMA instruction set is not enabled"
+#else
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fmadd_ps(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfmaddps(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fmadd_pd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfmaddpd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fmadd_ss(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfmaddss(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fmadd_sd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfmaddsd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fmsub_ps(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfmsubps(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fmsub_pd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfmsubpd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fmsub_ss(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfmsubss(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fmsub_sd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfmsubsd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fnmadd_ps(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfnmaddps(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fnmadd_pd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfnmaddpd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fnmadd_ss(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfnmaddss(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fnmadd_sd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfnmaddsd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fnmsub_ps(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfnmsubps(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fnmsub_pd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfnmsubpd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fnmsub_ss(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfnmsubss(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fnmsub_sd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfnmsubsd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fmaddsub_ps(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfmaddsubps(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fmaddsub_pd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfmaddsubpd(__A, __B, __C);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_fmsubadd_ps(__m128 __A, __m128 __B, __m128 __C)
+{
+ return (__m128)__builtin_ia32_vfmsubaddps(__A, __B, __C);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_fmsubadd_pd(__m128d __A, __m128d __B, __m128d __C)
+{
+ return (__m128d)__builtin_ia32_vfmsubaddpd(__A, __B, __C);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_fmadd_ps(__m256 __A, __m256 __B, __m256 __C)
+{
+ return (__m256)__builtin_ia32_vfmaddps256(__A, __B, __C);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_fmadd_pd(__m256d __A, __m256d __B, __m256d __C)
+{
+ return (__m256d)__builtin_ia32_vfmaddpd256(__A, __B, __C);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_fmsub_ps(__m256 __A, __m256 __B, __m256 __C)
+{
+ return (__m256)__builtin_ia32_vfmsubps256(__A, __B, __C);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_fmsub_pd(__m256d __A, __m256d __B, __m256d __C)
+{
+ return (__m256d)__builtin_ia32_vfmsubpd256(__A, __B, __C);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_fnmadd_ps(__m256 __A, __m256 __B, __m256 __C)
+{
+ return (__m256)__builtin_ia32_vfnmaddps256(__A, __B, __C);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_fnmadd_pd(__m256d __A, __m256d __B, __m256d __C)
+{
+ return (__m256d)__builtin_ia32_vfnmaddpd256(__A, __B, __C);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_fnmsub_ps(__m256 __A, __m256 __B, __m256 __C)
+{
+ return (__m256)__builtin_ia32_vfnmsubps256(__A, __B, __C);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_fnmsub_pd(__m256d __A, __m256d __B, __m256d __C)
+{
+ return (__m256d)__builtin_ia32_vfnmsubpd256(__A, __B, __C);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_fmaddsub_ps(__m256 __A, __m256 __B, __m256 __C)
+{
+ return (__m256)__builtin_ia32_vfmaddsubps256(__A, __B, __C);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_fmaddsub_pd(__m256d __A, __m256d __B, __m256d __C)
+{
+ return (__m256d)__builtin_ia32_vfmaddsubpd256(__A, __B, __C);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_fmsubadd_ps(__m256 __A, __m256 __B, __m256 __C)
+{
+ return (__m256)__builtin_ia32_vfmsubaddps256(__A, __B, __C);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_fmsubadd_pd(__m256d __A, __m256d __B, __m256d __C)
+{
+ return (__m256d)__builtin_ia32_vfmsubaddpd256(__A, __B, __C);
+}
+
+#endif /* __FMA__ */
+
+#endif /* __FMAINTRIN_H */
diff --git a/lib/Headers/immintrin.h b/lib/Headers/immintrin.h
index 1605525..15b65f3 100644
--- a/lib/Headers/immintrin.h
+++ b/lib/Headers/immintrin.h
@@ -72,4 +72,30 @@
#include <lzcntintrin.h>
#endif
+#ifdef __FMA__
+#include <fmaintrin.h>
+#endif
+
+#ifdef __RDRND__
+static __inline__ int __attribute__((__always_inline__, __nodebug__))
+_rdrand16_step(unsigned short *__p)
+{
+ return __builtin_ia32_rdrand16_step(__p);
+}
+
+static __inline__ int __attribute__((__always_inline__, __nodebug__))
+_rdrand32_step(unsigned int *__p)
+{
+ return __builtin_ia32_rdrand32_step(__p);
+}
+
+#ifdef __x86_64__
+static __inline__ int __attribute__((__always_inline__, __nodebug__))
+_rdrand64_step(unsigned long long *__p)
+{
+ return __builtin_ia32_rdrand64_step(__p);
+}
+#endif
+#endif /* __RDRND__ */
+
#endif /* __IMMINTRIN_H */
diff --git a/lib/Headers/stddef.h b/lib/Headers/stddef.h
index 9e87ee89..eb919b5 100644
--- a/lib/Headers/stddef.h
+++ b/lib/Headers/stddef.h
@@ -43,10 +43,20 @@ typedef __WCHAR_TYPE__ wchar_t;
#undef NULL
#ifdef __cplusplus
-#undef __null // VC++ hack.
-#define NULL __null
+# if !defined(__MINGW32__) && !defined(_MSC_VER)
+# define NULL __null
+# else
+# define NULL 0
+# endif
#else
-#define NULL ((void*)0)
+# define NULL ((void*)0)
+#endif
+
+#ifdef __cplusplus
+#if defined(_MSC_EXTENSIONS) && defined(_NATIVE_NULLPTR_SUPPORTED)
+namespace std { typedef decltype(nullptr) nullptr_t; }
+using ::std::nullptr_t;
+#endif
#endif
#define offsetof(t, d) __builtin_offsetof(t, d)
diff --git a/lib/Headers/wmmintrin.h b/lib/Headers/wmmintrin.h
index 8f58850..dca896f 100644
--- a/lib/Headers/wmmintrin.h
+++ b/lib/Headers/wmmintrin.h
@@ -24,11 +24,13 @@
#ifndef _WMMINTRIN_H
#define _WMMINTRIN_H
-#if !defined (__AES__)
-# error "AES instructions not enabled"
+#include <emmintrin.h>
+
+#if !defined (__AES__) && !defined (__PCLMUL__)
+# error "AES/PCLMUL instructions not enabled"
#else
-#include <xmmintrin.h>
+#ifdef __AES__
static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
_mm_aesenc_si128(__m128i __V, __m128i __R)
@@ -64,4 +66,14 @@ _mm_aesimc_si128(__m128i __V)
__builtin_ia32_aeskeygenassist128((C), (R))
#endif /* __AES__ */
+
+#ifdef __PCLMUL__
+
+#define _mm_clmulepi64_si128(__X, __Y, __I) \
+ ((__m128i)__builtin_ia32_pclmulqdq128((__v2di)(__m128i)(__X), \
+ (__v2di)(__m128i)(__Y), (char)(__I)))
+
+#endif /* __PCLMUL__ */
+
+#endif /* __AES__ || __PCLMUL__ */
#endif /* _WMMINTRIN_H */
diff --git a/lib/Headers/x86intrin.h b/lib/Headers/x86intrin.h
index f5e4d88..556cd01 100644
--- a/lib/Headers/x86intrin.h
+++ b/lib/Headers/x86intrin.h
@@ -46,10 +46,18 @@
#include <popcntintrin.h>
#endif
+#ifdef __SSE4A__
+#include <ammintrin.h>
+#endif
+
#ifdef __FMA4__
#include <fma4intrin.h>
#endif
-// FIXME: SSE4A, XOP, LWP, ABM
+#ifdef __XOP__
+#include <xopintrin.h>
+#endif
+
+// FIXME: LWP
#endif /* __X86INTRIN_H */
diff --git a/lib/Headers/xopintrin.h b/lib/Headers/xopintrin.h
new file mode 100644
index 0000000..d107be4
--- /dev/null
+++ b/lib/Headers/xopintrin.h
@@ -0,0 +1,411 @@
+/*===---- xopintrin.h - FMA4 intrinsics ------------------------------------===
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __X86INTRIN_H
+#error "Never use <fma4intrin.h> directly; include <x86intrin.h> instead."
+#endif
+
+#ifndef __XOPINTRIN_H
+#define __XOPINTRIN_H
+
+#ifndef __XOP__
+# error "XOP instruction set is not enabled"
+#else
+
+#include <fma4intrin.h>
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maccs_epi16(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacssww((__v8hi)__A, (__v8hi)__B, (__v8hi)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_macc_epi16(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacsww((__v8hi)__A, (__v8hi)__B, (__v8hi)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maccsd_epi16(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacsswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maccd_epi16(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maccs_epi32(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacssdd((__v4si)__A, (__v4si)__B, (__v4si)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_macc_epi32(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacsdd((__v4si)__A, (__v4si)__B, (__v4si)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maccslo_epi32(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacssdql((__v4si)__A, (__v4si)__B, (__v2di)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_macclo_epi32(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacsdql((__v4si)__A, (__v4si)__B, (__v2di)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maccshi_epi32(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacssdqh((__v4si)__A, (__v4si)__B, (__v2di)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_macchi_epi32(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmacsdqh((__v4si)__A, (__v4si)__B, (__v2di)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maddsd_epi16(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmadcsswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_maddd_epi16(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpmadcswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddw_epi8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddbw((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddd_epi8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddbd((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddq_epi8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddbq((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddd_epi16(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddwd((__v8hi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddq_epi16(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddwq((__v8hi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddq_epi32(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphadddq((__v4si)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddw_epu8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddubw((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddd_epu8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddubd((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddq_epu8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddubq((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddd_epu16(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphadduwd((__v8hi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddq_epu16(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphadduwq((__v8hi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_haddq_epu32(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphaddudq((__v4si)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_hsubw_epi8(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphsubbw((__v16qi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_hsubd_epi16(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphsubwd((__v8hi)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_hsubq_epi32(__m128i __A)
+{
+ return (__m128i)__builtin_ia32_vphsubdq((__v4si)__A);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_cmov_si128(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpcmov(__A, __B, __C);
+}
+
+static __inline__ __m256i __attribute__((__always_inline__, __nodebug__))
+_mm256_cmov_si256(__m256i __A, __m256i __B, __m256i __C)
+{
+ return (__m256i)__builtin_ia32_vpcmov_256(__A, __B, __C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_perm_epi8(__m128i __A, __m128i __B, __m128i __C)
+{
+ return (__m128i)__builtin_ia32_vpperm((__v16qi)__A, (__v16qi)__B, (__v16qi)__C);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_rot_epi8(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vprotb((__v16qi)__A, (__v16qi)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_rot_epi16(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vprotw((__v8hi)__A, (__v8hi)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_rot_epi32(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vprotd((__v4si)__A, (__v4si)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_rot_epi64(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vprotq((__v2di)__A, (__v2di)__B);
+}
+
+#define _mm_roti_epi8(A, N) __extension__ ({ \
+ __m128i __A = (A); \
+ (__m128i)__builtin_ia32_vprotbi((__v16qi)__A, (N)); })
+
+#define _mm_roti_epi16(A, N) __extension__ ({ \
+ __m128i __A = (A); \
+ (__m128i)__builtin_ia32_vprotwi((__v8hi)__A, (N)); })
+
+#define _mm_roti_epi32(A, N) __extension__ ({ \
+ __m128i __A = (A); \
+ (__m128i)__builtin_ia32_vprotdi((__v4si)__A, (N)); })
+
+#define _mm_roti_epi64(A, N) __extension__ ({ \
+ __m128i __A = (A); \
+ (__m128i)__builtin_ia32_vprotqi((__v2di)__A, (N)); })
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_shl_epi8(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshlb((__v16qi)__A, (__v16qi)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_shl_epi16(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshlw((__v8hi)__A, (__v8hi)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_shl_epi32(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshld((__v4si)__A, (__v4si)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_shl_epi64(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshlq((__v2di)__A, (__v2di)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_sha_epi8(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshab((__v16qi)__A, (__v16qi)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_sha_epi16(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshaw((__v8hi)__A, (__v8hi)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_sha_epi32(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshad((__v4si)__A, (__v4si)__B);
+}
+
+static __inline__ __m128i __attribute__((__always_inline__, __nodebug__))
+_mm_sha_epi64(__m128i __A, __m128i __B)
+{
+ return (__m128i)__builtin_ia32_vpshaq((__v2di)__A, (__v2di)__B);
+}
+
+#define _mm_com_epu8(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomub((__v16qi)__A, (__v16qi)__B, (N)); })
+
+#define _mm_com_epu16(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomuw((__v8hi)__A, (__v8hi)__B, (N)); })
+
+#define _mm_com_epu32(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomud((__v4si)__A, (__v4si)__B, (N)); })
+
+#define _mm_com_epu64(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomuq((__v2di)__A, (__v2di)__B, (N)); })
+
+#define _mm_com_epi8(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomb((__v16qi)__A, (__v16qi)__B, (N)); })
+
+#define _mm_com_epi16(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomw((__v8hi)__A, (__v8hi)__B, (N)); })
+
+#define _mm_com_epi32(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomd((__v4si)__A, (__v4si)__B, (N)); })
+
+#define _mm_com_epi64(A, B, N) __extension__ ({ \
+ __m128i __A = (A); \
+ __m128i __B = (B); \
+ (__m128i)__builtin_ia32_vpcomq((__v2di)__A, (__v2di)__B, (N)); })
+
+#define _mm_permute2_pd(X, Y, C, I) __extension__ ({ \
+ __m128d __X = (X); \
+ __m128d __Y = (Y); \
+ __m128i __C = (C); \
+ (__m128d)__builtin_ia32_vpermil2pd((__v2df)__X, (__v2df)__Y, \
+ (__v2di)__C, (I)); })
+
+#define _mm256_permute2_pd(X, Y, C, I) __extension__ ({ \
+ __m256d __X = (X); \
+ __m256d __Y = (Y); \
+ __m256i __C = (C); \
+ (__m256d)__builtin_ia32_vpermil2pd256((__v4df)__X, (__v4df)__Y, \
+ (__v4di)__C, (I)); })
+
+#define _mm_permute2_ps(X, Y, C, I) __extension__ ({ \
+ __m128 __X = (X); \
+ __m128 __Y = (Y); \
+ __m128i __C = (C); \
+ (__m128)__builtin_ia32_vpermil2ps((__v4sf)__X, (__v4sf)__Y, \
+ (__v4si)__C, (I)); })
+
+#define _mm256_permute2_ps(X, Y, C, I) __extension__ ({ \
+ __m256 __X = (X); \
+ __m256 __Y = (Y); \
+ __m256i __C = (C); \
+ (__m256)__builtin_ia32_vpermil2ps256((__v8sf)__X, (__v8sf)__Y, \
+ (__v8si)__C, (I)); })
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_frcz_ss(__m128 __A)
+{
+ return (__m128)__builtin_ia32_vfrczss((__v4sf)__A);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_frcz_sd(__m128d __A)
+{
+ return (__m128d)__builtin_ia32_vfrczsd((__v2df)__A);
+}
+
+static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
+_mm_frcz_ps(__m128 __A)
+{
+ return (__m128)__builtin_ia32_vfrczps((__v4sf)__A);
+}
+
+static __inline__ __m128d __attribute__((__always_inline__, __nodebug__))
+_mm_frcz_pd(__m128d __A)
+{
+ return (__m128d)__builtin_ia32_vfrczpd((__v2df)__A);
+}
+
+static __inline__ __m256 __attribute__((__always_inline__, __nodebug__))
+_mm256_frcz_ps(__m256 __A)
+{
+ return (__m256)__builtin_ia32_vfrczps256((__v8sf)__A);
+}
+
+static __inline__ __m256d __attribute__((__always_inline__, __nodebug__))
+_mm256_frcz_pd(__m256d __A)
+{
+ return (__m256d)__builtin_ia32_vfrczpd256((__v4df)__A);
+}
+
+#endif /* __XOP__ */
+
+#endif /* __XOPINTRIN_H */
diff --git a/lib/Lex/CMakeLists.txt b/lib/Lex/CMakeLists.txt
index 0a2ffdb..241abbc 100644
--- a/lib/Lex/CMakeLists.txt
+++ b/lib/Lex/CMakeLists.txt
@@ -2,8 +2,6 @@
set(LLVM_LINK_COMPONENTS support)
-set(LLVM_USED_LIBS clangBasic)
-
add_clang_library(clangLex
HeaderMap.cpp
HeaderSearch.cpp
@@ -28,4 +26,12 @@ add_clang_library(clangLex
TokenLexer.cpp
)
-add_dependencies(clangLex ClangDiagnosticLex ClangAttrSpellings)
+add_dependencies(clangLex
+ ClangAttrSpellings
+ ClangDiagnosticCommon
+ ClangDiagnosticLex
+ )
+
+target_link_libraries(clangLex
+ clangBasic
+ )
diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp
index d688e23..bb3a673 100644
--- a/lib/Lex/HeaderSearch.cpp
+++ b/lib/Lex/HeaderSearch.cpp
@@ -84,7 +84,7 @@ void HeaderSearch::PrintStats() {
}
/// CreateHeaderMap - This method returns a HeaderMap for the specified
-/// FileEntry, uniquing them through the the 'HeaderMaps' datastructure.
+/// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) {
// We expect the number of headermaps to be small, and almost always empty.
// If it ever grows, use of a linear search should be re-evaluated.
@@ -390,10 +390,10 @@ void HeaderSearch::setTarget(const TargetInfo &Target) {
//===----------------------------------------------------------------------===//
-/// LookupFile - Given a "foo" or <foo> reference, look up the indicated file,
+/// LookupFile - Given a "foo" or \<foo> reference, look up the indicated file,
/// return null on failure. isAngled indicates whether the file reference is
-/// for system #include's or not (i.e. using <> instead of ""). CurFileEnt, if
-/// non-null, indicates where the #including file is, in case a relative search
+/// for system \#include's or not (i.e. using <> instead of ""). CurFileEnt, if
+/// non-null, indicates where the \#including file is, in case a relative search
/// is needed.
const FileEntry *HeaderSearch::LookupFile(
StringRef Filename,
@@ -442,11 +442,19 @@ const FileEntry *HeaderSearch::LookupFile(
// Leave CurDir unset.
// This file is a system header or C++ unfriendly if the old file is.
//
- // Note that the temporary 'DirInfo' is required here, as either call to
- // getFileInfo could resize the vector and we don't want to rely on order
- // of evaluation.
- unsigned DirInfo = getFileInfo(CurFileEnt).DirInfo;
- getFileInfo(FE).DirInfo = DirInfo;
+ // Note that we only use one of FromHFI/ToHFI at once, due to potential
+ // reallocation of the underlying vector potentially making the first
+ // reference binding dangling.
+ HeaderFileInfo &FromHFI = getFileInfo(CurFileEnt);
+ unsigned DirInfo = FromHFI.DirInfo;
+ bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader;
+ StringRef Framework = FromHFI.Framework;
+
+ HeaderFileInfo &ToHFI = getFileInfo(FE);
+ ToHFI.DirInfo = DirInfo;
+ ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader;
+ ToHFI.Framework = Framework;
+
if (SearchPath != NULL) {
StringRef SearchPathRef(CurFileEnt->getDir()->getName());
SearchPath->clear();
@@ -510,6 +518,16 @@ const FileEntry *HeaderSearch::LookupFile(
if (HFI.DirInfo == SrcMgr::C_User && InUserSpecifiedSystemFramework)
HFI.DirInfo = SrcMgr::C_System;
+ // If the filename matches a known system header prefix, override
+ // whether the file is a system header.
+ for (unsigned j = SystemHeaderPrefixes.size(); j; --j) {
+ if (Filename.startswith(SystemHeaderPrefixes[j-1].first)) {
+ HFI.DirInfo = SystemHeaderPrefixes[j-1].second ? SrcMgr::C_System
+ : SrcMgr::C_User;
+ break;
+ }
+ }
+
// If this file is found in a header map and uses the framework style of
// includes, then this header is part of a framework we're building.
if (CurDir->isIndexHeaderMap()) {
@@ -556,7 +574,7 @@ const FileEntry *HeaderSearch::LookupFile(
}
/// LookupSubframeworkHeader - Look up a subframework for the specified
-/// #include file. For example, if #include'ing <HIToolbox/HIToolbox.h> from
+/// \#include file. For example, if \#include'ing <HIToolbox/HIToolbox.h> from
/// within ".../Carbon.framework/Headers/Carbon.h", check to see if HIToolbox
/// is a subframework within Carbon.framework. If so, return the FileEntry
/// for the designated file, otherwise return null.
@@ -739,9 +757,6 @@ void HeaderSearch::setHeaderFileInfoForUID(HeaderFileInfo HFI, unsigned UID) {
FileInfo[UID] = HFI;
}
-/// ShouldEnterIncludeFile - Mark the specified file as a target of of a
-/// #include, #include_next, or #import directive. Return false if #including
-/// the file will have no effect or true if we should include it.
bool HeaderSearch::ShouldEnterIncludeFile(const FileEntry *File, bool isImport){
++NumIncluded; // Count # of attempted #includes.
@@ -1032,4 +1047,3 @@ void HeaderSearch::collectAllModules(llvm::SmallVectorImpl<Module *> &Modules) {
Modules.push_back(M->getValue());
}
}
-
diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp
index 535a852..5212dd8 100644
--- a/lib/Lex/Lexer.cpp
+++ b/lib/Lex/Lexer.cpp
@@ -127,7 +127,7 @@ Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *InputFile, Preprocessor &PP)
}
/// Lexer constructor - Create a new raw lexer object. This object is only
-/// suitable for calls to 'LexRawToken'. This lexer assumes that the text
+/// suitable for calls to 'LexFromRawLexer'. This lexer assumes that the text
/// range will outlive it, so it doesn't take ownership of it.
Lexer::Lexer(SourceLocation fileloc, const LangOptions &langOpts,
const char *BufStart, const char *BufPtr, const char *BufEnd)
@@ -140,7 +140,7 @@ Lexer::Lexer(SourceLocation fileloc, const LangOptions &langOpts,
}
/// Lexer constructor - Create a new raw lexer object. This object is only
-/// suitable for calls to 'LexRawToken'. This lexer assumes that the text
+/// suitable for calls to 'LexFromRawLexer'. This lexer assumes that the text
/// range will outlive it, so it doesn't take ownership of it.
Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *FromFile,
const SourceManager &SM, const LangOptions &langOpts)
@@ -544,7 +544,6 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer,
if (InPreprocessorDirective) {
// If we've hit the end of the file, we're done.
if (TheTok.getKind() == tok::eof) {
- InPreprocessorDirective = false;
break;
}
@@ -820,10 +819,6 @@ static CharSourceRange makeRangeFromFileLocs(CharSourceRange Range,
return CharSourceRange::getCharRange(Begin, End);
}
-/// \brief Accepts a range and returns a character range with file locations.
-///
-/// Returns a null range if a part of the range resides inside a macro
-/// expansion or the range does not reside on the same FileID.
CharSourceRange Lexer::makeFileCharRange(CharSourceRange Range,
const SourceManager &SM,
const LangOptions &LangOpts) {
@@ -1091,20 +1086,21 @@ static inline bool isIdentifierBody(unsigned char c) {
}
/// isHorizontalWhitespace - Return true if this character is horizontal
-/// whitespace: ' ', '\t', '\f', '\v'. Note that this returns false for '\0'.
+/// whitespace: ' ', '\\t', '\\f', '\\v'. Note that this returns false for
+/// '\\0'.
static inline bool isHorizontalWhitespace(unsigned char c) {
return (CharInfo[c] & CHAR_HORZ_WS) ? true : false;
}
/// isVerticalWhitespace - Return true if this character is vertical
-/// whitespace: '\n', '\r'. Note that this returns false for '\0'.
+/// whitespace: '\\n', '\\r'. Note that this returns false for '\\0'.
static inline bool isVerticalWhitespace(unsigned char c) {
return (CharInfo[c] & CHAR_VERT_WS) ? true : false;
}
/// isWhitespace - Return true if this character is horizontal or vertical
-/// whitespace: ' ', '\t', '\f', '\v', '\n', '\r'. Note that this returns false
-/// for '\0'.
+/// whitespace: ' ', '\\t', '\\f', '\\v', '\\n', '\\r'. Note that this returns
+/// false for '\\0'.
static inline bool isWhitespace(unsigned char c) {
return (CharInfo[c] & (CHAR_HORZ_WS|CHAR_VERT_WS)) ? true : false;
}
@@ -1124,6 +1120,11 @@ static inline bool isRawStringDelimBody(unsigned char c) {
true : false;
}
+// Allow external clients to make use of CharInfo.
+bool Lexer::isIdentifierBodyChar(char c, const LangOptions &LangOpts) {
+ return isIdentifierBody(c) || (c == '$' && LangOpts.DollarIdents);
+}
+
//===----------------------------------------------------------------------===//
// Diagnostics forwarding code.
@@ -1564,8 +1565,20 @@ void Lexer::LexNumericConstant(Token &Result, const char *CurPtr) {
}
// If we have a hex FP constant, continue.
- if ((C == '-' || C == '+') && (PrevCh == 'P' || PrevCh == 'p'))
- return LexNumericConstant(Result, ConsumeChar(CurPtr, Size, Result));
+ if ((C == '-' || C == '+') && (PrevCh == 'P' || PrevCh == 'p')) {
+ // Outside C99, we accept hexadecimal floating point numbers as a
+ // not-quite-conforming extension. Only do so if this looks like it's
+ // actually meant to be a hexfloat, and not if it has a ud-suffix.
+ bool IsHexFloat = true;
+ if (!LangOpts.C99) {
+ if (!isHexaLiteral(BufferPtr, LangOpts))
+ IsHexFloat = false;
+ else if (std::find(BufferPtr, CurPtr, '_') != CurPtr)
+ IsHexFloat = false;
+ }
+ if (IsHexFloat)
+ return LexNumericConstant(Result, ConsumeChar(CurPtr, Size, Result));
+ }
// Update the location of token as well as BufferPtr.
const char *TokStart = BufferPtr;
@@ -1635,7 +1648,7 @@ void Lexer::LexStringLiteral(Token &Result, const char *CurPtr,
if (C == '\n' || C == '\r' || // Newline.
(C == 0 && CurPtr-1 == BufferEnd)) { // End of file.
if (!isLexingRawMode() && !LangOpts.AsmPreprocessor)
- Diag(BufferPtr, diag::warn_unterminated_string);
+ Diag(BufferPtr, diag::ext_unterminated_string);
FormTokenWithChars(Result, CurPtr-1, tok::unknown);
return;
}
@@ -1755,7 +1768,7 @@ void Lexer::LexAngledStringLiteral(Token &Result, const char *CurPtr) {
// Skip escaped characters.
if (C == '\\') {
// Skip the escaped character.
- C = getAndAdvanceChar(CurPtr, Result);
+ getAndAdvanceChar(CurPtr, Result);
} else if (C == '\n' || C == '\r' || // Newline.
(C == 0 && (CurPtr-1 == BufferEnd || // End of file.
isCodeCompletionPoint(CurPtr-1)))) {
@@ -1793,7 +1806,7 @@ void Lexer::LexCharConstant(Token &Result, const char *CurPtr,
char C = getAndAdvanceChar(CurPtr, Result);
if (C == '\'') {
if (!isLexingRawMode() && !LangOpts.AsmPreprocessor)
- Diag(BufferPtr, diag::err_empty_character);
+ Diag(BufferPtr, diag::ext_empty_character);
FormTokenWithChars(Result, CurPtr, tok::unknown);
return;
}
@@ -1803,11 +1816,11 @@ void Lexer::LexCharConstant(Token &Result, const char *CurPtr,
if (C == '\\') {
// Skip the escaped character.
// FIXME: UCN's
- C = getAndAdvanceChar(CurPtr, Result);
+ getAndAdvanceChar(CurPtr, Result);
} else if (C == '\n' || C == '\r' || // Newline.
(C == 0 && CurPtr-1 == BufferEnd)) { // End of file.
if (!isLexingRawMode() && !LangOpts.AsmPreprocessor)
- Diag(BufferPtr, diag::warn_unterminated_char);
+ Diag(BufferPtr, diag::ext_unterminated_char);
FormTokenWithChars(Result, CurPtr-1, tok::unknown);
return;
} else if (C == 0) {
@@ -1924,8 +1937,6 @@ bool Lexer::SkipBCPLComment(Token &Result, const char *CurPtr) {
CurPtr = EscapePtr-2;
else
break; // This is a newline, we're done.
-
- C = *CurPtr;
}
// Otherwise, this is a hard case. Fall back on getAndAdvanceChar to
@@ -2022,7 +2033,7 @@ bool Lexer::SaveBCPLComment(Token &Result, const char *CurPtr) {
// directly.
FormTokenWithChars(Result, CurPtr, tok::comment);
- if (!ParsingPreprocessorDirective)
+ if (!ParsingPreprocessorDirective || LexingRawMode)
return true;
// If this BCPL-style comment is in a macro definition, transmogrify it into
@@ -2043,8 +2054,8 @@ bool Lexer::SaveBCPLComment(Token &Result, const char *CurPtr) {
}
/// isBlockCommentEndOfEscapedNewLine - Return true if the specified newline
-/// character (either \n or \r) is part of an escaped newline sequence. Issue a
-/// diagnostic if so. We know that the newline is inside of a block comment.
+/// character (either \\n or \\r) is part of an escaped newline sequence. Issue
+/// a diagnostic if so. We know that the newline is inside of a block comment.
static bool isEndOfBlockCommentWithEscapedNewLine(const char *CurPtr,
Lexer *L) {
assert(CurPtr[0] == '\n' || CurPtr[0] == '\r');
@@ -2110,12 +2121,12 @@ static bool isEndOfBlockCommentWithEscapedNewLine(const char *CurPtr,
#undef bool
#endif
-/// SkipBlockComment - We have just read the /* characters from input. Read
-/// until we find the */ characters that terminate the comment. Note that we
-/// don't bother decoding trigraphs or escaped newlines in block comments,
-/// because they cannot cause the comment to end. The only thing that can
-/// happen is the comment could end with an escaped newline between the */ end
-/// of comment.
+/// We have just read from input the / and * characters that started a comment.
+/// Read until we find the * and / characters that terminate the comment.
+/// Note that we don't bother decoding trigraphs or escaped newlines in block
+/// comments, because they cannot cause the comment to end. The only thing
+/// that can happen is the comment could end with an escaped newline between
+/// the terminating * and /.
///
/// If we're in KeepCommentMode or any CommentHandler has inserted
/// some tokens, this will store the first token and return true.
@@ -2286,10 +2297,9 @@ bool Lexer::SkipBlockComment(Token &Result, const char *CurPtr) {
/// ReadToEndOfLine - Read the rest of the current preprocessor line as an
/// uninterpreted string. This switches the lexer out of directive mode.
-std::string Lexer::ReadToEndOfLine() {
+void Lexer::ReadToEndOfLine(SmallVectorImpl<char> *Result) {
assert(ParsingPreprocessorDirective && ParsingFilename == false &&
"Must be in a preprocessing directive!");
- std::string Result;
Token Tmp;
// CurPtr - Cache BufferPtr in an automatic variable.
@@ -2298,7 +2308,8 @@ std::string Lexer::ReadToEndOfLine() {
char Char = getAndAdvanceChar(CurPtr, Tmp);
switch (Char) {
default:
- Result += Char;
+ if (Result)
+ Result->push_back(Char);
break;
case 0: // Null.
// Found end of file?
@@ -2306,11 +2317,12 @@ std::string Lexer::ReadToEndOfLine() {
if (isCodeCompletionPoint(CurPtr-1)) {
PP->CodeCompleteNaturalLanguage();
cutOffLexing();
- return Result;
+ return;
}
// Nope, normal character, continue.
- Result += Char;
+ if (Result)
+ Result->push_back(Char);
break;
}
// FALL THROUGH.
@@ -2329,8 +2341,8 @@ std::string Lexer::ReadToEndOfLine() {
}
assert(Tmp.is(tok::eod) && "Unexpected token!");
- // Finally, we're done, return the string we found.
- return Result;
+ // Finally, we're done;
+ return;
}
}
}
@@ -2383,7 +2395,7 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
BufferPtr = CurPtr;
// Finally, let the preprocessor handle this.
- return PP->HandleEndOfFile(Result);
+ return PP->HandleEndOfFile(Result, isPragmaLexer());
}
/// isNextPPTokenLParen - Return 1 if the next unexpanded token lexed from
@@ -2418,7 +2430,7 @@ unsigned Lexer::isNextPPTokenLParen() {
return Tok.is(tok::l_paren);
}
-/// FindConflictEnd - Find the end of a version control conflict marker.
+/// \brief Find the end of a version control conflict marker.
static const char *FindConflictEnd(const char *CurPtr, const char *BufferEnd,
ConflictMarkerKind CMK) {
const char *Terminator = CMK == CMK_Perforce ? "<<<<\n" : ">>>>>>>";
@@ -2625,7 +2637,8 @@ LexNextToken:
ParsingPreprocessorDirective = false;
// Restore comment saving mode, in case it was disabled for directive.
- SetCommentRetentionState(PP->getCommentRetentionState());
+ if (PP)
+ SetCommentRetentionState(PP->getCommentRetentionState());
// Since we consumed a newline, we are back at the start of a line.
IsAtStartOfLine = true;
diff --git a/lib/Lex/LiteralSupport.cpp b/lib/Lex/LiteralSupport.cpp
index c1d228b..9e3c778 100644
--- a/lib/Lex/LiteralSupport.cpp
+++ b/lib/Lex/LiteralSupport.cpp
@@ -250,6 +250,39 @@ static bool ProcessUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf,
return true;
}
+/// MeasureUCNEscape - Determine the number of bytes within the resulting string
+/// which this UCN will occupy.
+static int MeasureUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf,
+ const char *ThisTokEnd, unsigned CharByteWidth,
+ const LangOptions &Features, bool &HadError) {
+ // UTF-32: 4 bytes per escape.
+ if (CharByteWidth == 4)
+ return 4;
+
+ uint32_t UcnVal = 0;
+ unsigned short UcnLen = 0;
+ FullSourceLoc Loc;
+
+ if (!ProcessUCNEscape(ThisTokBegin, ThisTokBuf, ThisTokEnd, UcnVal,
+ UcnLen, Loc, 0, Features, true)) {
+ HadError = true;
+ return 0;
+ }
+
+ // UTF-16: 2 bytes for BMP, 4 bytes otherwise.
+ if (CharByteWidth == 2)
+ return UcnVal <= 0xFFFF ? 2 : 4;
+
+ // UTF-8.
+ if (UcnVal < 0x80)
+ return 1;
+ if (UcnVal < 0x800)
+ return 2;
+ if (UcnVal < 0x10000)
+ return 3;
+ return 4;
+}
+
/// EncodeUCNEscape - Read the Universal Character Name, check constraints and
/// convert the UTF32 to UTF8 or UTF16. This is a subroutine of
/// StringLiteralParser. When we decide to implement UCN's for identifiers,
@@ -265,7 +298,7 @@ static void EncodeUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf,
unsigned short UcnLen = 0;
if (!ProcessUCNEscape(ThisTokBegin, ThisTokBuf, ThisTokEnd, UcnVal, UcnLen,
Loc, Diags, Features, true)) {
- HadError = 1;
+ HadError = true;
return;
}
@@ -289,7 +322,7 @@ static void EncodeUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf,
// using reinterpret_cast.
UTF16 *ResultPtr = reinterpret_cast<UTF16*>(ResultBuf);
- if (UcnVal < (UTF32)0xFFFF) {
+ if (UcnVal <= (UTF32)0xFFFF) {
*ResultPtr = UcnVal;
ResultBuf += 2;
return;
@@ -756,6 +789,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) {
}
+/// \verbatim
/// user-defined-character-literal: [C++11 lex.ext]
/// character-literal ud-suffix
/// ud-suffix:
@@ -791,6 +825,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) {
/// \U hex-quad hex-quad
/// hex-quad:
/// hex-digit hex-digit hex-digit hex-digit
+/// \endverbatim
///
CharLiteralParser::CharLiteralParser(const char *begin, const char *end,
SourceLocation Loc, Preprocessor &PP,
@@ -971,7 +1006,7 @@ CharLiteralParser::CharLiteralParser(const char *begin, const char *end,
Value = (signed char)Value;
}
-
+/// \verbatim
/// string-literal: [C++0x lex.string]
/// encoding-prefix " [s-char-sequence] "
/// encoding-prefix R raw-string
@@ -1023,6 +1058,7 @@ CharLiteralParser::CharLiteralParser(const char *begin, const char *end,
/// \U hex-quad hex-quad
/// hex-quad:
/// hex-digit hex-digit hex-digit hex-digit
+/// \endverbatim
///
StringLiteralParser::
StringLiteralParser(const Token *StringToks, unsigned NumStringToks,
@@ -1037,10 +1073,8 @@ StringLiteralParser(const Token *StringToks, unsigned NumStringToks,
void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){
// The literal token may have come from an invalid source location (e.g. due
// to a PCH error), in which case the token length will be 0.
- if (NumStringToks == 0 || StringToks[0].getLength() < 2) {
- hadError = true;
- return;
- }
+ if (NumStringToks == 0 || StringToks[0].getLength() < 2)
+ return DiagnoseLexingError(SourceLocation());
// Scan all of the string portions, remember the max individual token length,
// computing a bound on the concatenated string length, and see whether any
@@ -1057,10 +1091,8 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){
// Implement Translation Phase #6: concatenation of string literals
/// (C99 5.1.1.2p1). The common case is only one string fragment.
for (unsigned i = 1; i != NumStringToks; ++i) {
- if (StringToks[i].getLength() < 2) {
- hadError = true;
- return;
- }
+ if (StringToks[i].getLength() < 2)
+ return DiagnoseLexingError(StringToks[i].getLocation());
// The string could be shorter than this if it needs cleaning, but this is a
// reasonable bound, which is all we need.
@@ -1123,10 +1155,8 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){
unsigned ThisTokLen =
Lexer::getSpelling(StringToks[i], ThisTokBuf, SM, Features,
&StringInvalid);
- if (StringInvalid) {
- hadError = true;
- continue;
- }
+ if (StringInvalid)
+ return DiagnoseLexingError(StringToks[i].getLocation());
const char *ThisTokBegin = ThisTokBuf;
const char *ThisTokEnd = ThisTokBuf+ThisTokLen;
@@ -1192,7 +1222,11 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){
if (DiagnoseBadString(StringToks[i]))
hadError = true;
} else {
- assert(ThisTokBuf[0] == '"' && "Expected quote, lexer broken?");
+ if (ThisTokBuf[0] != '"') {
+ // The file may have come from PCH and then changed after loading the
+ // PCH; Fail gracefully.
+ return DiagnoseLexingError(StringToks[i].getLocation());
+ }
++ThisTokBuf; // skip "
// Check if this is a pascal string
@@ -1296,45 +1330,10 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){
}
}
-
/// copyStringFragment - This function copies from Start to End into ResultPtr.
/// Performs widening for multi-byte characters.
bool StringLiteralParser::CopyStringFragment(StringRef Fragment) {
- assert(CharByteWidth==1 || CharByteWidth==2 || CharByteWidth==4);
- ConversionResult result = conversionOK;
- // Copy the character span over.
- if (CharByteWidth == 1) {
- if (!isLegalUTF8String(reinterpret_cast<const UTF8*>(Fragment.begin()),
- reinterpret_cast<const UTF8*>(Fragment.end())))
- result = sourceIllegal;
- memcpy(ResultPtr, Fragment.data(), Fragment.size());
- ResultPtr += Fragment.size();
- } else if (CharByteWidth == 2) {
- UTF8 const *sourceStart = (UTF8 const *)Fragment.data();
- // FIXME: Make the type of the result buffer correct instead of
- // using reinterpret_cast.
- UTF16 *targetStart = reinterpret_cast<UTF16*>(ResultPtr);
- ConversionFlags flags = strictConversion;
- result = ConvertUTF8toUTF16(
- &sourceStart,sourceStart + Fragment.size(),
- &targetStart,targetStart + 2*Fragment.size(),flags);
- if (result==conversionOK)
- ResultPtr = reinterpret_cast<char*>(targetStart);
- } else if (CharByteWidth == 4) {
- UTF8 const *sourceStart = (UTF8 const *)Fragment.data();
- // FIXME: Make the type of the result buffer correct instead of
- // using reinterpret_cast.
- UTF32 *targetStart = reinterpret_cast<UTF32*>(ResultPtr);
- ConversionFlags flags = strictConversion;
- result = ConvertUTF8toUTF32(
- &sourceStart,sourceStart + Fragment.size(),
- &targetStart,targetStart + 4*Fragment.size(),flags);
- if (result==conversionOK)
- ResultPtr = reinterpret_cast<char*>(targetStart);
- }
- assert((result != targetExhausted)
- && "ConvertUTF8toUTFXX exhausted target buffer");
- return result != conversionOK;
+ return !ConvertUTF8toWide(CharByteWidth, Fragment, ResultPtr);
}
bool StringLiteralParser::DiagnoseBadString(const Token &Tok) {
@@ -1349,6 +1348,12 @@ bool StringLiteralParser::DiagnoseBadString(const Token &Tok) {
return !NoErrorOnBadEncoding;
}
+void StringLiteralParser::DiagnoseLexingError(SourceLocation Loc) {
+ hadError = true;
+ if (Diags)
+ Diags->Report(Loc, diag::err_lexing_string);
+}
+
/// getOffsetOfStringByte - This function returns the offset of the
/// specified byte of the string data represented by Token. This handles
/// advancing over escape sequences in the string.
@@ -1365,14 +1370,31 @@ unsigned StringLiteralParser::getOffsetOfStringByte(const Token &Tok,
if (StringInvalid)
return 0;
+ const char *SpellingStart = SpellingPtr;
+ const char *SpellingEnd = SpellingPtr+TokLen;
+
+ // Handle UTF-8 strings just like narrow strings.
+ if (SpellingPtr[0] == 'u' && SpellingPtr[1] == '8')
+ SpellingPtr += 2;
+
assert(SpellingPtr[0] != 'L' && SpellingPtr[0] != 'u' &&
SpellingPtr[0] != 'U' && "Doesn't handle wide or utf strings yet");
+ // For raw string literals, this is easy.
+ if (SpellingPtr[0] == 'R') {
+ assert(SpellingPtr[1] == '"' && "Should be a raw string literal!");
+ // Skip 'R"'.
+ SpellingPtr += 2;
+ while (*SpellingPtr != '(') {
+ ++SpellingPtr;
+ assert(SpellingPtr < SpellingEnd && "Missing ( for raw string literal");
+ }
+ // Skip '('.
+ ++SpellingPtr;
+ return SpellingPtr - SpellingStart + ByteNo;
+ }
- const char *SpellingStart = SpellingPtr;
- const char *SpellingEnd = SpellingPtr+TokLen;
-
- // Skip over the leading quote.
+ // Skip over the leading quote
assert(SpellingPtr[0] == '"' && "Should be a string literal!");
++SpellingPtr;
@@ -1389,11 +1411,23 @@ unsigned StringLiteralParser::getOffsetOfStringByte(const Token &Tok,
// Otherwise, this is an escape character. Advance over it.
bool HadError = false;
- ProcessCharEscape(SpellingPtr, SpellingEnd, HadError,
- FullSourceLoc(Tok.getLocation(), SM),
- CharByteWidth*8, Diags);
+ if (SpellingPtr[1] == 'u' || SpellingPtr[1] == 'U') {
+ const char *EscapePtr = SpellingPtr;
+ unsigned Len = MeasureUCNEscape(SpellingStart, SpellingPtr, SpellingEnd,
+ 1, Features, HadError);
+ if (Len > ByteNo) {
+ // ByteNo is somewhere within the escape sequence.
+ SpellingPtr = EscapePtr;
+ break;
+ }
+ ByteNo -= Len;
+ } else {
+ ProcessCharEscape(SpellingPtr, SpellingEnd, HadError,
+ FullSourceLoc(Tok.getLocation(), SM),
+ CharByteWidth*8, Diags);
+ --ByteNo;
+ }
assert(!HadError && "This method isn't valid on erroneous strings");
- --ByteNo;
}
return SpellingPtr-SpellingStart;
diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp
index 625a204..74b9cbc 100644
--- a/lib/Lex/PPDirectives.cpp
+++ b/lib/Lex/PPDirectives.cpp
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file implements # directive processing for the Preprocessor.
-//
+///
+/// \file
+/// \brief Implements # directive processing for the Preprocessor.
+///
//===----------------------------------------------------------------------===//
#include "clang/Lex/Preprocessor.h"
@@ -61,8 +62,8 @@ MacroInfo *Preprocessor::CloneMacroInfo(const MacroInfo &MacroToClone) {
return MI;
}
-/// ReleaseMacroInfo - Release the specified MacroInfo. This memory will
-/// be reused for allocating new MacroInfo objects.
+/// \brief Release the specified MacroInfo to be reused for allocating
+/// new MacroInfo objects.
void Preprocessor::ReleaseMacroInfo(MacroInfo *MI) {
MacroInfoChain *MIChain = (MacroInfoChain*) MI;
if (MacroInfoChain *Prev = MIChain->Prev) {
@@ -82,8 +83,8 @@ void Preprocessor::ReleaseMacroInfo(MacroInfo *MI) {
MI->Destroy();
}
-/// DiscardUntilEndOfDirective - Read and discard all tokens remaining on the
-/// current line until the tok::eod token is found.
+/// \brief Read and discard all tokens remaining on the current line until
+/// the tok::eod token is found.
void Preprocessor::DiscardUntilEndOfDirective() {
Token Tmp;
do {
@@ -92,11 +93,13 @@ void Preprocessor::DiscardUntilEndOfDirective() {
} while (Tmp.isNot(tok::eod));
}
-/// ReadMacroName - Lex and validate a macro name, which occurs after a
-/// #define or #undef. This sets the token kind to eod and discards the rest
-/// of the macro line if the macro name is invalid. isDefineUndef is 1 if
-/// this is due to a a #define, 2 if #undef directive, 0 if it is something
-/// else (e.g. #ifdef).
+/// \brief Lex and validate a macro name, which occurs after a
+/// \#define or \#undef.
+///
+/// This sets the token kind to eod and discards the rest
+/// of the macro line if the macro name is invalid. \p isDefineUndef is 1 if
+/// this is due to a a \#define, 2 if \#undef directive, 0 if it is something
+/// else (e.g. \#ifdef).
void Preprocessor::ReadMacroName(Token &MacroNameTok, char isDefineUndef) {
// Read the token, don't allow macro expansion on it.
LexUnexpandedToken(MacroNameTok);
@@ -157,8 +160,9 @@ void Preprocessor::ReadMacroName(Token &MacroNameTok, char isDefineUndef) {
return DiscardUntilEndOfDirective();
}
-/// CheckEndOfDirective - Ensure that the next token is a tok::eod token. If
-/// not, emit a diagnostic and consume up until the eod. If EnableMacros is
+/// \brief Ensure that the next token is a tok::eod token.
+///
+/// If not, emit a diagnostic and consume up until the eod. If EnableMacros is
/// true, then we consider macros that expand to zero tokens as being ok.
void Preprocessor::CheckEndOfDirective(const char *DirType, bool EnableMacros) {
Token Tmp;
@@ -191,14 +195,14 @@ void Preprocessor::CheckEndOfDirective(const char *DirType, bool EnableMacros) {
-/// SkipExcludedConditionalBlock - We just read a #if or related directive and
-/// decided that the subsequent tokens are in the #if'd out portion of the
-/// file. Lex the rest of the file, until we see an #endif. If
+/// SkipExcludedConditionalBlock - We just read a \#if or related directive and
+/// decided that the subsequent tokens are in the \#if'd out portion of the
+/// file. Lex the rest of the file, until we see an \#endif. If
/// FoundNonSkipPortion is true, then we have already emitted code for part of
-/// this #if directive, so #else/#elif blocks should never be entered. If ElseOk
-/// is true, then #else directives are ok, if not, then we have already seen one
-/// so a #else directive is a duplicate. When this returns, the caller can lex
-/// the first valid token.
+/// this \#if directive, so \#else/\#elif blocks should never be entered.
+/// If ElseOk is true, then \#else directives are ok, if not, then we have
+/// already seen one so a \#else directive is a duplicate. When this returns,
+/// the caller can lex the first valid token.
void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
bool FoundNonSkipPortion,
bool FoundElse,
@@ -317,7 +321,6 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
} else if (Directive[0] == 'e') {
StringRef Sub = Directive.substr(1);
if (Sub == "ndif") { // "endif"
- CheckEndOfDirective("endif");
PPConditionalInfo CondInfo;
CondInfo.WasSkipping = true; // Silence bogus warning.
bool InCond = CurPPLexer->popConditionalLevel(CondInfo);
@@ -326,9 +329,16 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
// If we popped the outermost skipping block, we're done skipping!
if (!CondInfo.WasSkipping) {
+ // Restore the value of LexingRawMode so that trailing comments
+ // are handled correctly, if we've reached the outermost block.
+ CurPPLexer->LexingRawMode = false;
+ CheckEndOfDirective("endif");
+ CurPPLexer->LexingRawMode = true;
if (Callbacks)
Callbacks->Endif(Tok.getLocation(), CondInfo.IfLoc);
break;
+ } else {
+ DiscardUntilEndOfDirective();
}
} else if (Sub == "lse") { // "else".
// #else directive in a skipping conditional. If not in some other
@@ -346,7 +356,11 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
// entered, enter the #else block now.
if (!CondInfo.WasSkipping && !CondInfo.FoundNonSkip) {
CondInfo.FoundNonSkip = true;
+ // Restore the value of LexingRawMode so that trailing comments
+ // are handled correctly.
+ CurPPLexer->LexingRawMode = false;
CheckEndOfDirective("else");
+ CurPPLexer->LexingRawMode = true;
if (Callbacks)
Callbacks->Else(Tok.getLocation(), CondInfo.IfLoc);
break;
@@ -484,9 +498,6 @@ void Preprocessor::PTHSkipExcludedConditionalBlock() {
}
}
-/// LookupFile - Given a "foo" or <foo> reference, look up the indicated file,
-/// return null on failure. isAngled indicates whether the file reference is
-/// for system #include's or not (i.e. using <> instead of "").
const FileEntry *Preprocessor::LookupFile(
StringRef Filename,
bool isAngled,
@@ -553,6 +564,21 @@ const FileEntry *Preprocessor::LookupFile(
// Preprocessor Directive Handling.
//===----------------------------------------------------------------------===//
+class Preprocessor::ResetMacroExpansionHelper {
+public:
+ ResetMacroExpansionHelper(Preprocessor *pp)
+ : PP(pp), save(pp->DisableMacroExpansion) {
+ if (pp->MacroExpansionInDirectivesOverride)
+ pp->DisableMacroExpansion = false;
+ }
+ ~ResetMacroExpansionHelper() {
+ PP->DisableMacroExpansion = save;
+ }
+private:
+ Preprocessor *PP;
+ bool save;
+};
+
/// HandleDirective - This callback is invoked when the lexer sees a # token
/// at the start of a line. This consumes the directive, modifies the
/// lexer/preprocessor state, and advances the lexer(s) so that the next token
@@ -604,6 +630,10 @@ void Preprocessor::HandleDirective(Token &Result) {
Diag(Result, diag::ext_embedded_directive);
}
+ // Temporarily enable macro expansion if set so
+ // and reset to previous state when returning from this function.
+ ResetMacroExpansionHelper helper(this);
+
TryAgain:
switch (Result.getKind()) {
case tok::eod:
@@ -774,23 +804,19 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val,
Val = NextVal;
}
- // Reject 0, this is needed both by #line numbers and flags.
- if (Val == 0) {
- PP.Diag(DigitTok, DiagID);
- PP.DiscardUntilEndOfDirective();
- return true;
- }
-
- if (DigitTokBegin[0] == '0')
+ if (DigitTokBegin[0] == '0' && Val)
PP.Diag(DigitTok.getLocation(), diag::warn_pp_line_decimal);
return false;
}
-/// HandleLineDirective - Handle #line directive: C99 6.10.4. The two
-/// acceptable forms are:
+/// \brief Handle a \#line directive: C99 6.10.4.
+///
+/// The two acceptable forms are:
+/// \verbatim
/// # line digit-sequence
/// # line digit-sequence "s-char-sequence"
+/// \endverbatim
void Preprocessor::HandleLineDirective(Token &Tok) {
// Read the line # and string argument. Per C99 6.10.4p5, these tokens are
// expanded.
@@ -801,6 +827,9 @@ void Preprocessor::HandleLineDirective(Token &Tok) {
unsigned LineNo;
if (GetLineValue(DigitTok, LineNo, diag::err_pp_line_requires_integer,*this))
return;
+
+ if (LineNo == 0)
+ Diag(DigitTok, diag::ext_pp_line_zero);
// Enforce C99 6.10.4p3: "The digit sequence shall not specify ... a
// number greater than 2147483647". C90 requires that the line # be <= 32767.
@@ -1018,15 +1047,13 @@ void Preprocessor::HandleUserDiagnosticDirective(Token &Tok,
// tokens. For example, this is allowed: "#warning ` 'foo". GCC does
// collapse multiple consequtive white space between tokens, but this isn't
// specified by the standard.
- std::string Message = CurLexer->ReadToEndOfLine();
+ SmallString<128> Message;
+ CurLexer->ReadToEndOfLine(&Message);
// Find the first non-whitespace character, so that we can make the
// diagnostic more succinct.
- StringRef Msg(Message);
- size_t i = Msg.find_first_not_of(' ');
- if (i < Msg.size())
- Msg = Msg.substr(i);
-
+ StringRef Msg = Message.str().ltrim(" ");
+
if (isWarning)
Diag(Tok, diag::pp_hash_warning) << Msg;
else
@@ -1135,7 +1162,7 @@ void Preprocessor::HandleMacroPrivateDirective(Token &Tok) {
//===----------------------------------------------------------------------===//
/// GetIncludeFilenameSpelling - Turn the specified lexer token into a fully
-/// checked and spelled filename, e.g. as an operand of #include. This returns
+/// checked and spelled filename, e.g. as an operand of \#include. This returns
/// true if the input filename was in <>'s or false if it were in ""'s. The
/// caller is expected to provide a buffer that is large enough to hold the
/// spelling of the filename, but is also expected to handle the case when
@@ -1179,11 +1206,14 @@ bool Preprocessor::GetIncludeFilenameSpelling(SourceLocation Loc,
return isAngled;
}
-/// ConcatenateIncludeName - Handle cases where the #include name is expanded
-/// from a macro as multiple tokens, which need to be glued together. This
-/// occurs for code like:
-/// #define FOO <a/b.h>
-/// #include FOO
+/// \brief Handle cases where the \#include name is expanded from a macro
+/// as multiple tokens, which need to be glued together.
+///
+/// This occurs for code like:
+/// \code
+/// \#define FOO <a/b.h>
+/// \#include FOO
+/// \endcode
/// because in this case, "<a/b.h>" is returned as 7 tokens, not one.
///
/// This code concatenates and consumes tokens up to the '>' token. It returns
@@ -1238,10 +1268,10 @@ bool Preprocessor::ConcatenateIncludeName(
return true;
}
-/// HandleIncludeDirective - The "#include" tokens have just been read, read the
-/// file to be included from the lexer, then include it! This is a common
-/// routine with functionality shared between #include, #include_next and
-/// #import. LookupFrom is set when this is a #include_next directive, it
+/// HandleIncludeDirective - The "\#include" tokens have just been read, read
+/// the file to be included from the lexer, then include it! This is a common
+/// routine with functionality shared between \#include, \#include_next and
+/// \#import. LookupFrom is set when this is a \#include_next directive, it
/// specifies the file to start searching from.
void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
Token &IncludeTok,
@@ -1360,9 +1390,28 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
}
if (File == 0) {
- if (!SuppressIncludeNotFoundError)
- Diag(FilenameTok, diag::err_pp_file_not_found) << Filename;
- return;
+ if (!SuppressIncludeNotFoundError) {
+ // If the file could not be located and it was included via angle
+ // brackets, we can attempt a lookup as though it were a quoted path to
+ // provide the user with a possible fixit.
+ if (isAngled) {
+ File = LookupFile(Filename, false, LookupFrom, CurDir,
+ Callbacks ? &SearchPath : 0,
+ Callbacks ? &RelativePath : 0,
+ getLangOpts().Modules ? &SuggestedModule : 0);
+ if (File) {
+ SourceRange Range(FilenameTok.getLocation(), CharEnd);
+ Diag(FilenameTok, diag::err_pp_file_not_found_not_fatal) <<
+ Filename <<
+ FixItHint::CreateReplacement(Range, "\"" + Filename.str() + "\"");
+ }
+ }
+ // If the file is still not found, just go with the vanilla diagnostic
+ if (!File)
+ Diag(FilenameTok, diag::err_pp_file_not_found) << Filename;
+ }
+ if (!File)
+ return;
}
// If we are supposed to import a module rather than including the header,
@@ -1465,7 +1514,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
EnterSourceFile(FID, CurDir, FilenameTok.getLocation());
}
-/// HandleIncludeNextDirective - Implements #include_next.
+/// HandleIncludeNextDirective - Implements \#include_next.
///
void Preprocessor::HandleIncludeNextDirective(SourceLocation HashLoc,
Token &IncludeNextTok) {
@@ -1488,7 +1537,7 @@ void Preprocessor::HandleIncludeNextDirective(SourceLocation HashLoc,
return HandleIncludeDirective(HashLoc, IncludeNextTok, Lookup);
}
-/// HandleMicrosoftImportDirective - Implements #import for Microsoft Mode
+/// HandleMicrosoftImportDirective - Implements \#import for Microsoft Mode
void Preprocessor::HandleMicrosoftImportDirective(Token &Tok) {
// The Microsoft #import directive takes a type library and generates header
// files from it, and includes those. This is beyond the scope of what clang
@@ -1502,7 +1551,7 @@ void Preprocessor::HandleMicrosoftImportDirective(Token &Tok) {
DiscardUntilEndOfDirective();
}
-/// HandleImportDirective - Implements #import.
+/// HandleImportDirective - Implements \#import.
///
void Preprocessor::HandleImportDirective(SourceLocation HashLoc,
Token &ImportTok) {
@@ -1634,7 +1683,7 @@ bool Preprocessor::ReadMacroDefinitionArgList(MacroInfo *MI, Token &Tok) {
}
}
-/// HandleDefineDirective - Implements #define. This consumes the entire macro
+/// HandleDefineDirective - Implements \#define. This consumes the entire macro
/// line then lets the caller lex the next real token.
void Preprocessor::HandleDefineDirective(Token &DefineTok) {
++NumDefined;
@@ -1841,7 +1890,7 @@ void Preprocessor::HandleDefineDirective(Token &DefineTok) {
Callbacks->MacroDefined(MacroNameTok, MI);
}
-/// HandleUndefDirective - Implements #undef.
+/// HandleUndefDirective - Implements \#undef.
///
void Preprocessor::HandleUndefDirective(Token &UndefTok) {
++NumUndefined;
@@ -1882,10 +1931,10 @@ void Preprocessor::HandleUndefDirective(Token &UndefTok) {
// Preprocessor Conditional Directive Handling.
//===----------------------------------------------------------------------===//
-/// HandleIfdefDirective - Implements the #ifdef/#ifndef directive. isIfndef is
-/// true when this is a #ifndef directive. ReadAnyTokensBeforeDirective is true
-/// if any tokens have been returned or pp-directives activated before this
-/// #ifndef has been lexed.
+/// HandleIfdefDirective - Implements the \#ifdef/\#ifndef directive. isIfndef
+/// is true when this is a \#ifndef directive. ReadAnyTokensBeforeDirective is
+/// true if any tokens have been returned or pp-directives activated before this
+/// \#ifndef has been lexed.
///
void Preprocessor::HandleIfdefDirective(Token &Result, bool isIfndef,
bool ReadAnyTokensBeforeDirective) {
@@ -1947,7 +1996,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result, bool isIfndef,
}
}
-/// HandleIfDirective - Implements the #if directive.
+/// HandleIfDirective - Implements the \#if directive.
///
void Preprocessor::HandleIfDirective(Token &IfToken,
bool ReadAnyTokensBeforeDirective) {
@@ -1984,7 +2033,7 @@ void Preprocessor::HandleIfDirective(Token &IfToken,
}
}
-/// HandleEndifDirective - Implements the #endif directive.
+/// HandleEndifDirective - Implements the \#endif directive.
///
void Preprocessor::HandleEndifDirective(Token &EndifToken) {
++NumEndif;
@@ -2010,7 +2059,7 @@ void Preprocessor::HandleEndifDirective(Token &EndifToken) {
Callbacks->Endif(EndifToken.getLocation(), CondInfo.IfLoc);
}
-/// HandleElseDirective - Implements the #else directive.
+/// HandleElseDirective - Implements the \#else directive.
///
void Preprocessor::HandleElseDirective(Token &Result) {
++NumElse;
@@ -2039,7 +2088,7 @@ void Preprocessor::HandleElseDirective(Token &Result) {
/*FoundElse*/true, Result.getLocation());
}
-/// HandleElifDirective - Implements the #elif directive.
+/// HandleElifDirective - Implements the \#elif directive.
///
void Preprocessor::HandleElifDirective(Token &ElifToken) {
++NumElse;
diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp
index b6689df..e824320 100644
--- a/lib/Lex/PPLexerChange.cpp
+++ b/lib/Lex/PPLexerChange.cpp
@@ -31,7 +31,7 @@ PPCallbacks::~PPCallbacks() {}
//===----------------------------------------------------------------------===//
/// isInPrimaryFile - Return true if we're in the top-level file, not in a
-/// #include. This looks through macro expansions and active _Pragma lexers.
+/// \#include. This looks through macro expansions and active _Pragma lexers.
bool Preprocessor::isInPrimaryFile() const {
if (IsFileLexer())
return IncludeMacroStack.empty();
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index fe70585..ebdb644 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -215,7 +215,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
// If this is a function-like macro, read the arguments.
if (MI->isFunctionLike()) {
- // C99 6.10.3p10: If the preprocessing token immediately after the the macro
+ // C99 6.10.3p10: If the preprocessing token immediately after the macro
// name isn't a '(', this macro should not be expanded.
if (!isNextPPTokenLParen())
return true;
@@ -242,9 +242,27 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
// Remember where the token is expanded.
SourceLocation ExpandLoc = Identifier.getLocation();
-
- if (Callbacks) Callbacks->MacroExpands(Identifier, MI,
- SourceRange(ExpandLoc, ExpansionEnd));
+ SourceRange ExpansionRange(ExpandLoc, ExpansionEnd);
+
+ if (Callbacks) {
+ if (InMacroArgs) {
+ // We can have macro expansion inside a conditional directive while
+ // reading the function macro arguments. To ensure, in that case, that
+ // MacroExpands callbacks still happen in source order, queue this
+ // callback to have it happen after the function macro callback.
+ DelayedMacroExpandsCallbacks.push_back(
+ MacroExpandsInfo(Identifier, MI, ExpansionRange));
+ } else {
+ Callbacks->MacroExpands(Identifier, MI, ExpansionRange);
+ if (!DelayedMacroExpandsCallbacks.empty()) {
+ for (unsigned i=0, e = DelayedMacroExpandsCallbacks.size(); i!=e; ++i) {
+ MacroExpandsInfo &Info = DelayedMacroExpandsCallbacks[i];
+ Callbacks->MacroExpands(Info.Tok, Info.MI, Info.Range);
+ }
+ DelayedMacroExpandsCallbacks.clear();
+ }
+ }
+ }
// If we started lexing a macro, enter the macro expansion body.
@@ -469,10 +487,12 @@ MacroArgs *Preprocessor::ReadFunctionLikeMacroArgs(Token &MacroName,
} else if (MI->isVariadic() &&
(NumActuals+1 == MinArgsExpected || // A(x, ...) -> A(X)
(NumActuals == 0 && MinArgsExpected == 2))) {// A(x,...) -> A()
- // Varargs where the named vararg parameter is missing: ok as extension.
- // #define A(x, ...)
- // A("blah")
+ // Varargs where the named vararg parameter is missing: OK as extension.
+ // #define A(x, ...)
+ // A("blah")
Diag(Tok, diag::ext_missing_varargs_arg);
+ Diag(MI->getDefinitionLoc(), diag::note_macro_here)
+ << MacroName.getIdentifierInfo();
// Remember this occurred, allowing us to elide the comma when used for
// cases like:
@@ -599,6 +619,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("address_sanitizer", LangOpts.AddressSanitizer)
.Case("attribute_analyzer_noreturn", true)
.Case("attribute_availability", true)
+ .Case("attribute_availability_with_message", true)
.Case("attribute_cf_returns_not_retained", true)
.Case("attribute_cf_returns_retained", true)
.Case("attribute_deprecated_with_message", true)
@@ -612,6 +633,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("attribute_objc_method_family", true)
.Case("attribute_overloadable", true)
.Case("attribute_unavailable_with_message", true)
+ .Case("attribute_unused_on_fields", true)
.Case("blocks", LangOpts.Blocks)
.Case("cxx_exceptions", LangOpts.Exceptions)
.Case("cxx_rtti", LangOpts.RTTI)
@@ -625,15 +647,16 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("objc_fixed_enum", LangOpts.ObjC2)
.Case("objc_instancetype", LangOpts.ObjC2)
.Case("objc_modules", LangOpts.ObjC2 && LangOpts.Modules)
- .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI)
- .Case("objc_weak_class", LangOpts.ObjCNonFragileABI)
+ .Case("objc_nonfragile_abi", LangOpts.ObjCRuntime.isNonFragile())
+ .Case("objc_weak_class", LangOpts.ObjCRuntime.hasWeakClassImport())
.Case("ownership_holds", true)
.Case("ownership_returns", true)
.Case("ownership_takes", true)
.Case("objc_bool", true)
- .Case("objc_subscripting", LangOpts.ObjCNonFragileABI)
+ .Case("objc_subscripting", LangOpts.ObjCRuntime.isNonFragile())
.Case("objc_array_literals", LangOpts.ObjC2)
.Case("objc_dictionary_literals", LangOpts.ObjC2)
+ .Case("objc_boxed_expressions", LangOpts.ObjC2)
.Case("arc_cf_code_audited", true)
// C11 features
.Case("c_alignas", LangOpts.C11)
@@ -772,6 +795,7 @@ static bool HasAttribute(const IdentifierInfo *II) {
if (Name.startswith("__") && Name.endswith("__") && Name.size() >= 4)
Name = Name.substr(2, Name.size() - 4);
+ // FIXME: Do we need to handle namespaces here?
return llvm::StringSwitch<bool>(Name)
#include "clang/Lex/AttrSpellings.inc"
.Default(false);
@@ -1030,7 +1054,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
if (Tok.is(tok::l_paren)) {
// Read the identifier
Lex(Tok);
- if (Tok.is(tok::identifier)) {
+ if (Tok.is(tok::identifier) || Tok.is(tok::kw_const)) {
FeatureII = Tok.getIdentifierInfo();
// Read the ')'.
diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp
index e2a192b..c9cc4ad 100644
--- a/lib/Lex/Pragma.cpp
+++ b/lib/Lex/Pragma.cpp
@@ -100,9 +100,12 @@ void PragmaNamespace::HandlePragma(Preprocessor &PP,
// Preprocessor Pragma Directive Handling.
//===----------------------------------------------------------------------===//
-/// HandlePragmaDirective - The "#pragma" directive has been parsed. Lex the
+/// HandlePragmaDirective - The "\#pragma" directive has been parsed. Lex the
/// rest of the pragma, passing it to the registered pragma handlers.
void Preprocessor::HandlePragmaDirective(unsigned Introducer) {
+ if (!PragmasEnabled)
+ return;
+
++NumPragma;
// Invoke the first level of pragma handlers which reads the namespace id.
@@ -314,7 +317,7 @@ void Preprocessor::HandleMicrosoft__pragma(Token &Tok) {
return Lex(Tok);
}
-/// HandlePragmaOnce - Handle #pragma once. OnceTok is the 'once'.
+/// HandlePragmaOnce - Handle \#pragma once. OnceTok is the 'once'.
///
void Preprocessor::HandlePragmaOnce(Token &OnceTok) {
if (isInPrimaryFile()) {
@@ -336,7 +339,7 @@ void Preprocessor::HandlePragmaMark() {
}
-/// HandlePragmaPoison - Handle #pragma GCC poison. PoisonTok is the 'poison'.
+/// HandlePragmaPoison - Handle \#pragma GCC poison. PoisonTok is the 'poison'.
///
void Preprocessor::HandlePragmaPoison(Token &PoisonTok) {
Token Tok;
@@ -378,7 +381,7 @@ void Preprocessor::HandlePragmaPoison(Token &PoisonTok) {
}
}
-/// HandlePragmaSystemHeader - Implement #pragma GCC system_header. We know
+/// HandlePragmaSystemHeader - Implement \#pragma GCC system_header. We know
/// that the whole directive has been parsed.
void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) {
if (isInPrimaryFile()) {
@@ -411,7 +414,7 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) {
false, false, true, false);
}
-/// HandlePragmaDependency - Handle #pragma GCC dependency "foo" blah.
+/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah.
///
void Preprocessor::HandlePragmaDependency(Token &DependencyTok) {
Token FilenameTok;
@@ -464,9 +467,12 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) {
}
}
-/// HandlePragmaComment - Handle the microsoft #pragma comment extension. The
-/// syntax is:
-/// #pragma comment(linker, "foo")
+/// \brief Handle the microsoft \#pragma comment extension.
+///
+/// The syntax is:
+/// \code
+/// \#pragma comment(linker, "foo")
+/// \endcode
/// 'linker' is one of five identifiers: compiler, exestr, lib, linker, user.
/// "foo" is a string, which is fully macro expanded, and permits string
/// concatenation, embedded escape characters etc. See MSDN for more details.
@@ -552,11 +558,15 @@ void Preprocessor::HandlePragmaComment(Token &Tok) {
Callbacks->PragmaComment(CommentLoc, II, ArgumentString);
}
-/// HandlePragmaMessage - Handle the microsoft and gcc #pragma message
+/// HandlePragmaMessage - Handle the microsoft and gcc \#pragma message
/// extension. The syntax is:
-/// #pragma message(string)
+/// \code
+/// \#pragma message(string)
+/// \endcode
/// OR, in GCC mode:
-/// #pragma message string
+/// \code
+/// \#pragma message string
+/// \endcode
/// string is a string, which is fully macro expanded, and permits string
/// concatenation, embedded escape characters, etc... See MSDN for more details.
void Preprocessor::HandlePragmaMessage(Token &Tok) {
@@ -679,9 +689,12 @@ IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) {
return LookUpIdentifierInfo(MacroTok);
}
-/// HandlePragmaPushMacro - Handle #pragma push_macro.
+/// \brief Handle \#pragma push_macro.
+///
/// The syntax is:
-/// #pragma push_macro("macro")
+/// \code
+/// \#pragma push_macro("macro")
+/// \endcode
void Preprocessor::HandlePragmaPushMacro(Token &PushMacroTok) {
// Parse the pragma directive and get the macro IdentifierInfo*.
IdentifierInfo *IdentInfo = ParsePragmaPushOrPopMacro(PushMacroTok);
@@ -703,9 +716,12 @@ void Preprocessor::HandlePragmaPushMacro(Token &PushMacroTok) {
PragmaPushMacroInfo[IdentInfo].push_back(MacroCopyToPush);
}
-/// HandlePragmaPopMacro - Handle #pragma pop_macro.
+/// \brief Handle \#pragma pop_macro.
+///
/// The syntax is:
+/// \code
/// #pragma pop_macro("macro")
+/// \endcode
void Preprocessor::HandlePragmaPopMacro(Token &PopMacroTok) {
SourceLocation MessageLoc = PopMacroTok.getLocation();
@@ -931,7 +947,7 @@ bool Preprocessor::LexOnOffSwitch(tok::OnOffSwitch &Result) {
}
namespace {
-/// PragmaOnceHandler - "#pragma once" marks the file as atomically included.
+/// PragmaOnceHandler - "\#pragma once" marks the file as atomically included.
struct PragmaOnceHandler : public PragmaHandler {
PragmaOnceHandler() : PragmaHandler("once") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -941,7 +957,7 @@ struct PragmaOnceHandler : public PragmaHandler {
}
};
-/// PragmaMarkHandler - "#pragma mark ..." is ignored by the compiler, and the
+/// PragmaMarkHandler - "\#pragma mark ..." is ignored by the compiler, and the
/// rest of the line is not lexed.
struct PragmaMarkHandler : public PragmaHandler {
PragmaMarkHandler() : PragmaHandler("mark") {}
@@ -951,7 +967,7 @@ struct PragmaMarkHandler : public PragmaHandler {
}
};
-/// PragmaPoisonHandler - "#pragma poison x" marks x as not usable.
+/// PragmaPoisonHandler - "\#pragma poison x" marks x as not usable.
struct PragmaPoisonHandler : public PragmaHandler {
PragmaPoisonHandler() : PragmaHandler("poison") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -960,7 +976,7 @@ struct PragmaPoisonHandler : public PragmaHandler {
}
};
-/// PragmaSystemHeaderHandler - "#pragma system_header" marks the current file
+/// PragmaSystemHeaderHandler - "\#pragma system_header" marks the current file
/// as a system header, which silences warnings in it.
struct PragmaSystemHeaderHandler : public PragmaHandler {
PragmaSystemHeaderHandler() : PragmaHandler("system_header") {}
@@ -994,6 +1010,10 @@ struct PragmaDebugHandler : public PragmaHandler {
llvm_unreachable("This is an assertion!");
} else if (II->isStr("crash")) {
*(volatile int*) 0x11 = 0;
+ } else if (II->isStr("parser_crash")) {
+ Token Crasher;
+ Crasher.setKind(tok::annot_pragma_parser_crash);
+ PP.EnterToken(Crasher);
} else if (II->isStr("llvm_fatal_error")) {
llvm::report_fatal_error("#pragma clang __debug llvm_fatal_error");
} else if (II->isStr("llvm_unreachable")) {
@@ -1023,7 +1043,7 @@ struct PragmaDebugHandler : public PragmaHandler {
};
-/// PragmaDiagnosticHandler - e.g. '#pragma GCC diagnostic ignored "-Wformat"'
+/// PragmaDiagnosticHandler - e.g. '\#pragma GCC diagnostic ignored "-Wformat"'
struct PragmaDiagnosticHandler : public PragmaHandler {
private:
const char *Namespace;
@@ -1117,7 +1137,7 @@ public:
}
};
-/// PragmaCommentHandler - "#pragma comment ...".
+/// PragmaCommentHandler - "\#pragma comment ...".
struct PragmaCommentHandler : public PragmaHandler {
PragmaCommentHandler() : PragmaHandler("comment") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -1126,7 +1146,7 @@ struct PragmaCommentHandler : public PragmaHandler {
}
};
-/// PragmaIncludeAliasHandler - "#pragma include_alias("...")".
+/// PragmaIncludeAliasHandler - "\#pragma include_alias("...")".
struct PragmaIncludeAliasHandler : public PragmaHandler {
PragmaIncludeAliasHandler() : PragmaHandler("include_alias") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -1135,7 +1155,7 @@ struct PragmaIncludeAliasHandler : public PragmaHandler {
}
};
-/// PragmaMessageHandler - "#pragma message("...")".
+/// PragmaMessageHandler - "\#pragma message("...")".
struct PragmaMessageHandler : public PragmaHandler {
PragmaMessageHandler() : PragmaHandler("message") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -1144,7 +1164,7 @@ struct PragmaMessageHandler : public PragmaHandler {
}
};
-/// PragmaPushMacroHandler - "#pragma push_macro" saves the value of the
+/// PragmaPushMacroHandler - "\#pragma push_macro" saves the value of the
/// macro on the top of the stack.
struct PragmaPushMacroHandler : public PragmaHandler {
PragmaPushMacroHandler() : PragmaHandler("push_macro") {}
@@ -1155,7 +1175,7 @@ struct PragmaPushMacroHandler : public PragmaHandler {
};
-/// PragmaPopMacroHandler - "#pragma pop_macro" sets the value of the
+/// PragmaPopMacroHandler - "\#pragma pop_macro" sets the value of the
/// macro to the value on the top of the stack.
struct PragmaPopMacroHandler : public PragmaHandler {
PragmaPopMacroHandler() : PragmaHandler("pop_macro") {}
@@ -1167,7 +1187,7 @@ struct PragmaPopMacroHandler : public PragmaHandler {
// Pragma STDC implementations.
-/// PragmaSTDC_FENV_ACCESSHandler - "#pragma STDC FENV_ACCESS ...".
+/// PragmaSTDC_FENV_ACCESSHandler - "\#pragma STDC FENV_ACCESS ...".
struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler {
PragmaSTDC_FENV_ACCESSHandler() : PragmaHandler("FENV_ACCESS") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -1180,7 +1200,7 @@ struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler {
}
};
-/// PragmaSTDC_CX_LIMITED_RANGEHandler - "#pragma STDC CX_LIMITED_RANGE ...".
+/// PragmaSTDC_CX_LIMITED_RANGEHandler - "\#pragma STDC CX_LIMITED_RANGE ...".
struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler {
PragmaSTDC_CX_LIMITED_RANGEHandler()
: PragmaHandler("CX_LIMITED_RANGE") {}
@@ -1191,7 +1211,7 @@ struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler {
}
};
-/// PragmaSTDC_UnknownHandler - "#pragma STDC ...".
+/// PragmaSTDC_UnknownHandler - "\#pragma STDC ...".
struct PragmaSTDC_UnknownHandler : public PragmaHandler {
PragmaSTDC_UnknownHandler() {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -1202,7 +1222,7 @@ struct PragmaSTDC_UnknownHandler : public PragmaHandler {
};
/// PragmaARCCFCodeAuditedHandler -
-/// #pragma clang arc_cf_code_audited begin/end
+/// \#pragma clang arc_cf_code_audited begin/end
struct PragmaARCCFCodeAuditedHandler : public PragmaHandler {
PragmaARCCFCodeAuditedHandler() : PragmaHandler("arc_cf_code_audited") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
@@ -1259,7 +1279,7 @@ struct PragmaARCCFCodeAuditedHandler : public PragmaHandler {
/// RegisterBuiltinPragmas - Install the standard preprocessor pragmas:
-/// #pragma GCC poison/system_header/dependency and #pragma once.
+/// \#pragma GCC poison/system_header/dependency and \#pragma once.
void Preprocessor::RegisterBuiltinPragmas() {
AddPragmaHandler(new PragmaOnceHandler());
AddPragmaHandler(new PragmaMarkHandler());
diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp
index 89d19fd..dfdeba3 100644
--- a/lib/Lex/PreprocessingRecord.cpp
+++ b/lib/Lex/PreprocessingRecord.cpp
@@ -48,7 +48,7 @@ PreprocessingRecord::PreprocessingRecord(SourceManager &SM,
}
/// \brief Returns a pair of [Begin, End) iterators of preprocessed entities
-/// that source range \arg R encompasses.
+/// that source range \p Range encompasses.
std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator>
PreprocessingRecord::getPreprocessedEntitiesInRange(SourceRange Range) {
if (Range.isInvalid())
@@ -89,7 +89,7 @@ static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID,
///
/// Can be used to avoid implicit deserializations of preallocated
/// preprocessed entities if we only care about entities of a specific file
-/// and not from files #included in the range given at
+/// and not from files \#included in the range given at
/// \see getPreprocessedEntitiesInRange.
bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) {
if (FID.isInvalid())
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index 06e5685..614530c 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -66,54 +66,6 @@ Preprocessor::Preprocessor(DiagnosticsEngine &diags, LangOptions &opts,
Record(0), MIChainHead(0), MICache(0)
{
OwnsHeaderSearch = OwnsHeaders;
-
- if (!DelayInitialization) {
- assert(Target && "Must provide target information for PP initialization");
- Initialize(*Target);
- }
-}
-
-Preprocessor::~Preprocessor() {
- assert(BacktrackPositions.empty() && "EnableBacktrack/Backtrack imbalance!");
-
- while (!IncludeMacroStack.empty()) {
- delete IncludeMacroStack.back().TheLexer;
- delete IncludeMacroStack.back().TheTokenLexer;
- IncludeMacroStack.pop_back();
- }
-
- // Free any macro definitions.
- for (MacroInfoChain *I = MIChainHead ; I ; I = I->Next)
- I->MI.Destroy();
-
- // Free any cached macro expanders.
- for (unsigned i = 0, e = NumCachedTokenLexers; i != e; ++i)
- delete TokenLexerCache[i];
-
- // Free any cached MacroArgs.
- for (MacroArgs *ArgList = MacroArgCache; ArgList; )
- ArgList = ArgList->deallocate();
-
- // Release pragma information.
- delete PragmaHandlers;
-
- // Delete the scratch buffer info.
- delete ScratchBuf;
-
- // Delete the header search info, if we own it.
- if (OwnsHeaderSearch)
- delete &HeaderInfo;
-
- delete Callbacks;
-}
-
-void Preprocessor::Initialize(const TargetInfo &Target) {
- assert((!this->Target || this->Target == &Target) &&
- "Invalid override of target information");
- this->Target = &Target;
-
- // Initialize information about built-ins.
- BuiltinInfo.InitializeTarget(Target);
ScratchBuf = new ScratchBuffer(SourceMgr);
CounterValue = 0; // __COUNTER__ starts at 0.
@@ -134,10 +86,12 @@ void Preprocessor::Initialize(const TargetInfo &Target) {
// Macro expansion is enabled.
DisableMacroExpansion = false;
+ MacroExpansionInDirectivesOverride = false;
InMacroArgs = false;
InMacroArgPreExpansion = false;
NumCachedTokenLexers = 0;
-
+ PragmasEnabled = true;
+
CachedLexPos = 0;
// We haven't read anything from the external source.
@@ -170,7 +124,54 @@ void Preprocessor::Initialize(const TargetInfo &Target) {
Ident___exception_info = Ident___exception_code = Ident___abnormal_termination = 0;
Ident_GetExceptionInfo = Ident_GetExceptionCode = Ident_AbnormalTermination = 0;
}
+
+ if (!DelayInitialization) {
+ assert(Target && "Must provide target information for PP initialization");
+ Initialize(*Target);
+ }
+}
+
+Preprocessor::~Preprocessor() {
+ assert(BacktrackPositions.empty() && "EnableBacktrack/Backtrack imbalance!");
+
+ while (!IncludeMacroStack.empty()) {
+ delete IncludeMacroStack.back().TheLexer;
+ delete IncludeMacroStack.back().TheTokenLexer;
+ IncludeMacroStack.pop_back();
+ }
+
+ // Free any macro definitions.
+ for (MacroInfoChain *I = MIChainHead ; I ; I = I->Next)
+ I->MI.Destroy();
+
+ // Free any cached macro expanders.
+ for (unsigned i = 0, e = NumCachedTokenLexers; i != e; ++i)
+ delete TokenLexerCache[i];
+
+ // Free any cached MacroArgs.
+ for (MacroArgs *ArgList = MacroArgCache; ArgList; )
+ ArgList = ArgList->deallocate();
+
+ // Release pragma information.
+ delete PragmaHandlers;
+
+ // Delete the scratch buffer info.
+ delete ScratchBuf;
+
+ // Delete the header search info, if we own it.
+ if (OwnsHeaderSearch)
+ delete &HeaderInfo;
+
+ delete Callbacks;
+}
+
+void Preprocessor::Initialize(const TargetInfo &Target) {
+ assert((!this->Target || this->Target == &Target) &&
+ "Invalid override of target information");
+ this->Target = &Target;
+ // Initialize information about built-ins.
+ BuiltinInfo.InitializeTarget(Target);
HeaderInfo.setTarget(Target);
}
@@ -236,6 +237,20 @@ void Preprocessor::PrintStats() {
llvm::errs() << (NumFastTokenPaste+NumTokenPaste)
<< " token paste (##) operations performed, "
<< NumFastTokenPaste << " on the fast path.\n";
+
+ llvm::errs() << "\nPreprocessor Memory: " << getTotalMemory() << "B total";
+
+ llvm::errs() << "\n BumpPtr: " << BP.getTotalMemory();
+ llvm::errs() << "\n Macro Expanded Tokens: "
+ << llvm::capacity_in_bytes(MacroExpandedTokens);
+ llvm::errs() << "\n Predefines Buffer: " << Predefines.capacity();
+ llvm::errs() << "\n Macros: " << llvm::capacity_in_bytes(Macros);
+ llvm::errs() << "\n #pragma push_macro Info: "
+ << llvm::capacity_in_bytes(PragmaPushMacroInfo);
+ llvm::errs() << "\n Poison Reasons: "
+ << llvm::capacity_in_bytes(PoisonReasons);
+ llvm::errs() << "\n Comment Handlers: "
+ << llvm::capacity_in_bytes(CommentHandlers) << "\n";
}
Preprocessor::macro_iterator
@@ -514,9 +529,19 @@ void Preprocessor::HandleIdentifier(Token &Identifier) {
// If the information about this identifier is out of date, update it from
// the external source.
+ // We have to treat __VA_ARGS__ in a special way, since it gets
+ // serialized with isPoisoned = true, but our preprocessor may have
+ // unpoisoned it if we're defining a C99 macro.
if (II.isOutOfDate()) {
+ bool CurrentIsPoisoned = false;
+ if (&II == Ident__VA_ARGS__)
+ CurrentIsPoisoned = Ident__VA_ARGS__->isPoisoned();
+
ExternalSource->updateOutOfDateIdentifier(II);
Identifier.setKind(II.getTokenID());
+
+ if (&II == Ident__VA_ARGS__)
+ II.setIsPoisoned(CurrentIsPoisoned);
}
// If this identifier was poisoned, and if it was not produced from a macro
@@ -622,14 +647,14 @@ void Preprocessor::LexAfterModuleImport(Token &Result) {
/*IsIncludeDirective=*/false);
}
-void Preprocessor::AddCommentHandler(CommentHandler *Handler) {
+void Preprocessor::addCommentHandler(CommentHandler *Handler) {
assert(Handler && "NULL comment handler");
assert(std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler) ==
CommentHandlers.end() && "Comment handler already registered");
CommentHandlers.push_back(Handler);
}
-void Preprocessor::RemoveCommentHandler(CommentHandler *Handler) {
+void Preprocessor::removeCommentHandler(CommentHandler *Handler) {
std::vector<CommentHandler *>::iterator Pos
= std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler);
assert(Pos != CommentHandlers.end() && "Comment handler not registered");
diff --git a/lib/Lex/PreprocessorLexer.cpp b/lib/Lex/PreprocessorLexer.cpp
index a72bbca..a64c84d 100644
--- a/lib/Lex/PreprocessorLexer.cpp
+++ b/lib/Lex/PreprocessorLexer.cpp
@@ -27,7 +27,7 @@ PreprocessorLexer::PreprocessorLexer(Preprocessor *pp, FileID fid)
InitialNumSLocEntries = pp->getSourceManager().local_sloc_entry_size();
}
-/// LexIncludeFilename - After the preprocessor has parsed a #include, lex and
+/// \brief After the preprocessor has parsed a \#include, lex and
/// (potentially) macro expand the filename.
void PreprocessorLexer::LexIncludeFilename(Token &FilenameTok) {
assert(ParsingPreprocessorDirective &&
diff --git a/lib/Lex/TokenConcatenation.cpp b/lib/Lex/TokenConcatenation.cpp
index 84a46ed..dd7ebb0 100644
--- a/lib/Lex/TokenConcatenation.cpp
+++ b/lib/Lex/TokenConcatenation.cpp
@@ -14,6 +14,7 @@
#include "clang/Lex/TokenConcatenation.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cctype>
using namespace clang;
diff --git a/lib/Lex/TokenLexer.cpp b/lib/Lex/TokenLexer.cpp
index 696754c..ade40da 100644
--- a/lib/Lex/TokenLexer.cpp
+++ b/lib/Lex/TokenLexer.cpp
@@ -252,9 +252,9 @@ void TokenLexer::ExpandFunctionArguments() {
const Token *ArgToks = ActualArgs->getUnexpArgument(ArgNo);
unsigned NumToks = MacroArgs::getArgLength(ArgToks);
if (NumToks) { // Not an empty argument?
- // If this is the GNU ", ## __VA_ARG__" extension, and we just learned
- // that __VA_ARG__ expands to multiple tokens, avoid a pasting error when
- // the expander trys to paste ',' with the first token of the __VA_ARG__
+ // If this is the GNU ", ## __VA_ARGS__" extension, and we just learned
+ // that __VA_ARGS__ expands to multiple tokens, avoid a pasting error when
+ // the expander trys to paste ',' with the first token of the __VA_ARGS__
// expansion.
if (PasteBefore && ResultToks.size() >= 2 &&
ResultToks[ResultToks.size()-2].is(tok::comma) &&
@@ -568,8 +568,8 @@ bool TokenLexer::PasteTokens(Token &Tok) {
<< Buffer.str();
}
- // Do not consume the RHS.
- --CurToken;
+ // An error has occurred so exit loop.
+ break;
}
// Turn ## into 'unknown' to avoid # ## # from looking like a paste
@@ -578,7 +578,7 @@ bool TokenLexer::PasteTokens(Token &Tok) {
Result.setKind(tok::unknown);
}
- // Transfer properties of the LHS over the the Result.
+ // Transfer properties of the LHS over the Result.
Result.setFlagValue(Token::StartOfLine , Tok.isAtStartOfLine());
Result.setFlagValue(Token::LeadingSpace, Tok.hasLeadingSpace());
diff --git a/lib/Makefile b/lib/Makefile
index 2eb72a9..1f14aa0 100755
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -8,7 +8,7 @@
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ..
-PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \
+PARALLEL_DIRS = Headers Basic Lex Parse AST ASTMatchers Sema CodeGen Analysis \
StaticAnalyzer Edit Rewrite ARCMigrate Serialization Frontend \
FrontendTool Tooling Driver
diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt
index 6c980ce..55e2aeb 100644
--- a/lib/Parse/CMakeLists.txt
+++ b/lib/Parse/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(LLVM_USED_LIBS clangBasic clangAST clangLex clangSema)
-
add_clang_library(clangParse
ParseAST.cpp
ParseCXXInlineMethods.cpp
@@ -16,4 +14,21 @@ add_clang_library(clangParse
Parser.cpp
)
-add_dependencies(clangParse ClangAttrClasses ClangAttrList ClangDeclNodes ClangDiagnosticParse ClangStmtNodes ClangAttrLateParsed)
+add_dependencies(clangParse
+ ClangAttrClasses
+ ClangAttrLateParsed
+ ClangAttrList
+ ClangAttrParsedAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangDiagnosticParse
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangParse
+ clangBasic
+ clangAST
+ clangLex
+ clangSema
+ )
diff --git a/lib/Parse/ParseAST.cpp b/lib/Parse/ParseAST.cpp
index d1c2624..bd4f859 100644
--- a/lib/Parse/ParseAST.cpp
+++ b/lib/Parse/ParseAST.cpp
@@ -12,11 +12,13 @@
//===----------------------------------------------------------------------===//
#include "clang/Parse/ParseAST.h"
+#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/SemaConsumer.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExternalASTSource.h"
#include "clang/AST/Stmt.h"
@@ -77,28 +79,29 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
S.getPreprocessor().EnterMainSourceFile();
P.Initialize();
S.Initialize();
-
- if (ExternalASTSource *External = S.getASTContext().getExternalSource())
- External->StartTranslationUnit(Consumer);
-
- bool Abort = false;
+
+ // C11 6.9p1 says translation units must have at least one top-level
+ // declaration. C++ doesn't have this restriction. We also don't want to
+ // complain if we have a precompiled header, although technically if the PCH
+ // is empty we should still emit the (pedantic) diagnostic.
Parser::DeclGroupPtrTy ADecl;
-
- while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file.
- // If we got a null return and something *was* parsed, ignore it. This
- // is due to a top-level semicolon, an action override, or a parse error
- // skipping something.
- if (ADecl) {
- if (!Consumer->HandleTopLevelDecl(ADecl.get())) {
- Abort = true;
- break;
- }
- }
- };
-
- if (Abort)
- return;
-
+ ExternalASTSource *External = S.getASTContext().getExternalSource();
+ if (External)
+ External->StartTranslationUnit(Consumer);
+
+ if (P.ParseTopLevelDecl(ADecl)) {
+ if (!External && !S.getLangOpts().CPlusPlus)
+ P.Diag(diag::ext_empty_translation_unit);
+ } else {
+ do {
+ // If we got a null return and something *was* parsed, ignore it. This
+ // is due to a top-level semicolon, an action override, or a parse error
+ // skipping something.
+ if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
+ return;
+ } while (!P.ParseTopLevelDecl(ADecl));
+ }
+
// Process any TopLevelDecls generated by #pragma weak.
for (SmallVector<Decl*,2>::iterator
I = S.WeakTopLevelDecls().begin(),
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index c7b29d9..abce27c 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -16,6 +16,7 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Scope.h"
#include "clang/AST/DeclTemplate.h"
+#include "RAIIObjectsForParser.h"
using namespace clang;
/// ParseCXXInlineMethodDef - We parsed and verified that the specified
@@ -45,7 +46,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
else {
FnD = Actions.ActOnCXXMemberDeclarator(getCurScope(), AS, D,
move(TemplateParams), 0,
- VS, /*HasDeferredInit=*/false);
+ VS, ICIS_NoInit);
if (FnD) {
Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs,
false, true);
@@ -108,6 +109,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
// or if we are about to parse function member template then consume
// the tokens and store them for parsing at the end of the translation unit.
if (getLangOpts().DelayedTemplateParsing &&
+ DefinitionKind == FDK_Definition &&
((Actions.CurContext->isDependentContext() ||
TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) &&
!Actions.IsInsideALocalClassWithinATemplateFunction())) {
@@ -458,7 +460,7 @@ void Parser::ParseLexedMemberInitializers(ParsingClass &Class) {
Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
- {
+ if (!Class.LateParsedDeclarations.empty()) {
// C++11 [expr.prim.general]p4:
// Otherwise, if a member-declarator declares a non-static data member
// (9.2) of a class X, the expression this is a prvalue of type "pointer
@@ -492,7 +494,7 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) {
ConsumeAnyToken();
SourceLocation EqualLoc;
-
+
ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false,
EqualLoc);
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 7995e68..b830d9c 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -14,6 +14,7 @@
#include "clang/Parse/Parser.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Basic/OpenCL.h"
+#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/PrettyDeclStackTrace.h"
@@ -37,6 +38,8 @@ TypeResult Parser::ParseTypeName(SourceRange *Range,
AccessSpecifier AS,
Decl **OwnedType) {
DeclSpecContext DSC = getDeclSpecContextFromDeclaratorContext(Context);
+ if (DSC == DSC_normal)
+ DSC = DSC_type_specifier;
// Parse the common declaration-specifiers piece.
DeclSpec DS(AttrFactory);
@@ -156,7 +159,7 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs,
}
} else {
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc,
- 0, SourceLocation(), 0, 0);
+ 0, SourceLocation(), 0, 0, AttributeList::AS_GNU);
}
}
if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
@@ -272,67 +275,175 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
if (!ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) {
AttributeList *attr =
Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), 0, AttrNameLoc,
- ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size());
- if (BuiltinType && attr->getKind() == AttributeList::AT_iboutletcollection)
+ ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size(),
+ AttributeList::AS_GNU);
+ if (BuiltinType && attr->getKind() == AttributeList::AT_IBOutletCollection)
Diag(Tok, diag::err_iboutletcollection_builtintype);
}
}
+/// \brief Parses a single argument for a declspec, including the
+/// surrounding parens.
+void Parser::ParseMicrosoftDeclSpecWithSingleArg(IdentifierInfo *AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs)
+{
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ AttrName->getNameStart(), tok::r_paren))
+ return;
+
+ ExprResult ArgExpr(ParseConstantExpression());
+ if (ArgExpr.isInvalid()) {
+ T.skipToEnd();
+ return;
+ }
+ Expr *ExprList = ArgExpr.take();
+ Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(),
+ &ExprList, 1, AttributeList::AS_Declspec);
+
+ T.consumeClose();
+}
+
+/// \brief Determines whether a declspec is a "simple" one requiring no
+/// arguments.
+bool Parser::IsSimpleMicrosoftDeclSpec(IdentifierInfo *Ident) {
+ return llvm::StringSwitch<bool>(Ident->getName())
+ .Case("dllimport", true)
+ .Case("dllexport", true)
+ .Case("noreturn", true)
+ .Case("nothrow", true)
+ .Case("noinline", true)
+ .Case("naked", true)
+ .Case("appdomain", true)
+ .Case("process", true)
+ .Case("jitintrinsic", true)
+ .Case("noalias", true)
+ .Case("restrict", true)
+ .Case("novtable", true)
+ .Case("selectany", true)
+ .Case("thread", true)
+ .Default(false);
+}
+
+/// \brief Attempts to parse a declspec which is not simple (one that takes
+/// parameters). Will return false if we properly handled the declspec, or
+/// true if it is an unknown declspec.
+void Parser::ParseComplexMicrosoftDeclSpec(IdentifierInfo *Ident,
+ SourceLocation Loc,
+ ParsedAttributes &Attrs) {
+ // Try to handle the easy case first -- these declspecs all take a single
+ // parameter as their argument.
+ if (llvm::StringSwitch<bool>(Ident->getName())
+ .Case("uuid", true)
+ .Case("align", true)
+ .Case("allocate", true)
+ .Default(false)) {
+ ParseMicrosoftDeclSpecWithSingleArg(Ident, Loc, Attrs);
+ } else if (Ident->getName() == "deprecated") {
+ // The deprecated declspec has an optional single argument, so we will
+ // check for a l-paren to decide whether we should parse an argument or
+ // not.
+ if (Tok.getKind() == tok::l_paren)
+ ParseMicrosoftDeclSpecWithSingleArg(Ident, Loc, Attrs);
+ else
+ Attrs.addNew(Ident, Loc, 0, Loc, 0, SourceLocation(), 0, 0,
+ AttributeList::AS_Declspec);
+ } else if (Ident->getName() == "property") {
+ // The property declspec is more complex in that it can take one or two
+ // assignment expressions as a parameter, but the lhs of the assignment
+ // must be named get or put.
+ //
+ // For right now, we will just skip to the closing right paren of the
+ // property expression.
+ //
+ // FIXME: we should deal with __declspec(property) at some point because it
+ // is used in the platform SDK headers for the Parallel Patterns Library
+ // and ATL.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ Ident->getNameStart(), tok::r_paren))
+ return;
+ T.skipToEnd();
+ } else {
+ // We don't recognize this as a valid declspec, but instead of creating the
+ // attribute and allowing sema to warn about it, we will warn here instead.
+ // This is because some attributes have multiple spellings, but we need to
+ // disallow that for declspecs (such as align vs aligned). If we made the
+ // attribute, we'd have to split the valid declspec spelling logic into
+ // both locations.
+ Diag(Loc, diag::warn_ms_declspec_unknown) << Ident;
+
+ // If there's an open paren, we should eat the open and close parens under
+ // the assumption that this unknown declspec has parameters.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (!T.consumeOpen())
+ T.skipToEnd();
+ }
+}
-/// ParseMicrosoftDeclSpec - Parse an __declspec construct
-///
/// [MS] decl-specifier:
/// __declspec ( extended-decl-modifier-seq )
///
/// [MS] extended-decl-modifier-seq:
/// extended-decl-modifier[opt]
/// extended-decl-modifier extended-decl-modifier-seq
-
-void Parser::ParseMicrosoftDeclSpec(ParsedAttributes &attrs) {
+void Parser::ParseMicrosoftDeclSpec(ParsedAttributes &Attrs) {
assert(Tok.is(tok::kw___declspec) && "Not a declspec!");
ConsumeToken();
- if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
- "declspec")) {
- SkipUntil(tok::r_paren, true); // skip until ) or ;
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after, "__declspec",
+ tok::r_paren))
return;
- }
- while (Tok.getIdentifierInfo()) {
- IdentifierInfo *AttrName = Tok.getIdentifierInfo();
- SourceLocation AttrNameLoc = ConsumeToken();
-
- // FIXME: Remove this when we have proper __declspec(property()) support.
- // Just skip everything inside property().
- if (AttrName->getName() == "property") {
- ConsumeParen();
- SkipUntil(tok::r_paren);
+ // An empty declspec is perfectly legal and should not warn. Additionally,
+ // you can specify multiple attributes per declspec.
+ while (Tok.getKind() != tok::r_paren) {
+ // We expect either a well-known identifier or a generic string. Anything
+ // else is a malformed declspec.
+ bool IsString = Tok.getKind() == tok::string_literal ? true : false;
+ if (!IsString && Tok.getKind() != tok::identifier &&
+ Tok.getKind() != tok::kw_restrict) {
+ Diag(Tok, diag::err_ms_declspec_type);
+ T.skipToEnd();
+ return;
}
- if (Tok.is(tok::l_paren)) {
- ConsumeParen();
- // FIXME: This doesn't parse __declspec(property(get=get_func_name))
- // correctly.
- ExprResult ArgExpr(ParseAssignmentExpression());
- if (!ArgExpr.isInvalid()) {
- Expr *ExprList = ArgExpr.take();
- attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
- SourceLocation(), &ExprList, 1, true);
+
+ IdentifierInfo *AttrName;
+ SourceLocation AttrNameLoc;
+ if (IsString) {
+ SmallString<8> StrBuffer;
+ bool Invalid = false;
+ StringRef Str = PP.getSpelling(Tok, StrBuffer, &Invalid);
+ if (Invalid) {
+ T.skipToEnd();
+ return;
}
- if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
- SkipUntil(tok::r_paren, false);
+ AttrName = PP.getIdentifierInfo(Str);
+ AttrNameLoc = ConsumeStringToken();
} else {
- attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc,
- 0, SourceLocation(), 0, 0, true);
+ AttrName = Tok.getIdentifierInfo();
+ AttrNameLoc = ConsumeToken();
}
+
+ if (IsString || IsSimpleMicrosoftDeclSpec(AttrName))
+ // If we have a generic string, we will allow it because there is no
+ // documented list of allowable string declspecs, but we know they exist
+ // (for instance, SAL declspecs in older versions of MSVC).
+ //
+ // Alternatively, if the identifier is a simple one, then it requires no
+ // arguments and can be turned into an attribute directly.
+ Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(),
+ 0, 0, AttributeList::AS_Declspec);
+ else
+ ParseComplexMicrosoftDeclSpec(AttrName, AttrNameLoc, Attrs);
}
- if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
- SkipUntil(tok::r_paren, false);
- return;
+ T.consumeClose();
}
void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) {
// Treat these like attributes
- // FIXME: Allow Sema to distinguish between these and real attributes!
while (Tok.is(tok::kw___fastcall) || Tok.is(tok::kw___stdcall) ||
Tok.is(tok::kw___thiscall) || Tok.is(tok::kw___cdecl) ||
Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) ||
@@ -340,12 +451,8 @@ void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) {
Tok.is(tok::kw___unaligned)) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
- if (Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) ||
- Tok.is(tok::kw___ptr32))
- // FIXME: Support these properly!
- continue;
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
- SourceLocation(), 0, 0, true);
+ SourceLocation(), 0, 0, AttributeList::AS_MSTypespec);
}
}
@@ -355,7 +462,7 @@ void Parser::ParseBorlandTypeAttributes(ParsedAttributes &attrs) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
- SourceLocation(), 0, 0, true);
+ SourceLocation(), 0, 0, AttributeList::AS_MSTypespec);
}
}
@@ -365,7 +472,7 @@ void Parser::ParseOpenCLAttributes(ParsedAttributes &attrs) {
SourceLocation AttrNameLoc = ConsumeToken();
attrs.addNew(PP.getIdentifierInfo("opencl_kernel_function"),
AttrNameLoc, 0, AttrNameLoc, 0,
- SourceLocation(), 0, 0, false);
+ SourceLocation(), 0, 0, AttributeList::AS_GNU);
}
}
@@ -374,42 +481,42 @@ void Parser::ParseOpenCLQualifiers(DeclSpec &DS) {
switch(Tok.getKind()) {
// OpenCL qualifiers:
case tok::kw___private:
- case tok::kw_private:
+ case tok::kw_private:
DS.getAttributes().addNewInteger(
- Actions.getASTContext(),
+ Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, 0);
break;
-
+
case tok::kw___global:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_global);
break;
-
+
case tok::kw___local:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_local);
break;
-
+
case tok::kw___constant:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_constant);
break;
-
+
case tok::kw___read_only:
DS.getAttributes().addNewInteger(
- Actions.getASTContext(),
+ Actions.getASTContext(),
PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_read_only);
break;
-
+
case tok::kw___write_only:
DS.getAttributes().addNewInteger(
- Actions.getASTContext(),
+ Actions.getASTContext(),
PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_write_only);
break;
-
+
case tok::kw___read_write:
DS.getAttributes().addNewInteger(
Actions.getASTContext(),
@@ -490,21 +597,21 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) {
if (AfterMinor == ActualLength) {
ConsumeToken();
-
+
// We had major.minor.
if (Major == 0 && Minor == 0) {
Diag(Tok, diag::err_zero_version);
return VersionTuple();
}
- return VersionTuple(Major, Minor);
+ return VersionTuple(Major, Minor);
}
// If what follows is not a '.', we have a problem.
if (ThisTokBegin[AfterMinor] != '.') {
Diag(Tok, diag::err_expected_version);
SkipUntil(tok::comma, tok::r_paren, true, true, true);
- return VersionTuple();
+ return VersionTuple();
}
// Parse the subminor version.
@@ -599,7 +706,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
if (UnavailableLoc.isValid()) {
Diag(KeywordLoc, diag::err_availability_redundant)
<< Keyword << SourceRange(UnavailableLoc);
- }
+ }
UnavailableLoc = KeywordLoc;
if (Tok.isNot(tok::comma))
@@ -607,8 +714,8 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
ConsumeToken();
continue;
- }
-
+ }
+
if (Tok.isNot(tok::equal)) {
Diag(Tok, diag::err_expected_equal_after)
<< Keyword;
@@ -625,10 +732,10 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
MessageExpr = ParseStringLiteralExpression();
break;
}
-
+
SourceRange VersionRange;
VersionTuple Version = ParseVersionTuple(VersionRange);
-
+
if (Version.empty()) {
SkipUntil(tok::r_paren);
return;
@@ -641,13 +748,13 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
Index = Deprecated;
else if (Keyword == Ident_obsoleted)
Index = Obsoleted;
- else
+ else
Index = Unknown;
if (Index < Unknown) {
if (!Changes[Index].KeywordLoc.isInvalid()) {
Diag(KeywordLoc, diag::err_availability_redundant)
- << Keyword
+ << Keyword
<< SourceRange(Changes[Index].KeywordLoc,
Changes[Index].VersionRange.getEnd());
}
@@ -693,15 +800,15 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
}
// Record this attribute
- attrs.addNew(&Availability,
- SourceRange(AvailabilityLoc, T.getCloseLocation()),
+ attrs.addNew(&Availability,
+ SourceRange(AvailabilityLoc, T.getCloseLocation()),
0, AvailabilityLoc,
Platform, PlatformLoc,
Changes[Introduced],
Changes[Deprecated],
- Changes[Obsoleted],
+ Changes[Obsoleted],
UnavailableLoc, MessageExpr.take(),
- false, false);
+ AttributeList::AS_GNU);
}
@@ -739,16 +846,16 @@ void Parser::ParseLexedAttributes(ParsingClass &Class) {
if (!AlreadyHasClassScope)
Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
- {
+ if (!Class.LateParsedDeclarations.empty()) {
// Allow 'this' within late-parsed attributes.
- Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate,
+ Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate,
/*TypeQuals=*/0);
-
+
for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i){
Class.LateParsedDeclarations[i]->ParseLexedAttributes();
}
}
-
+
if (!AlreadyHasClassScope)
Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
@@ -770,7 +877,7 @@ void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
/// \brief Finish parsing an attribute for which parsing was delayed.
/// This will be called at the end of parsing a class declaration
/// for each LateParsedAttribute. We consume the saved tokens and
-/// create an attribute with the arguments filled in. We add this
+/// create an attribute with the arguments filled in. We add this
/// to the Attribute list for the decl.
void Parser::ParseLexedAttribute(LateParsedAttribute &LA,
bool EnterScope, bool OnDefinition) {
@@ -885,10 +992,10 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName,
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
-
+
ExprVector ArgExprs(Actions);
bool ArgExprsOk = true;
-
+
// now parse the list of expressions
while (Tok.isNot(tok::r_paren)) {
ExprResult ArgExpr(ParseAssignmentExpression());
@@ -906,7 +1013,7 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName,
// Match the ')'.
if (ArgExprsOk && !T.consumeClose()) {
Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(),
- ArgExprs.take(), ArgExprs.size());
+ ArgExprs.take(), ArgExprs.size(), AttributeList::AS_GNU);
}
if (EndLoc)
*EndLoc = T.getCloseLocation();
@@ -975,7 +1082,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
// Must temporarily exit the objective-c container scope for
// parsing c none objective-c decls.
ObjCDeclContextSwitch ObjCDC(*this);
-
+
Decl *SingleDecl = 0;
Decl *OwnedType = 0;
switch (Tok.getKind()) {
@@ -992,7 +1099,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
SingleDecl = ParseNamespace(Context, DeclEnd, InlineLoc);
break;
}
- return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs,
+ return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs,
true);
case tok::kw_namespace:
ProhibitAttributes(attrs);
@@ -1010,7 +1117,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
default:
return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs, true);
}
-
+
// This routine returns a DeclGroup, if the thing we parsed only contains a
// single decl, convert it now. Alias declarations can also declare a type;
// include that too if it is present.
@@ -1019,10 +1126,12 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
/// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl]
/// declaration-specifiers init-declarator-list[opt] ';'
+/// [C++11] attribute-specifier-seq decl-specifier-seq[opt]
+/// init-declarator-list ';'
///[C90/C++]init-declarator-list ';' [TODO]
/// [OMP] threadprivate-directive [TODO]
///
-/// for-range-declaration: [C++0x 6.5p1: stmt.ranged]
+/// for-range-declaration: [C++11 6.5p1: stmt.ranged]
/// attribute-specifier-seq[opt] type-specifier-seq declarator
///
/// If RequireSemi is false, this does not check for a ';' at the end of the
@@ -1031,12 +1140,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
/// If FRI is non-null, we might be parsing a for-range-declaration instead
/// of a simple-declaration. If we find that we are, we also parse the
/// for-range-initializer, and place it here.
-Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts,
- unsigned Context,
- SourceLocation &DeclEnd,
- ParsedAttributes &attrs,
- bool RequireSemi,
- ForRangeInit *FRI) {
+Parser::DeclGroupPtrTy
+Parser::ParseSimpleDeclaration(StmtVector &Stmts, unsigned Context,
+ SourceLocation &DeclEnd,
+ ParsedAttributesWithRange &attrs,
+ bool RequireSemi, ForRangeInit *FRI) {
// Parse the common declaration-specifiers piece.
ParsingDeclSpec DS(*this);
DS.takeAttributesFrom(attrs);
@@ -1047,14 +1155,15 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts,
// C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
// declaration-specifiers init-declarator-list[opt] ';'
if (Tok.is(tok::semi)) {
+ DeclEnd = Tok.getLocation();
if (RequireSemi) ConsumeToken();
Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none,
DS);
DS.complete(TheDecl);
return Actions.ConvertDeclToDeclGroup(TheDecl);
}
-
- return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI);
+
+ return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI);
}
/// Returns true if this might be the start of a declarator, or a common typo
@@ -1161,15 +1270,33 @@ void Parser::SkipMalformedDecl() {
case tok::kw_inline:
// 'inline namespace' at the start of a line is almost certainly
- // a good place to pick back up parsing.
- if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace))
+ // a good place to pick back up parsing, except in an Objective-C
+ // @interface context.
+ if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace) &&
+ (!ParsingInObjCContainer || CurParsedObjCImpl))
return;
break;
case tok::kw_namespace:
// 'namespace' at the start of a line is almost certainly a good
- // place to pick back up parsing.
- if (Tok.isAtStartOfLine())
+ // place to pick back up parsing, except in an Objective-C
+ // @interface context.
+ if (Tok.isAtStartOfLine() &&
+ (!ParsingInObjCContainer || CurParsedObjCImpl))
+ return;
+ break;
+
+ case tok::at:
+ // @end is very much like } in Objective-C contexts.
+ if (NextToken().isObjCAtKeyword(tok::objc_end) &&
+ ParsingInObjCContainer)
+ return;
+ break;
+
+ case tok::minus:
+ case tok::plus:
+ // - and + probably start new method declarations in Objective-C contexts.
+ if (Tok.isAtStartOfLine() && ParsingInObjCContainer)
return;
break;
@@ -1214,7 +1341,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
// declaration. We have to check this because __attribute__ might be the
// start of a function definition in GCC-extended K&R C.
!isDeclarationAfterDeclarator()) {
-
+
if (isStartOfFunctionDefinition(D)) {
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
Diag(Tok, diag::err_function_declared_typedef);
@@ -1227,7 +1354,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs);
return Actions.ConvertDeclToDeclGroup(TheDecl);
}
-
+
if (isDeclarationSpecifier()) {
// If there is an invalid declaration specifier right after the function
// prototype, then we must be in a missing semicolon case where this isn't
@@ -1269,7 +1396,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
DeclsInGroup.push_back(FirstDecl);
bool ExpectSemi = Context != Declarator::ForContext;
-
+
// If we don't have a comma, it is either the end of the list (a ';') or an
// error, bail out.
while (Tok.is(tok::comma)) {
@@ -1303,7 +1430,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
Decl *ThisDecl = ParseDeclarationAfterDeclarator(D);
D.complete(ThisDecl);
if (ThisDecl)
- DeclsInGroup.push_back(ThisDecl);
+ DeclsInGroup.push_back(ThisDecl);
}
}
@@ -1311,10 +1438,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
*DeclEnd = Tok.getLocation();
if (ExpectSemi &&
- ExpectAndConsume(tok::semi,
- Context == Declarator::FileContext
- ? diag::err_invalid_token_after_toplevel_declarator
- : diag::err_expected_semi_declaration)) {
+ ExpectAndConsumeSemi(Context == Declarator::FileContext
+ ? diag::err_invalid_token_after_toplevel_declarator
+ : diag::err_expected_semi_declaration)) {
// Okay, there was no semicolon and one was expected. If we see a
// declaration specifier, just assume it was missing and continue parsing.
// Otherwise things are very confused and we skip to recover.
@@ -1388,7 +1514,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
case ParsedTemplateInfo::NonTemplate:
ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
break;
-
+
case ParsedTemplateInfo::Template:
case ParsedTemplateInfo::ExplicitSpecialization:
ThisDecl = Actions.ActOnTemplateDeclarator(getCurScope(),
@@ -1397,9 +1523,9 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
TemplateInfo.TemplateParams->size()),
D);
break;
-
+
case ParsedTemplateInfo::ExplicitInstantiation: {
- DeclResult ThisRes
+ DeclResult ThisRes
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
TemplateInfo.TemplateLoc,
@@ -1408,7 +1534,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
SkipUntil(tok::semi, true, true);
return 0;
}
-
+
ThisDecl = ThisRes.get();
break;
}
@@ -1441,10 +1567,11 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteInitializer(getCurScope(), ThisDecl);
+ Actions.FinalizeDeclaration(ThisDecl);
cutOffParsing();
return 0;
}
-
+
ExprResult Init(ParseInitializer());
if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) {
@@ -1497,7 +1624,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
Actions.AddInitializerToDecl(ThisDecl, Initializer.take(),
/*DirectInit=*/true, TypeContainsAuto);
}
- } else if (getLangOpts().CPlusPlus0x && Tok.is(tok::l_brace)) {
+ } else if (getLangOpts().CPlusPlus0x && Tok.is(tok::l_brace) &&
+ (!CurParsedObjCImpl || !D.isFunctionDeclarator())) {
// Parse C++0x braced-init-list.
Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists);
@@ -1543,7 +1671,8 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS,
// Validate declspec for type-name.
unsigned Specs = DS.getParsedSpecifiers();
- if (DSC == DSC_type_specifier && !DS.hasTypeSpecifier()) {
+ if ((DSC == DSC_type_specifier || DSC == DSC_trailing) &&
+ !DS.hasTypeSpecifier()) {
Diag(Tok, diag::err_expected_type);
DS.SetTypeSpecError();
} else if (Specs == DeclSpec::PQ_None && !DS.getNumProtocolQualifiers() &&
@@ -1635,12 +1764,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
assert(!DS.hasTypeSpecifier() && "Type specifier checked above");
// Since we know that this either implicit int (which is rare) or an
- // error, do lookahead to try to do better recovery. This never applies within
- // a type specifier.
- // FIXME: Don't bail out here in languages with no implicit int (like
- // C++ with no -fms-extensions). This is much more likely to be an undeclared
- // type or typo than a use of implicit int.
- if (DSC != DSC_type_specifier &&
+ // error, do lookahead to try to do better recovery. This never applies
+ // within a type specifier. Outside of C++, we allow this even if the
+ // language doesn't "officially" support implicit int -- we support
+ // implicit int as an extension in C99 and C11. Allegedly, MS also
+ // supports implicit int in C++ mode.
+ if (DSC != DSC_type_specifier && DSC != DSC_trailing &&
+ (!getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt) &&
isValidAfterIdentifierInDeclarator(NextToken())) {
// If this token is valid for implicit int, e.g. "static x = 4", then
// we just avoid eating the identifier, so it will be parsed as the
@@ -1648,6 +1778,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
return false;
}
+ if (getLangOpts().CPlusPlus &&
+ DS.getStorageClassSpec() == DeclSpec::SCS_auto) {
+ // Don't require a type specifier if we have the 'auto' storage class
+ // specifier in C++98 -- we'll promote it to a type specifier.
+ return false;
+ }
+
// Otherwise, if we don't consume this token, we are going to emit an
// error anyway. Try to recover from various common problems. Check
// to see if this was a reference to a tag name without a tag specified.
@@ -1671,9 +1808,20 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
}
if (TagName) {
+ IdentifierInfo *TokenName = Tok.getIdentifierInfo();
+ LookupResult R(Actions, TokenName, SourceLocation(),
+ Sema::LookupOrdinaryName);
+
Diag(Loc, diag::err_use_of_tag_name_without_tag)
- << Tok.getIdentifierInfo() << TagName << getLangOpts().CPlusPlus
- << FixItHint::CreateInsertion(Tok.getLocation(),FixitTagName);
+ << TokenName << TagName << getLangOpts().CPlusPlus
+ << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName);
+
+ if (Actions.LookupParsedName(R, getCurScope(), SS)) {
+ for (LookupResult::iterator I = R.begin(), IEnd = R.end();
+ I != IEnd; ++I)
+ Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type)
+ << TokenName << TagName;
+ }
// Parse this as a tag as if the missing tag were present.
if (TagKind == tok::kw_enum)
@@ -1685,11 +1833,55 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
}
}
- // This is almost certainly an invalid type name. Let the action emit a
+ // Determine whether this identifier could plausibly be the name of something
+ // being declared (with a missing type).
+ if (DSC != DSC_type_specifier && DSC != DSC_trailing &&
+ (!SS || DSC == DSC_top_level || DSC == DSC_class)) {
+ // Look ahead to the next token to try to figure out what this declaration
+ // was supposed to be.
+ switch (NextToken().getKind()) {
+ case tok::comma:
+ case tok::equal:
+ case tok::kw_asm:
+ case tok::l_brace:
+ case tok::l_square:
+ case tok::semi:
+ // This looks like a variable declaration. The type is probably missing.
+ // We're done parsing decl-specifiers.
+ return false;
+
+ case tok::l_paren: {
+ // static x(4); // 'x' is not a type
+ // x(int n); // 'x' is not a type
+ // x (*p)[]; // 'x' is a type
+ //
+ // Since we're in an error case (or the rare 'implicit int in C++' MS
+ // extension), we can afford to perform a tentative parse to determine
+ // which case we're in.
+ TentativeParsingAction PA(*this);
+ ConsumeToken();
+ TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false);
+ PA.Revert();
+ if (TPR == TPResult::False())
+ return false;
+ // The identifier is followed by a parenthesized declarator.
+ // It's supposed to be a type.
+ break;
+ }
+
+ default:
+ // This is probably supposed to be a type. This includes cases like:
+ // int f(itn);
+ // struct S { unsinged : 4; };
+ break;
+ }
+ }
+
+ // This is almost certainly an invalid type name. Let the action emit a
// diagnostic and attempt to recover.
ParsedType T;
- if (Actions.DiagnoseUnknownTypeName(*Tok.getIdentifierInfo(), Loc,
- getCurScope(), SS, T)) {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (Actions.DiagnoseUnknownTypeName(II, Loc, getCurScope(), SS, T)) {
// The action emitted a diagnostic, so we don't have to.
if (T) {
// The action has suggested that the type T could be used. Set that as
@@ -1700,11 +1892,15 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T);
DS.SetRangeEnd(Tok.getLocation());
ConsumeToken();
-
+ // There may be other declaration specifiers after this.
+ return true;
+ } else if (II != Tok.getIdentifierInfo()) {
+ // If no type was suggested, the correction is to a keyword
+ Tok.setKind(II->getTokenID());
// There may be other declaration specifiers after this.
return true;
}
-
+
// Fall through; the action had no suggestion for us.
} else {
// The action did not emit a diagnostic, so emit one now.
@@ -1729,7 +1925,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
///
/// \param Context the declarator context, which is one of the
/// Declarator::TheContext enumerator values.
-Parser::DeclSpecContext
+Parser::DeclSpecContext
Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) {
if (Context == Declarator::MemberContext)
return DSC_class;
@@ -1806,8 +2002,12 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
ExprVector ArgExprs(Actions);
ArgExprs.push_back(ArgExpr.release());
+ // FIXME: This should not be GNU, but we since the attribute used is
+ // based on the spelling, and there is no true spelling for
+ // C++11 attributes, this isn't accepted.
Attrs.addNew(PP.getIdentifierInfo("aligned"), KWLoc, 0, KWLoc,
- 0, T.getOpenLocation(), ArgExprs.take(), 1, false, true);
+ 0, T.getOpenLocation(), ArgExprs.take(), 1,
+ AttributeList::AS_GNU);
}
/// ParseDeclarationSpecifiers
@@ -1845,8 +2045,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
DS.SetRangeStart(Tok.getLocation());
DS.SetRangeEnd(Tok.getLocation());
}
-
+
bool EnteringContext = (DSContext == DSC_class || DSContext == DSC_top_level);
+ bool AttrsLastTime = false;
+ ParsedAttributesWithRange attrs(AttrFactory);
while (1) {
bool isInvalid = false;
const char *PrevSpec = 0;
@@ -1857,14 +2059,32 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
switch (Tok.getKind()) {
default:
DoneWithDeclSpec:
- // [C++0x] decl-specifier-seq: decl-specifier attribute-specifier-seq[opt]
- MaybeParseCXX0XAttributes(DS.getAttributes());
+ if (!AttrsLastTime)
+ ProhibitAttributes(attrs);
+ else
+ DS.takeAttributesFrom(attrs);
// If this is not a declaration specifier token, we're done reading decl
// specifiers. First verify that DeclSpec's are consistent.
DS.Finish(Diags, PP);
return;
+ case tok::l_square:
+ case tok::kw_alignas:
+ if (!isCXX11AttributeSpecifier())
+ goto DoneWithDeclSpec;
+
+ ProhibitAttributes(attrs);
+ // FIXME: It would be good to recover by accepting the attributes,
+ // but attempting to do that now would cause serious
+ // madness in terms of diagnostics.
+ attrs.clear();
+ attrs.Range = SourceRange();
+
+ ParseCXX11Attributes(attrs);
+ AttrsLastTime = true;
+ continue;
+
case tok::code_completion: {
Sema::ParserCompletionContext CCC = Sema::PCC_Namespace;
if (DS.hasTypeSpecifier()) {
@@ -1875,25 +2095,25 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
Scope::FunctionPrototypeScope |
Scope::AtCatchScope)) == 0;
bool AllowNestedNameSpecifiers
- = DSContext == DSC_top_level ||
+ = DSContext == DSC_top_level ||
(DSContext == DSC_class && DS.isFriendSpecified());
Actions.CodeCompleteDeclSpec(getCurScope(), DS,
- AllowNonIdentifiers,
+ AllowNonIdentifiers,
AllowNestedNameSpecifiers);
return cutOffParsing();
- }
-
+ }
+
if (getCurScope()->getFnParent() || getCurScope()->getBlockParent())
CCC = Sema::PCC_LocalDeclarationSpecifiers;
else if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate)
- CCC = DSContext == DSC_class? Sema::PCC_MemberTemplate
+ CCC = DSContext == DSC_class? Sema::PCC_MemberTemplate
: Sema::PCC_Template;
else if (DSContext == DSC_class)
CCC = Sema::PCC_Class;
else if (CurParsedObjCImpl)
CCC = Sema::PCC_ObjCImplementation;
-
+
Actions.CodeCompleteOrdinaryName(getCurScope(), CCC);
return cutOffParsing();
}
@@ -1910,7 +2130,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
continue;
case tok::annot_cxxscope: {
- if (DS.hasTypeSpecifier())
+ if (DS.hasTypeSpecifier() || DS.isTypeAltiVecVector())
goto DoneWithDeclSpec;
CXXScopeSpec SS;
@@ -1940,10 +2160,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
//
// the name is instead considered to name the constructor of
// class C.
- //
+ //
// Thus, if the template-name is actually the constructor
// name, then the code is ill-formed; this interpretation is
- // reinforced by the NAD status of core issue 635.
+ // reinforced by the NAD status of core issue 635.
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next);
if ((DSContext == DSC_top_level ||
(DSContext == DSC_class && DS.isFriendSpecified())) &&
@@ -1980,7 +2200,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
if (Tok.getAnnotationValue()) {
ParsedType T = getTypeAnnotation(Tok);
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename,
- Tok.getAnnotationEndLoc(),
+ Tok.getAnnotationEndLoc(),
PrevSpec, DiagID, T);
}
else
@@ -1996,7 +2216,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// check whether this is a constructor declaration.
if ((DSContext == DSC_top_level ||
(DSContext == DSC_class && DS.isFriendSpecified())) &&
- Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(),
+ Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(),
&SS)) {
if (isConstructorDeclarator())
goto DoneWithDeclSpec;
@@ -2049,7 +2269,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
DiagID, T);
} else
DS.SetTypeSpecError();
-
+
if (isInvalid)
break;
@@ -2058,10 +2278,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
- // Objective-C interface.
+ // Objective-C interface.
if (Tok.is(tok::less) && getLangOpts().ObjC1)
ParseObjCProtocolQualifiers(DS);
-
+
continue;
}
@@ -2082,7 +2302,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// We're done with the declaration-specifiers.
goto DoneWithDeclSpec;
-
+
// typedef-name
case tok::kw_decltype:
case tok::identifier: {
@@ -2108,6 +2328,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid))
break;
+ // [AltiVec] 2.2: [If the 'vector' specifier is used] The syntax does not
+ // allow the use of a typedef name as a type specifier.
+ if (DS.isTypeAltiVecVector())
+ goto DoneWithDeclSpec;
+
ParsedType TypeRep =
Actions.getTypeName(*Tok.getIdentifierInfo(),
Tok.getLocation(), getCurScope());
@@ -2136,10 +2361,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
- // Objective-C interface.
+ // Objective-C interface.
if (Tok.is(tok::less) && getLangOpts().ObjC1)
ParseObjCProtocolQualifiers(DS);
-
+
// Need to support trailing type qualifiers (e.g. "id<p> const").
// If a type specifier follows, it will be diagnosed elsewhere.
continue;
@@ -2179,9 +2404,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
continue;
// Microsoft single token adornments.
- case tok::kw___forceinline:
- // FIXME: Add handling here!
- break;
+ case tok::kw___forceinline: {
+ isInvalid = DS.SetFunctionSpecInline(Loc, PrevSpec, DiagID);
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ // FIXME: This does not work correctly if it is set to be a declspec
+ // attribute, and a GNU attribute is simply incorrect.
+ DS.getAttributes().addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
+ SourceLocation(), 0, 0, AttributeList::AS_GNU);
+ continue;
+ }
case tok::kw___ptr64:
case tok::kw___ptr32:
@@ -2266,7 +2498,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// alignment-specifier
case tok::kw__Alignas:
if (!getLangOpts().C11)
- Diag(Tok, diag::ext_c11_alignas);
+ Diag(Tok, diag::ext_c11_alignment) << Tok.getName();
ParseAlignmentSpecifier(DS.getAttributes());
continue;
@@ -2285,7 +2517,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
case tok::kw___module_private__:
isInvalid = DS.setModulePrivateSpec(Loc, PrevSpec, DiagID);
break;
-
+
// constexpr
case tok::kw_constexpr:
isInvalid = DS.SetConstexprSpec(Loc, PrevSpec, DiagID);
@@ -2422,15 +2654,15 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// cv-qualifier:
case tok::kw_const:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID,
- getLangOpts());
+ getLangOpts(), /*IsTypeSpec*/true);
break;
case tok::kw_volatile:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, DiagID,
- getLangOpts());
+ getLangOpts(), /*IsTypeSpec*/true);
break;
case tok::kw_restrict:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
- getLangOpts());
+ getLangOpts(), /*IsTypeSpec*/true);
break;
// C++ typename-specifier:
@@ -2461,7 +2693,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
continue;
// OpenCL qualifiers:
- case tok::kw_private:
+ case tok::kw_private:
if (!getLangOpts().OpenCL)
goto DoneWithDeclSpec;
case tok::kw___private:
@@ -2473,7 +2705,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
case tok::kw___read_write:
ParseOpenCLQualifiers(DS);
break;
-
+
case tok::less:
// GCC ObjC supports types like "<SomeProtocol>" as a synonym for
// "id<SomeProtocol>". This is hopelessly old fashioned and dangerous,
@@ -2485,7 +2717,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
Diag(Loc, diag::warn_objc_protocol_qualifier_missing_id)
<< FixItHint::CreateInsertion(Loc, "id")
<< SourceRange(Loc, DS.getSourceRange().getEnd());
-
+
// Need to support trailing type qualifiers (e.g. "id<p> const").
// If a type specifier follows, it will be diagnosed elsewhere.
continue;
@@ -2494,7 +2726,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
if (isInvalid) {
assert(PrevSpec && "Method did not return previous specifier!");
assert(DiagID);
-
+
if (DiagID == diag::ext_duplicate_declspec)
Diag(Tok, DiagID)
<< PrevSpec << FixItHint::CreateRemoval(Tok.getLocation());
@@ -2505,6 +2737,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
DS.SetRangeEnd(Tok.getLocation());
if (DiagID != diag::err_bool_redeclaration)
ConsumeToken();
+
+ AttrsLastTime = false;
}
}
@@ -2526,8 +2760,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
/// [GNU] declarator[opt] ':' constant-expression attributes[opt]
///
void Parser::
-ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) {
-
+ParseStructDeclaration(ParsingDeclSpec &DS, FieldCallback &Fields) {
+
if (Tok.is(tok::kw___extension__)) {
// __extension__ silences extension warnings in the subexpression.
ExtensionRAIIObject O(Diags); // Use RAII to do this.
@@ -2541,7 +2775,9 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) {
// If there are no declarators, this is a free-standing declaration
// specifier. Let the actions module cope with it.
if (Tok.is(tok::semi)) {
- Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS);
+ Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none,
+ DS);
+ DS.complete(TheDecl);
return;
}
@@ -2549,8 +2785,7 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) {
bool FirstDeclarator = true;
SourceLocation CommaLoc;
while (1) {
- ParsingDeclRAIIObject PD(*this);
- FieldDeclarator DeclaratorInfo(DS);
+ ParsingFieldDeclarator DeclaratorInfo(*this, DS);
DeclaratorInfo.D.setCommaLoc(CommaLoc);
// Attributes are only allowed here on successive declarators.
@@ -2578,8 +2813,7 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) {
MaybeParseGNUAttributes(DeclaratorInfo.D);
// We're done with this declarator; invoke the callback.
- Decl *D = Fields.invoke(DeclaratorInfo);
- PD.complete(D);
+ Fields.invoke(DeclaratorInfo);
// If we don't have a comma, it is either the end of the list (a ';')
// or an error, bail out.
@@ -2630,16 +2864,10 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
// Check for extraneous top-level semicolon.
if (Tok.is(tok::semi)) {
- Diag(Tok, diag::ext_extra_struct_semi)
- << DeclSpec::getSpecifierName((DeclSpec::TST)TagType)
- << FixItHint::CreateRemoval(Tok.getLocation());
- ConsumeToken();
+ ConsumeExtraSemi(InsideStruct, TagType);
continue;
}
- // Parse all the comma separated declarators.
- DeclSpec DS(AttrFactory);
-
if (!Tok.is(tok::at)) {
struct CFieldCallback : FieldCallback {
Parser &P;
@@ -2650,16 +2878,18 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
SmallVectorImpl<Decl *> &FieldDecls) :
P(P), TagDecl(TagDecl), FieldDecls(FieldDecls) {}
- virtual Decl *invoke(FieldDeclarator &FD) {
+ void invoke(ParsingFieldDeclarator &FD) {
// Install the declarator into the current TagDecl.
Decl *Field = P.Actions.ActOnField(P.getCurScope(), TagDecl,
FD.D.getDeclSpec().getSourceRange().getBegin(),
FD.D, FD.BitfieldSize);
FieldDecls.push_back(Field);
- return Field;
+ FD.complete(Field);
}
} Callback(*this, TagDecl, FieldDecls);
+ // Parse all the comma separated declarators.
+ ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, Callback);
} else { // Handle @defs
ConsumeToken();
@@ -2752,30 +2982,46 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
return cutOffParsing();
}
+ // If attributes exist after tag, parse them.
+ ParsedAttributesWithRange attrs(AttrFactory);
+ MaybeParseGNUAttributes(attrs);
+ MaybeParseCXX0XAttributes(attrs);
+
+ // If declspecs exist after tag, parse them.
+ while (Tok.is(tok::kw___declspec))
+ ParseMicrosoftDeclSpec(attrs);
+
SourceLocation ScopedEnumKWLoc;
bool IsScopedUsingClassTag = false;
+ // In C++11, recognize 'enum class' and 'enum struct'.
if (getLangOpts().CPlusPlus0x &&
(Tok.is(tok::kw_class) || Tok.is(tok::kw_struct))) {
Diag(Tok, diag::warn_cxx98_compat_scoped_enum);
IsScopedUsingClassTag = Tok.is(tok::kw_class);
ScopedEnumKWLoc = ConsumeToken();
- }
- // C++11 [temp.explicit]p12: The usual access controls do not apply to names
- // used to specify explicit instantiations. We extend this to also cover
- // explicit specializations.
- Sema::SuppressAccessChecksRAII SuppressAccess(Actions,
- TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
- TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ // Attributes are not allowed between these keywords. Diagnose,
+ // but then just treat them like they appeared in the right place.
+ ProhibitAttributes(attrs);
- // If attributes exist after tag, parse them.
- ParsedAttributes attrs(AttrFactory);
- MaybeParseGNUAttributes(attrs);
+ // They are allowed afterwards, though.
+ MaybeParseGNUAttributes(attrs);
+ MaybeParseCXX0XAttributes(attrs);
+ while (Tok.is(tok::kw___declspec))
+ ParseMicrosoftDeclSpec(attrs);
+ }
- // If declspecs exist after tag, parse them.
- while (Tok.is(tok::kw___declspec))
- ParseMicrosoftDeclSpec(attrs);
+ // C++11 [temp.explicit]p12:
+ // The usual access controls do not apply to names used to specify
+ // explicit instantiations.
+ // We extend this to also cover explicit specializations. Note that
+ // we don't suppress if this turns out to be an elaborated type
+ // specifier.
+ bool shouldDelayDiagsInTag =
+ (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
// Enum definitions should not be parsed in a trailing-return-type.
bool AllowDeclaration = DSC != DSC_trailing;
@@ -2789,8 +3035,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
// "enum foo : bar;" is not a potential typo for "enum foo::bar;"
// if a fixed underlying type is allowed.
ColonProtectionRAIIObject X(*this, AllowFixedUnderlyingType);
-
- if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
+
+ if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
/*EnteringContext=*/false))
return;
@@ -2831,32 +3077,35 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
IsScopedUsingClassTag = false;
}
- // Stop suppressing access control now we've parsed the enum name.
- SuppressAccess.done();
+ // Okay, end the suppression area. We'll decide whether to emit the
+ // diagnostics in a second.
+ if (shouldDelayDiagsInTag)
+ diagsFromTag.done();
TypeResult BaseType;
// Parse the fixed underlying type.
+ bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope;
if (AllowFixedUnderlyingType && Tok.is(tok::colon)) {
bool PossibleBitfield = false;
- if (getCurScope()->getFlags() & Scope::ClassScope) {
+ if (CanBeBitfield) {
// If we're in class scope, this can either be an enum declaration with
// an underlying type, or a declaration of a bitfield member. We try to
// use a simple disambiguation scheme first to catch the common cases
- // (integer literal, sizeof); if it's still ambiguous, we then consider
- // anything that's a simple-type-specifier followed by '(' as an
- // expression. This suffices because function types are not valid
+ // (integer literal, sizeof); if it's still ambiguous, we then consider
+ // anything that's a simple-type-specifier followed by '(' as an
+ // expression. This suffices because function types are not valid
// underlying types anyway.
TPResult TPR = isExpressionOrTypeSpecifierSimple(NextToken().getKind());
- // If the next token starts an expression, we know we're parsing a
+ // If the next token starts an expression, we know we're parsing a
// bit-field. This is the common case.
if (TPR == TPResult::True())
PossibleBitfield = true;
// If the next token starts a type-specifier-seq, it may be either a
// a fixed underlying type or the start of a function-style cast in C++;
- // lookahead one more token to see if it's obvious that we have a
+ // lookahead one more token to see if it's obvious that we have a
// fixed underlying type.
- else if (TPR == TPResult::False() &&
+ else if (TPR == TPResult::False() &&
GetLookAheadToken(2).getKind() == tok::semi) {
// Consume the ':'.
ConsumeToken();
@@ -2894,7 +3143,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
if (!PossibleBitfield) {
SourceRange Range;
BaseType = ParseTypeName(&Range);
-
+
if (!getLangOpts().CPlusPlus0x && !getLangOpts().ObjC2)
Diag(StartLoc, diag::ext_ms_enum_fixed_underlying_type)
<< Range;
@@ -2914,16 +3163,39 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
// enum foo {..}; void bar() { enum foo x; } <- use of old foo.
//
Sema::TagUseKind TUK;
- if (DS.isFriendSpecified())
- TUK = Sema::TUK_Friend;
- else if (!AllowDeclaration)
+ if (!AllowDeclaration) {
TUK = Sema::TUK_Reference;
- else if (Tok.is(tok::l_brace))
- TUK = Sema::TUK_Definition;
- else if (Tok.is(tok::semi) && DSC != DSC_type_specifier)
- TUK = Sema::TUK_Declaration;
- else
+ } else if (Tok.is(tok::l_brace)) {
+ if (DS.isFriendSpecified()) {
+ Diag(Tok.getLocation(), diag::err_friend_decl_defines_type)
+ << SourceRange(DS.getFriendSpecLoc());
+ ConsumeBrace();
+ SkipUntil(tok::r_brace);
+ TUK = Sema::TUK_Friend;
+ } else {
+ TUK = Sema::TUK_Definition;
+ }
+ } else if (DSC != DSC_type_specifier &&
+ (Tok.is(tok::semi) ||
+ (Tok.isAtStartOfLine() &&
+ !isValidAfterTypeSpecifier(CanBeBitfield)))) {
+ TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration;
+ if (Tok.isNot(tok::semi)) {
+ // A semicolon was missing after this declaration. Diagnose and recover.
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl,
+ "enum");
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::semi);
+ }
+ } else {
TUK = Sema::TUK_Reference;
+ }
+
+ // If this is an elaborated type specifier, and we delayed
+ // diagnostics before, just merge them into the current pool.
+ if (TUK == Sema::TUK_Reference && shouldDelayDiagsInTag) {
+ diagsFromTag.redelay();
+ }
MultiTemplateParamsArg TParams;
if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
@@ -2947,6 +3219,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
TemplateInfo.TemplateParams->size());
}
+ if (TUK == Sema::TUK_Reference)
+ ProhibitAttributes(attrs);
+
if (!Name && TUK != Sema::TUK_Definition) {
Diag(Tok, diag::err_enumerator_unnamed_no_def);
@@ -2966,52 +3241,44 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
IsScopedUsingClassTag, BaseType);
if (IsDependent) {
- // This enum has a dependent nested-name-specifier. Handle it as a
+ // This enum has a dependent nested-name-specifier. Handle it as a
// dependent tag.
if (!Name) {
DS.SetTypeSpecError();
Diag(Tok, diag::err_expected_type_name_after_typename);
return;
}
-
+
TypeResult Type = Actions.ActOnDependentTag(getCurScope(), DeclSpec::TST_enum,
- TUK, SS, Name, StartLoc,
+ TUK, SS, Name, StartLoc,
NameLoc);
if (Type.isInvalid()) {
DS.SetTypeSpecError();
return;
}
-
+
if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc,
NameLoc.isValid() ? NameLoc : StartLoc,
PrevSpec, DiagID, Type.get()))
Diag(StartLoc, DiagID) << PrevSpec;
-
+
return;
}
if (!TagDecl) {
- // The action failed to produce an enumeration tag. If this is a
+ // The action failed to produce an enumeration tag. If this is a
// definition, consume the entire definition.
if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
ConsumeBrace();
SkipUntil(tok::r_brace);
}
-
+
DS.SetTypeSpecError();
return;
}
- if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
- if (TUK == Sema::TUK_Friend) {
- Diag(Tok, diag::err_friend_decl_defines_type)
- << SourceRange(DS.getFriendSpecLoc());
- ConsumeBrace();
- SkipUntil(tok::r_brace);
- } else {
- ParseEnumBody(StartLoc, TagDecl);
- }
- }
+ if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference)
+ ParseEnumBody(StartLoc, TagDecl);
if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc,
NameLoc.isValid() ? NameLoc : StartLoc,
@@ -3051,13 +3318,15 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
SourceLocation IdentLoc = ConsumeToken();
// If attributes exist after the enumerator, parse them.
- ParsedAttributes attrs(AttrFactory);
+ ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseGNUAttributes(attrs);
+ MaybeParseCXX0XAttributes(attrs);
+ ProhibitAttributes(attrs);
SourceLocation EqualLoc;
ExprResult AssignedVal;
- ParsingDeclRAIIObject PD(*this);
-
+ ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent);
+
if (Tok.is(tok::equal)) {
EqualLoc = ConsumeToken();
AssignedVal = ParseConstantExpression();
@@ -3072,26 +3341,27 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
attrs.getList(), EqualLoc,
AssignedVal.release());
PD.complete(EnumConstDecl);
-
+
EnumConstantDecls.push_back(EnumConstDecl);
LastEnumConstDecl = EnumConstDecl;
if (Tok.is(tok::identifier)) {
// We're missing a comma between enumerators.
SourceLocation Loc = PP.getLocForEndOfToken(PrevTokLocation);
- Diag(Loc, diag::err_enumerator_list_missing_comma)
+ Diag(Loc, diag::err_enumerator_list_missing_comma)
<< FixItHint::CreateInsertion(Loc, ", ");
continue;
}
-
+
if (Tok.isNot(tok::comma))
break;
SourceLocation CommaLoc = ConsumeToken();
if (Tok.isNot(tok::identifier)) {
if (!getLangOpts().C99 && !getLangOpts().CPlusPlus0x)
- Diag(CommaLoc, diag::ext_enumerator_list_comma)
- << getLangOpts().CPlusPlus
+ Diag(CommaLoc, getLangOpts().CPlusPlus ?
+ diag::ext_enumerator_list_comma_cxx :
+ diag::ext_enumerator_list_comma_c)
<< FixItHint::CreateRemoval(CommaLoc);
else if (getLangOpts().CPlusPlus0x)
Diag(CommaLoc, diag::warn_cxx98_compat_enumerator_list_comma)
@@ -3114,6 +3384,18 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
EnumScope.Exit();
Actions.ActOnTagFinishDefinition(getCurScope(), EnumDecl,
T.getCloseLocation());
+
+ // The next token must be valid after an enum definition. If not, a ';'
+ // was probably forgotten.
+ bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope;
+ if (!isValidAfterTypeSpecifier(CanBeBitfield)) {
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, "enum");
+ // Push this token back into the preprocessor and change our current token
+ // to ';' so that the rest of the code recovers as though there were an
+ // ';' after the definition.
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::semi);
+ }
}
/// isTypeSpecifierQualifier - Return true if the current token could be the
@@ -3171,14 +3453,14 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const {
case tok::kw__Decimal64:
case tok::kw__Decimal128:
case tok::kw___vector:
-
+
// struct-or-union-specifier (C99) or class-specifier (C++)
case tok::kw_class:
case tok::kw_struct:
case tok::kw_union:
// enum-specifier
case tok::kw_enum:
-
+
// typedef-name
case tok::annot_typename:
return true;
@@ -3319,16 +3601,16 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
return true;
if (Tok.is(tok::identifier))
return false;
-
+
// If we're in Objective-C and we have an Objective-C class type followed
- // by an identifier and then either ':' or ']', in a place where an
+ // by an identifier and then either ':' or ']', in a place where an
// expression is permitted, then this is probably a class message send
// missing the initial '['. In this case, we won't consider this to be
// the start of a declaration.
- if (DisambiguatingWithExpression &&
+ if (DisambiguatingWithExpression &&
isStartOfObjCClassMessageMissingOpenBracket())
return false;
-
+
return isDeclarationSpecifier();
case tok::coloncolon: // ::foo::bar
@@ -3353,7 +3635,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
// Modules
case tok::kw___module_private__:
-
+
// type-specifiers
case tok::kw_short:
case tok::kw_long:
@@ -3423,7 +3705,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
case tok::annot_typename:
return !DisambiguatingWithExpression ||
!isStartOfObjCClassMessageMissingOpenBracket();
-
+
case tok::kw___declspec:
case tok::kw___cdecl:
case tok::kw___stdcall:
@@ -3453,7 +3735,7 @@ bool Parser::isConstructorDeclarator() {
// Parse the C++ scope specifier.
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
+ if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
/*EnteringContext=*/true)) {
TPA.Revert();
return false;
@@ -3540,10 +3822,10 @@ bool Parser::isConstructorDeclarator() {
/// ParseTypeQualifierListOpt
/// type-qualifier-list: [C99 6.7.5]
/// type-qualifier
-/// [vendor] attributes
+/// [vendor] attributes
/// [ only if VendorAttributesAllowed=true ]
/// type-qualifier-list type-qualifier
-/// [vendor] type-qualifier-list attributes
+/// [vendor] type-qualifier-list attributes
/// [ only if VendorAttributesAllowed=true ]
/// [C++0x] attribute-specifier[opt] is allowed before cv-qualifier-seq
/// [ only if CXX0XAttributesAllowed=true ]
@@ -3571,22 +3853,22 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS,
case tok::code_completion:
Actions.CodeCompleteTypeQualifiers(DS);
return cutOffParsing();
-
+
case tok::kw_const:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , Loc, PrevSpec, DiagID,
- getLangOpts());
+ getLangOpts(), /*IsTypeSpec*/false);
break;
case tok::kw_volatile:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, DiagID,
- getLangOpts());
+ getLangOpts(), /*IsTypeSpec*/false);
break;
case tok::kw_restrict:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID,
- getLangOpts());
+ getLangOpts(), /*IsTypeSpec*/false);
break;
// OpenCL qualifiers:
- case tok::kw_private:
+ case tok::kw_private:
if (!getLangOpts().OpenCL)
goto DoneWithTypeQuals;
case tok::kw___private:
@@ -3692,7 +3974,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
DirectDeclParseFunction DirectDeclParser) {
if (Diags.hasAllExtensionsSilenced())
D.setExtension();
-
+
// C++ member pointers start with a '::' or a nested-name.
// Member pointers get special handling, since there's no place for the
// scope spec in the generic path below.
@@ -3886,7 +4168,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
if (D.getCXXScopeSpec().isEmpty()) {
bool EnteringContext = D.getContext() == Declarator::FileContext ||
D.getContext() == Declarator::MemberContext;
- ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), ParsedType(),
+ ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), ParsedType(),
EnteringContext);
}
@@ -3899,9 +4181,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
// C++0x [dcl.fct]p14:
// There is a syntactic ambiguity when an ellipsis occurs at the end
- // of a parameter-declaration-clause without a preceding comma. In
- // this case, the ellipsis is parsed as part of the
- // abstract-declarator if the type of the parameter names a template
+ // of a parameter-declaration-clause without a preceding comma. In
+ // this case, the ellipsis is parsed as part of the
+ // abstract-declarator if the type of the parameter names a template
// parameter pack that has not been expanded; otherwise, it is parsed
// as part of the parameter-declaration-clause.
if (Tok.is(tok::ellipsis) && D.getCXXScopeSpec().isEmpty() &&
@@ -3940,9 +4222,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
AllowConstructorName = (D.getContext() == Declarator::MemberContext);
SourceLocation TemplateKWLoc;
- if (ParseUnqualifiedId(D.getCXXScopeSpec(),
- /*EnteringContext=*/true,
- /*AllowDestructorName=*/true,
+ if (ParseUnqualifiedId(D.getCXXScopeSpec(),
+ /*EnteringContext=*/true,
+ /*AllowDestructorName=*/true,
AllowConstructorName,
ParsedType(),
TemplateKWLoc,
@@ -3992,6 +4274,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
// portion is empty), if an abstract-declarator is allowed.
D.SetIdentifier(0, Tok.getLocation());
} else {
+ if (Tok.getKind() == tok::annot_pragma_parser_crash)
+ *(volatile int*) 0x11 = 0;
if (D.getContext() == Declarator::MemberContext)
Diag(Tok, diag::err_expected_member_name_or_semi)
<< D.getDeclSpec().getSourceRange();
@@ -4020,17 +4304,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
// The paren may be part of a C++ direct initializer, eg. "int x(1);".
// In such a case, check if we actually have a function declarator; if it
// is not, the declarator has been fully parsed.
- if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) {
- // When not in file scope, warn for ambiguous function declarators, just
- // in case the author intended it as a variable definition.
- bool warnIfAmbiguous = D.getContext() != Declarator::FileContext;
- if (!isCXXFunctionDeclarator(warnIfAmbiguous))
- break;
- }
+ bool IsAmbiguous = false;
+ if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit() &&
+ !isCXXFunctionDeclarator(&IsAmbiguous))
+ break;
ParsedAttributes attrs(AttrFactory);
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
- ParseFunctionDeclarator(D, attrs, T);
+ ParseFunctionDeclarator(D, attrs, T, IsAmbiguous);
PrototypeScope.Exit();
} else if (Tok.is(tok::l_square)) {
ParseBracketDeclarator(D);
@@ -4038,7 +4319,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
break;
}
}
-}
+}
/// ParseParenDeclarator - We parsed the declarator D up to a paren. This is
/// only called before the identifier, so these are most likely just grouping
@@ -4124,7 +4405,7 @@ void Parser::ParseParenDeclarator(Declarator &D) {
ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator);
// Match the ')'.
T.consumeClose();
- D.AddTypeInfo(DeclaratorChunk::getParen(T.getOpenLocation(),
+ D.AddTypeInfo(DeclaratorChunk::getParen(T.getOpenLocation(),
T.getCloseLocation()),
attrs, T.getCloseLocation());
@@ -4147,7 +4428,7 @@ void Parser::ParseParenDeclarator(Declarator &D) {
// function prototype scope, including parameter declarators.
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope|Scope::DeclScope);
- ParseFunctionDeclarator(D, attrs, T, RequiresArg);
+ ParseFunctionDeclarator(D, attrs, T, false, RequiresArg);
PrototypeScope.Exit();
}
@@ -4173,8 +4454,9 @@ void Parser::ParseParenDeclarator(Declarator &D) {
void Parser::ParseFunctionDeclarator(Declarator &D,
ParsedAttributes &FirstArgAttrs,
BalancedDelimiterTracker &Tracker,
+ bool IsAmbiguous,
bool RequiresArg) {
- assert(getCurScope()->isFunctionPrototypeScope() &&
+ assert(getCurScope()->isFunctionPrototypeScope() &&
"Should call from a Function scope");
// lparen is already consumed!
assert(D.isPastIdentifier() && "Should not call before identifier!");
@@ -4198,7 +4480,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
SmallVector<SourceRange, 2> DynamicExceptionRanges;
ExprResult NoexceptExpr;
ParsedAttributes FnAttrs(AttrFactory);
- ParsedType TrailingReturnType;
+ TypeResult TrailingReturnType;
Actions.ActOnStartFunctionDeclarator();
@@ -4248,16 +4530,16 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
}
// C++11 [expr.prim.general]p3:
- // If a declaration declares a member function or member function
- // template of a class X, the expression this is a prvalue of type
+ // If a declaration declares a member function or member function
+ // template of a class X, the expression this is a prvalue of type
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
- // and the end of the function-definition, member-declarator, or
+ // and the end of the function-definition, member-declarator, or
// declarator.
- bool IsCXX11MemberFunction =
+ bool IsCXX11MemberFunction =
getLangOpts().CPlusPlus0x &&
(D.getContext() == Declarator::MemberContext ||
(D.getContext() == Declarator::FileContext &&
- D.getCXXScopeSpec().isValid() &&
+ D.getCXXScopeSpec().isValid() &&
Actions.CurContext->isRecord()));
Sema::CXXThisScopeRAII ThisScope(Actions,
dyn_cast<CXXRecordDecl>(Actions.CurContext),
@@ -4280,7 +4562,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
if (getLangOpts().CPlusPlus0x && Tok.is(tok::arrow)) {
Diag(Tok, diag::warn_cxx98_compat_trailing_return_type);
SourceRange Range;
- TrailingReturnType = ParseTrailingReturnType(Range).get();
+ TrailingReturnType = ParseTrailingReturnType(Range);
if (Range.getEnd().isValid())
EndLoc = Range.getEnd();
}
@@ -4290,7 +4572,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
// Remember that we parsed a function type, and remember the attributes.
D.AddTypeInfo(DeclaratorChunk::getFunction(HasProto,
/*isVariadic=*/EllipsisLoc.isValid(),
- EllipsisLoc,
+ IsAmbiguous, EllipsisLoc,
ParamInfo.data(), ParamInfo.size(),
DS.getTypeQualifiers(),
RefQualifierIsLValueRef,
@@ -4303,7 +4585,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
DynamicExceptions.size(),
NoexceptExpr.isUsable() ?
NoexceptExpr.get() : 0,
- Tracker.getOpenLocation(),
+ Tracker.getOpenLocation(),
EndLoc, D,
TrailingReturnType),
FnAttrs, EndLoc);
@@ -4528,7 +4810,7 @@ void Parser::ParseParameterDeclarationClause(
// Consume the '='.
ConsumeToken();
- // The argument isn't actually potentially evaluated unless it is
+ // The argument isn't actually potentially evaluated unless it is
// used.
EnterExpressionEvaluationContext Eval(Actions,
Sema::PotentiallyEvaluatedIfUsed,
@@ -4560,7 +4842,7 @@ void Parser::ParseParameterDeclarationClause(
if (Tok.isNot(tok::comma)) {
if (Tok.is(tok::ellipsis)) {
EllipsisLoc = ConsumeToken(); // Consume the ellipsis.
-
+
if (!getLangOpts().CPlusPlus) {
// We have ellipsis without a preceding ',', which is ill-formed
// in C. Complain and provide the fix.
@@ -4568,7 +4850,7 @@ void Parser::ParseParameterDeclarationClause(
<< FixItHint::CreateInsertion(EllipsisLoc, ", ");
}
}
-
+
break;
}
@@ -4598,7 +4880,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) {
T.consumeClose();
ParsedAttributes attrs(AttrFactory);
MaybeParseCXX0XAttributes(attrs);
-
+
// Remember that we parsed the empty array type.
ExprResult NumElements;
D.AddTypeInfo(DeclaratorChunk::getArray(0, false, false, 0,
@@ -4646,7 +4928,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) {
// Handle the case where we have '[*]' as the array size. However, a leading
// star could be the start of an expression, for example 'X[*p + 4]'. Verify
- // the the token after the star is a ']'. Since stars in arrays are
+ // the token after the star is a ']'. Since stars in arrays are
// infrequent, use of lookahead is not costly here.
if (Tok.is(tok::star) && GetLookAheadToken(1).is(tok::r_square)) {
ConsumeToken(); // Eat the '*'.
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 5e6c4f5..3dc96cf 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -444,6 +444,13 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
CXXScopeSpec SS;
SourceLocation TypenameLoc;
bool IsTypeName;
+ ParsedAttributesWithRange attrs(AttrFactory);
+
+ // FIXME: Simply skip the attributes and diagnose, don't bother parsing them.
+ MaybeParseCXX0XAttributes(attrs);
+ ProhibitAttributes(attrs);
+ attrs.clear();
+ attrs.Range = SourceRange();
// Ignore optional 'typename'.
// FIXME: This is wrong; we should parse this as a typename-specifier.
@@ -480,7 +487,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
return 0;
}
- ParsedAttributes attrs(AttrFactory);
+ MaybeParseCXX0XAttributes(attrs);
// Maybe this is an alias-declaration.
bool IsAliasDecl = Tok.is(tok::equal);
@@ -533,9 +540,14 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
TypeAlias = ParseTypeName(0, TemplateInfo.Kind ?
Declarator::AliasTemplateContext :
Declarator::AliasDeclContext, AS, OwnedType);
- } else
+ } else {
+ // C++11 attributes are not allowed on a using-declaration, but GNU ones
+ // are.
+ ProhibitAttributes(attrs);
+
// Parse (optional) attributes (most likely GNU strong-using extension).
MaybeParseGNUAttributes(attrs);
+ }
// Eat ';'.
DeclEnd = Tok.getLocation();
@@ -572,6 +584,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
MultiTemplateParamsArg TemplateParamsArg(Actions,
TemplateParams ? TemplateParams->data() : 0,
TemplateParams ? TemplateParams->size() : 0);
+ // FIXME: Propagate attributes.
return Actions.ActOnAliasDeclaration(getCurScope(), AS, TemplateParamsArg,
UsingLoc, Name, TypeAlias);
}
@@ -874,10 +887,12 @@ Parser::TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
}
// We have an identifier; check whether it is actually a type.
+ IdentifierInfo *CorrectedII = 0;
ParsedType Type = Actions.getTypeName(*Id, IdLoc, getCurScope(), &SS, true,
false, ParsedType(),
/*IsCtorOrDtorName=*/false,
- /*NonTrivialTypeSourceInfo=*/true);
+ /*NonTrivialTypeSourceInfo=*/true,
+ &CorrectedII);
if (!Type) {
Diag(IdLoc, diag::err_expected_class_name);
return true;
@@ -900,6 +915,77 @@ Parser::TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
}
+void Parser::ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs) {
+ while (Tok.is(tok::kw___single_inheritance) ||
+ Tok.is(tok::kw___multiple_inheritance) ||
+ Tok.is(tok::kw___virtual_inheritance)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0,
+ SourceLocation(), 0, 0, AttributeList::AS_GNU);
+ }
+}
+
+/// Determine whether the following tokens are valid after a type-specifier
+/// which could be a standalone declaration. This will conservatively return
+/// true if there's any doubt, and is appropriate for insert-';' fixits.
+bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
+ // This switch enumerates the valid "follow" set for type-specifiers.
+ switch (Tok.getKind()) {
+ default: break;
+ case tok::semi: // struct foo {...} ;
+ case tok::star: // struct foo {...} * P;
+ case tok::amp: // struct foo {...} & R = ...
+ case tok::identifier: // struct foo {...} V ;
+ case tok::r_paren: //(struct foo {...} ) {4}
+ case tok::annot_cxxscope: // struct foo {...} a:: b;
+ case tok::annot_typename: // struct foo {...} a ::b;
+ case tok::annot_template_id: // struct foo {...} a<int> ::b;
+ case tok::l_paren: // struct foo {...} ( x);
+ case tok::comma: // __builtin_offsetof(struct foo{...} ,
+ return true;
+ case tok::colon:
+ return CouldBeBitfield; // enum E { ... } : 2;
+ // Type qualifiers
+ case tok::kw_const: // struct foo {...} const x;
+ case tok::kw_volatile: // struct foo {...} volatile x;
+ case tok::kw_restrict: // struct foo {...} restrict x;
+ case tok::kw_inline: // struct foo {...} inline foo() {};
+ // Storage-class specifiers
+ case tok::kw_static: // struct foo {...} static x;
+ case tok::kw_extern: // struct foo {...} extern x;
+ case tok::kw_typedef: // struct foo {...} typedef x;
+ case tok::kw_register: // struct foo {...} register x;
+ case tok::kw_auto: // struct foo {...} auto x;
+ case tok::kw_mutable: // struct foo {...} mutable x;
+ case tok::kw_constexpr: // struct foo {...} constexpr x;
+ // As shown above, type qualifiers and storage class specifiers absolutely
+ // can occur after class specifiers according to the grammar. However,
+ // almost no one actually writes code like this. If we see one of these,
+ // it is much more likely that someone missed a semi colon and the
+ // type/storage class specifier we're seeing is part of the *next*
+ // intended declaration, as in:
+ //
+ // struct foo { ... }
+ // typedef int X;
+ //
+ // We'd really like to emit a missing semicolon error instead of emitting
+ // an error on the 'int' saying that you can't have two type specifiers in
+ // the same declaration of X. Because of this, we look ahead past this
+ // token to see if it's a type specifier. If so, we know the code is
+ // otherwise invalid, so we can produce the expected semi error.
+ if (!isKnownToBeTypeSpecifier(NextToken()))
+ return true;
+ break;
+ case tok::r_brace: // struct bar { struct foo {...} }
+ // Missing ';' at end of struct is accepted as an extension in C mode.
+ if (!getLangOpts().CPlusPlus)
+ return true;
+ break;
+ }
+ return false;
+}
+
/// ParseClassSpecifier - Parse a C++ class-specifier [C++ class] or
/// elaborated-type-specifier [C++ dcl.type.elab]; we can't tell which
/// until we reach the start of a definition or see a token that
@@ -968,11 +1054,15 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// As an extension we do not perform access checking on the names used to
// specify explicit specializations either. This is important to allow
// specializing traits classes for private types.
- Sema::SuppressAccessChecksRAII SuppressAccess(Actions,
- TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
- TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ //
+ // Note that we don't suppress if this turns out to be an elaborated
+ // type specifier.
+ bool shouldDelayDiagsInTag =
+ (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
+ TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+ SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
- ParsedAttributes attrs(AttrFactory);
+ ParsedAttributesWithRange attrs(AttrFactory);
// If attributes exist after tag, parse them.
if (Tok.is(tok::kw___attribute))
ParseGNUAttributes(attrs);
@@ -981,6 +1071,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
while (Tok.is(tok::kw___declspec))
ParseMicrosoftDeclSpec(attrs);
+ // Parse inheritance specifiers.
+ if (Tok.is(tok::kw___single_inheritance) ||
+ Tok.is(tok::kw___multiple_inheritance) ||
+ Tok.is(tok::kw___virtual_inheritance))
+ ParseMicrosoftInheritanceClassAttributes(attrs);
+
// If C++0x attributes exist here, parse them.
// FIXME: Are we consistent with the ordering of parsing of different
// styles of attributes?
@@ -1103,10 +1199,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
}
}
- // As soon as we're finished parsing the class's template-id, turn access
- // checking back on.
- SuppressAccess.done();
-
// There are four options here.
// - If we are in a trailing return type, this is always just a reference,
// and we must not try to parse a definition. For instance,
@@ -1144,11 +1236,29 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// Okay, this is a class definition.
TUK = Sema::TUK_Definition;
}
- } else if (Tok.is(tok::semi) && DSC != DSC_type_specifier)
+ } else if (DSC != DSC_type_specifier &&
+ (Tok.is(tok::semi) ||
+ (Tok.isAtStartOfLine() && !isValidAfterTypeSpecifier(false)))) {
TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration;
- else
+ if (Tok.isNot(tok::semi)) {
+ // A semicolon was missing after this declaration. Diagnose and recover.
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl,
+ TagType == DeclSpec::TST_class ? "class" :
+ TagType == DeclSpec::TST_struct ? "struct" : "union");
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::semi);
+ }
+ } else
TUK = Sema::TUK_Reference;
+ // If this is an elaborated type specifier, and we delayed
+ // diagnostics before, just merge them into the current pool.
+ if (shouldDelayDiagsInTag) {
+ diagsFromTag.done();
+ if (TUK == Sema::TUK_Reference)
+ diagsFromTag.redelay();
+ }
+
if (!Name && !TemplateId && (DS.getTypeSpecType() == DeclSpec::TST_error ||
TUK != Sema::TUK_Definition)) {
if (DS.getTypeSpecType() != DeclSpec::TST_error) {
@@ -1175,6 +1285,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
TUK == Sema::TUK_Declaration) {
// This is an explicit instantiation of a class template.
+ ProhibitAttributes(attrs);
+
TagOrTempResult
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
@@ -1196,6 +1308,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
} else if (TUK == Sema::TUK_Reference ||
(TUK == Sema::TUK_Friend &&
TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) {
+ ProhibitAttributes(attrs);
TypeResult = Actions.ActOnTagTemplateIdType(TUK, TagType, StartLoc,
TemplateId->SS,
TemplateId->TemplateKWLoc,
@@ -1260,6 +1373,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
//
// template struct Outer<int>::Inner;
//
+ ProhibitAttributes(attrs);
+
TagOrTempResult
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
@@ -1268,6 +1383,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
NameLoc, attrs.getList());
} else if (TUK == Sema::TUK_Friend &&
TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) {
+ ProhibitAttributes(attrs);
+
TagOrTempResult =
Actions.ActOnTemplatedFriendTag(getCurScope(), DS.getFriendSpecLoc(),
TagType, StartLoc, SS,
@@ -1281,6 +1398,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// FIXME: Diagnose this particular error.
}
+ if (TUK != Sema::TUK_Declaration && TUK != Sema::TUK_Definition)
+ ProhibitAttributes(attrs);
+
bool IsDependent = false;
// Don't pass down template parameter lists if this is just a tag
@@ -1344,77 +1464,19 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// impossible token occurs next, we assume that the programmer forgot a ; at
// the end of the declaration and recover that way.
//
- // This switch enumerates the valid "follow" set for definition.
- if (TUK == Sema::TUK_Definition) {
- bool ExpectedSemi = true;
- switch (Tok.getKind()) {
- default: break;
- case tok::semi: // struct foo {...} ;
- case tok::star: // struct foo {...} * P;
- case tok::amp: // struct foo {...} & R = ...
- case tok::identifier: // struct foo {...} V ;
- case tok::r_paren: //(struct foo {...} ) {4}
- case tok::annot_cxxscope: // struct foo {...} a:: b;
- case tok::annot_typename: // struct foo {...} a ::b;
- case tok::annot_template_id: // struct foo {...} a<int> ::b;
- case tok::l_paren: // struct foo {...} ( x);
- case tok::comma: // __builtin_offsetof(struct foo{...} ,
- ExpectedSemi = false;
- break;
- // Type qualifiers
- case tok::kw_const: // struct foo {...} const x;
- case tok::kw_volatile: // struct foo {...} volatile x;
- case tok::kw_restrict: // struct foo {...} restrict x;
- case tok::kw_inline: // struct foo {...} inline foo() {};
- // Storage-class specifiers
- case tok::kw_static: // struct foo {...} static x;
- case tok::kw_extern: // struct foo {...} extern x;
- case tok::kw_typedef: // struct foo {...} typedef x;
- case tok::kw_register: // struct foo {...} register x;
- case tok::kw_auto: // struct foo {...} auto x;
- case tok::kw_mutable: // struct foo {...} mutable x;
- case tok::kw_constexpr: // struct foo {...} constexpr x;
- // As shown above, type qualifiers and storage class specifiers absolutely
- // can occur after class specifiers according to the grammar. However,
- // almost no one actually writes code like this. If we see one of these,
- // it is much more likely that someone missed a semi colon and the
- // type/storage class specifier we're seeing is part of the *next*
- // intended declaration, as in:
- //
- // struct foo { ... }
- // typedef int X;
- //
- // We'd really like to emit a missing semicolon error instead of emitting
- // an error on the 'int' saying that you can't have two type specifiers in
- // the same declaration of X. Because of this, we look ahead past this
- // token to see if it's a type specifier. If so, we know the code is
- // otherwise invalid, so we can produce the expected semi error.
- if (!isKnownToBeTypeSpecifier(NextToken()))
- ExpectedSemi = false;
- break;
-
- case tok::r_brace: // struct bar { struct foo {...} }
- // Missing ';' at end of struct is accepted as an extension in C mode.
- if (!getLangOpts().CPlusPlus)
- ExpectedSemi = false;
- break;
- }
-
- // C++ [temp]p3 In a template-declaration which defines a class, no
- // declarator is permitted.
- if (TemplateInfo.Kind)
- ExpectedSemi = true;
-
- if (ExpectedSemi) {
- ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl,
- TagType == DeclSpec::TST_class ? "class"
- : TagType == DeclSpec::TST_struct? "struct" : "union");
- // Push this token back into the preprocessor and change our current token
- // to ';' so that the rest of the code recovers as though there were an
- // ';' after the definition.
- PP.EnterToken(Tok);
- Tok.setKind(tok::semi);
- }
+ // Also enforce C++ [temp]p3:
+ // In a template-declaration which defines a class, no declarator
+ // is permitted.
+ if (TUK == Sema::TUK_Definition &&
+ (TemplateInfo.Kind || !isValidAfterTypeSpecifier(false))) {
+ ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl,
+ TagType == DeclSpec::TST_class ? "class" :
+ TagType == DeclSpec::TST_struct ? "struct" : "union");
+ // Push this token back into the preprocessor and change our current token
+ // to ';' so that the rest of the code recovers as though there were an
+ // ';' after the definition.
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::semi);
}
}
@@ -1696,12 +1758,16 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
}
// Access declarations.
+ bool MalformedTypeSpec = false;
if (!TemplateInfo.Kind &&
- (Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) &&
- !TryAnnotateCXXScopeToken() &&
- Tok.is(tok::annot_cxxscope)) {
- bool isAccessDecl = false;
- if (NextToken().is(tok::identifier))
+ (Tok.is(tok::identifier) || Tok.is(tok::coloncolon))) {
+ if (TryAnnotateCXXScopeToken())
+ MalformedTypeSpec = true;
+
+ bool isAccessDecl;
+ if (Tok.isNot(tok::annot_cxxscope))
+ isAccessDecl = false;
+ else if (NextToken().is(tok::identifier))
isAccessDecl = GetLookAheadToken(2).is(tok::semi);
else
isAccessDecl = NextToken().is(tok::kw_operator);
@@ -1798,6 +1864,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
// Parse the common declaration-specifiers piece.
ParsingDeclSpec DS(*this, TemplateDiags);
DS.takeAttributesFrom(attrs);
+ if (MalformedTypeSpec)
+ DS.SetTypeSpecError();
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
&CommonLateParsedAttrs);
@@ -1915,9 +1983,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
LateParsedAttrs.clear();
// Consume the ';' - it's optional unless we have a delete or default
- if (Tok.is(tok::semi)) {
- ConsumeToken();
- }
+ if (Tok.is(tok::semi))
+ ConsumeExtraSemi(AfterMemberFunctionDefinition);
return;
}
@@ -1961,18 +2028,19 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
// goes before or after the GNU attributes and __asm__.
ParseOptionalCXX0XVirtSpecifierSeq(VS);
- bool HasDeferredInitializer = false;
+ InClassInitStyle HasInClassInit = ICIS_NoInit;
if ((Tok.is(tok::equal) || Tok.is(tok::l_brace)) && !HasInitializer) {
if (BitfieldSize.get()) {
Diag(Tok, diag::err_bitfield_member_init);
SkipUntil(tok::comma, true, true);
} else {
HasInitializer = true;
- HasDeferredInitializer = !DeclaratorInfo.isDeclarationOfFunction() &&
- DeclaratorInfo.getDeclSpec().getStorageClassSpec()
- != DeclSpec::SCS_static &&
- DeclaratorInfo.getDeclSpec().getStorageClassSpec()
- != DeclSpec::SCS_typedef;
+ if (!DeclaratorInfo.isDeclarationOfFunction() &&
+ DeclaratorInfo.getDeclSpec().getStorageClassSpec()
+ != DeclSpec::SCS_static &&
+ DeclaratorInfo.getDeclSpec().getStorageClassSpec()
+ != DeclSpec::SCS_typedef)
+ HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit;
}
}
@@ -1990,7 +2058,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclaratorInfo,
move(TemplateParams),
BitfieldSize.release(),
- VS, HasDeferredInitializer);
+ VS, HasInClassInit);
if (AccessAttrs)
Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs,
false, true);
@@ -2006,15 +2074,15 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
LateParsedAttrs.clear();
// Handle the initializer.
- if (HasDeferredInitializer) {
+ if (HasInClassInit != ICIS_NoInit) {
// The initializer was deferred; parse it and cache the tokens.
Diag(Tok, getLangOpts().CPlusPlus0x ?
diag::warn_cxx98_compat_nonstatic_member_init :
diag::ext_nonstatic_member_init);
if (DeclaratorInfo.isArrayOfUnknownBound()) {
- // C++0x [dcl.array]p3: An array bound may also be omitted when the
- // declarator is followed by an initializer.
+ // C++11 [dcl.array]p3: An array bound may also be omitted when the
+ // declarator is followed by an initializer.
//
// A brace-or-equal-initializer for a member-declarator is not an
// initializer in the grammar, so this is ill-formed.
@@ -2266,10 +2334,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
// Check for extraneous top-level semicolon.
if (Tok.is(tok::semi)) {
- Diag(Tok, diag::ext_extra_struct_semi)
- << DeclSpec::getSpecifierName((DeclSpec::TST)TagType)
- << FixItHint::CreateRemoval(Tok.getLocation());
- ConsumeToken();
+ ConsumeExtraSemi(InsideStruct, TagType);
continue;
}
@@ -2779,7 +2844,7 @@ IdentifierInfo *Parser::TryParseCXX11AttributeIdentifier(SourceLocation &Loc) {
StringRef Spelling = PP.getSpelling(Tok.getLocation(), SpellingBuf);
if (std::isalpha(Spelling[0])) {
Loc = ConsumeToken();
- return &PP.getIdentifierTable().get(Spelling.data());
+ return &PP.getIdentifierTable().get(Spelling);
}
return 0;
}
@@ -2870,28 +2935,31 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
}
bool AttrParsed = false;
- // No scoped names are supported; ideally we could put all non-standard
- // attributes into namespaces.
- if (!ScopeName) {
- switch (AttributeList::getKind(AttrName)) {
- // No arguments
- case AttributeList::AT_carries_dependency:
- case AttributeList::AT_noreturn: {
- if (Tok.is(tok::l_paren)) {
- Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments)
- << AttrName->getName();
- break;
- }
-
- attrs.addNew(AttrName, AttrLoc, 0, AttrLoc, 0,
- SourceLocation(), 0, 0, false, true);
- AttrParsed = true;
+ switch (AttributeList::getKind(AttrName, ScopeName,
+ AttributeList::AS_CXX11)) {
+ // No arguments
+ case AttributeList::AT_CarriesDependency:
+ // FIXME: implement generic support of attributes with C++11 syntax
+ // see Parse/ParseDecl.cpp: ParseGNUAttributes
+ case AttributeList::AT_FallThrough:
+ case AttributeList::AT_NoReturn: {
+ if (Tok.is(tok::l_paren)) {
+ Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments)
+ << AttrName->getName();
break;
}
- // Silence warnings
- default: break;
- }
+ attrs.addNew(AttrName,
+ SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc,
+ AttrLoc),
+ ScopeName, ScopeLoc, 0,
+ SourceLocation(), 0, 0, AttributeList::AS_CXX11);
+ AttrParsed = true;
+ break;
+ }
+
+ // Silence warnings
+ default: break;
}
// Skip the entire parameter clause, if any
@@ -2917,7 +2985,7 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
SkipUntil(tok::r_square, false);
}
-/// ParseCXX11Attributes - Parse a C++0x attribute-specifier-seq.
+/// ParseCXX11Attributes - Parse a C++11 attribute-specifier-seq.
///
/// attribute-specifier-seq:
/// attribute-specifier-seq[opt] attribute-specifier
@@ -2991,10 +3059,7 @@ void Parser::ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType,
// Check for extraneous top-level semicolon.
if (Tok.is(tok::semi)) {
- Diag(Tok, diag::ext_extra_struct_semi)
- << DeclSpec::getSpecifierName((DeclSpec::TST)TagType)
- << FixItHint::CreateRemoval(Tok.getLocation());
- ConsumeToken();
+ ConsumeExtraSemi(InsideStruct, TagType);
continue;
}
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 6d31396..8d4668b 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -6,17 +6,19 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file implements the Expression parsing implementation. Expressions in
-// C99 basically consist of a bunch of binary operators with unary operators and
-// other random stuff at the leaves.
-//
-// In the C99 grammar, these unary operators bind tightest and are represented
-// as the 'cast-expression' production. Everything else is either a binary
-// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are
-// handled by ParseCastExpression, the higher level pieces are handled by
-// ParseBinaryExpression.
-//
+///
+/// \file
+/// \brief Provides the Expression parsing implementation.
+///
+/// Expressions in C99 basically consist of a bunch of binary operators with
+/// unary operators and other random stuff at the leaves.
+///
+/// In the C99 grammar, these unary operators bind tightest and are represented
+/// as the 'cast-expression' production. Everything else is either a binary
+/// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are
+/// handled by ParseCastExpression, the higher level pieces are handled by
+/// ParseBinaryExpression.
+///
//===----------------------------------------------------------------------===//
#include "clang/Parse/Parser.h"
@@ -30,8 +32,7 @@
#include "llvm/ADT/SmallString.h"
using namespace clang;
-/// getBinOpPrecedence - Return the precedence of the specified binary operator
-/// token.
+/// \brief Return the precedence of the specified binary operator token.
static prec::Level getBinOpPrecedence(tok::TokenKind Kind,
bool GreaterThanIsOperator,
bool CPlusPlus0x) {
@@ -92,8 +93,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind,
}
-/// ParseExpression - Simple precedence-based parser for binary/ternary
-/// operators.
+/// \brief Simple precedence-based parser for binary/ternary operators.
///
/// Note: we diverge from the C99 grammar when parsing the assignment-expression
/// production. C99 specifies that the LHS of an assignment operator should be
@@ -104,6 +104,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind,
/// consistency, we parse the LHS as a conditional-expression, then check for
/// l-value-ness in semantic analysis stages.
///
+/// \verbatim
/// pm-expression: [C++ 5.5]
/// cast-expression
/// pm-expression '.*' cast-expression
@@ -175,6 +176,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind,
/// expression: [C99 6.5.17]
/// assignment-expression ...[opt]
/// expression ',' assignment-expression ...[opt]
+/// \endverbatim
ExprResult Parser::ParseExpression(TypeCastState isTypeCast) {
ExprResult LHS(ParseAssignmentExpression(isTypeCast));
return ParseRHSOfBinaryExpression(move(LHS), prec::Comma);
@@ -182,8 +184,8 @@ ExprResult Parser::ParseExpression(TypeCastState isTypeCast) {
/// This routine is called when the '@' is seen and consumed.
/// Current token is an Identifier and is not a 'try'. This
-/// routine is necessary to disambiguate @try-statement from,
-/// for example, @encode-expression.
+/// routine is necessary to disambiguate \@try-statement from,
+/// for example, \@encode-expression.
///
ExprResult
Parser::ParseExpressionWithLeadingAt(SourceLocation AtLoc) {
@@ -211,7 +213,7 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) {
return ParseRHSOfBinaryExpression(move(LHS), prec::Comma);
}
-/// ParseAssignmentExpression - Parse an expr that doesn't include commas.
+/// \brief Parse an expr that doesn't include (top-level) commas.
ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression);
@@ -228,11 +230,12 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
return ParseRHSOfBinaryExpression(move(LHS), prec::Assignment);
}
-/// ParseAssignmentExprWithObjCMessageExprStart - Parse an assignment expression
-/// where part of an objc message send has already been parsed. In this case
-/// LBracLoc indicates the location of the '[' of the message send, and either
-/// ReceiverName or ReceiverExpr is non-null indicating the receiver of the
-/// message.
+/// \brief Parse an assignment expression where part of an Objective-C message
+/// send has already been parsed.
+///
+/// In this case \p LBracLoc indicates the location of the '[' of the message
+/// send, and either \p ReceiverName or \p ReceiverExpr is non-null indicating
+/// the receiver of the message.
///
/// Since this handles full assignment-expression's, it handles postfix
/// expressions and other binary operators for these expressions as well.
@@ -262,8 +265,8 @@ ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
return Actions.ActOnConstantExpression(Res);
}
-/// ParseRHSOfBinaryExpression - Parse a binary expression that starts with
-/// LHS and has a precedence of at least MinPrec.
+/// \brief Parse a binary expression that starts with \p LHS and has a
+/// precedence of at least \p MinPrec.
ExprResult
Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
prec::Level NextTokPrec = getBinOpPrecedence(Tok.getKind(),
@@ -439,10 +442,11 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
}
}
-/// ParseCastExpression - Parse a cast-expression, or, if isUnaryExpression is
-/// true, parse a unary-expression. isAddressOfOperand exists because an
-/// id-expression that is the operand of address-of gets special treatment
-/// due to member pointers.
+/// \brief Parse a cast-expression, or, if \p isUnaryExpression is true,
+/// parse a unary-expression.
+///
+/// \p isAddressOfOperand exists because an id-expression that is the
+/// operand of address-of gets special treatment due to member pointers.
///
ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
bool isAddressOfOperand,
@@ -480,12 +484,15 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback {
};
}
-/// ParseCastExpression - Parse a cast-expression, or, if isUnaryExpression is
-/// true, parse a unary-expression. isAddressOfOperand exists because an
-/// id-expression that is the operand of address-of gets special treatment
-/// due to member pointers. NotCastExpr is set to true if the token is not the
-/// start of a cast-expression, and no diagnostic is emitted in this case.
+/// \brief Parse a cast-expression, or, if \pisUnaryExpression is true, parse
+/// a unary-expression.
///
+/// \p isAddressOfOperand exists because an id-expression that is the operand
+/// of address-of gets special treatment due to member pointers. NotCastExpr
+/// is set to true if the token is not the start of a cast-expression, and no
+/// diagnostic is emitted in this case.
+///
+/// \verbatim
/// cast-expression: [C99 6.5.4]
/// unary-expression
/// '(' type-name ')' cast-expression
@@ -500,6 +507,7 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback {
/// [C++11] 'sizeof' '...' '(' identifier ')'
/// [GNU] '__alignof' unary-expression
/// [GNU] '__alignof' '(' type-name ')'
+/// [C11] '_Alignof' '(' type-name ')'
/// [C++11] 'alignof' '(' type-id ')'
/// [GNU] '&&' identifier
/// [C++11] 'noexcept' '(' expression ')' [C++11 5.3.7]
@@ -531,9 +539,9 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback {
/// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')'
/// [GNU] '__null'
/// [OBJC] '[' objc-message-expr ']'
-/// [OBJC] '@selector' '(' objc-selector-arg ')'
-/// [OBJC] '@protocol' '(' identifier ')'
-/// [OBJC] '@encode' '(' type-name ')'
+/// [OBJC] '\@selector' '(' objc-selector-arg ')'
+/// [OBJC] '\@protocol' '(' identifier ')'
+/// [OBJC] '\@encode' '(' type-name ')'
/// [OBJC] objc-string-literal
/// [C++] simple-type-specifier '(' expression-list[opt] ')' [C++ 5.2.3]
/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3]
@@ -641,6 +649,7 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback {
/// [Embarcadero] expression-trait:
/// '__is_lvalue_expr'
/// '__is_rvalue_expr'
+/// \endverbatim
///
ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
bool isAddressOfOperand,
@@ -846,6 +855,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
break;
case tok::kw___func__: // primary-expression: __func__ [C99 6.4.2.2]
case tok::kw___FUNCTION__: // primary-expression: __FUNCTION__ [GNU]
+ case tok::kw_L__FUNCTION__: // primary-expression: L__FUNCTION__ [MS]
case tok::kw___PRETTY_FUNCTION__: // primary-expression: __P..Y_F..N__ [GNU]
Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), SavedKind);
ConsumeToken();
@@ -912,12 +922,15 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
return move(Res);
}
- case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression
- // unary-expression: 'sizeof' '(' type-name ')'
- case tok::kw_alignof:
+ case tok::kw__Alignof: // unary-expression: '_Alignof' '(' type-name ')'
+ if (!getLangOpts().C11)
+ Diag(Tok, diag::ext_c11_alignment) << Tok.getName();
+ // fallthrough
+ case tok::kw_alignof: // unary-expression: 'alignof' '(' type-id ')'
case tok::kw___alignof: // unary-expression: '__alignof' unary-expression
// unary-expression: '__alignof' '(' type-name ')'
- // unary-expression: 'alignof' '(' type-id ')'
+ case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression
+ // unary-expression: 'sizeof' '(' type-name ')'
case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression
return ParseUnaryExprOrTypeTraitExpression();
case tok::ampamp: { // unary-expression: '&&' identifier
@@ -1228,9 +1241,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
return ParsePostfixExpressionSuffix(Res);
}
-/// ParsePostfixExpressionSuffix - Once the leading part of a postfix-expression
-/// is parsed, this method parses any suffixes that apply.
+/// \brief Once the leading part of a postfix-expression is parsed, this
+/// method parses any suffixes that apply.
///
+/// \verbatim
/// postfix-expression: [C99 6.5.2]
/// primary-expression
/// postfix-expression '[' expression ']'
@@ -1246,7 +1260,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
/// argument-expression-list: [C99 6.5.2]
/// argument-expression ...[opt]
/// argument-expression-list ',' assignment-expression ...[opt]
-///
+/// \endverbatim
ExprResult
Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
// Now that the primary-expression piece of the postfix-expression has been
@@ -1498,11 +1512,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
/// type-id. OpTok is the operand token (typeof/sizeof/alignof). Returns the
/// expression (isCastExpr == false) or the type (isCastExpr == true).
///
+/// \verbatim
/// unary-expression: [C99 6.5.3]
/// 'sizeof' unary-expression
/// 'sizeof' '(' type-name ')'
/// [GNU] '__alignof' unary-expression
/// [GNU] '__alignof' '(' type-name ')'
+/// [C11] '_Alignof' '(' type-name ')'
/// [C++0x] 'alignof' '(' type-id ')'
///
/// [GNU] typeof-specifier:
@@ -1513,7 +1529,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
/// [OpenCL 1.1 6.11.12] vec_step built-in function:
/// vec_step ( expressions )
/// vec_step ( type-name )
-///
+/// \endverbatim
ExprResult
Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
bool &isCastExpr,
@@ -1522,7 +1538,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
assert((OpTok.is(tok::kw_typeof) || OpTok.is(tok::kw_sizeof) ||
OpTok.is(tok::kw___alignof) || OpTok.is(tok::kw_alignof) ||
- OpTok.is(tok::kw_vec_step)) &&
+ OpTok.is(tok::kw__Alignof) || OpTok.is(tok::kw_vec_step)) &&
"Not a typeof/sizeof/alignof/vec_step expression!");
ExprResult Operand;
@@ -1571,17 +1587,22 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
}
-/// ParseUnaryExprOrTypeTraitExpression - Parse a sizeof or alignof expression.
+/// \brief Parse a sizeof or alignof expression.
+///
+/// \verbatim
/// unary-expression: [C99 6.5.3]
/// 'sizeof' unary-expression
/// 'sizeof' '(' type-name ')'
/// [C++0x] 'sizeof' '...' '(' identifier ')'
/// [GNU] '__alignof' unary-expression
/// [GNU] '__alignof' '(' type-name ')'
+/// [C11] '_Alignof' '(' type-name ')'
/// [C++0x] 'alignof' '(' type-id ')'
+/// \endverbatim
ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
- assert((Tok.is(tok::kw_sizeof) || Tok.is(tok::kw___alignof)
- || Tok.is(tok::kw_alignof) || Tok.is(tok::kw_vec_step)) &&
+ assert((Tok.is(tok::kw_sizeof) || Tok.is(tok::kw___alignof) ||
+ Tok.is(tok::kw_alignof) || Tok.is(tok::kw__Alignof) ||
+ Tok.is(tok::kw_vec_step)) &&
"Not a sizeof/alignof/vec_step expression!");
Token OpTok = Tok;
ConsumeToken();
@@ -1629,7 +1650,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
RParenLoc);
}
- if (OpTok.is(tok::kw_alignof))
+ if (OpTok.is(tok::kw_alignof) || OpTok.is(tok::kw__Alignof))
Diag(OpTok, diag::warn_cxx98_compat_alignof);
EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated);
@@ -1643,7 +1664,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
CastRange);
UnaryExprOrTypeTrait ExprKind = UETT_SizeOf;
- if (OpTok.is(tok::kw_alignof) || OpTok.is(tok::kw___alignof))
+ if (OpTok.is(tok::kw_alignof) || OpTok.is(tok::kw___alignof) ||
+ OpTok.is(tok::kw__Alignof))
ExprKind = UETT_AlignOf;
else if (OpTok.is(tok::kw_vec_step))
ExprKind = UETT_VecStep;
@@ -1667,6 +1689,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
/// ParseBuiltinPrimaryExpression
///
+/// \verbatim
/// primary-expression: [C99 6.5.1]
/// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')'
/// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')'
@@ -1679,7 +1702,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
/// [GNU] identifier
/// [GNU] offsetof-member-designator '.' identifier
/// [GNU] offsetof-member-designator '[' expression ']'
-///
+/// \endverbatim
ExprResult Parser::ParseBuiltinPrimaryExpression() {
ExprResult Res;
const IdentifierInfo *BuiltinII = Tok.getIdentifierInfo();
@@ -1869,6 +1892,7 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
/// in ExprType. If stopIfCastExpr is true, it will only return the parsed type,
/// not the parsed cast-expression.
///
+/// \verbatim
/// primary-expression: [C99 6.5.1]
/// '(' expression ')'
/// [GNU] '(' compound-statement ')' (if !ParenExprOnly)
@@ -1883,12 +1907,12 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() {
/// (__bridge type-name) cast-expression
/// (__bridge_transfer type-name) cast-expression
/// (__bridge_retained type-name) cast-expression
+/// \endverbatim
ExprResult
Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
bool isTypeCast, ParsedType &CastTy,
SourceLocation &RParenLoc) {
assert(Tok.is(tok::l_paren) && "Not a paren expr!");
- GreaterThanIsOperatorScope G(GreaterThanIsOperator, true);
BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.consumeOpen())
return ExprError();
@@ -2102,10 +2126,11 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
/// ParseCompoundLiteralExpression - We have parsed the parenthesized type-name
/// and we are at the left brace.
///
+/// \verbatim
/// postfix-expression: [C99 6.5.2]
/// '(' type-name ')' '{' initializer-list '}'
/// '(' type-name ')' '{' initializer-list ',' '}'
-///
+/// \endverbatim
ExprResult
Parser::ParseCompoundLiteralExpression(ParsedType Ty,
SourceLocation LParenLoc,
@@ -2123,8 +2148,10 @@ Parser::ParseCompoundLiteralExpression(ParsedType Ty,
/// form string literals, and also handles string concatenation [C99 5.1.1.2,
/// translation phase #6].
///
+/// \verbatim
/// primary-expression: [C99 6.5.1]
/// string-literal
+/// \verbatim
ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) {
assert(isTokenStringLiteral() && "Not a string literal!");
@@ -2145,6 +2172,7 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) {
/// ParseGenericSelectionExpression - Parse a C11 generic-selection
/// [C11 6.5.1.1].
///
+/// \verbatim
/// generic-selection:
/// _Generic ( assignment-expression , generic-assoc-list )
/// generic-assoc-list:
@@ -2153,6 +2181,7 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) {
/// generic-association:
/// type-name : assignment-expression
/// default : assignment-expression
+/// \endverbatim
ExprResult Parser::ParseGenericSelectionExpression() {
assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected");
SourceLocation KeyLoc = ConsumeToken();
@@ -2239,6 +2268,7 @@ ExprResult Parser::ParseGenericSelectionExpression() {
/// ParseExpressionList - Used for C/C++ (argument-)expression-list.
///
+/// \verbatim
/// argument-expression-list:
/// assignment-expression
/// argument-expression-list , assignment-expression
@@ -2257,7 +2287,7 @@ ExprResult Parser::ParseGenericSelectionExpression() {
/// [C++0x] initializer-clause:
/// [C++0x] assignment-expression
/// [C++0x] braced-init-list
-///
+/// \endverbatim
bool Parser::ParseExpressionList(SmallVectorImpl<Expr*> &Exprs,
SmallVectorImpl<SourceLocation> &CommaLocs,
void (Sema::*Completer)(Scope *S,
@@ -2297,10 +2327,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr*> &Exprs,
/// ParseBlockId - Parse a block-id, which roughly looks like int (int x).
///
+/// \verbatim
/// [clang] block-id:
/// [clang] specifier-qualifier-list block-declarator
-///
-void Parser::ParseBlockId() {
+/// \endverbatim
+void Parser::ParseBlockId(SourceLocation CaretLoc) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Type);
return cutOffParsing();
@@ -2320,18 +2351,19 @@ void Parser::ParseBlockId() {
MaybeParseGNUAttributes(DeclaratorInfo);
// Inform sema that we are starting a block.
- Actions.ActOnBlockArguments(DeclaratorInfo, getCurScope());
+ Actions.ActOnBlockArguments(CaretLoc, DeclaratorInfo, getCurScope());
}
/// ParseBlockLiteralExpression - Parse a block literal, which roughly looks
/// like ^(int x){ return x+1; }
///
+/// \verbatim
/// block-literal:
/// [clang] '^' block-args[opt] compound-statement
/// [clang] '^' block-id compound-statement
/// [clang] block-args:
/// [clang] '(' parameter-list ')'
-///
+/// \endverbatim
ExprResult Parser::ParseBlockLiteralExpression() {
assert(Tok.is(tok::caret) && "block literal starts with ^");
SourceLocation CaretLoc = ConsumeToken();
@@ -2377,13 +2409,13 @@ ExprResult Parser::ParseBlockLiteralExpression() {
MaybeParseGNUAttributes(ParamInfo);
// Inform sema that we are starting a block.
- Actions.ActOnBlockArguments(ParamInfo, getCurScope());
+ Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope());
} else if (!Tok.is(tok::l_brace)) {
- ParseBlockId();
+ ParseBlockId(CaretLoc);
} else {
// Otherwise, pretend we saw (void).
ParsedAttributes attrs(AttrFactory);
- ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false,
+ ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, false,
SourceLocation(),
0, 0, 0,
true, SourceLocation(),
@@ -2400,7 +2432,7 @@ ExprResult Parser::ParseBlockLiteralExpression() {
MaybeParseGNUAttributes(ParamInfo);
// Inform sema that we are starting a block.
- Actions.ActOnBlockArguments(ParamInfo, getCurScope());
+ Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope());
}
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 7152184..afac257 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -36,7 +36,7 @@ static int SelectDigraphErrorMessage(tok::TokenKind Kind) {
}
// Are the two tokens adjacent in the same source file?
-static bool AreTokensAdjacent(Preprocessor &PP, Token &First, Token &Second) {
+bool Parser::areTokensAdjacent(const Token &First, const Token &Second) {
SourceManager &SM = PP.getSourceManager();
SourceLocation FirstLoc = SM.getSpellingLoc(First.getLocation());
SourceLocation FirstEnd = FirstLoc.getLocWithOffset(First.getLength());
@@ -80,7 +80,7 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
return;
Token SecondToken = GetLookAheadToken(2);
- if (!SecondToken.is(tok::colon) || !AreTokensAdjacent(PP, Next, SecondToken))
+ if (!SecondToken.is(tok::colon) || !areTokensAdjacent(Next, SecondToken))
return;
TemplateTy Template;
@@ -642,7 +642,13 @@ llvm::Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro){
while (Tok.isNot(tok::r_square)) {
if (!first) {
if (Tok.isNot(tok::comma)) {
- if (Tok.is(tok::code_completion)) {
+ // Provide a completion for a lambda introducer here. Except
+ // in Objective-C, where this is Almost Surely meant to be a message
+ // send. In that case, fail here and let the ObjC message
+ // expression parser perform the completion.
+ if (Tok.is(tok::code_completion) &&
+ !(getLangOpts().ObjC1 && Intro.Default == LCD_None &&
+ !Intro.Captures.empty())) {
Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro,
/*AfterAmpersand=*/false);
ConsumeCodeCompletionToken();
@@ -792,10 +798,10 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
MaybeParseCXX0XAttributes(Attr, &DeclEndLoc);
// Parse trailing-return-type[opt].
- ParsedType TrailingReturnType;
+ TypeResult TrailingReturnType;
if (Tok.is(tok::arrow)) {
SourceRange Range;
- TrailingReturnType = ParseTrailingReturnType(Range).get();
+ TrailingReturnType = ParseTrailingReturnType(Range);
if (Range.getEnd().isValid())
DeclEndLoc = Range.getEnd();
}
@@ -804,7 +810,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true,
/*isVariadic=*/EllipsisLoc.isValid(),
- EllipsisLoc,
+ /*isAmbiguous=*/false, EllipsisLoc,
ParamInfo.data(), ParamInfo.size(),
DS.getTypeQualifiers(),
/*RefQualifierIsLValueRef=*/true,
@@ -838,10 +844,10 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
}
// Parse the return type, if there is one.
- ParsedType TrailingReturnType;
+ TypeResult TrailingReturnType;
if (Tok.is(tok::arrow)) {
SourceRange Range;
- TrailingReturnType = ParseTrailingReturnType(Range).get();
+ TrailingReturnType = ParseTrailingReturnType(Range);
if (Range.getEnd().isValid())
DeclEndLoc = Range.getEnd();
}
@@ -849,6 +855,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
ParsedAttributes Attr(AttrFactory);
D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true,
/*isVariadic=*/false,
+ /*isAmbiguous=*/false,
/*EllipsisLoc=*/SourceLocation(),
/*Params=*/0, /*NumParams=*/0,
/*TypeQuals=*/0,
@@ -921,7 +928,7 @@ ExprResult Parser::ParseCXXCasts() {
// diagnose error, suggest fix, and recover parsing.
Token Next = NextToken();
if (Tok.is(tok::l_square) && Tok.getLength() == 2 && Next.is(tok::colon) &&
- AreTokensAdjacent(PP, Tok, Next))
+ areTokensAdjacent(Tok, Next))
FixDigraph(*this, PP, Tok, Next, Kind, /*AtDigraph*/true);
if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName))
@@ -1235,8 +1242,6 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
MultiExprArg(&InitList, 1),
SourceLocation());
} else {
- GreaterThanIsOperatorScope G(GreaterThanIsOperator, true);
-
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
@@ -1298,7 +1303,12 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut,
return true;
}
+ ParsedAttributesWithRange attrs(AttrFactory);
+ MaybeParseCXX0XAttributes(attrs);
+
if (!isCXXConditionDeclaration()) {
+ ProhibitAttributes(attrs);
+
// Parse the expression.
ExprOut = ParseExpression(); // expression
DeclOut = 0;
@@ -1379,39 +1389,6 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut,
return false;
}
-/// \brief Determine whether the current token starts a C++
-/// simple-type-specifier.
-bool Parser::isCXXSimpleTypeSpecifier() const {
- switch (Tok.getKind()) {
- case tok::annot_typename:
- case tok::kw_short:
- case tok::kw_long:
- case tok::kw___int64:
- case tok::kw___int128:
- case tok::kw_signed:
- case tok::kw_unsigned:
- case tok::kw_void:
- case tok::kw_char:
- case tok::kw_int:
- case tok::kw_half:
- case tok::kw_float:
- case tok::kw_double:
- case tok::kw_wchar_t:
- case tok::kw_char16_t:
- case tok::kw_char32_t:
- case tok::kw_bool:
- case tok::kw_decltype:
- case tok::kw_typeof:
- case tok::kw___underlying_type:
- return true;
-
- default:
- break;
- }
-
- return false;
-}
-
/// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers.
/// This should only be called when the current token is known to be part of
/// simple-type-specifier.
@@ -2426,10 +2403,14 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) {
// Array delete?
bool ArrayDelete = false;
if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) {
- // FIXME: This could be the start of a lambda-expression. We should
- // disambiguate this, but that will require arbitrary lookahead if
- // the next token is '(':
- // delete [](int*){ /* ... */
+ // C++11 [expr.delete]p1:
+ // Whenever the delete keyword is followed by empty square brackets, it
+ // shall be interpreted as [array delete].
+ // [Footnote: A lambda expression with a lambda-introducer that consists
+ // of empty square brackets can follow the delete keyword if
+ // the lambda expression is enclosed in parentheses.]
+ // FIXME: Produce a better diagnostic if the '[]' is unambiguously a
+ // lambda-introducer.
ArrayDelete = true;
BalancedDelimiterTracker T(*this, tok::l_square);
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index 789a8ae..db35a38 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -308,16 +308,16 @@ public:
MethodImplKind(MethodImplKind) {
}
- Decl *invoke(FieldDeclarator &FD) {
+ void invoke(ParsingFieldDeclarator &FD) {
if (FD.D.getIdentifier() == 0) {
P.Diag(AtLoc, diag::err_objc_property_requires_field_name)
<< FD.D.getSourceRange();
- return 0;
+ return;
}
if (FD.BitfieldSize) {
P.Diag(AtLoc, diag::err_objc_property_bitfield)
<< FD.D.getSourceRange();
- return 0;
+ return;
}
// Install the property declarator into interfaceDecl.
@@ -344,7 +344,7 @@ public:
if (!isOverridingProperty)
Props.push_back(Property);
- return Property;
+ FD.complete(Property);
}
};
@@ -375,9 +375,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
while (1) {
// If this is a method prototype, parse it.
if (Tok.is(tok::minus) || Tok.is(tok::plus)) {
- Decl *methodPrototype =
- ParseObjCMethodPrototype(MethodImplKind, false);
- allMethods.push_back(methodPrototype);
+ if (Decl *methodPrototype =
+ ParseObjCMethodPrototype(MethodImplKind, false))
+ allMethods.push_back(methodPrototype);
// Consume the ';' here, since ParseObjCMethodPrototype() is re-used for
// method definitions.
if (ExpectAndConsumeSemi(diag::err_expected_semi_after_method_proto)) {
@@ -420,7 +420,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
// erroneous r_brace would cause an infinite loop if not handled here.
if (Tok.is(tok::r_brace))
break;
- ParsedAttributes attrs(AttrFactory);
+ ParsedAttributesWithRange attrs(AttrFactory);
allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs));
continue;
}
@@ -493,7 +493,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
OCDS, AtLoc, LParenLoc, MethodImplKind);
// Parse all the comma separated declarators.
- DeclSpec DS(AttrFactory);
+ ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, Callback);
ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list);
@@ -894,6 +894,7 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
DeclSpec declSpec(AttrFactory);
declSpec.setObjCQualifiers(&DS);
ParseSpecifierQualifierList(declSpec);
+ declSpec.SetRangeEnd(Tok.getLocation());
Declarator declarator(declSpec, context);
ParseDeclarator(declarator);
@@ -965,7 +966,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
tok::TokenKind mType,
tok::ObjCKeywordKind MethodImplKind,
bool MethodDefinition) {
- ParsingDeclRAIIObject PD(*this);
+ ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent);
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus,
@@ -1000,8 +1001,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
if (!SelIdent && Tok.isNot(tok::colon)) { // missing selector name.
Diag(Tok, diag::err_expected_selector_for_method)
<< SourceRange(mLoc, Tok.getLocation());
- // Skip until we get a ; or {}.
- SkipUntil(tok::r_brace);
+ // Skip until we get a ; or @.
+ SkipUntil(tok::at, true /*StopAtSemi*/, true /*don't consume*/);
return 0;
}
@@ -1105,7 +1106,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
}
bool isVariadic = false;
-
+ bool cStyleParamWarned = false;
// Parse the (optional) parameter list.
while (Tok.is(tok::comma)) {
ConsumeToken();
@@ -1114,6 +1115,10 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
ConsumeToken();
break;
}
+ if (!cStyleParamWarned) {
+ Diag(Tok, diag::warn_cstyle_param);
+ cStyleParamWarned = true;
+ }
DeclSpec DS(AttrFactory);
ParseDeclarationSpecifiers(DS);
// Parse the declarator.
@@ -1125,7 +1130,6 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
ParmDecl.getIdentifierLoc(),
Param,
0));
-
}
// FIXME: Add support for optional parameter list...
@@ -1258,9 +1262,7 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
// Check for extraneous top-level semicolon.
if (Tok.is(tok::semi)) {
- Diag(Tok, diag::ext_extra_ivar_semi)
- << FixItHint::CreateRemoval(Tok.getLocation());
- ConsumeToken();
+ ConsumeExtraSemi(InstanceVariableList);
continue;
}
@@ -1304,7 +1306,7 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
P(P), IDecl(IDecl), visibility(V), AllIvarDecls(AllIvarDecls) {
}
- Decl *invoke(FieldDeclarator &FD) {
+ void invoke(ParsingFieldDeclarator &FD) {
P.Actions.ActOnObjCContainerStartDefinition(IDecl);
// Install the declarator into the interface decl.
Decl *Field
@@ -1314,12 +1316,12 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
P.Actions.ActOnObjCContainerFinishDefinition();
if (Field)
AllIvarDecls.push_back(Field);
- return Field;
+ FD.complete(Field);
}
} Callback(*this, interfaceDecl, visibility, AllIvarDecls);
// Parse all the comma separated declarators.
- DeclSpec DS(AttrFactory);
+ ParsingDeclSpec DS(*this);
ParseStructDeclaration(DS, Callback);
if (Tok.is(tok::semi)) {
@@ -1348,15 +1350,15 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
/// objc-protocol-forward-reference
///
/// objc-protocol-definition:
-/// @protocol identifier
+/// \@protocol identifier
/// objc-protocol-refs[opt]
/// objc-interface-decl-list
-/// @end
+/// \@end
///
/// objc-protocol-forward-reference:
-/// @protocol identifier-list ';'
+/// \@protocol identifier-list ';'
///
-/// "@protocol identifier ;" should be resolved as "@protocol
+/// "\@protocol identifier ;" should be resolved as "\@protocol
/// identifier-list ;": objc-interface-decl-list may not start with a
/// semicolon in the first alternative if objc-protocol-refs are omitted.
Parser::DeclGroupPtrTy
@@ -1573,10 +1575,16 @@ void Parser::ObjCImplParsingDataRAII::finish(SourceRange AtEnd) {
assert(!Finished);
P.Actions.DefaultSynthesizeProperties(P.getCurScope(), Dcl);
for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i)
- P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i]);
+ P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i],
+ true/*Methods*/);
P.Actions.ActOnAtEnd(P.getCurScope(), AtEnd);
+ if (HasCFunction)
+ for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i)
+ P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i],
+ false/*c-functions*/);
+
/// \brief Clear and free the cached objc methods.
for (LateParsedObjCMethodContainer::iterator
I = LateParsedObjCMethods.begin(),
@@ -1608,8 +1616,8 @@ Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) {
SourceLocation classLoc = ConsumeToken(); // consume class-name;
ExpectAndConsume(tok::semi, diag::err_expected_semi_after,
"@compatibility_alias");
- return Actions.ActOnCompatiblityAlias(atLoc, aliasId, aliasLoc,
- classId, classLoc);
+ return Actions.ActOnCompatibilityAlias(atLoc, aliasId, aliasLoc,
+ classId, classLoc);
}
/// property-synthesis:
@@ -1913,6 +1921,43 @@ Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) {
AutoreleasePoolBody.take());
}
+/// StashAwayMethodOrFunctionBodyTokens - Consume the tokens and store them
+/// for later parsing.
+void Parser::StashAwayMethodOrFunctionBodyTokens(Decl *MDecl) {
+ LexedMethod* LM = new LexedMethod(this, MDecl);
+ CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM);
+ CachedTokens &Toks = LM->Toks;
+ // Begin by storing the '{' or 'try' or ':' token.
+ Toks.push_back(Tok);
+ if (Tok.is(tok::kw_try)) {
+ ConsumeToken();
+ if (Tok.is(tok::colon)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ while (Tok.isNot(tok::l_brace)) {
+ ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ }
+ }
+ Toks.push_back(Tok); // also store '{'
+ }
+ else if (Tok.is(tok::colon)) {
+ ConsumeToken();
+ while (Tok.isNot(tok::l_brace)) {
+ ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ }
+ Toks.push_back(Tok); // also store '{'
+ }
+ ConsumeBrace();
+ // Consume everything up to (and including) the matching right brace.
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ while (Tok.is(tok::kw_catch)) {
+ ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false);
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ }
+}
+
/// objc-method-def: objc-method-proto ';'[opt] '{' body '}'
///
Decl *Parser::ParseObjCMethodDefinition() {
@@ -1950,23 +1995,10 @@ Decl *Parser::ParseObjCMethodDefinition() {
// Allow the rest of sema to find private method decl implementations.
Actions.AddAnyMethodToGlobalPool(MDecl);
-
- if (CurParsedObjCImpl) {
- // Consume the tokens and store them for later parsing.
- LexedMethod* LM = new LexedMethod(this, MDecl);
- CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM);
- CachedTokens &Toks = LM->Toks;
- // Begin by storing the '{' token.
- Toks.push_back(Tok);
- ConsumeBrace();
- // Consume everything up to (and including) the matching right brace.
- ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
-
- } else {
- ConsumeBrace();
- SkipUntil(tok::r_brace, /*StopAtSemi=*/false);
- }
-
+ assert (CurParsedObjCImpl
+ && "ParseObjCMethodDefinition - Method out of @implementation");
+ // Consume the tokens and store them for later parsing.
+ StashAwayMethodOrFunctionBodyTokens(MDecl);
return MDecl;
}
@@ -2066,6 +2098,10 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) {
// Objective-C dictionary literal
return ParsePostfixExpressionSuffix(ParseObjCDictionaryLiteral(AtLoc));
+ case tok::l_paren:
+ // Objective-C boxed expression
+ return ParsePostfixExpressionSuffix(ParseObjCBoxedExpr(AtLoc));
+
default:
if (Tok.getIdentifierInfo() == 0)
return ExprError(Diag(AtLoc, diag::err_unexpected_at));
@@ -2077,8 +2113,23 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) {
return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc));
case tok::objc_selector:
return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc));
- default:
- return ExprError(Diag(AtLoc, diag::err_unexpected_at));
+ default: {
+ const char *str = 0;
+ if (GetLookAheadToken(1).is(tok::l_brace)) {
+ char ch = Tok.getIdentifierInfo()->getNameStart()[0];
+ str =
+ ch == 't' ? "try"
+ : (ch == 'f' ? "finally"
+ : (ch == 'a' ? "autoreleasepool" : 0));
+ }
+ if (str) {
+ SourceLocation kwLoc = Tok.getLocation();
+ return ExprError(Diag(AtLoc, diag::err_unexpected_at) <<
+ FixItHint::CreateReplacement(kwLoc, str));
+ }
+ else
+ return ExprError(Diag(AtLoc, diag::err_unexpected_at));
+ }
}
}
}
@@ -2112,7 +2163,7 @@ bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) {
Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope))
TryAnnotateTypeOrScopeToken();
- if (!isCXXSimpleTypeSpecifier()) {
+ if (!Actions.isSimpleTypeSpecifier(Tok.getKind())) {
// objc-receiver:
// expression
ExprResult Receiver = ParseExpression();
@@ -2449,10 +2500,14 @@ Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc,
}
// Parse the, optional, argument list, comma separated.
while (Tok.is(tok::comma)) {
- ConsumeToken(); // Eat the ','.
+ SourceLocation commaLoc = ConsumeToken(); // Eat the ','.
/// Parse the expression after ','
ExprResult Res(ParseAssignmentExpression());
if (Res.isInvalid()) {
+ if (Tok.is(tok::colon)) {
+ Diag(commaLoc, diag::note_extra_comma_message_arg) <<
+ FixItHint::CreateRemoval(commaLoc);
+ }
// We must manually skip to a ']', otherwise the expression skipper will
// stop at the ']' when it skips to the ';'. We want it to skip beyond
// the enclosing expression.
@@ -2580,6 +2635,31 @@ ExprResult Parser::ParseObjCNumericLiteral(SourceLocation AtLoc) {
return Owned(Actions.BuildObjCNumericLiteral(AtLoc, Lit.take()));
}
+/// ParseObjCBoxedExpr -
+/// objc-box-expression:
+/// @( assignment-expression )
+ExprResult
+Parser::ParseObjCBoxedExpr(SourceLocation AtLoc) {
+ if (Tok.isNot(tok::l_paren))
+ return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+ ExprResult ValueExpr(ParseAssignmentExpression());
+ if (T.consumeClose())
+ return ExprError();
+
+ if (ValueExpr.isInvalid())
+ return ExprError();
+
+ // Wrap the sub-expression in a parenthesized expression, to distinguish
+ // a boxed expression from a literal.
+ SourceLocation LPLoc = T.getOpenLocation(), RPLoc = T.getCloseLocation();
+ ValueExpr = Actions.ActOnParenExpr(LPLoc, RPLoc, ValueExpr.take());
+ return Owned(Actions.BuildObjCBoxedExpr(SourceRange(AtLoc, RPLoc),
+ ValueExpr.take()));
+}
+
ExprResult Parser::ParseObjCArrayLiteral(SourceLocation AtLoc) {
ExprVector ElementExprs(Actions); // array elements.
ConsumeBracket(); // consume the l_square.
@@ -2698,7 +2778,7 @@ Parser::ParseObjCEncodeExpression(SourceLocation AtLoc) {
}
/// objc-protocol-expression
-/// @protocol ( protocol-name )
+/// \@protocol ( protocol-name )
ExprResult
Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) {
SourceLocation ProtoLoc = ConsumeToken();
@@ -2713,12 +2793,13 @@ Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) {
return ExprError(Diag(Tok, diag::err_expected_ident));
IdentifierInfo *protocolId = Tok.getIdentifierInfo();
- ConsumeToken();
+ SourceLocation ProtoIdLoc = ConsumeToken();
T.consumeClose();
return Owned(Actions.ParseObjCProtocolExpression(protocolId, AtLoc, ProtoLoc,
T.getOpenLocation(),
+ ProtoIdLoc,
T.getCloseLocation()));
}
@@ -2785,8 +2866,15 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
T.getCloseLocation()));
}
-Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) {
-
+void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) {
+ // MCDecl might be null due to error in method or c-function prototype, etc.
+ Decl *MCDecl = LM.D;
+ bool skip = MCDecl &&
+ ((parseMethod && !Actions.isObjCMethodDecl(MCDecl)) ||
+ (!parseMethod && Actions.isObjCMethodDecl(MCDecl)));
+ if (skip)
+ return;
+
// Save the current token position.
SourceLocation OrigLoc = Tok.getLocation();
@@ -2796,40 +2884,32 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) {
LM.Toks.push_back(Tok);
PP.EnterTokenStream(LM.Toks.data(), LM.Toks.size(), true, false);
- // MDecl might be null due to error in method prototype, etc.
- Decl *MDecl = LM.D;
// Consume the previously pushed token.
ConsumeAnyToken();
- assert(Tok.is(tok::l_brace) && "Inline objective-c method not starting with '{'");
- SourceLocation BraceLoc = Tok.getLocation();
- // Enter a scope for the method body.
+ assert((Tok.is(tok::l_brace) || Tok.is(tok::kw_try) ||
+ Tok.is(tok::colon)) &&
+ "Inline objective-c method not starting with '{' or 'try' or ':'");
+ // Enter a scope for the method or c-fucntion body.
ParseScope BodyScope(this,
- Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope);
-
- // Tell the actions module that we have entered a method definition with the
- // specified Declarator for the method.
- Actions.ActOnStartOfObjCMethodDef(getCurScope(), MDecl);
-
- if (SkipFunctionBodies && trySkippingFunctionBody()) {
- BodyScope.Exit();
- return Actions.ActOnFinishFunctionBody(MDecl, 0);
- }
-
- StmtResult FnBody(ParseCompoundStatementBody());
+ parseMethod
+ ? Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope
+ : Scope::FnScope|Scope::DeclScope);
- // If the function body could not be parsed, make a bogus compoundstmt.
- if (FnBody.isInvalid()) {
- Sema::CompoundScopeRAII CompoundScope(Actions);
- FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc,
- MultiStmtArg(Actions), false);
+ // Tell the actions module that we have entered a method or c-function definition
+ // with the specified Declarator for the method/function.
+ if (parseMethod)
+ Actions.ActOnStartOfObjCMethodDef(getCurScope(), MCDecl);
+ else
+ Actions.ActOnStartOfFunctionDef(getCurScope(), MCDecl);
+ if (Tok.is(tok::kw_try))
+ MCDecl = ParseFunctionTryBlock(MCDecl, BodyScope);
+ else {
+ if (Tok.is(tok::colon))
+ ParseConstructorInitializer(MCDecl);
+ MCDecl = ParseFunctionStatementBody(MCDecl, BodyScope);
}
-
- // Leave the function body scope.
- BodyScope.Exit();
-
- MDecl = Actions.ActOnFinishFunctionBody(MDecl, FnBody.take());
-
+
if (Tok.getLocation() != OrigLoc) {
// Due to parsing error, we either went over the cached tokens or
// there are still cached tokens left. If it's the latter case skip the
@@ -2842,5 +2922,5 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) {
ConsumeAnyToken();
}
- return MDecl;
+ return;
}
diff --git a/lib/Parse/ParsePragma.h b/lib/Parse/ParsePragma.h
index ebb185a..fef6960 100644
--- a/lib/Parse/ParsePragma.h
+++ b/lib/Parse/ParsePragma.h
@@ -30,10 +30,9 @@ public:
};
class PragmaGCCVisibilityHandler : public PragmaHandler {
- Sema &Actions;
public:
- explicit PragmaGCCVisibilityHandler(Sema &A) : PragmaHandler("visibility"),
- Actions(A) {}
+ explicit PragmaGCCVisibilityHandler(Sema &/*A*/)
+ : PragmaHandler("visibility") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &FirstToken);
@@ -70,11 +69,9 @@ public:
};
class PragmaUnusedHandler : public PragmaHandler {
- Sema &Actions;
- Parser &parser;
public:
- PragmaUnusedHandler(Sema &A, Parser& p)
- : PragmaHandler("unused"), Actions(A), parser(p) {}
+ PragmaUnusedHandler(Sema &/*A*/)
+ : PragmaHandler("unused") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &FirstToken);
@@ -102,10 +99,9 @@ public:
class PragmaOpenCLExtensionHandler : public PragmaHandler {
Sema &Actions;
- Parser &parser;
public:
- PragmaOpenCLExtensionHandler(Sema &S, Parser& p) :
- PragmaHandler("EXTENSION"), Actions(S), parser(p) {}
+ PragmaOpenCLExtensionHandler(Sema &A) :
+ PragmaHandler("EXTENSION"), Actions(A) {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &FirstToken);
};
@@ -113,10 +109,9 @@ public:
class PragmaFPContractHandler : public PragmaHandler {
Sema &Actions;
- Parser &parser;
public:
- PragmaFPContractHandler(Sema &S, Parser& p) :
- PragmaHandler("FP_CONTRACT"), Actions(S), parser(p) {}
+ PragmaFPContractHandler(Sema &A) :
+ PragmaHandler("FP_CONTRACT"), Actions(A) {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &FirstToken);
};
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 44320df..d2e4309 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -20,6 +20,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallString.h"
using namespace clang;
//===----------------------------------------------------------------------===//
@@ -771,7 +772,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
DeclsInGroup.data(), DeclsInGroup.size());
StmtResult R = Actions.ActOnDeclStmt(Res, LabelLoc, Tok.getLocation());
- ExpectAndConsume(tok::semi, diag::err_expected_semi_declaration);
+ ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
if (R.isUsable())
Stmts.push_back(R.release());
}
@@ -895,6 +896,16 @@ bool Parser::ParseParenExprOrCondition(ExprResult &ExprResult,
// Otherwise the condition is valid or the rparen is present.
T.consumeClose();
+
+ // Check for extraneous ')'s to catch things like "if (foo())) {". We know
+ // that all callers are looking for a statement after the condition, so ")"
+ // isn't valid.
+ while (Tok.is(tok::r_paren)) {
+ Diag(Tok, diag::err_extraneous_rparen_in_condition)
+ << FixItHint::CreateRemoval(Tok.getLocation());
+ ConsumeParen();
+ }
+
return false;
}
@@ -938,7 +949,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
if (ParseParenExprOrCondition(CondExp, CondVar, IfLoc, true))
return StmtError();
- FullExprArg FullCondExp(Actions.MakeFullExpr(CondExp.get()));
+ FullExprArg FullCondExp(Actions.MakeFullExpr(CondExp.get(), IfLoc));
// C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if
// there is no compound stmt. C90 does not have this clause. We only do this
@@ -1164,7 +1175,7 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
if (ParseParenExprOrCondition(Cond, CondVar, WhileLoc, true))
return StmtError();
- FullExprArg FullCond(Actions.MakeFullExpr(Cond.get()));
+ FullExprArg FullCond(Actions.MakeFullExpr(Cond.get(), WhileLoc));
// C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if
// there is no compound stmt. C90 does not have this clause. We only do this
@@ -1248,6 +1259,12 @@ StmtResult Parser::ParseDoStatement() {
// Parse the parenthesized condition.
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
+
+ // FIXME: Do not just parse the attribute contents and throw them away
+ ParsedAttributesWithRange attrs(AttrFactory);
+ MaybeParseCXX0XAttributes(attrs);
+ ProhibitAttributes(attrs);
+
ExprResult Cond = ParseExpression();
T.consumeClose();
DoScope.Exit();
@@ -1288,7 +1305,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
return StmtError();
}
- bool C99orCXXorObjC = getLangOpts().C99 || getLangOpts().CPlusPlus || getLangOpts().ObjC1;
+ bool C99orCXXorObjC = getLangOpts().C99 || getLangOpts().CPlusPlus ||
+ getLangOpts().ObjC1;
// C99 6.8.5p5 - In C99, the for statement is a block. This is not
// the case for C90. Start the loop scope.
@@ -1336,8 +1354,12 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
return StmtError();
}
+ ParsedAttributesWithRange attrs(AttrFactory);
+ MaybeParseCXX0XAttributes(attrs);
+
// Parse the first part of the for specifier.
if (Tok.is(tok::semi)) { // for (;
+ ProhibitAttributes(attrs);
// no first part, eat the ';'.
ConsumeToken();
} else if (isForInitDeclaration()) { // for (int X = 4;
@@ -1382,6 +1404,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
Diag(Tok, diag::err_expected_semi_for);
}
} else {
+ ProhibitAttributes(attrs);
Value = ParseExpression();
ForEach = isTokIdentifier_in();
@@ -1441,7 +1464,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
Second.get());
}
SecondPartIsInvalid = Second.isInvalid();
- SecondPart = Actions.MakeFullExpr(Second.get());
+ SecondPart = Actions.MakeFullExpr(Second.get(), ForLoc);
}
if (Tok.isNot(tok::semi)) {
@@ -1469,6 +1492,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
// statememt before parsing the body, in order to be able to deduce the type
// of an auto-typed loop variable.
StmtResult ForRangeStmt;
+ StmtResult ForEachStmt;
+
if (ForRange) {
ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, T.getOpenLocation(),
FirstPart.take(),
@@ -1480,9 +1505,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
// Similarly, we need to do the semantic analysis for a for-range
// statement immediately in order to close over temporaries correctly.
} else if (ForEach) {
- if (!Collection.isInvalid())
- Collection =
- Actions.ActOnObjCForCollectionOperand(ForLoc, Collection.take());
+ ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc, T.getOpenLocation(),
+ FirstPart.take(),
+ Collection.take(),
+ T.getCloseLocation());
}
// C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if
@@ -1512,11 +1538,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
return StmtError();
if (ForEach)
- return Actions.ActOnObjCForCollectionStmt(ForLoc, T.getOpenLocation(),
- FirstPart.take(),
- Collection.take(),
- T.getCloseLocation(),
- Body.take());
+ return Actions.FinishObjCForCollectionStmt(ForEachStmt.take(),
+ Body.take());
if (ForRange)
return Actions.FinishCXXForRangeStmt(ForRangeStmt.take(), Body.take());
@@ -1617,9 +1640,24 @@ StmtResult Parser::ParseReturnStatement() {
/// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled,
/// this routine is called to collect the tokens for an MS asm statement.
+///
+/// [MS] ms-asm-statement:
+/// ms-asm-block
+/// ms-asm-block ms-asm-statement
+///
+/// [MS] ms-asm-block:
+/// '__asm' ms-asm-line '\n'
+/// '__asm' '{' ms-asm-instruction-block[opt] '}' ';'[opt]
+///
+/// [MS] ms-asm-instruction-block
+/// ms-asm-line
+/// ms-asm-line '\n' ms-asm-instruction-block
+///
StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
SourceManager &SrcMgr = PP.getSourceManager();
SourceLocation EndLoc = AsmLoc;
+ SmallVector<Token, 4> AsmToks;
+ SmallVector<unsigned, 4> LineEnds;
do {
bool InBraces = false;
unsigned short savedBraceCount = 0;
@@ -1648,8 +1686,10 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
// If we hit EOF, we're done, period.
if (Tok.is(tok::eof))
break;
- // When we consume the closing brace, we're done.
- if (InBraces && BraceCount == savedBraceCount)
+
+ // The asm keyword is a statement separator, so multiple asm statements
+ // are allowed.
+ if (!InAsmComment && Tok.is(tok::kw_asm))
break;
if (!InAsmComment && Tok.is(tok::semi)) {
@@ -1681,18 +1721,28 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
break;
}
}
+ if (!InAsmComment && InBraces && Tok.is(tok::r_brace) &&
+ BraceCount == (savedBraceCount + 1)) {
+ // Consume the closing brace, and finish
+ EndLoc = ConsumeBrace();
+ break;
+ }
// Consume the next token; make sure we don't modify the brace count etc.
// if we are in a comment.
EndLoc = TokLoc;
if (InAsmComment)
PP.Lex(Tok);
- else
+ else {
+ AsmToks.push_back(Tok);
ConsumeAnyToken();
+ }
TokLoc = Tok.getLocation();
++NumTokensRead;
} while (1);
+ LineEnds.push_back(AsmToks.size());
+
if (InBraces && BraceCount != savedBraceCount) {
// __asm without closing brace (this can happen at EOF).
Diag(Tok, diag::err_expected_rbrace);
@@ -1709,24 +1759,10 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
break;
EndLoc = ConsumeToken();
} while (1);
- // FIXME: Need to actually grab the data and pass it on to Sema. Ideally,
- // what Sema wants is a string of the entire inline asm, with one instruction
- // per line and all the __asm keywords stripped out, and a way of mapping
- // from any character of that string to its location in the original source
- // code. I'm not entirely sure how to go about that, though.
- Token t;
- t.setKind(tok::string_literal);
- t.setLiteralData("\"/*FIXME: not done*/\"");
- t.clearFlag(Token::NeedsCleaning);
- t.setLength(21);
- ExprResult AsmString(Actions.ActOnStringLiteral(&t, 1));
- ExprVector Constraints(Actions);
- ExprVector Exprs(Actions);
- ExprVector Clobbers(Actions);
- return Actions.ActOnAsmStmt(AsmLoc, true, true, 0, 0, 0,
- move_arg(Constraints), move_arg(Exprs),
- AsmString.take(), move_arg(Clobbers),
- EndLoc, true);
+
+ // FIXME: We should be passing source locations for better diagnostics.
+ return Actions.ActOnMSAsmStmt(AsmLoc, llvm::makeArrayRef(AsmToks),
+ llvm::makeArrayRef(LineEnds), EndLoc);
}
/// ParseAsmStatement - Parse a GNU extended asm statement.
@@ -1748,23 +1784,12 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
/// asm-string-literal
/// asm-clobbers ',' asm-string-literal
///
-/// [MS] ms-asm-statement:
-/// ms-asm-block
-/// ms-asm-block ms-asm-statement
-///
-/// [MS] ms-asm-block:
-/// '__asm' ms-asm-line '\n'
-/// '__asm' '{' ms-asm-instruction-block[opt] '}' ';'[opt]
-///
-/// [MS] ms-asm-instruction-block
-/// ms-asm-line
-/// ms-asm-line '\n' ms-asm-instruction-block
-///
StmtResult Parser::ParseAsmStatement(bool &msAsm) {
assert(Tok.is(tok::kw_asm) && "Not an asm stmt");
SourceLocation AsmLoc = ConsumeToken();
- if (getLangOpts().MicrosoftExt && Tok.isNot(tok::l_paren) && !isTypeQualifier()) {
+ if (getLangOpts().MicrosoftExt && Tok.isNot(tok::l_paren) &&
+ !isTypeQualifier()) {
msAsm = true;
return ParseMicrosoftAsmStatement(AsmLoc);
}
@@ -2067,7 +2092,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) {
return move(TryBlock);
// Borland allows SEH-handlers with 'try'
-
+
if ((Tok.is(tok::identifier) &&
Tok.getIdentifierInfo() == getSEHExceptKeyword()) ||
Tok.is(tok::kw___finally)) {
@@ -2107,7 +2132,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) {
if (Handlers.empty())
return StmtError();
- return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.take(), move_arg(Handlers));
+ return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.take(),move_arg(Handlers));
}
}
@@ -2203,10 +2228,10 @@ void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
case IEB_Parse:
// Parse the statements below.
break;
-
+
case IEB_Dependent:
llvm_unreachable("Dependent case handled above");
-
+
case IEB_Skip:
Braces.skipToEnd();
return;
diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp
index 5c3e2ba..ade918f 100644
--- a/lib/Parse/ParseTemplate.cpp
+++ b/lib/Parse/ParseTemplate.cpp
@@ -90,7 +90,8 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
// Tell the action that names should be checked in the context of
// the declaration to come.
- ParsingDeclRAIIObject ParsingTemplateParams(*this);
+ ParsingDeclRAIIObject
+ ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent);
// Parse multiple levels of template headers within this template
// parameter scope, e.g.,
@@ -213,11 +214,15 @@ Parser::ParseSingleDeclarationAfterTemplate(
return ParseUsingDirectiveOrDeclaration(Context, TemplateInfo, DeclEnd,
prefixAttrs);
- // Parse the declaration specifiers, stealing the accumulated
- // diagnostics from the template parameters.
+ // Parse the declaration specifiers, stealing any diagnostics from
+ // the template parameters.
ParsingDeclSpec DS(*this, &DiagsFromTParams);
- DS.takeAttributesFrom(prefixAttrs);
+ // Move the attributes from the prefix into the DS.
+ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
+ ProhibitAttributes(prefixAttrs);
+ else
+ DS.takeAttributesFrom(prefixAttrs);
ParseDeclarationSpecifiers(DS, TemplateInfo, AS,
getDeclSpecContextFromDeclaratorContext(Context));
@@ -259,7 +264,7 @@ Parser::ParseSingleDeclarationAfterTemplate(
}
// Eat the semi colon after the declaration.
- ExpectAndConsume(tok::semi, diag::err_expected_semi_declaration);
+ ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
if (LateParsedAttrs.size() > 0)
ParseLexedAttributeList(LateParsedAttrs, ThisDecl, true, false);
DeclaratorInfo.complete(ThisDecl);
@@ -314,6 +319,11 @@ bool Parser::ParseTemplateParameters(unsigned Depth,
Failed = ParseTemplateParameterList(Depth, TemplateParams);
if (Tok.is(tok::greatergreater)) {
+ // No diagnostic required here: a template-parameter-list can only be
+ // followed by a declaration or, for a template template parameter, the
+ // 'class' keyword. Therefore, the second '>' will be diagnosed later.
+ // This matters for elegant diagnosis of:
+ // template<template<typename>> struct S;
Tok.setKind(tok::greater);
RAngleLoc = Tok.getLocation();
Tok.setLocation(Tok.getLocation().getLocWithOffset(1));
@@ -711,34 +721,104 @@ Parser::ParseTemplateIdAfterTemplateName(TemplateTy Template,
}
}
- if (Tok.isNot(tok::greater) && Tok.isNot(tok::greatergreater)) {
+ // What will be left once we've consumed the '>'.
+ tok::TokenKind RemainingToken;
+ const char *ReplacementStr = "> >";
+
+ switch (Tok.getKind()) {
+ default:
Diag(Tok.getLocation(), diag::err_expected_greater);
return true;
- }
- // Determine the location of the '>' or '>>'. Only consume this
- // token if the caller asked us to.
- RAngleLoc = Tok.getLocation();
+ case tok::greater:
+ // Determine the location of the '>' token. Only consume this token
+ // if the caller asked us to.
+ RAngleLoc = Tok.getLocation();
+ if (ConsumeLastToken)
+ ConsumeToken();
+ return false;
- if (Tok.is(tok::greatergreater)) {
- const char *ReplaceStr = "> >";
- if (NextToken().is(tok::greater) || NextToken().is(tok::greatergreater))
- ReplaceStr = "> > ";
+ case tok::greatergreater:
+ RemainingToken = tok::greater;
+ break;
- Diag(Tok.getLocation(), getLangOpts().CPlusPlus0x ?
- diag::warn_cxx98_compat_two_right_angle_brackets :
- diag::err_two_right_angle_brackets_need_space)
- << FixItHint::CreateReplacement(SourceRange(Tok.getLocation()),
- ReplaceStr);
+ case tok::greatergreatergreater:
+ RemainingToken = tok::greatergreater;
+ break;
- Tok.setKind(tok::greater);
- if (!ConsumeLastToken) {
- // Since we're not supposed to consume the '>>' token, we need
- // to insert a second '>' token after the first.
- PP.EnterToken(Tok);
- }
- } else if (ConsumeLastToken)
+ case tok::greaterequal:
+ RemainingToken = tok::equal;
+ ReplacementStr = "> =";
+ break;
+
+ case tok::greatergreaterequal:
+ RemainingToken = tok::greaterequal;
+ break;
+ }
+
+ // This template-id is terminated by a token which starts with a '>'. Outside
+ // C++11, this is now error recovery, and in C++11, this is error recovery if
+ // the token isn't '>>'.
+
+ RAngleLoc = Tok.getLocation();
+
+ // The source range of the '>>' or '>=' at the start of the token.
+ CharSourceRange ReplacementRange =
+ CharSourceRange::getCharRange(RAngleLoc,
+ Lexer::AdvanceToTokenCharacter(RAngleLoc, 2, PP.getSourceManager(),
+ getLangOpts()));
+
+ // A hint to put a space between the '>>'s. In order to make the hint as
+ // clear as possible, we include the characters either side of the space in
+ // the replacement, rather than just inserting a space at SecondCharLoc.
+ FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange,
+ ReplacementStr);
+
+ // A hint to put another space after the token, if it would otherwise be
+ // lexed differently.
+ FixItHint Hint2;
+ Token Next = NextToken();
+ if ((RemainingToken == tok::greater ||
+ RemainingToken == tok::greatergreater) &&
+ (Next.is(tok::greater) || Next.is(tok::greatergreater) ||
+ Next.is(tok::greatergreatergreater) || Next.is(tok::equal) ||
+ Next.is(tok::greaterequal) || Next.is(tok::greatergreaterequal) ||
+ Next.is(tok::equalequal)) &&
+ areTokensAdjacent(Tok, Next))
+ Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " ");
+
+ unsigned DiagId = diag::err_two_right_angle_brackets_need_space;
+ if (getLangOpts().CPlusPlus0x && Tok.is(tok::greatergreater))
+ DiagId = diag::warn_cxx98_compat_two_right_angle_brackets;
+ else if (Tok.is(tok::greaterequal))
+ DiagId = diag::err_right_angle_bracket_equal_needs_space;
+ Diag(Tok.getLocation(), DiagId) << Hint1 << Hint2;
+
+ // Strip the initial '>' from the token.
+ if (RemainingToken == tok::equal && Next.is(tok::equal) &&
+ areTokensAdjacent(Tok, Next)) {
+ // Join two adjacent '=' tokens into one, for cases like:
+ // void (*p)() = f<int>;
+ // return f<int>==p;
ConsumeToken();
+ Tok.setKind(tok::equalequal);
+ Tok.setLength(Tok.getLength() + 1);
+ } else {
+ Tok.setKind(RemainingToken);
+ Tok.setLength(Tok.getLength() - 1);
+ }
+ Tok.setLocation(Lexer::AdvanceToTokenCharacter(RAngleLoc, 1,
+ PP.getSourceManager(),
+ getLangOpts()));
+
+ if (!ConsumeLastToken) {
+ // Since we're not supposed to consume the '>' token, we need to push
+ // this token and revert the current token back to the '>'.
+ PP.EnterToken(Tok);
+ Tok.setKind(tok::greater);
+ Tok.setLength(1);
+ Tok.setLocation(RAngleLoc);
+ }
return false;
}
@@ -1132,7 +1212,8 @@ Decl *Parser::ParseExplicitInstantiation(unsigned Context,
SourceLocation &DeclEnd,
AccessSpecifier AS) {
// This isn't really required here.
- ParsingDeclRAIIObject ParsingTemplateParams(*this);
+ ParsingDeclRAIIObject
+ ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent);
return ParseSingleDeclarationAfterTemplate(Context,
ParsedTemplateInfo(ExternLoc,
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index 28c5e8b..1a4df47 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -671,7 +671,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
// initializer that follows the declarator. Note that ctor-style
// initializers are not possible in contexts where abstract declarators
// are allowed.
- if (!mayBeAbstract && !isCXXFunctionDeclarator(false/*warnIfAmbiguous*/))
+ if (!mayBeAbstract && !isCXXFunctionDeclarator())
break;
// direct-declarator '(' parameter-declaration-clause ')'
@@ -735,6 +735,7 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
case tok::kw_alignof:
case tok::kw_noexcept:
case tok::kw_nullptr:
+ case tok::kw__Alignof:
case tok::kw___null:
case tok::kw___alignof:
case tok::kw___builtin_choose_expr:
@@ -744,6 +745,7 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
case tok::kw___imag:
case tok::kw___real:
case tok::kw___FUNCTION__:
+ case tok::kw_L__FUNCTION__:
case tok::kw___PRETTY_FUNCTION__:
case tok::kw___has_nothrow_assign:
case tok::kw___has_nothrow_copy:
@@ -827,6 +829,10 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
/// be either a decl-specifier or a function-style cast, and TPResult::Error()
/// if a parsing error was found and reported.
///
+/// If HasMissingTypename is provided, a name with a dependent scope specifier
+/// will be treated as ambiguous if the 'typename' keyword is missing. If this
+/// happens, *HasMissingTypename will be set to 'true'.
+///
/// decl-specifier:
/// storage-class-specifier
/// type-specifier
@@ -918,7 +924,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
/// [GNU] restrict
///
Parser::TPResult
-Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
+Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
+ bool *HasMissingTypename) {
switch (Tok.getKind()) {
case tok::identifier: // foo::bar
// Check for need to substitute AltiVec __vector keyword
@@ -931,9 +938,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
// recurse to handle whatever we get.
if (TryAnnotateTypeOrScopeToken())
return TPResult::Error();
- if (Tok.is(tok::identifier))
- return TPResult::False();
- return isCXXDeclarationSpecifier(BracedCastResult);
+ if (Tok.is(tok::identifier)) {
+ const Token &Next = NextToken();
+ return (!getLangOpts().ObjC1 && Next.is(tok::identifier)) ?
+ TPResult::True() : TPResult::False();
+ }
+ return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
case tok::coloncolon: { // ::foo::bar
const Token &Next = NextToken();
@@ -947,7 +957,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
// recurse to handle whatever we get.
if (TryAnnotateTypeOrScopeToken())
return TPResult::Error();
- return isCXXDeclarationSpecifier(BracedCastResult);
+ return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
// decl-specifier:
// storage-class-specifier
@@ -1049,12 +1059,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
bool isIdentifier = Tok.is(tok::identifier);
TPResult TPR = TPResult::False();
if (!isIdentifier)
- TPR = isCXXDeclarationSpecifier(BracedCastResult);
+ TPR = isCXXDeclarationSpecifier(BracedCastResult,
+ HasMissingTypename);
PA.Revert();
if (isIdentifier ||
TPR == TPResult::True() || TPR == TPResult::Error())
return TPResult::Error();
+
+ if (HasMissingTypename) {
+ // We can't tell whether this is a missing 'typename' or a valid
+ // expression.
+ *HasMissingTypename = true;
+ return TPResult::Ambiguous();
+ }
}
}
return TPResult::False();
@@ -1218,21 +1236,24 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() {
return TPResult::Error();
}
-Parser::TPResult Parser::TryParseDeclarationSpecifier() {
- TPResult TPR = isCXXDeclarationSpecifier();
+Parser::TPResult
+Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) {
+ TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
+ HasMissingTypename);
if (TPR != TPResult::Ambiguous())
return TPR;
if (Tok.is(tok::kw_typeof))
TryParseTypeofSpecifier();
else {
+ if (Tok.is(tok::annot_cxxscope))
+ ConsumeToken();
ConsumeToken();
if (getLangOpts().ObjC1 && Tok.is(tok::less))
TryParseProtocolQualifiers();
}
- assert(Tok.is(tok::l_paren) && "Expected '('!");
return TPResult::Ambiguous();
}
@@ -1246,7 +1267,7 @@ Parser::TPResult Parser::TryParseDeclarationSpecifier() {
/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
/// exception-specification[opt]
///
-bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
+bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
// C++ 8.2p1:
// The ambiguity arising from the similarity between a function-style cast and
@@ -1260,27 +1281,36 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
TentativeParsingAction PA(*this);
ConsumeParen();
- TPResult TPR = TryParseParameterDeclarationClause();
- if (TPR == TPResult::Ambiguous() && Tok.isNot(tok::r_paren))
- TPR = TPResult::False();
+ bool InvalidAsDeclaration = false;
+ TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration);
+ if (TPR == TPResult::Ambiguous()) {
+ if (Tok.isNot(tok::r_paren))
+ TPR = TPResult::False();
+ else {
+ const Token &Next = NextToken();
+ if (Next.is(tok::amp) || Next.is(tok::ampamp) ||
+ Next.is(tok::kw_const) || Next.is(tok::kw_volatile) ||
+ Next.is(tok::kw_throw) || Next.is(tok::kw_noexcept) ||
+ Next.is(tok::l_square) || isCXX0XVirtSpecifier(Next) ||
+ Next.is(tok::l_brace) || Next.is(tok::kw_try) ||
+ Next.is(tok::equal) || Next.is(tok::arrow))
+ // The next token cannot appear after a constructor-style initializer,
+ // and can appear next in a function definition. This must be a function
+ // declarator.
+ TPR = TPResult::True();
+ else if (InvalidAsDeclaration)
+ // Use the absence of 'typename' as a tie-breaker.
+ TPR = TPResult::False();
+ }
+ }
- SourceLocation TPLoc = Tok.getLocation();
PA.Revert();
- // In case of an error, let the declaration parsing code handle it.
- if (TPR == TPResult::Error())
- return true;
+ if (IsAmbiguous && TPR == TPResult::Ambiguous())
+ *IsAmbiguous = true;
- if (TPR == TPResult::Ambiguous()) {
- // Function declarator has precedence over constructor-style initializer.
- // Emit a warning just in case the author intended a variable definition.
- if (warnIfAmbiguous)
- Diag(Tok, diag::warn_parens_disambiguated_as_function_decl)
- << SourceRange(Tok.getLocation(), TPLoc);
- return true;
- }
-
- return TPR == TPResult::True();
+ // In case of an error, let the declaration parsing code handle it.
+ return TPR != TPResult::False();
}
/// parameter-declaration-clause:
@@ -1300,10 +1330,11 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
/// attributes[opt] '=' assignment-expression
///
-Parser::TPResult Parser::TryParseParameterDeclarationClause() {
+Parser::TPResult
+Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) {
if (Tok.is(tok::r_paren))
- return TPResult::True();
+ return TPResult::Ambiguous();
// parameter-declaration-list[opt] '...'[opt]
// parameter-declaration-list ',' '...'
@@ -1333,7 +1364,7 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() {
// decl-specifier-seq
// A parameter-declaration's initializer must be preceded by an '=', so
// decl-specifier-seq '{' is not a parameter in C++11.
- TPResult TPR = TryParseDeclarationSpecifier();
+ TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration);
if (TPR != TPResult::Ambiguous())
return TPR;
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index f1b99fb..3725e2b 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -23,6 +23,22 @@
#include "clang/AST/ASTConsumer.h"
using namespace clang;
+namespace {
+/// \brief A comment handler that passes comments found by the preprocessor
+/// to the parser action.
+class ActionCommentHandler : public CommentHandler {
+ Sema &S;
+
+public:
+ explicit ActionCommentHandler(Sema &S) : S(S) { }
+
+ virtual bool HandleComment(Preprocessor &PP, SourceRange Comment) {
+ S.ActOnComment(Comment);
+ return false;
+ }
+};
+} // end anonymous namespace
+
IdentifierInfo *Parser::getSEHExceptKeyword() {
// __except is accepted as a (contextual) keyword
if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland))
@@ -35,7 +51,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies)
: PP(pp), Actions(actions), Diags(PP.getDiagnostics()),
GreaterThanIsOperator(true), ColonIsSacred(false),
InMessageExpression(false), TemplateParameterDepth(0),
- SkipFunctionBodies(SkipFunctionBodies) {
+ ParsingInObjCContainer(false), SkipFunctionBodies(SkipFunctionBodies) {
Tok.setKind(tok::eof);
Actions.CurScope = 0;
NumCachedScopes = 0;
@@ -59,7 +75,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies)
MSStructHandler.reset(new PragmaMSStructHandler(actions));
PP.AddPragmaHandler(MSStructHandler.get());
- UnusedHandler.reset(new PragmaUnusedHandler(actions, *this));
+ UnusedHandler.reset(new PragmaUnusedHandler(actions));
PP.AddPragmaHandler(UnusedHandler.get());
WeakHandler.reset(new PragmaWeakHandler(actions));
@@ -68,17 +84,19 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies)
RedefineExtnameHandler.reset(new PragmaRedefineExtnameHandler(actions));
PP.AddPragmaHandler(RedefineExtnameHandler.get());
- FPContractHandler.reset(new PragmaFPContractHandler(actions, *this));
+ FPContractHandler.reset(new PragmaFPContractHandler(actions));
PP.AddPragmaHandler("STDC", FPContractHandler.get());
if (getLangOpts().OpenCL) {
- OpenCLExtensionHandler.reset(
- new PragmaOpenCLExtensionHandler(actions, *this));
+ OpenCLExtensionHandler.reset(new PragmaOpenCLExtensionHandler(actions));
PP.AddPragmaHandler("OPENCL", OpenCLExtensionHandler.get());
PP.AddPragmaHandler("OPENCL", FPContractHandler.get());
}
-
+
+ CommentSemaHandler.reset(new ActionCommentHandler(actions));
+ PP.addCommentHandler(CommentSemaHandler.get());
+
PP.setCodeCompletionHandler(*this);
}
@@ -185,7 +203,7 @@ bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID,
bool Parser::ExpectAndConsumeSemi(unsigned DiagID) {
if (Tok.is(tok::semi) || Tok.is(tok::code_completion)) {
- ConsumeAnyToken();
+ ConsumeToken();
return false;
}
@@ -202,6 +220,42 @@ bool Parser::ExpectAndConsumeSemi(unsigned DiagID) {
return ExpectAndConsume(tok::semi, DiagID);
}
+void Parser::ConsumeExtraSemi(ExtraSemiKind Kind, unsigned TST) {
+ if (!Tok.is(tok::semi)) return;
+
+ bool HadMultipleSemis = false;
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc = Tok.getLocation();
+ ConsumeToken();
+
+ while ((Tok.is(tok::semi) && !Tok.isAtStartOfLine())) {
+ HadMultipleSemis = true;
+ EndLoc = Tok.getLocation();
+ ConsumeToken();
+ }
+
+ // C++11 allows extra semicolons at namespace scope, but not in any of the
+ // other contexts.
+ if (Kind == OutsideFunction && getLangOpts().CPlusPlus) {
+ if (getLangOpts().CPlusPlus0x)
+ Diag(StartLoc, diag::warn_cxx98_compat_top_level_semi)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ else
+ Diag(StartLoc, diag::ext_extra_semi_cxx11)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ return;
+ }
+
+ if (Kind != AfterMemberFunctionDefinition || HadMultipleSemis)
+ Diag(StartLoc, diag::ext_extra_semi)
+ << Kind << DeclSpec::getSpecifierName((DeclSpec::TST)TST)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+ else
+ // A single semicolon is valid after a member function definition.
+ Diag(StartLoc, diag::warn_extra_semi_after_mem_fn_def)
+ << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
+}
+
//===----------------------------------------------------------------------===//
// Error recovery.
//===----------------------------------------------------------------------===//
@@ -396,6 +450,9 @@ Parser::~Parser() {
PP.RemovePragmaHandler("STDC", FPContractHandler.get());
FPContractHandler.reset();
+
+ PP.removeCommentHandler(CommentSemaHandler.get());
+
PP.clearCodeCompletionHandler();
assert(TemplateIds.empty() && "Still alive TemplateIdAnnotations around?");
@@ -412,10 +469,6 @@ void Parser::Initialize() {
// Prime the lexer look-ahead.
ConsumeToken();
- if (Tok.is(tok::eof) &&
- !getLangOpts().CPlusPlus) // Empty source file is an extension in C
- Diag(Tok, diag::ext_empty_source_file);
-
// Initialization for Objective-C context sensitive keywords recognition.
// Referenced in Parser::ParseObjCTypeQualifierList.
if (getLangOpts().ObjC1) {
@@ -582,11 +635,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
HandlePragmaPack();
return DeclGroupPtrTy();
case tok::semi:
- Diag(Tok, getLangOpts().CPlusPlus0x ?
- diag::warn_cxx98_compat_top_level_semi : diag::ext_top_level_semi)
- << FixItHint::CreateRemoval(Tok.getLocation());
-
- ConsumeToken();
+ ConsumeExtraSemi(OutsideFunction);
// TODO: Invoke action for top-level semicolon.
return DeclGroupPtrTy();
case tok::r_brace:
@@ -641,7 +690,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
case tok::kw_export: // As in 'export template'
case tok::kw_static_assert:
case tok::kw__Static_assert:
- // A function definition cannot start with a these keywords.
+ // A function definition cannot start with any of these keywords.
{
SourceLocation DeclEnd;
StmtVector Stmts(Actions);
@@ -708,8 +757,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
dont_know:
// We can't tell whether this is a function-definition or declaration yet.
if (DS) {
- DS->takeAttributesFrom(attrs);
- return ParseDeclarationOrFunctionDefinition(*DS);
+ return ParseDeclarationOrFunctionDefinition(attrs, DS);
} else {
return ParseDeclarationOrFunctionDefinition(attrs);
}
@@ -729,7 +777,7 @@ bool Parser::isDeclarationAfterDeclarator() {
if (KW.is(tok::kw_default) || KW.is(tok::kw_delete))
return false;
}
-
+
return Tok.is(tok::equal) || // int X()= -> not a function def
Tok.is(tok::comma) || // int X(), -> not a function def
Tok.is(tok::semi) || // int X(); -> not a function def
@@ -777,20 +825,24 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
/// [OMP] threadprivate-directive [TODO]
///
Parser::DeclGroupPtrTy
-Parser::ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS,
- AccessSpecifier AS) {
+Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs,
+ ParsingDeclSpec &DS,
+ AccessSpecifier AS) {
// Parse the common declaration-specifiers piece.
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level);
// C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
// declaration-specifiers init-declarator-list[opt] ';'
if (Tok.is(tok::semi)) {
+ ProhibitAttributes(attrs);
ConsumeToken();
Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS, DS);
DS.complete(TheDecl);
return Actions.ConvertDeclToDeclGroup(TheDecl);
}
+ DS.takeAttributesFrom(attrs);
+
// ObjC2 allows prefix attributes on class interfaces and protocols.
// FIXME: This still needs better diagnostics. We should only accept
// attributes here, no types, etc.
@@ -831,16 +883,20 @@ Parser::ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS,
}
Parser::DeclGroupPtrTy
-Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs,
+Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributesWithRange &attrs,
+ ParsingDeclSpec *DS,
AccessSpecifier AS) {
- ParsingDeclSpec DS(*this);
- DS.takeAttributesFrom(attrs);
- // Must temporarily exit the objective-c container scope for
- // parsing c constructs and re-enter objc container scope
- // afterwards.
- ObjCDeclContextSwitch ObjCDC(*this);
-
- return ParseDeclarationOrFunctionDefinition(DS, AS);
+ if (DS) {
+ return ParseDeclOrFunctionDefInternal(attrs, *DS, AS);
+ } else {
+ ParsingDeclSpec PDS(*this);
+ // Must temporarily exit the objective-c container scope for
+ // parsing c constructs and re-enter objc container scope
+ // afterwards.
+ ObjCDeclContextSwitch ObjCDC(*this);
+
+ return ParseDeclOrFunctionDefInternal(attrs, PDS, AS);
+ }
}
/// ParseFunctionDefinition - We parsed and verified that the specified
@@ -914,6 +970,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
// In delayed template parsing mode, for function template we consume the
// tokens and store them for late parsing at the end of the translation unit.
if (getLangOpts().DelayedTemplateParsing &&
+ Tok.isNot(tok::equal) &&
TemplateInfo.Kind == ParsedTemplateInfo::Template) {
MultiTemplateParamsArg TemplateParameterLists(Actions,
TemplateInfo.TemplateParams->data(),
@@ -947,7 +1004,28 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
}
return DP;
}
-
+ else if (CurParsedObjCImpl &&
+ !TemplateInfo.TemplateParams &&
+ (Tok.is(tok::l_brace) || Tok.is(tok::kw_try) ||
+ Tok.is(tok::colon)) &&
+ Actions.CurContext->isTranslationUnit()) {
+ MultiTemplateParamsArg TemplateParameterLists(Actions, 0, 0);
+ ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope);
+ Scope *ParentScope = getCurScope()->getParent();
+
+ D.setFunctionDefinitionKind(FDK_Definition);
+ Decl *FuncDecl = Actions.HandleDeclarator(ParentScope, D,
+ move(TemplateParameterLists));
+ D.complete(FuncDecl);
+ D.getMutableDeclSpec().abort();
+ if (FuncDecl) {
+ // Consume the tokens and store them for later parsing.
+ StashAwayMethodOrFunctionBodyTokens(FuncDecl);
+ CurParsedObjCImpl->HasCFunction = true;
+ return FuncDecl;
+ }
+ }
+
// Enter a scope for the function body.
ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope);
@@ -1130,10 +1208,7 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) {
ParseDeclarator(ParmDeclarator);
}
- if (Tok.is(tok::semi)) {
- ConsumeToken();
- } else {
- Diag(Tok, diag::err_expected_semi_declaration);
+ if (ExpectAndConsumeSemi(diag::err_expected_semi_declaration)) {
// Skip to end of block or statement
SkipUntil(tok::semi, true);
if (Tok.is(tok::semi))
@@ -1251,7 +1326,8 @@ TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) {
bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) {
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)
|| Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)
- || Tok.is(tok::kw_decltype)) && "Cannot be a type or scope token!");
+ || Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id))
+ && "Cannot be a type or scope token!");
if (Tok.is(tok::kw_typename)) {
// Parse a C++ typename-specifier, e.g., "typename T::type".
@@ -1267,10 +1343,23 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) {
0, /*IsTypename*/true))
return true;
if (!SS.isSet()) {
- if (getLangOpts().MicrosoftExt)
- Diag(Tok.getLocation(), diag::warn_expected_qualified_after_typename);
- else
- Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
+ if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) ||
+ Tok.is(tok::annot_decltype)) {
+ // Attempt to recover by skipping the invalid 'typename'
+ if (Tok.is(tok::annot_decltype) ||
+ (!TryAnnotateTypeOrScopeToken(EnteringContext, NeedType) &&
+ Tok.isAnnotation())) {
+ unsigned DiagID = diag::err_expected_qualified_after_typename;
+ // MS compatibility: MSVC permits using known types with typename.
+ // e.g. "typedef typename T* pointer_type"
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::warn_expected_qualified_after_typename;
+ Diag(Tok.getLocation(), DiagID);
+ return false;
+ }
+ }
+
+ Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
return true;
}
@@ -1423,8 +1512,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) {
/// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
/// annotates C++ scope specifiers and template-ids. This returns
-/// true if the token was annotated or there was an error that could not be
-/// recovered from.
+/// true if there was an error that could not be recovered from.
///
/// Note that this routine emits an error if you call it with ::new or ::delete
/// as the current tokens, so only call it in contexts where these are invalid.
@@ -1678,13 +1766,13 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) {
return Actions.ConvertDeclToDeclGroup(Import.get());
}
-bool Parser::BalancedDelimiterTracker::diagnoseOverflow() {
+bool BalancedDelimiterTracker::diagnoseOverflow() {
P.Diag(P.Tok, diag::err_parser_impl_limit_overflow);
P.SkipUntil(tok::eof);
return true;
}
-bool Parser::BalancedDelimiterTracker::expectAndConsume(unsigned DiagID,
+bool BalancedDelimiterTracker::expectAndConsume(unsigned DiagID,
const char *Msg,
tok::TokenKind SkipToToc ) {
LOpen = P.Tok.getLocation();
@@ -1697,7 +1785,7 @@ bool Parser::BalancedDelimiterTracker::expectAndConsume(unsigned DiagID,
return diagnoseOverflow();
}
-bool Parser::BalancedDelimiterTracker::diagnoseMissingClose() {
+bool BalancedDelimiterTracker::diagnoseMissingClose() {
assert(!P.Tok.is(Close) && "Should have consumed closing delimiter");
const char *LHSName = "unknown";
@@ -1715,6 +1803,6 @@ bool Parser::BalancedDelimiterTracker::diagnoseMissingClose() {
return true;
}
-void Parser::BalancedDelimiterTracker::skipToEnd() {
+void BalancedDelimiterTracker::skipToEnd() {
P.SkipUntil(Close, false);
}
diff --git a/lib/Parse/RAIIObjectsForParser.h b/lib/Parse/RAIIObjectsForParser.h
index ef17aee..455c4af 100644
--- a/lib/Parse/RAIIObjectsForParser.h
+++ b/lib/Parse/RAIIObjectsForParser.h
@@ -16,13 +16,230 @@
#define LLVM_CLANG_PARSE_RAII_OBJECTS_FOR_PARSER_H
#include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/DelayedDiagnostic.h"
+#include "clang/Sema/Sema.h"
namespace clang {
- // TODO: move ParsingDeclRAIIObject here.
// TODO: move ParsingClassDefinition here.
// TODO: move TentativeParsingAction here.
-
-
+
+ /// \brief A RAII object used to temporarily suppress access-like
+ /// checking. Access-like checks are those associated with
+ /// controlling the use of a declaration, like C++ access control
+ /// errors and deprecation warnings. They are contextually
+ /// dependent, in that they can only be resolved with full
+ /// information about what's being declared. They are also
+ /// suppressed in certain contexts, like the template arguments of
+ /// an explicit instantiation. However, those suppression contexts
+ /// cannot necessarily be fully determined in advance; for
+ /// example, something starting like this:
+ /// template <> class std::vector<A::PrivateType>
+ /// might be the entirety of an explicit instantiation:
+ /// template <> class std::vector<A::PrivateType>;
+ /// or just an elaborated type specifier:
+ /// template <> class std::vector<A::PrivateType> make_vector<>();
+ /// Therefore this class collects all the diagnostics and permits
+ /// them to be re-delayed in a new context.
+ class SuppressAccessChecks {
+ Sema &S;
+ sema::DelayedDiagnosticPool DiagnosticPool;
+ Sema::ParsingDeclState State;
+ bool Active;
+
+ public:
+ /// Begin suppressing access-like checks
+ SuppressAccessChecks(Parser &P, bool activate = true)
+ : S(P.getActions()), DiagnosticPool(NULL) {
+ if (activate) {
+ State = S.PushParsingDeclaration(DiagnosticPool);
+ Active = true;
+ } else {
+ Active = false;
+ }
+ }
+
+ void done() {
+ assert(Active && "trying to end an inactive suppression");
+ S.PopParsingDeclaration(State, NULL);
+ Active = false;
+ }
+
+ void redelay() {
+ assert(!Active && "redelaying without having ended first");
+ if (!DiagnosticPool.pool_empty())
+ S.redelayDiagnostics(DiagnosticPool);
+ assert(DiagnosticPool.pool_empty());
+ }
+
+ ~SuppressAccessChecks() {
+ if (Active) done();
+ }
+ };
+
+ /// \brief RAII object used to inform the actions that we're
+ /// currently parsing a declaration. This is active when parsing a
+ /// variable's initializer, but not when parsing the body of a
+ /// class or function definition.
+ class ParsingDeclRAIIObject {
+ Sema &Actions;
+ sema::DelayedDiagnosticPool DiagnosticPool;
+ Sema::ParsingDeclState State;
+ bool Popped;
+
+ // Do not implement.
+ ParsingDeclRAIIObject(const ParsingDeclRAIIObject &other);
+ ParsingDeclRAIIObject &operator=(const ParsingDeclRAIIObject &other);
+
+ public:
+ enum NoParent_t { NoParent };
+ ParsingDeclRAIIObject(Parser &P, NoParent_t _)
+ : Actions(P.getActions()), DiagnosticPool(NULL) {
+ push();
+ }
+
+ /// Creates a RAII object whose pool is optionally parented by another.
+ ParsingDeclRAIIObject(Parser &P,
+ const sema::DelayedDiagnosticPool *parentPool)
+ : Actions(P.getActions()), DiagnosticPool(parentPool) {
+ push();
+ }
+
+ /// Creates a RAII object and, optionally, initialize its
+ /// diagnostics pool by stealing the diagnostics from another
+ /// RAII object (which is assumed to be the current top pool).
+ ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *other)
+ : Actions(P.getActions()),
+ DiagnosticPool(other ? other->DiagnosticPool.getParent() : NULL) {
+ if (other) {
+ DiagnosticPool.steal(other->DiagnosticPool);
+ other->abort();
+ }
+ push();
+ }
+
+ ~ParsingDeclRAIIObject() {
+ abort();
+ }
+
+ sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() {
+ return DiagnosticPool;
+ }
+ const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const {
+ return DiagnosticPool;
+ }
+
+ /// Resets the RAII object for a new declaration.
+ void reset() {
+ abort();
+ push();
+ }
+
+ /// Signals that the context was completed without an appropriate
+ /// declaration being parsed.
+ void abort() {
+ pop(0);
+ }
+
+ void complete(Decl *D) {
+ assert(!Popped && "ParsingDeclaration has already been popped!");
+ pop(D);
+ }
+
+ /// Unregister this object from Sema, but remember all the
+ /// diagnostics that were emitted into it.
+ void abortAndRemember() {
+ pop(0);
+ }
+
+ private:
+ void push() {
+ State = Actions.PushParsingDeclaration(DiagnosticPool);
+ Popped = false;
+ }
+
+ void pop(Decl *D) {
+ if (!Popped) {
+ Actions.PopParsingDeclaration(State, D);
+ Popped = true;
+ }
+ }
+ };
+
+ /// A class for parsing a DeclSpec.
+ class ParsingDeclSpec : public DeclSpec {
+ ParsingDeclRAIIObject ParsingRAII;
+
+ public:
+ ParsingDeclSpec(Parser &P)
+ : DeclSpec(P.getAttrFactory()),
+ ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {}
+ ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII)
+ : DeclSpec(P.getAttrFactory()),
+ ParsingRAII(P, RAII) {}
+
+ const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const {
+ return ParsingRAII.getDelayedDiagnosticPool();
+ }
+
+ void complete(Decl *D) {
+ ParsingRAII.complete(D);
+ }
+
+ void abort() {
+ ParsingRAII.abort();
+ }
+ };
+
+ /// A class for parsing a declarator.
+ class ParsingDeclarator : public Declarator {
+ ParsingDeclRAIIObject ParsingRAII;
+
+ public:
+ ParsingDeclarator(Parser &P, const ParsingDeclSpec &DS, TheContext C)
+ : Declarator(DS, C), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) {
+ }
+
+ const ParsingDeclSpec &getDeclSpec() const {
+ return static_cast<const ParsingDeclSpec&>(Declarator::getDeclSpec());
+ }
+
+ ParsingDeclSpec &getMutableDeclSpec() const {
+ return const_cast<ParsingDeclSpec&>(getDeclSpec());
+ }
+
+ void clear() {
+ Declarator::clear();
+ ParsingRAII.reset();
+ }
+
+ void complete(Decl *D) {
+ ParsingRAII.complete(D);
+ }
+ };
+
+ /// A class for parsing a field declarator.
+ class ParsingFieldDeclarator : public FieldDeclarator {
+ ParsingDeclRAIIObject ParsingRAII;
+
+ public:
+ ParsingFieldDeclarator(Parser &P, const ParsingDeclSpec &DS)
+ : FieldDeclarator(DS), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) {
+ }
+
+ const ParsingDeclSpec &getDeclSpec() const {
+ return static_cast<const ParsingDeclSpec&>(D.getDeclSpec());
+ }
+
+ ParsingDeclSpec &getMutableDeclSpec() const {
+ return const_cast<ParsingDeclSpec&>(getDeclSpec());
+ }
+
+ void complete(Decl *D) {
+ ParsingRAII.complete(D);
+ }
+ };
+
/// ExtensionRAIIObject - This saves the state of extension warnings when
/// constructed and disables them. When destructed, it restores them back to
/// the way they used to be. This is used to handle __extension__ in the
@@ -137,6 +354,81 @@ namespace clang {
}
};
+ /// \brief RAII class that helps handle the parsing of an open/close delimiter
+ /// pair, such as braces { ... } or parentheses ( ... ).
+ class BalancedDelimiterTracker : public GreaterThanIsOperatorScope {
+ Parser& P;
+ tok::TokenKind Kind, Close;
+ SourceLocation (Parser::*Consumer)();
+ SourceLocation LOpen, LClose;
+
+ unsigned short &getDepth() {
+ switch (Kind) {
+ case tok::l_brace: return P.BraceCount;
+ case tok::l_square: return P.BracketCount;
+ case tok::l_paren: return P.ParenCount;
+ default: llvm_unreachable("Wrong token kind");
+ }
+ }
+
+ enum { MaxDepth = 256 };
+
+ bool diagnoseOverflow();
+ bool diagnoseMissingClose();
+
+ public:
+ BalancedDelimiterTracker(Parser& p, tok::TokenKind k)
+ : GreaterThanIsOperatorScope(p.GreaterThanIsOperator, true),
+ P(p), Kind(k)
+ {
+ switch (Kind) {
+ default: llvm_unreachable("Unexpected balanced token");
+ case tok::l_brace:
+ Close = tok::r_brace;
+ Consumer = &Parser::ConsumeBrace;
+ break;
+ case tok::l_paren:
+ Close = tok::r_paren;
+ Consumer = &Parser::ConsumeParen;
+ break;
+
+ case tok::l_square:
+ Close = tok::r_square;
+ Consumer = &Parser::ConsumeBracket;
+ break;
+ }
+ }
+
+ SourceLocation getOpenLocation() const { return LOpen; }
+ SourceLocation getCloseLocation() const { return LClose; }
+ SourceRange getRange() const { return SourceRange(LOpen, LClose); }
+
+ bool consumeOpen() {
+ if (!P.Tok.is(Kind))
+ return true;
+
+ if (getDepth() < MaxDepth) {
+ LOpen = (P.*Consumer)();
+ return false;
+ }
+
+ return diagnoseOverflow();
+ }
+
+ bool expectAndConsume(unsigned DiagID,
+ const char *Msg = "",
+ tok::TokenKind SkipToTok = tok::unknown);
+ bool consumeClose() {
+ if (P.Tok.is(Close)) {
+ LClose = (P.*Consumer)();
+ return false;
+ }
+
+ return diagnoseMissingClose();
+ }
+ void skipToEnd();
+ };
+
} // end namespace clang
#endif
diff --git a/lib/Rewrite/CMakeLists.txt b/lib/Rewrite/CMakeLists.txt
index 2a05040..af8f6d4 100644
--- a/lib/Rewrite/CMakeLists.txt
+++ b/lib/Rewrite/CMakeLists.txt
@@ -1,11 +1,10 @@
-set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend)
-
add_clang_library(clangRewrite
DeltaTree.cpp
FixItRewriter.cpp
FrontendActions.cpp
HTMLPrint.cpp
HTMLRewrite.cpp
+ InclusionRewriter.cpp
RewriteMacros.cpp
RewriteModernObjC.cpp
RewriteObjC.cpp
@@ -18,5 +17,17 @@ add_clang_library(clangRewrite
add_dependencies(clangRewrite
ClangAttrClasses
ClangAttrList
+ ClangAttrParsedAttrList
+ ClangCommentNodes
ClangDeclNodes
- ClangStmtNodes)
+ ClangDiagnosticCommon
+ ClangDiagnosticFrontend
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangRewrite
+ clangBasic
+ clangAST
+ clangParse
+ clangFrontend
+ )
diff --git a/lib/Rewrite/FrontendActions.cpp b/lib/Rewrite/FrontendActions.cpp
index 1753325..9bc218e 100644
--- a/lib/Rewrite/FrontendActions.cpp
+++ b/lib/Rewrite/FrontendActions.cpp
@@ -155,7 +155,7 @@ bool FixItRecompile::BeginInvocation(CompilerInstance &CI) {
ASTConsumer *RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) {
if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "cpp")) {
- if (CI.getLangOpts().ObjCNonFragileABI)
+ if (CI.getLangOpts().ObjCRuntime.isNonFragile())
return CreateModernObjCRewriter(InFile, OS,
CI.getDiagnostics(), CI.getLangOpts(),
CI.getDiagnosticOpts().NoRewriteMacros);
@@ -181,3 +181,12 @@ void RewriteTestAction::ExecuteAction() {
DoRewriteTest(CI.getPreprocessor(), OS);
}
+
+void RewriteIncludesAction::ExecuteAction() {
+ CompilerInstance &CI = getCompilerInstance();
+ raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile());
+ if (!OS) return;
+
+ RewriteIncludesInInput(CI.getPreprocessor(), OS,
+ CI.getPreprocessorOutputOpts());
+}
diff --git a/lib/Rewrite/HTMLRewrite.cpp b/lib/Rewrite/HTMLRewrite.cpp
index dc39dde..236b98f 100644
--- a/lib/Rewrite/HTMLRewrite.cpp
+++ b/lib/Rewrite/HTMLRewrite.cpp
@@ -325,11 +325,12 @@ void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID,
" .msgControl { background-color:#bbbbbb; color:#000000 }\n"
" .mrange { background-color:#dfddf3 }\n"
" .mrange { border-bottom:1px solid #6F9DBE }\n"
- " .PathIndex { font-weight: bold; padding:0px 5px 0px 5px; "
+ " .PathIndex { font-weight: bold; padding:0px 5px; "
"margin-right:5px; }\n"
" .PathIndex { -webkit-border-radius:8px }\n"
" .PathIndexEvent { background-color:#bfba87 }\n"
" .PathIndexControl { background-color:#8c8c8c }\n"
+ " .PathNav a { text-decoration:none; font-size: larger }\n"
" .CodeInsertionHint { font-weight: bold; background-color: #10dd10 }\n"
" .CodeRemovalHint { background-color:#de1010 }\n"
" .CodeRemovalHint { border-bottom:1px solid #6F9DBE }\n"
@@ -495,6 +496,11 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
// Inform the preprocessor that we don't want comments.
TmpPP.SetCommentRetentionState(false, false);
+ // We don't want pragmas either. Although we filtered out #pragma, removing
+ // _Pragma and __pragma is much harder.
+ bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled();
+ TmpPP.setPragmasEnabled(false);
+
// Enter the tokens we just lexed. This will cause them to be macro expanded
// but won't enter sub-files (because we removed #'s).
TmpPP.EnterTokenStream(&TokenStream[0], TokenStream.size(), false, false);
@@ -571,6 +577,7 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
"<span class='macro'>", Expansion.c_str());
}
- // Restore diagnostics object back to its own thing.
+ // Restore the preprocessor's old state.
TmpPP.setDiagnostics(*OldDiags);
+ TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled);
}
diff --git a/lib/Rewrite/InclusionRewriter.cpp b/lib/Rewrite/InclusionRewriter.cpp
new file mode 100644
index 0000000..3dfc3b0
--- /dev/null
+++ b/lib/Rewrite/InclusionRewriter.cpp
@@ -0,0 +1,361 @@
+//===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This code rewrites include invocations into their expansions. This gives you
+// a file with all included files merged into it.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Rewrite/Rewriters.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/PreprocessorOutputOptions.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+class InclusionRewriter : public PPCallbacks {
+ /// Information about which #includes were actually performed,
+ /// created by preprocessor callbacks.
+ struct FileChange {
+ SourceLocation From;
+ FileID Id;
+ SrcMgr::CharacteristicKind FileType;
+ FileChange(SourceLocation From) : From(From) {
+ }
+ };
+ Preprocessor &PP; ///< Used to find inclusion directives.
+ SourceManager &SM; ///< Used to read and manage source files.
+ raw_ostream &OS; ///< The destination stream for rewritten contents.
+ bool ShowLineMarkers; ///< Show #line markers.
+ bool UseLineDirective; ///< Use of line directives or line markers.
+ typedef std::map<unsigned, FileChange> FileChangeMap;
+ FileChangeMap FileChanges; /// Tracks which files were included where.
+ /// Used transitively for building up the FileChanges mapping over the
+ /// various \c PPCallbacks callbacks.
+ FileChangeMap::iterator LastInsertedFileChange;
+public:
+ InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers);
+ bool Process(FileID FileId, SrcMgr::CharacteristicKind FileType);
+private:
+ virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+ SrcMgr::CharacteristicKind FileType,
+ FileID PrevFID);
+ virtual void FileSkipped(const FileEntry &ParentFile,
+ const Token &FilenameTok,
+ SrcMgr::CharacteristicKind FileType);
+ virtual void InclusionDirective(SourceLocation HashLoc,
+ const Token &IncludeTok,
+ StringRef FileName,
+ bool IsAngled,
+ const FileEntry *File,
+ SourceLocation EndLoc,
+ StringRef SearchPath,
+ StringRef RelativePath);
+ void WriteLineInfo(const char *Filename, int Line,
+ SrcMgr::CharacteristicKind FileType,
+ StringRef EOL, StringRef Extra = StringRef());
+ void OutputContentUpTo(const MemoryBuffer &FromFile,
+ unsigned &WriteFrom, unsigned WriteTo,
+ StringRef EOL, int &lines,
+ bool EnsureNewline = false);
+ void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken,
+ const MemoryBuffer &FromFile, StringRef EOL,
+ unsigned &NextToWrite, int &Lines);
+ const FileChange *FindFileChangeLocation(SourceLocation Loc) const;
+ StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken);
+};
+
+} // end anonymous namespace
+
+/// Initializes an InclusionRewriter with a \p PP source and \p OS destination.
+InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS,
+ bool ShowLineMarkers)
+ : PP(PP), SM(PP.getSourceManager()), OS(OS),
+ ShowLineMarkers(ShowLineMarkers),
+ LastInsertedFileChange(FileChanges.end()) {
+ // If we're in microsoft mode, use normal #line instead of line markers.
+ UseLineDirective = PP.getLangOpts().MicrosoftExt;
+}
+
+/// Write appropriate line information as either #line directives or GNU line
+/// markers depending on what mode we're in, including the \p Filename and
+/// \p Line we are located at, using the specified \p EOL line separator, and
+/// any \p Extra context specifiers in GNU line directives.
+void InclusionRewriter::WriteLineInfo(const char *Filename, int Line,
+ SrcMgr::CharacteristicKind FileType,
+ StringRef EOL, StringRef Extra) {
+ if (!ShowLineMarkers)
+ return;
+ if (UseLineDirective) {
+ OS << "#line" << ' ' << Line << ' ' << '"' << Filename << '"';
+ } else {
+ // Use GNU linemarkers as described here:
+ // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
+ OS << '#' << ' ' << Line << ' ' << '"' << Filename << '"';
+ if (!Extra.empty())
+ OS << Extra;
+ if (FileType == SrcMgr::C_System)
+ // "`3' This indicates that the following text comes from a system header
+ // file, so certain warnings should be suppressed."
+ OS << " 3";
+ else if (FileType == SrcMgr::C_ExternCSystem)
+ // as above for `3', plus "`4' This indicates that the following text
+ // should be treated as being wrapped in an implicit extern "C" block."
+ OS << " 3 4";
+ }
+ OS << EOL;
+}
+
+/// FileChanged - Whenever the preprocessor enters or exits a #include file
+/// it invokes this handler.
+void InclusionRewriter::FileChanged(SourceLocation Loc,
+ FileChangeReason Reason,
+ SrcMgr::CharacteristicKind NewFileType,
+ FileID) {
+ if (Reason != EnterFile)
+ return;
+ if (LastInsertedFileChange == FileChanges.end())
+ // we didn't reach this file (eg: the main file) via an inclusion directive
+ return;
+ LastInsertedFileChange->second.Id = FullSourceLoc(Loc, SM).getFileID();
+ LastInsertedFileChange->second.FileType = NewFileType;
+ LastInsertedFileChange = FileChanges.end();
+}
+
+/// Called whenever an inclusion is skipped due to canonical header protection
+/// macros.
+void InclusionRewriter::FileSkipped(const FileEntry &/*ParentFile*/,
+ const Token &/*FilenameTok*/,
+ SrcMgr::CharacteristicKind /*FileType*/) {
+ assert(LastInsertedFileChange != FileChanges.end() && "A file, that wasn't "
+ "found via an inclusion directive, was skipped");
+ FileChanges.erase(LastInsertedFileChange);
+ LastInsertedFileChange = FileChanges.end();
+}
+
+/// This should be called whenever the preprocessor encounters include
+/// directives. It does not say whether the file has been included, but it
+/// provides more information about the directive (hash location instead
+/// of location inside the included file). It is assumed that the matching
+/// FileChanged() or FileSkipped() is called after this.
+void InclusionRewriter::InclusionDirective(SourceLocation HashLoc,
+ const Token &/*IncludeTok*/,
+ StringRef /*FileName*/,
+ bool /*IsAngled*/,
+ const FileEntry * /*File*/,
+ SourceLocation /*EndLoc*/,
+ StringRef /*SearchPath*/,
+ StringRef /*RelativePath*/) {
+ assert(LastInsertedFileChange == FileChanges.end() && "Another inclusion "
+ "directive was found before the previous one was processed");
+ std::pair<FileChangeMap::iterator, bool> p = FileChanges.insert(
+ std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc)));
+ assert(p.second && "Unexpected revisitation of the same include directive");
+ LastInsertedFileChange = p.first;
+}
+
+/// Simple lookup for a SourceLocation (specifically one denoting the hash in
+/// an inclusion directive) in the map of inclusion information, FileChanges.
+const InclusionRewriter::FileChange *
+InclusionRewriter::FindFileChangeLocation(SourceLocation Loc) const {
+ FileChangeMap::const_iterator I = FileChanges.find(Loc.getRawEncoding());
+ if (I != FileChanges.end())
+ return &I->second;
+ return NULL;
+}
+
+/// Detect the likely line ending style of \p FromFile by examining the first
+/// newline found within it.
+static StringRef DetectEOL(const MemoryBuffer &FromFile) {
+ // detect what line endings the file uses, so that added content does not mix
+ // the style
+ const char *Pos = strchr(FromFile.getBufferStart(), '\n');
+ if (Pos == NULL)
+ return "\n";
+ if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r')
+ return "\n\r";
+ if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r')
+ return "\r\n";
+ return "\n";
+}
+
+/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at
+/// \p WriteTo - 1.
+void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile,
+ unsigned &WriteFrom, unsigned WriteTo,
+ StringRef EOL, int &Line,
+ bool EnsureNewline) {
+ if (WriteTo <= WriteFrom)
+ return;
+ OS.write(FromFile.getBufferStart() + WriteFrom, WriteTo - WriteFrom);
+ // count lines manually, it's faster than getPresumedLoc()
+ Line += std::count(FromFile.getBufferStart() + WriteFrom,
+ FromFile.getBufferStart() + WriteTo, '\n');
+ if (EnsureNewline) {
+ char LastChar = FromFile.getBufferStart()[WriteTo - 1];
+ if (LastChar != '\n' && LastChar != '\r')
+ OS << EOL;
+ }
+ WriteFrom = WriteTo;
+}
+
+/// Print characters from \p FromFile starting at \p NextToWrite up until the
+/// inclusion directive at \p StartToken, then print out the inclusion
+/// inclusion directive disabled by a #if directive, updating \p NextToWrite
+/// and \p Line to track the number of source lines visited and the progress
+/// through the \p FromFile buffer.
+void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex,
+ const Token &StartToken,
+ const MemoryBuffer &FromFile,
+ StringRef EOL,
+ unsigned &NextToWrite, int &Line) {
+ OutputContentUpTo(FromFile, NextToWrite,
+ SM.getFileOffset(StartToken.getLocation()), EOL, Line);
+ Token DirectiveToken;
+ do {
+ DirectiveLex.LexFromRawLexer(DirectiveToken);
+ } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof));
+ OS << "#if 0 /* expanded by -frewrite-includes */" << EOL;
+ OutputContentUpTo(FromFile, NextToWrite,
+ SM.getFileOffset(DirectiveToken.getLocation()) + DirectiveToken.getLength(),
+ EOL, Line);
+ OS << "#endif /* expanded by -frewrite-includes */" << EOL;
+}
+
+/// Find the next identifier in the pragma directive specified by \p RawToken.
+StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex,
+ Token &RawToken) {
+ RawLex.LexFromRawLexer(RawToken);
+ if (RawToken.is(tok::raw_identifier))
+ PP.LookUpIdentifierInfo(RawToken);
+ if (RawToken.is(tok::identifier))
+ return RawToken.getIdentifierInfo()->getName();
+ return StringRef();
+}
+
+/// Use a raw lexer to analyze \p FileId, inccrementally copying parts of it
+/// and including content of included files recursively.
+bool InclusionRewriter::Process(FileID FileId,
+ SrcMgr::CharacteristicKind FileType)
+{
+ bool Invalid;
+ const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid);
+ if (Invalid) // invalid inclusion
+ return true;
+ const char *FileName = FromFile.getBufferIdentifier();
+ Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts());
+ RawLex.SetCommentRetentionState(false);
+
+ StringRef EOL = DetectEOL(FromFile);
+
+ // Per the GNU docs: "1" indicates the start of a new file.
+ WriteLineInfo(FileName, 1, FileType, EOL, " 1");
+
+ if (SM.getFileIDSize(FileId) == 0)
+ return true;
+
+ // The next byte to be copied from the source file
+ unsigned NextToWrite = 0;
+ int Line = 1; // The current input file line number.
+
+ Token RawToken;
+ RawLex.LexFromRawLexer(RawToken);
+
+ // TODO: Consider adding a switch that strips possibly unimportant content,
+ // such as comments, to reduce the size of repro files.
+ while (RawToken.isNot(tok::eof)) {
+ if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) {
+ RawLex.setParsingPreprocessorDirective(true);
+ Token HashToken = RawToken;
+ RawLex.LexFromRawLexer(RawToken);
+ if (RawToken.is(tok::raw_identifier))
+ PP.LookUpIdentifierInfo(RawToken);
+ if (RawToken.is(tok::identifier)) {
+ switch (RawToken.getIdentifierInfo()->getPPKeywordID()) {
+ case tok::pp_include:
+ case tok::pp_include_next:
+ case tok::pp_import: {
+ CommentOutDirective(RawLex, HashToken, FromFile, EOL, NextToWrite,
+ Line);
+ if (const FileChange *Change = FindFileChangeLocation(
+ HashToken.getLocation())) {
+ // now include and recursively process the file
+ if (Process(Change->Id, Change->FileType))
+ // and set lineinfo back to this file, if the nested one was
+ // actually included
+ // `2' indicates returning to a file (after having included
+ // another file.
+ WriteLineInfo(FileName, Line, FileType, EOL, " 2");
+ } else
+ // fix up lineinfo (since commented out directive changed line
+ // numbers) for inclusions that were skipped due to header guards
+ WriteLineInfo(FileName, Line, FileType, EOL);
+ break;
+ }
+ case tok::pp_pragma: {
+ StringRef Identifier = NextIdentifierName(RawLex, RawToken);
+ if (Identifier == "clang" || Identifier == "GCC") {
+ if (NextIdentifierName(RawLex, RawToken) == "system_header") {
+ // keep the directive in, commented out
+ CommentOutDirective(RawLex, HashToken, FromFile, EOL,
+ NextToWrite, Line);
+ // update our own type
+ FileType = SM.getFileCharacteristic(RawToken.getLocation());
+ WriteLineInfo(FileName, Line, FileType, EOL);
+ }
+ } else if (Identifier == "once") {
+ // keep the directive in, commented out
+ CommentOutDirective(RawLex, HashToken, FromFile, EOL,
+ NextToWrite, Line);
+ WriteLineInfo(FileName, Line, FileType, EOL);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ RawLex.setParsingPreprocessorDirective(false);
+ }
+ RawLex.LexFromRawLexer(RawToken);
+ }
+ OutputContentUpTo(FromFile, NextToWrite,
+ SM.getFileOffset(SM.getLocForEndOfFile(FileId)) + 1, EOL, Line,
+ /*EnsureNewline*/true);
+ return true;
+}
+
+/// InclusionRewriterInInput - Implement -frewrite-includes mode.
+void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
+ const PreprocessorOutputOptions &Opts) {
+ SourceManager &SM = PP.getSourceManager();
+ InclusionRewriter *Rewrite = new InclusionRewriter(PP, *OS,
+ Opts.ShowLineMarkers);
+ PP.addPPCallbacks(Rewrite);
+
+ // First let the preprocessor process the entire file and call callbacks.
+ // Callbacks will record which #include's were actually performed.
+ PP.EnterMainSourceFile();
+ Token Tok;
+ // Only preprocessor directives matter here, so disable macro expansion
+ // everywhere else as an optimization.
+ // TODO: It would be even faster if the preprocessor could be switched
+ // to a mode where it would parse only preprocessor directives and comments,
+ // nothing else matters for parsing or processing.
+ PP.SetMacroExpansionOnlyInDirectives();
+ do {
+ PP.Lex(Tok);
+ } while (Tok.isNot(tok::eof));
+ Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User);
+ OS->flush();
+}
diff --git a/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp
index 94fba64..9f42fca 100644
--- a/lib/Rewrite/RewriteModernObjC.cpp
+++ b/lib/Rewrite/RewriteModernObjC.cpp
@@ -102,7 +102,6 @@ namespace {
FunctionDecl *CFStringFunctionDecl;
FunctionDecl *SuperContructorFunctionDecl;
FunctionDecl *CurFunctionDef;
- FunctionDecl *CurFunctionDeclToDeclareForBlock;
/* Misc. containers needed for meta-data rewrite. */
SmallVector<ObjCImplementationDecl *, 8> ClassImplementation;
@@ -110,7 +109,7 @@ namespace {
llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs;
llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols;
llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCWrittenInterfaces;
- llvm::SmallPtrSet<TagDecl*, 8> TagsDefinedInIvarDecls;
+ llvm::SmallPtrSet<TagDecl*, 32> GlobalDefinedTags;
SmallVector<ObjCInterfaceDecl*, 32> ObjCInterfacesSeen;
/// DefinedNonLazyClasses - List of defined "non-lazy" classes.
SmallVector<ObjCInterfaceDecl*, 8> DefinedNonLazyClasses;
@@ -304,9 +303,12 @@ namespace {
void RewriteFunctionDecl(FunctionDecl *FD);
void RewriteBlockPointerType(std::string& Str, QualType Type);
void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD);
+ void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD);
void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl);
void RewriteTypeOfDecl(VarDecl *VD);
void RewriteObjCQualifiedInterfaceTypes(Expr *E);
+
+ std::string getIvarAccessString(ObjCIvarDecl *D);
// Expression Rewriting.
Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S);
@@ -317,11 +319,12 @@ namespace {
Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp);
Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp);
Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp);
- Stmt *RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp);
+ Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp);
Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp);
Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp);
Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp);
Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S);
+ Stmt *RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S);
Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S);
Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S);
Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S,
@@ -337,7 +340,7 @@ namespace {
// Block specific rewrite rules.
void RewriteBlockPointerDecl(NamedDecl *VD);
- void RewriteByRefVar(VarDecl *VD);
+ void RewriteByRefVar(VarDecl *VD, bool firstDecl, bool lastDecl);
Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD);
Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE);
void RewriteBlockPointerFunctionArgs(FunctionDecl *FD);
@@ -346,6 +349,10 @@ namespace {
std::string &Result);
void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result);
+ bool IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, TagDecl *Tag,
+ bool &IsNamedDefinition);
+ void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl,
+ std::string &Result);
bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result);
@@ -354,12 +361,19 @@ namespace {
virtual void Initialize(ASTContext &context);
- // Misc. AST transformation routines. Somtimes they end up calling
+ // Misc. AST transformation routines. Sometimes they end up calling
// rewriting routines on the new ASTs.
CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD,
Expr **args, unsigned nargs,
SourceLocation StartLoc=SourceLocation(),
SourceLocation EndLoc=SourceLocation());
+
+ Expr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor,
+ QualType msgSendType,
+ QualType returnType,
+ SmallVectorImpl<QualType> &ArgTypes,
+ SmallVectorImpl<Expr*> &MsgExprs,
+ ObjCMethodDecl *Method);
Stmt *SynthMessageExpr(ObjCMessageExpr *Exp,
SourceLocation StartLoc=SourceLocation(),
@@ -387,23 +401,23 @@ namespace {
std::string &Result);
void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol,
std::string &Result);
- virtual void RewriteObjCProtocolListMetaData(
+ void RewriteObjCProtocolListMetaData(
const ObjCList<ObjCProtocolDecl> &Prots,
StringRef prefix, StringRef ClassName, std::string &Result);
- virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl,
+ void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl,
std::string &Result);
- virtual void RewriteClassSetupInitHook(std::string &Result);
+ void RewriteClassSetupInitHook(std::string &Result);
- virtual void RewriteMetaDataIntoBuffer(std::string &Result);
- virtual void WriteImageInfo(std::string &Result);
- virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl,
+ void RewriteMetaDataIntoBuffer(std::string &Result);
+ void WriteImageInfo(std::string &Result);
+ void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl,
std::string &Result);
- virtual void RewriteCategorySetupInitHook(std::string &Result);
+ void RewriteCategorySetupInitHook(std::string &Result);
// Rewriting ivar
- virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar,
+ void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar,
std::string &Result);
- virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV);
+ Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV);
std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag);
@@ -622,7 +636,6 @@ void RewriteModernObjC::InitializeCommon(ASTContext &context) {
NSStringRecord = 0;
CurMethodDef = 0;
CurFunctionDef = 0;
- CurFunctionDeclToDeclareForBlock = 0;
GlobalVarDecl = 0;
GlobalConstructionExp = 0;
SuperStructDecl = 0;
@@ -768,29 +781,104 @@ void RewriteModernObjC::RewriteInclude() {
}
}
-static std::string getIvarAccessString(ObjCIvarDecl *OID) {
- const ObjCInterfaceDecl *ClassDecl = OID->getContainingInterface();
- std::string S;
- S = "((struct ";
- S += ClassDecl->getIdentifier()->getName();
- S += "_IMPL *)self)->";
- S += OID->getName();
+static void WriteInternalIvarName(const ObjCInterfaceDecl *IDecl,
+ ObjCIvarDecl *IvarDecl, std::string &Result) {
+ Result += "OBJC_IVAR_$_";
+ Result += IDecl->getName();
+ Result += "$";
+ Result += IvarDecl->getName();
+}
+
+std::string
+RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) {
+ const ObjCInterfaceDecl *ClassDecl = D->getContainingInterface();
+
+ // Build name of symbol holding ivar offset.
+ std::string IvarOffsetName;
+ WriteInternalIvarName(ClassDecl, D, IvarOffsetName);
+
+
+ std::string S = "(*(";
+ QualType IvarT = D->getType();
+
+ if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) {
+ RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl();
+ RD = RD->getDefinition();
+ if (RD && !RD->getDeclName().getAsIdentifierInfo()) {
+ // decltype(((Foo_IMPL*)0)->bar) *
+ ObjCContainerDecl *CDecl =
+ dyn_cast<ObjCContainerDecl>(D->getDeclContext());
+ // ivar in class extensions requires special treatment.
+ if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
+ CDecl = CatDecl->getClassInterface();
+ std::string RecName = CDecl->getName();
+ RecName += "_IMPL";
+ RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl,
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get(RecName.c_str()));
+ QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD));
+ unsigned UnsignedIntSize =
+ static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy));
+ Expr *Zero = IntegerLiteral::Create(*Context,
+ llvm::APInt(UnsignedIntSize, 0),
+ Context->UnsignedIntTy, SourceLocation());
+ Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero);
+ ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(),
+ Zero);
+ FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(),
+ SourceLocation(),
+ &Context->Idents.get(D->getNameAsString()),
+ IvarT, 0,
+ /*BitWidth=*/0, /*Mutable=*/true,
+ ICIS_NoInit);
+ MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(),
+ FD->getType(), VK_LValue,
+ OK_Ordinary);
+ IvarT = Context->getDecltypeType(ME, ME->getType());
+ }
+ }
+ convertObjCTypeToCStyleType(IvarT);
+ QualType castT = Context->getPointerType(IvarT);
+ std::string TypeString(castT.getAsString(Context->getPrintingPolicy()));
+ S += TypeString;
+ S += ")";
+
+ // ((char *)self + IVAR_OFFSET_SYMBOL_NAME)
+ S += "((char *)self + ";
+ S += IvarOffsetName;
+ S += "))";
+ ReferencedIvars[const_cast<ObjCInterfaceDecl *>(ClassDecl)].insert(D);
return S;
}
+/// mustSynthesizeSetterGetterMethod - returns true if setter or getter has not
+/// been found in the class implementation. In this case, it must be synthesized.
+static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP,
+ ObjCPropertyDecl *PD,
+ bool getter) {
+ return getter ? !IMP->getInstanceMethod(PD->getGetterName())
+ : !IMP->getInstanceMethod(PD->getSetterName());
+
+}
+
void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID,
ObjCImplementationDecl *IMD,
ObjCCategoryImplDecl *CID) {
static bool objcGetPropertyDefined = false;
static bool objcSetPropertyDefined = false;
- SourceLocation startLoc = PID->getLocStart();
- InsertText(startLoc, "// ");
- const char *startBuf = SM->getCharacterData(startLoc);
- assert((*startBuf == '@') && "bogus @synthesize location");
- const char *semiBuf = strchr(startBuf, ';');
- assert((*semiBuf == ';') && "@synthesize: can't find ';'");
- SourceLocation onePastSemiLoc =
- startLoc.getLocWithOffset(semiBuf-startBuf+1);
+ SourceLocation startGetterSetterLoc;
+
+ if (PID->getLocStart().isValid()) {
+ SourceLocation startLoc = PID->getLocStart();
+ InsertText(startLoc, "// ");
+ const char *startBuf = SM->getCharacterData(startLoc);
+ assert((*startBuf == '@') && "bogus @synthesize location");
+ const char *semiBuf = strchr(startBuf, ';');
+ assert((*semiBuf == ';') && "@synthesize: can't find ';'");
+ startGetterSetterLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1);
+ }
+ else
+ startGetterSetterLoc = IMD ? IMD->getLocEnd() : CID->getLocEnd();
if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
return; // FIXME: is this correct?
@@ -802,7 +890,7 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID,
if (!OID)
return;
unsigned Attributes = PD->getPropertyAttributes();
- if (!PD->getGetterMethodDecl()->isDefined()) {
+ if (mustSynthesizeSetterGetterMethod(IMD, PD, true /*getter*/)) {
bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) &&
(Attributes & (ObjCPropertyDecl::OBJC_PR_retain |
ObjCPropertyDecl::OBJC_PR_copy));
@@ -854,10 +942,11 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID,
else
Getr += "return " + getIvarAccessString(OID);
Getr += "; }";
- InsertText(onePastSemiLoc, Getr);
+ InsertText(startGetterSetterLoc, Getr);
}
- if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined())
+ if (PD->isReadOnly() ||
+ !mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/))
return;
// Generate the 'setter' function.
@@ -895,8 +984,8 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID,
Setr += getIvarAccessString(OID) + " = ";
Setr += PD->getName();
}
- Setr += "; }";
- InsertText(onePastSemiLoc, Setr);
+ Setr += "; }\n";
+ InsertText(startGetterSetterLoc, Setr);
}
static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl,
@@ -985,17 +1074,13 @@ void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) {
SourceLocation LocStart = CatDecl->getLocStart();
// FIXME: handle category headers that are declared across multiple lines.
- ReplaceText(LocStart, 0, "// ");
- if (CatDecl->getIvarLBraceLoc().isValid())
- InsertText(CatDecl->getIvarLBraceLoc(), "// ");
- for (ObjCCategoryDecl::ivar_iterator
- I = CatDecl->ivar_begin(), E = CatDecl->ivar_end(); I != E; ++I) {
- ObjCIvarDecl *Ivar = (*I);
- SourceLocation LocStart = Ivar->getLocStart();
+ if (CatDecl->getIvarRBraceLoc().isValid()) {
+ ReplaceText(LocStart, 1, "/** ");
+ ReplaceText(CatDecl->getIvarRBraceLoc(), 1, "**/ ");
+ }
+ else {
ReplaceText(LocStart, 0, "// ");
- }
- if (CatDecl->getIvarRBraceLoc().isValid())
- InsertText(CatDecl->getIvarRBraceLoc(), "// ");
+ }
for (ObjCCategoryDecl::prop_iterator I = CatDecl->prop_begin(),
E = CatDecl->prop_end(); I != E; ++I)
@@ -1221,17 +1306,13 @@ void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) {
ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID);
if (IMD) {
- InsertText(IMD->getLocStart(), "// ");
- if (IMD->getIvarLBraceLoc().isValid())
- InsertText(IMD->getIvarLBraceLoc(), "// ");
- for (ObjCImplementationDecl::ivar_iterator
- I = IMD->ivar_begin(), E = IMD->ivar_end(); I != E; ++I) {
- ObjCIvarDecl *Ivar = (*I);
- SourceLocation LocStart = Ivar->getLocStart();
- ReplaceText(LocStart, 0, "// ");
+ if (IMD->getIvarRBraceLoc().isValid()) {
+ ReplaceText(IMD->getLocStart(), 1, "/** ");
+ ReplaceText(IMD->getIvarRBraceLoc(), 1, "**/ ");
+ }
+ else {
+ InsertText(IMD->getLocStart(), "// ");
}
- if (IMD->getIvarRBraceLoc().isValid())
- InsertText(IMD->getIvarRBraceLoc(), "// ");
}
else
InsertText(CID->getLocStart(), "// ");
@@ -1808,6 +1889,15 @@ void RewriteModernObjC::WarnAboutReturnGotoStmts(Stmt *S)
return;
}
+Stmt *RewriteModernObjC::RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
+ SourceLocation startLoc = S->getAtLoc();
+ ReplaceText(startLoc, strlen("@autoreleasepool"), "/* @autoreleasepool */");
+ ReplaceText(S->getSubStmt()->getLocStart(), 1,
+ "{ __AtAutoreleasePool __autoreleasepool; ");
+
+ return 0;
+}
+
Stmt *RewriteModernObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) {
ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt();
bool noCatch = S->getNumCatchStmts() == 0;
@@ -2245,6 +2335,32 @@ void RewriteModernObjC::RewriteBlockPointerTypeVariable(std::string& Str,
}
}
+void RewriteModernObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) {
+ SourceLocation FunLocStart = FD->getTypeSpecStartLoc();
+ const FunctionType *funcType = FD->getType()->getAs<FunctionType>();
+ const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(funcType);
+ if (!proto)
+ return;
+ QualType Type = proto->getResultType();
+ std::string FdStr = Type.getAsString(Context->getPrintingPolicy());
+ FdStr += " ";
+ FdStr += FD->getName();
+ FdStr += "(";
+ unsigned numArgs = proto->getNumArgs();
+ for (unsigned i = 0; i < numArgs; i++) {
+ QualType ArgType = proto->getArgType(i);
+ RewriteBlockPointerType(FdStr, ArgType);
+ if (i+1 < numArgs)
+ FdStr += ", ";
+ }
+ if (FD->isVariadic()) {
+ FdStr += (numArgs > 0) ? ", ...);\n" : "...);\n";
+ }
+ else
+ FdStr += ");\n";
+ InsertText(FunLocStart, FdStr);
+}
+
// SynthSuperContructorFunctionDecl - id __rw_objc_super(id obj, id super);
void RewriteModernObjC::SynthSuperContructorFunctionDecl() {
if (SuperContructorFunctionDecl)
@@ -2362,12 +2478,12 @@ void RewriteModernObjC::SynthMsgSendFpretFunctionDecl() {
SC_None, false);
}
-// SynthGetClassFunctionDecl - id objc_getClass(const char *name);
+// SynthGetClassFunctionDecl - Class objc_getClass(const char *name);
void RewriteModernObjC::SynthGetClassFunctionDecl() {
IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass");
SmallVector<QualType, 16> ArgTys;
ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst()));
- QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(),
+ QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(),
&ArgTys[0], ArgTys.size());
GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl,
SourceLocation(),
@@ -2395,12 +2511,12 @@ void RewriteModernObjC::SynthGetSuperClassFunctionDecl() {
false);
}
-// SynthGetMetaClassFunctionDecl - id objc_getMetaClass(const char *name);
+// SynthGetMetaClassFunctionDecl - Class objc_getMetaClass(const char *name);
void RewriteModernObjC::SynthGetMetaClassFunctionDecl() {
IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass");
SmallVector<QualType, 16> ArgTys;
ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst()));
- QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(),
+ QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(),
&ArgTys[0], ArgTys.size());
GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl,
SourceLocation(),
@@ -2471,7 +2587,7 @@ Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) {
return PE;
}
-Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) {
+Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) {
// synthesize declaration of helper functions needed in this routine.
if (!SelGetUidFunctionDecl)
SynthSelGetUidFunctionDecl();
@@ -2489,13 +2605,12 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp)
SmallVector<Expr*, 4> MsgExprs;
SmallVector<Expr*, 4> ClsExprs;
QualType argType = Context->getPointerType(Context->CharTy);
- QualType expType = Exp->getType();
- // Create a call to objc_getClass("NSNumber"). It will be th 1st argument.
- ObjCInterfaceDecl *Class =
- expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface();
+ // Create a call to objc_getClass("<BoxingClass>"). It will be the 1st argument.
+ ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod();
+ ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface();
- IdentifierInfo *clsName = Class->getIdentifier();
+ IdentifierInfo *clsName = BoxingClass->getIdentifier();
ClsExprs.push_back(StringLiteral::Create(*Context,
clsName->getName(),
StringLiteral::Ascii, false,
@@ -2506,12 +2621,11 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp)
StartLoc, EndLoc);
MsgExprs.push_back(Cls);
- // Create a call to sel_registerName("numberWithBool:"), etc.
+ // Create a call to sel_registerName("<BoxingMethod>:"), etc.
// it will be the 2nd argument.
SmallVector<Expr*, 4> SelExprs;
- ObjCMethodDecl *NumericMethod = Exp->getObjCNumericLiteralMethod();
SelExprs.push_back(StringLiteral::Create(*Context,
- NumericMethod->getSelector().getAsString(),
+ BoxingMethod->getSelector().getAsString(),
StringLiteral::Ascii, false,
argType, SourceLocation()));
CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl,
@@ -2519,25 +2633,25 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp)
StartLoc, EndLoc);
MsgExprs.push_back(SelExp);
- // User provided numeric literal is the 3rd, and last, argument.
- Expr *userExpr = Exp->getNumber();
- if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) {
+ // User provided sub-expression is the 3rd, and last, argument.
+ Expr *subExpr = Exp->getSubExpr();
+ if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(subExpr)) {
QualType type = ICE->getType();
const Expr *SubExpr = ICE->IgnoreParenImpCasts();
CastKind CK = CK_BitCast;
if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType())
CK = CK_IntegralToBoolean;
- userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr);
+ subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr);
}
- MsgExprs.push_back(userExpr);
+ MsgExprs.push_back(subExpr);
SmallVector<QualType, 4> ArgTypes;
ArgTypes.push_back(Context->getObjCIdType());
ArgTypes.push_back(Context->getObjCSelType());
- for (ObjCMethodDecl::param_iterator PI = NumericMethod->param_begin(),
- E = NumericMethod->param_end(); PI != E; ++PI)
+ for (ObjCMethodDecl::param_iterator PI = BoxingMethod->param_begin(),
+ E = BoxingMethod->param_end(); PI != E; ++PI)
ArgTypes.push_back((*PI)->getType());
-
+
QualType returnType = Exp->getType();
// Get the type, we will need to reference it in a couple spots.
QualType msgSendType = MsgSendFlavor->getType();
@@ -2547,13 +2661,13 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp)
VK_LValue, SourceLocation());
CastExpr *cast = NoTypeInfoCStyleCastExpr(Context,
- Context->getPointerType(Context->VoidTy),
- CK_BitCast, DRE);
+ Context->getPointerType(Context->VoidTy),
+ CK_BitCast, DRE);
// Now do the "normal" pointer to function cast.
QualType castType =
- getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(),
- NumericMethod->isVariadic());
+ getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(),
+ BoxingMethod->isVariadic());
castType = Context->getPointerType(castType);
cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast,
cast);
@@ -2613,7 +2727,7 @@ Stmt *RewriteModernObjC::RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp) {
&Context->Idents.get("arr"),
Context->getPointerType(Context->VoidPtrTy), 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
MemberExpr *ArrayLiteralME =
new (Context) MemberExpr(NSArrayCallExpr, false, ARRFD,
SourceLocation(),
@@ -2760,7 +2874,7 @@ Stmt *RewriteModernObjC::RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral
&Context->Idents.get("arr"),
Context->getPointerType(Context->VoidPtrTy), 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
MemberExpr *DictLiteralValueME =
new (Context) MemberExpr(NSValueCallExpr, false, ARRFD,
SourceLocation(),
@@ -2907,7 +3021,7 @@ QualType RewriteModernObjC::getSuperStructType() {
FieldTypes[i], 0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false));
+ ICIS_NoInit));
}
SuperStructDecl->completeDefinition();
@@ -2940,7 +3054,7 @@ QualType RewriteModernObjC::getConstantStringStructType() {
FieldTypes[i], 0,
/*BitWidth=*/0,
/*Mutable=*/true,
- /*HasInit=*/false));
+ ICIS_NoInit));
}
ConstantStringDecl->completeDefinition();
@@ -2948,6 +3062,112 @@ QualType RewriteModernObjC::getConstantStringStructType() {
return Context->getTagDeclType(ConstantStringDecl);
}
+/// getFunctionSourceLocation - returns start location of a function
+/// definition. Complication arises when function has declared as
+/// extern "C" or extern "C" {...}
+static SourceLocation getFunctionSourceLocation (RewriteModernObjC &R,
+ FunctionDecl *FD) {
+ if (FD->isExternC() && !FD->isMain()) {
+ const DeclContext *DC = FD->getDeclContext();
+ if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC))
+ // if it is extern "C" {...}, return function decl's own location.
+ if (!LSD->getRBraceLoc().isValid())
+ return LSD->getExternLoc();
+ }
+ if (FD->getStorageClassAsWritten() != SC_None)
+ R.RewriteBlockLiteralFunctionDecl(FD);
+ return FD->getTypeSpecStartLoc();
+}
+
+/// SynthMsgSendStretCallExpr - This routine translates message expression
+/// into a call to objc_msgSend_stret() entry point. Tricky part is that
+/// nil check on receiver must be performed before calling objc_msgSend_stret.
+/// MsgSendStretFlavor - function declaration objc_msgSend_stret(...)
+/// msgSendType - function type of objc_msgSend_stret(...)
+/// returnType - Result type of the method being synthesized.
+/// ArgTypes - type of the arguments passed to objc_msgSend_stret, starting with receiver type.
+/// MsgExprs - list of argument expressions being passed to objc_msgSend_stret,
+/// starting with receiver.
+/// Method - Method being rewritten.
+Expr *RewriteModernObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor,
+ QualType msgSendType,
+ QualType returnType,
+ SmallVectorImpl<QualType> &ArgTypes,
+ SmallVectorImpl<Expr*> &MsgExprs,
+ ObjCMethodDecl *Method) {
+ // Now do the "normal" pointer to function cast.
+ QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(),
+ Method ? Method->isVariadic() : false);
+ castType = Context->getPointerType(castType);
+
+ // build type for containing the objc_msgSend_stret object.
+ static unsigned stretCount=0;
+ std::string name = "__Stret"; name += utostr(stretCount);
+ std::string str =
+ "extern \"C\" void * __cdecl memset(void *_Dst, int _Val, size_t _Size);\n";
+ str += "struct "; str += name;
+ str += " {\n\t";
+ str += name;
+ str += "(id receiver, SEL sel";
+ for (unsigned i = 2; i < ArgTypes.size(); i++) {
+ std::string ArgName = "arg"; ArgName += utostr(i);
+ ArgTypes[i].getAsStringInternal(ArgName, Context->getPrintingPolicy());
+ str += ", "; str += ArgName;
+ }
+ // could be vararg.
+ for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) {
+ std::string ArgName = "arg"; ArgName += utostr(i);
+ MsgExprs[i]->getType().getAsStringInternal(ArgName,
+ Context->getPrintingPolicy());
+ str += ", "; str += ArgName;
+ }
+
+ str += ") {\n";
+ str += "\t if (receiver == 0)\n";
+ str += "\t memset((void*)&s, 0, sizeof(s));\n";
+ str += "\t else\n";
+ str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy());
+ str += ")(void *)objc_msgSend_stret)(receiver, sel";
+ for (unsigned i = 2; i < ArgTypes.size(); i++) {
+ str += ", arg"; str += utostr(i);
+ }
+ // could be vararg.
+ for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) {
+ str += ", arg"; str += utostr(i);
+ }
+
+ str += ");\n";
+ str += "\t}\n";
+ str += "\t"; str += returnType.getAsString(Context->getPrintingPolicy());
+ str += " s;\n";
+ str += "};\n\n";
+ SourceLocation FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef);
+ InsertText(FunLocStart, str);
+ ++stretCount;
+
+ // AST for __Stretn(receiver, args).s;
+ IdentifierInfo *ID = &Context->Idents.get(name);
+ FunctionDecl *FD = FunctionDecl::Create(*Context, TUDecl, SourceLocation(),
+ SourceLocation(), ID, castType, 0, SC_Extern,
+ SC_None, false, false);
+ DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, castType, VK_RValue,
+ SourceLocation());
+ CallExpr *STCE = new (Context) CallExpr(*Context, DRE, &MsgExprs[0], MsgExprs.size(),
+ castType, VK_LValue, SourceLocation());
+
+ FieldDecl *FieldD = FieldDecl::Create(*Context, 0, SourceLocation(),
+ SourceLocation(),
+ &Context->Idents.get("s"),
+ returnType, 0,
+ /*BitWidth=*/0, /*Mutable=*/true,
+ ICIS_NoInit);
+ MemberExpr *ME = new (Context) MemberExpr(STCE, false, FieldD, SourceLocation(),
+ FieldD->getType(), VK_LValue,
+ OK_Ordinary);
+
+ return ME;
+}
+
Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SourceLocation StartLoc,
SourceLocation EndLoc) {
@@ -3013,17 +3233,14 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
ClassDecl->getIdentifier()->getName(),
StringLiteral::Ascii, false,
argType, SourceLocation()));
+ // (Class)objc_getClass("CurrentClass")
CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl,
&ClsExprs[0],
ClsExprs.size(),
StartLoc,
EndLoc);
- // (Class)objc_getClass("CurrentClass")
- CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context,
- Context->getObjCClassType(),
- CK_BitCast, Cls);
ClsExprs.clear();
- ClsExprs.push_back(ArgExpr);
+ ClsExprs.push_back(Cls);
Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl,
&ClsExprs[0], ClsExprs.size(),
StartLoc, EndLoc);
@@ -3096,7 +3313,10 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
&ClsExprs[0],
ClsExprs.size(),
StartLoc, EndLoc);
- MsgExprs.push_back(Cls);
+ CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context,
+ Context->getObjCIdType(),
+ CK_BitCast, Cls);
+ MsgExprs.push_back(ArgExpr);
break;
}
@@ -3124,16 +3344,13 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
ClassDecl->getIdentifier()->getName(),
StringLiteral::Ascii, false, argType,
SourceLocation()));
+ // (Class)objc_getClass("CurrentClass")
CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl,
&ClsExprs[0],
ClsExprs.size(),
StartLoc, EndLoc);
- // (Class)objc_getClass("CurrentClass")
- CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context,
- Context->getObjCClassType(),
- CK_BitCast, Cls);
ClsExprs.clear();
- ClsExprs.push_back(ArgExpr);
+ ClsExprs.push_back(Cls);
Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl,
&ClsExprs[0], ClsExprs.size(),
StartLoc, EndLoc);
@@ -3339,29 +3556,10 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
// expression which dictate which one to envoke depending on size of
// method's return type.
- // Create a reference to the objc_msgSend_stret() declaration.
- DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor,
- false, msgSendType,
- VK_LValue, SourceLocation());
- // Need to cast objc_msgSend_stret to "void *" (see above comment).
- cast = NoTypeInfoCStyleCastExpr(Context,
- Context->getPointerType(Context->VoidTy),
- CK_BitCast, STDRE);
- // Now do the "normal" pointer to function cast.
- castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(),
- Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : false);
- castType = Context->getPointerType(castType);
- cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast,
- cast);
-
- // Don't forget the parens to enforce the proper binding.
- PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast);
-
- FT = msgSendType->getAs<FunctionType>();
- CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0],
- MsgExprs.size(),
- FT->getResultType(), VK_RValue,
- SourceLocation());
+ Expr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor,
+ msgSendType, returnType,
+ ArgTypes, MsgExprs,
+ Exp->getMethodDecl());
// Build sizeof(returnType)
UnaryExprOrTypeTraitExpr *sizeofExpr =
@@ -3471,10 +3669,44 @@ bool RewriteModernObjC::BufferContainsPPDirectives(const char *startBuf,
return false;
}
+/// IsTagDefinedInsideClass - This routine checks that a named tagged type
+/// is defined inside an objective-c class. If so, it returns true.
+bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCContainerDecl *IDecl,
+ TagDecl *Tag,
+ bool &IsNamedDefinition) {
+ if (!IDecl)
+ return false;
+ SourceLocation TagLocation;
+ if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) {
+ RD = RD->getDefinition();
+ if (!RD || !RD->getDeclName().getAsIdentifierInfo())
+ return false;
+ IsNamedDefinition = true;
+ TagLocation = RD->getLocation();
+ return Context->getSourceManager().isBeforeInTranslationUnit(
+ IDecl->getLocation(), TagLocation);
+ }
+ if (EnumDecl *ED = dyn_cast<EnumDecl>(Tag)) {
+ if (!ED || !ED->getDeclName().getAsIdentifierInfo())
+ return false;
+ IsNamedDefinition = true;
+ TagLocation = ED->getLocation();
+ return Context->getSourceManager().isBeforeInTranslationUnit(
+ IDecl->getLocation(), TagLocation);
+
+ }
+ return false;
+}
+
/// RewriteObjCFieldDeclType - This routine rewrites a type into the buffer.
/// It handles elaborated types, as well as enum types in the process.
bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type,
std::string &Result) {
+ if (isa<TypedefType>(Type)) {
+ Result += "\t";
+ return false;
+ }
+
if (Type->isArrayType()) {
QualType ElemTy = Context->getBaseElementType(Type);
return RewriteObjCFieldDeclType(ElemTy, Result);
@@ -3490,12 +3722,11 @@ bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type,
assert(false && "class not allowed as an ivar type");
Result += RD->getName();
- if (TagsDefinedInIvarDecls.count(RD)) {
- // This struct is already defined. Do not write its definition again.
+ if (GlobalDefinedTags.count(RD)) {
+ // struct/union is defined globally, use it.
Result += " ";
return true;
}
- TagsDefinedInIvarDecls.insert(RD);
Result += " {\n";
for (RecordDecl::field_iterator i = RD->field_begin(),
e = RD->field_end(); i != e; ++i) {
@@ -3511,12 +3742,11 @@ bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type,
if (ED->isCompleteDefinition()) {
Result += "\n\tenum ";
Result += ED->getName();
- if (TagsDefinedInIvarDecls.count(ED)) {
- // This enum is already defined. Do not write its definition again.
+ if (GlobalDefinedTags.count(ED)) {
+ // Enum is globall defined, use it.
Result += " ";
return true;
}
- TagsDefinedInIvarDecls.insert(ED);
Result += " {\n";
for (EnumDecl::enumerator_iterator EC = ED->enumerator_begin(),
@@ -3567,6 +3797,41 @@ void RewriteModernObjC::RewriteObjCFieldDecl(FieldDecl *fieldDecl,
Result += ";\n";
}
+/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined
+/// named aggregate types into the input buffer.
+void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl,
+ std::string &Result) {
+ QualType Type = fieldDecl->getType();
+ if (isa<TypedefType>(Type))
+ return;
+ if (Type->isArrayType())
+ Type = Context->getBaseElementType(Type);
+ ObjCContainerDecl *IDecl =
+ dyn_cast<ObjCContainerDecl>(fieldDecl->getDeclContext());
+
+ TagDecl *TD = 0;
+ if (Type->isRecordType()) {
+ TD = Type->getAs<RecordType>()->getDecl();
+ }
+ else if (Type->isEnumeralType()) {
+ TD = Type->getAs<EnumType>()->getDecl();
+ }
+
+ if (TD) {
+ if (GlobalDefinedTags.count(TD))
+ return;
+
+ bool IsNamedDefinition = false;
+ if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) {
+ RewriteObjCFieldDeclType(Type, Result);
+ Result += ";";
+ }
+ if (IsNamedDefinition)
+ GlobalDefinedTags.insert(TD);
+ }
+
+}
+
/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to
/// an objective-c class with ivars.
void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl,
@@ -3595,6 +3860,12 @@ void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl,
return;
}
+ // Insert named struct/union definitions inside class to
+ // outer scope. This follows semantics of locally defined
+ // struct/unions in objective-c classes.
+ for (unsigned i = 0, e = IVars.size(); i < e; i++)
+ RewriteLocallyDefinedNamedAggregates(IVars[i], Result);
+
Result += "\nstruct ";
Result += CDecl->getNameAsString();
Result += "_IMPL {\n";
@@ -3604,7 +3875,7 @@ void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl,
Result += "_IMPL "; Result += RCDecl->getNameAsString();
Result += "_IVARS;\n";
}
- TagsDefinedInIvarDecls.clear();
+
for (unsigned i = 0, e = IVars.size(); i < e; i++)
RewriteObjCFieldDecl(IVars[i], Result);
@@ -3616,14 +3887,6 @@ void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl,
llvm_unreachable("struct already synthesize- RewriteObjCInternalStruct");
}
-static void WriteInternalIvarName(ObjCInterfaceDecl *IDecl,
- ObjCIvarDecl *IvarDecl, std::string &Result) {
- Result += "OBJC_IVAR_$_";
- Result += IDecl->getName();
- Result += "$";
- Result += IvarDecl->getName();
-}
-
/// RewriteIvarOffsetSymbols - Rewrite ivar offset symbols of those ivars which
/// have been referenced in an ivar access expression.
void RewriteModernObjC::RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl,
@@ -3961,8 +4224,8 @@ std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag,
unsigned hasCopy) {
std::string S = "\nstatic struct " + DescTag;
- S += " {\n unsigned long reserved;\n";
- S += " unsigned long Block_size;\n";
+ S += " {\n size_t reserved;\n";
+ S += " size_t Block_size;\n";
if (hasCopy) {
S += " void (*copy)(struct ";
S += ImplTag; S += "*, struct ";
@@ -3983,23 +4246,6 @@ std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag,
return S;
}
-/// getFunctionSourceLocation - returns start location of a function
-/// definition. Complication arises when function has declared as
-/// extern "C" or extern "C" {...}
-static SourceLocation getFunctionSourceLocation (FunctionDecl *FD) {
- if (!FD->isExternC() || FD->isMain())
- return FD->getTypeSpecStartLoc();
- const DeclContext *DC = FD->getDeclContext();
- if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) {
- SourceLocation BodyRBrace = LSD->getRBraceLoc();
- // if it is extern "C" {...}, return function decl's own location.
- if (BodyRBrace.isValid())
- return FD->getTypeSpecStartLoc();
- return LSD->getExternLoc();
- }
- return FD->getTypeSpecStartLoc();
-}
-
void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart,
StringRef FunName) {
bool RewriteSC = (GlobalVarDecl &&
@@ -4110,7 +4356,9 @@ void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart,
}
void RewriteModernObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) {
- SourceLocation FunLocStart = getFunctionSourceLocation(FD);
+ SourceLocation FunLocStart =
+ (!Blocks.empty()) ? getFunctionSourceLocation(*this, FD)
+ : FD->getTypeSpecStartLoc();
StringRef FuncName = FD->getName();
SynthesizeBlockLiterals(FunLocStart, FuncName);
@@ -4320,7 +4568,7 @@ Stmt *RewriteModernObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp
&Context->Idents.get("FuncPtr"),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(),
FD->getType(), VK_LValue,
OK_Ordinary);
@@ -4369,7 +4617,7 @@ Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) {
&Context->Idents.get("__forwarding"),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow,
FD, SourceLocation(),
FD->getType(), VK_LValue,
@@ -4380,7 +4628,7 @@ Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) {
&Context->Idents.get(Name),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(),
DeclRefExp->getType(), VK_LValue, OK_Ordinary);
@@ -4719,7 +4967,8 @@ std::string RewriteModernObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD,
/// ND=initializer-if-any};
///
///
-void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) {
+void RewriteModernObjC::RewriteByRefVar(VarDecl *ND, bool firstDecl,
+ bool lastDecl) {
int flag = 0;
int isa = 0;
SourceLocation DeclLoc = ND->getTypeSpecStartLoc();
@@ -4758,17 +5007,17 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) {
// Insert this type in global scope. It is needed by helper function.
SourceLocation FunLocStart;
if (CurFunctionDef)
- FunLocStart = getFunctionSourceLocation(CurFunctionDef);
+ FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef);
else {
assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null");
FunLocStart = CurMethodDef->getLocStart();
}
InsertText(FunLocStart, ByrefType);
+
if (Ty.isObjCGCWeak()) {
flag |= BLOCK_FIELD_IS_WEAK;
isa = 1;
}
-
if (HasCopyAndDispose) {
flag = BLOCK_BYREF_CALLER;
QualType Ty = ND->getType();
@@ -4788,8 +5037,13 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) {
bool hasInit = (ND->getInit() != 0);
// FIXME. rewriter does not support __block c++ objects which
// require construction.
- if (hasInit && dyn_cast<CXXConstructExpr>(ND->getInit()))
- hasInit = false;
+ if (hasInit)
+ if (CXXConstructExpr *CExp = dyn_cast<CXXConstructExpr>(ND->getInit())) {
+ CXXConstructorDecl *CXXDecl = CExp->getConstructor();
+ if (CXXDecl && CXXDecl->isDefaultConstructor())
+ hasInit = false;
+ }
+
unsigned flags = 0;
if (HasCopyAndDispose)
flags |= BLOCK_HAS_COPY_DISPOSE;
@@ -4798,21 +5052,36 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) {
RewriteByRefString(ByrefType, Name, ND);
std::string ForwardingCastType("(");
ForwardingCastType += ByrefType + " *)";
+ ByrefType += " " + Name + " = {(void*)";
+ ByrefType += utostr(isa);
+ ByrefType += "," + ForwardingCastType + "&" + Name + ", ";
+ ByrefType += utostr(flags);
+ ByrefType += ", ";
+ ByrefType += "sizeof(";
+ RewriteByRefString(ByrefType, Name, ND);
+ ByrefType += ")";
+ if (HasCopyAndDispose) {
+ ByrefType += ", __Block_byref_id_object_copy_";
+ ByrefType += utostr(flag);
+ ByrefType += ", __Block_byref_id_object_dispose_";
+ ByrefType += utostr(flag);
+ }
+
+ if (!firstDecl) {
+ // In multiple __block declarations, and for all but 1st declaration,
+ // find location of the separating comma. This would be start location
+ // where new text is to be inserted.
+ DeclLoc = ND->getLocation();
+ const char *startDeclBuf = SM->getCharacterData(DeclLoc);
+ const char *commaBuf = startDeclBuf;
+ while (*commaBuf != ',')
+ commaBuf--;
+ assert((*commaBuf == ',') && "RewriteByRefVar: can't find ','");
+ DeclLoc = DeclLoc.getLocWithOffset(commaBuf - startDeclBuf);
+ startBuf = commaBuf;
+ }
+
if (!hasInit) {
- ByrefType += " " + Name + " = {(void*)";
- ByrefType += utostr(isa);
- ByrefType += "," + ForwardingCastType + "&" + Name + ", ";
- ByrefType += utostr(flags);
- ByrefType += ", ";
- ByrefType += "sizeof(";
- RewriteByRefString(ByrefType, Name, ND);
- ByrefType += ")";
- if (HasCopyAndDispose) {
- ByrefType += ", __Block_byref_id_object_copy_";
- ByrefType += utostr(flag);
- ByrefType += ", __Block_byref_id_object_dispose_";
- ByrefType += utostr(flag);
- }
ByrefType += "};\n";
unsigned nameSize = Name.size();
// for block or function pointer declaration. Name is aleady
@@ -4822,6 +5091,7 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) {
ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType);
}
else {
+ ByrefType += ", ";
SourceLocation startLoc;
Expr *E = ND->getInit();
if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E))
@@ -4830,39 +5100,17 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) {
startLoc = E->getLocStart();
startLoc = SM->getExpansionLoc(startLoc);
endBuf = SM->getCharacterData(startLoc);
- ByrefType += " " + Name;
- ByrefType += " = {(void*)";
- ByrefType += utostr(isa);
- ByrefType += "," + ForwardingCastType + "&" + Name + ", ";
- ByrefType += utostr(flags);
- ByrefType += ", ";
- ByrefType += "sizeof(";
- RewriteByRefString(ByrefType, Name, ND);
- ByrefType += "), ";
- if (HasCopyAndDispose) {
- ByrefType += "__Block_byref_id_object_copy_";
- ByrefType += utostr(flag);
- ByrefType += ", __Block_byref_id_object_dispose_";
- ByrefType += utostr(flag);
- ByrefType += ", ";
- }
ReplaceText(DeclLoc, endBuf-startBuf, ByrefType);
-
- // Complete the newly synthesized compound expression by inserting a right
- // curly brace before the end of the declaration.
- // FIXME: This approach avoids rewriting the initializer expression. It
- // also assumes there is only one declarator. For example, the following
- // isn't currently supported by this routine (in general):
- //
- // double __block BYREFVAR = 1.34, BYREFVAR2 = 1.37;
- //
- const char *startInitializerBuf = SM->getCharacterData(startLoc);
- const char *semiBuf = strchr(startInitializerBuf, ';');
- assert((*semiBuf == ';') && "RewriteByRefVar: can't find ';'");
- SourceLocation semiLoc =
- startLoc.getLocWithOffset(semiBuf-startInitializerBuf);
- InsertText(semiLoc, "}");
+ const char separator = lastDecl ? ';' : ',';
+ const char *startInitializerBuf = SM->getCharacterData(startLoc);
+ const char *separatorBuf = strchr(startInitializerBuf, separator);
+ assert((*separatorBuf == separator) &&
+ "RewriteByRefVar: can't find ';' or ','");
+ SourceLocation separatorLoc =
+ startLoc.getLocWithOffset(separatorBuf-startInitializerBuf);
+
+ InsertText(separatorLoc, lastDecl ? "}" : "};\n");
}
return;
}
@@ -5214,8 +5462,8 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast<ObjCBoolLiteralExpr>(S))
return RewriteObjCBoolLiteralExpr(BoolLitExpr);
- if (ObjCNumericLiteral *NumericLitExpr = dyn_cast<ObjCNumericLiteral>(S))
- return RewriteObjCNumericLiteralExpr(NumericLitExpr);
+ if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(S))
+ return RewriteObjCBoxedExpr(BoxedExpr);
if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast<ObjCArrayLiteral>(S))
return RewriteObjCArrayLiteralExpr(ArrayLitExpr);
@@ -5247,6 +5495,11 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
return RewriteMessageExpr(MessExpr);
}
+ if (ObjCAutoreleasePoolStmt *StmtAutoRelease =
+ dyn_cast<ObjCAutoreleasePoolStmt>(S)) {
+ return RewriteObjCAutoreleasePoolStmt(StmtAutoRelease);
+ }
+
if (ObjCAtTryStmt *StmtTry = dyn_cast<ObjCAtTryStmt>(S))
return RewriteObjCTryStmt(StmtTry);
@@ -5300,7 +5553,7 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
assert(!BlockByRefDeclNo.count(ND) &&
"RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl");
BlockByRefDeclNo[ND] = uniqueByrefDeclCount++;
- RewriteByRefVar(VD);
+ RewriteByRefVar(VD, (DI == DS->decl_begin()), ((DI+1) == DE));
}
else
RewriteTypeOfDecl(VD);
@@ -5402,7 +5655,6 @@ void RewriteModernObjC::HandleDeclInMainFile(Decl *D) {
// FIXME: If this should support Obj-C++, support CXXTryStmt
if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) {
CurFunctionDef = FD;
- CurFunctionDeclToDeclareForBlock = FD;
CurrentBody = Body;
Body =
cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body));
@@ -5416,7 +5668,6 @@ void RewriteModernObjC::HandleDeclInMainFile(Decl *D) {
// and any copy/dispose helper functions.
InsertBlockLiteralsWithinFunction(FD);
CurFunctionDef = 0;
- CurFunctionDeclToDeclareForBlock = 0;
}
break;
}
@@ -5515,7 +5766,7 @@ static void Write_ProtocolExprReferencedMetadata(ASTContext *Context,
std::string &Result) {
// Also output .objc_protorefs$B section and its meta-data.
if (Context->getLangOpts().MicrosoftExt)
- Result += "__declspec(allocate(\".objc_protorefs$B\")) ";
+ Result += "static ";
Result += "struct _protocol_t *";
Result += "_OBJC_PROTOCOL_REFERENCE_$_";
Result += PDecl->getNameAsString();
@@ -5539,6 +5790,10 @@ void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) {
}
InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false);
+
+ if (ClassImplementation.size() || CategoryImplementation.size())
+ RewriteImplementations();
+
for (unsigned i = 0, e = ObjCInterfacesSeen.size(); i < e; i++) {
ObjCInterfaceDecl *CDecl = ObjCInterfacesSeen[i];
// Write struct declaration for the class matching its ivar declarations.
@@ -5547,9 +5802,6 @@ void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) {
// private ivars.
RewriteInterfaceDecl(CDecl);
}
-
- if (ClassImplementation.size() || CategoryImplementation.size())
- RewriteImplementations();
// Get the buffer corresponding to MainFileID. If we haven't changed it, then
// we are done.
@@ -5605,7 +5857,6 @@ void RewriteModernObjC::Initialize(ASTContext &context) {
Preamble += "#pragma section(\".objc_imageinfo$B\", long, read, write)\n";
Preamble += "#pragma section(\".objc_nlclslist$B\", long, read, write)\n";
Preamble += "#pragma section(\".objc_nlcatlist$B\", long, read, write)\n";
- Preamble += "#pragma section(\".objc_protorefs$B\", long, read, write)\n";
// These are generated but not necessary for functionality.
Preamble += "#pragma section(\".cat_cls_meth$B\", long, read, write)\n";
Preamble += "#pragma section(\".inst_meth$B\", long, read, write)\n";
@@ -5636,11 +5887,11 @@ void RewriteModernObjC::Initialize(ASTContext &context) {
Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);\n";
Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);\n";
- Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass";
+ Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass";
Preamble += "(const char *);\n";
Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass";
Preamble += "(struct objc_class *);\n";
- Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass";
+ Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass";
Preamble += "(const char *);\n";
Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);\n";
// @synchronized hooks.
@@ -5723,11 +5974,20 @@ void RewriteModernObjC::Initialize(ASTContext &context) {
Preamble += "\t arr[i] = va_arg(marker, void *);\n";
Preamble += "\tva_end( marker );\n";
Preamble += " };\n";
- Preamble += " __NSContainer_literal() {\n";
+ Preamble += " ~__NSContainer_literal() {\n";
Preamble += "\tdelete[] arr;\n";
Preamble += " }\n";
Preamble += "};\n";
+ // Declaration required for implementation of @autoreleasepool statement.
+ Preamble += "extern \"C\" __declspec(dllimport) void * objc_autoreleasePoolPush(void);\n";
+ Preamble += "extern \"C\" __declspec(dllimport) void objc_autoreleasePoolPop(void *);\n\n";
+ Preamble += "struct __AtAutoreleasePool {\n";
+ Preamble += " __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}\n";
+ Preamble += " ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}\n";
+ Preamble += " void * atautoreleasepoolobj;\n";
+ Preamble += "};\n";
+
// NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long
// as this avoids warning in any 64bit/32bit compilation model.
Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n";
@@ -6738,20 +6998,20 @@ void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl,
for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(),
PropEnd = IDecl->propimpl_end();
Prop != PropEnd; ++Prop) {
- if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
+ if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
continue;
- if (!(*Prop)->getPropertyIvarDecl())
+ if (!Prop->getPropertyIvarDecl())
continue;
- ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl();
+ ObjCPropertyDecl *PD = Prop->getPropertyDecl();
if (!PD)
continue;
if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl())
- if (!Getter->isDefined())
+ if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/))
InstanceMethods.push_back(Getter);
if (PD->isReadOnly())
continue;
if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl())
- if (!Setter->isDefined())
+ if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/))
InstanceMethods.push_back(Setter);
}
@@ -7002,11 +7262,11 @@ void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl,
for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(),
PropEnd = IDecl->propimpl_end();
Prop != PropEnd; ++Prop) {
- if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
+ if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
continue;
- if (!(*Prop)->getPropertyIvarDecl())
+ if (!Prop->getPropertyIvarDecl())
continue;
- ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl();
+ ObjCPropertyDecl *PD = Prop->getPropertyDecl();
if (!PD)
continue;
if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl())
@@ -7053,7 +7313,7 @@ void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl,
ClassProperties.push_back(*I);
Write_prop_list_t_initializer(*this, Context, Result, ClassProperties,
- /* Container */0,
+ /* Container */IDecl,
"_OBJC_$_PROP_LIST_",
FullCategoryName);
@@ -7189,7 +7449,7 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
if (BaseExpr->getType()->isObjCObjectPointerType()) {
const ObjCInterfaceType *iFaceDecl =
- dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType());
+ dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType());
assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null");
// lookup which class implements the instance variable.
ObjCInterfaceDecl *clsDeclared = 0;
@@ -7223,13 +7483,52 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
SourceLocation(),
addExpr);
QualType IvarT = D->getType();
+
+ if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) {
+ RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl();
+ RD = RD->getDefinition();
+ if (RD && !RD->getDeclName().getAsIdentifierInfo()) {
+ // decltype(((Foo_IMPL*)0)->bar) *
+ ObjCContainerDecl *CDecl =
+ dyn_cast<ObjCContainerDecl>(D->getDeclContext());
+ // ivar in class extensions requires special treatment.
+ if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
+ CDecl = CatDecl->getClassInterface();
+ std::string RecName = CDecl->getName();
+ RecName += "_IMPL";
+ RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl,
+ SourceLocation(), SourceLocation(),
+ &Context->Idents.get(RecName.c_str()));
+ QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD));
+ unsigned UnsignedIntSize =
+ static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy));
+ Expr *Zero = IntegerLiteral::Create(*Context,
+ llvm::APInt(UnsignedIntSize, 0),
+ Context->UnsignedIntTy, SourceLocation());
+ Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero);
+ ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(),
+ Zero);
+ FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(),
+ SourceLocation(),
+ &Context->Idents.get(D->getNameAsString()),
+ IvarT, 0,
+ /*BitWidth=*/0, /*Mutable=*/true,
+ ICIS_NoInit);
+ MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(),
+ FD->getType(), VK_LValue,
+ OK_Ordinary);
+ IvarT = Context->getDecltypeType(ME, ME->getType());
+ }
+ }
convertObjCTypeToCStyleType(IvarT);
QualType castT = Context->getPointerType(IvarT);
-
+
castExpr = NoTypeInfoCStyleCastExpr(Context,
castT,
CK_BitCast,
PE);
+
+
Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT,
VK_LValue, OK_Ordinary,
SourceLocation());
diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp
index 9c0737f..425cd77 100644
--- a/lib/Rewrite/RewriteObjC.cpp
+++ b/lib/Rewrite/RewriteObjC.cpp
@@ -349,13 +349,18 @@ namespace {
virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar,
std::string &Result) = 0;
- // Misc. AST transformation routines. Somtimes they end up calling
+ // Misc. AST transformation routines. Sometimes they end up calling
// rewriting routines on the new ASTs.
CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD,
Expr **args, unsigned nargs,
SourceLocation StartLoc=SourceLocation(),
SourceLocation EndLoc=SourceLocation());
-
+ CallExpr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor,
+ QualType msgSendType,
+ QualType returnType,
+ SmallVectorImpl<QualType> &ArgTypes,
+ SmallVectorImpl<Expr*> &MsgExprs,
+ ObjCMethodDecl *Method);
Stmt *SynthMessageExpr(ObjCMessageExpr *Exp,
SourceLocation StartLoc=SourceLocation(),
SourceLocation EndLoc=SourceLocation());
@@ -2592,7 +2597,7 @@ QualType RewriteObjC::getSuperStructType() {
FieldTypes[i], 0,
/*BitWidth=*/0,
/*Mutable=*/false,
- /*HasInit=*/false));
+ ICIS_NoInit));
}
SuperStructDecl->completeDefinition();
@@ -2625,7 +2630,7 @@ QualType RewriteObjC::getConstantStringStructType() {
FieldTypes[i], 0,
/*BitWidth=*/0,
/*Mutable=*/true,
- /*HasInit=*/false));
+ ICIS_NoInit));
}
ConstantStringDecl->completeDefinition();
@@ -2633,6 +2638,40 @@ QualType RewriteObjC::getConstantStringStructType() {
return Context->getTagDeclType(ConstantStringDecl);
}
+CallExpr *RewriteObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor,
+ QualType msgSendType,
+ QualType returnType,
+ SmallVectorImpl<QualType> &ArgTypes,
+ SmallVectorImpl<Expr*> &MsgExprs,
+ ObjCMethodDecl *Method) {
+ // Create a reference to the objc_msgSend_stret() declaration.
+ DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor,
+ false, msgSendType,
+ VK_LValue, SourceLocation());
+ // Need to cast objc_msgSend_stret to "void *" (see above comment).
+ CastExpr *cast = NoTypeInfoCStyleCastExpr(Context,
+ Context->getPointerType(Context->VoidTy),
+ CK_BitCast, STDRE);
+ // Now do the "normal" pointer to function cast.
+ QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(),
+ Method ? Method->isVariadic() : false);
+ castType = Context->getPointerType(castType);
+ cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast,
+ cast);
+
+ // Don't forget the parens to enforce the proper binding.
+ ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast);
+
+ const FunctionType *FT = msgSendType->getAs<FunctionType>();
+ CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0],
+ MsgExprs.size(),
+ FT->getResultType(), VK_RValue,
+ SourceLocation());
+ return STCE;
+
+}
+
+
Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SourceLocation StartLoc,
SourceLocation EndLoc) {
@@ -3023,30 +3062,11 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
// call to objc_msgSend_stret and hang both varieties on a conditional
// expression which dictate which one to envoke depending on size of
// method's return type.
-
- // Create a reference to the objc_msgSend_stret() declaration.
- DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor,
- false, msgSendType,
- VK_LValue, SourceLocation());
- // Need to cast objc_msgSend_stret to "void *" (see above comment).
- cast = NoTypeInfoCStyleCastExpr(Context,
- Context->getPointerType(Context->VoidTy),
- CK_BitCast, STDRE);
- // Now do the "normal" pointer to function cast.
- castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(),
- Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : false);
- castType = Context->getPointerType(castType);
- cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast,
- cast);
-
- // Don't forget the parens to enforce the proper binding.
- PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast);
-
- FT = msgSendType->getAs<FunctionType>();
- CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0],
- MsgExprs.size(),
- FT->getResultType(), VK_RValue,
- SourceLocation());
+
+ CallExpr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor,
+ msgSendType, returnType,
+ ArgTypes, MsgExprs,
+ Exp->getMethodDecl());
// Build sizeof(returnType)
UnaryExprOrTypeTraitExpr *sizeofExpr =
@@ -3887,7 +3907,7 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) {
&Context->Idents.get("FuncPtr"),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(),
FD->getType(), VK_LValue,
OK_Ordinary);
@@ -3936,7 +3956,7 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) {
&Context->Idents.get("__forwarding"),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow,
FD, SourceLocation(),
FD->getType(), VK_LValue,
@@ -3947,7 +3967,7 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) {
&Context->Idents.get(Name),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true,
- /*HasInit=*/false);
+ ICIS_NoInit);
ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(),
DeclRefExp->getType(), VK_LValue, OK_Ordinary);
@@ -5442,10 +5462,10 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe
IVE = CDecl->ivar_end();
}
Result += "\t,{{\"";
- Result += (*IVI)->getNameAsString();
+ Result += IVI->getNameAsString();
Result += "\", \"";
std::string TmpString, StrEncoding;
- Context->getObjCEncodingForType((*IVI)->getType(), TmpString, *IVI);
+ Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI);
QuoteDoublequotes(TmpString, StrEncoding);
Result += StrEncoding;
Result += "\", ";
@@ -5453,14 +5473,14 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe
Result += "}\n";
for (++IVI; IVI != IVE; ++IVI) {
Result += "\t ,{\"";
- Result += (*IVI)->getNameAsString();
+ Result += IVI->getNameAsString();
Result += "\", \"";
std::string TmpString, StrEncoding;
- Context->getObjCEncodingForType((*IVI)->getType(), TmpString, *IVI);
+ Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI);
QuoteDoublequotes(TmpString, StrEncoding);
Result += StrEncoding;
Result += "\", ";
- RewriteIvarOffsetComputation((*IVI), Result);
+ RewriteIvarOffsetComputation(*IVI, Result);
Result += "}\n";
}
@@ -5476,11 +5496,11 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe
for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(),
PropEnd = IDecl->propimpl_end();
Prop != PropEnd; ++Prop) {
- if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
+ if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
continue;
- if (!(*Prop)->getPropertyIvarDecl())
+ if (!Prop->getPropertyIvarDecl())
continue;
- ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl();
+ ObjCPropertyDecl *PD = Prop->getPropertyDecl();
if (!PD)
continue;
if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl())
@@ -5761,11 +5781,11 @@ void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *ID
for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(),
PropEnd = IDecl->propimpl_end();
Prop != PropEnd; ++Prop) {
- if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
+ if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
continue;
- if (!(*Prop)->getPropertyIvarDecl())
+ if (!Prop->getPropertyIvarDecl())
continue;
- ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl();
+ ObjCPropertyDecl *PD = Prop->getPropertyDecl();
if (!PD)
continue;
if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl())
@@ -6015,4 +6035,3 @@ Stmt *RewriteObjCFragileABI::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
ReplaceStmtWithRange(IV, Replacement, OldRange);
return Replacement;
}
-
diff --git a/lib/Rewrite/Rewriter.cpp b/lib/Rewrite/Rewriter.cpp
index 43fb01b..7c27114 100644
--- a/lib/Rewrite/Rewriter.cpp
+++ b/lib/Rewrite/Rewriter.cpp
@@ -15,9 +15,12 @@
#include "clang/Rewrite/Rewriter.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Decl.h"
-#include "clang/Lex/Lexer.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
using namespace clang;
raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
@@ -27,7 +30,7 @@ raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
}
/// \brief Return true if this character is non-new-line whitespace:
-/// ' ', '\t', '\f', '\v', '\r'.
+/// ' ', '\\t', '\\f', '\\v', '\\r'.
static inline bool isWhitespace(unsigned char c) {
switch (c) {
case ' ':
@@ -412,3 +415,72 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range,
return false;
}
+
+// A wrapper for a file stream that atomically overwrites the target.
+//
+// Creates a file output stream for a temporary file in the constructor,
+// which is later accessible via getStream() if ok() return true.
+// Flushes the stream and moves the temporary file to the target location
+// in the destructor.
+class AtomicallyMovedFile {
+public:
+ AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
+ bool &AllWritten)
+ : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) {
+ TempFilename = Filename;
+ TempFilename += "-%%%%%%%%";
+ int FD;
+ if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename,
+ /*makeAbsolute=*/true, 0664)) {
+ AllWritten = false;
+ Diagnostics.Report(clang::diag::err_unable_to_make_temp)
+ << TempFilename;
+ } else {
+ FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true));
+ }
+ }
+
+ ~AtomicallyMovedFile() {
+ if (!ok()) return;
+
+ FileStream->flush();
+#ifdef _WIN32
+ // Win32 does not allow rename/removing opened files.
+ FileStream.reset();
+#endif
+ if (llvm::error_code ec =
+ llvm::sys::fs::rename(TempFilename.str(), Filename)) {
+ AllWritten = false;
+ Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
+ << TempFilename << Filename << ec.message();
+ bool existed;
+ // If the remove fails, there's not a lot we can do - this is already an
+ // error.
+ llvm::sys::fs::remove(TempFilename.str(), existed);
+ }
+ }
+
+ bool ok() { return FileStream; }
+ llvm::raw_ostream &getStream() { return *FileStream; }
+
+private:
+ DiagnosticsEngine &Diagnostics;
+ StringRef Filename;
+ SmallString<128> TempFilename;
+ OwningPtr<llvm::raw_fd_ostream> FileStream;
+ bool &AllWritten;
+};
+
+bool Rewriter::overwriteChangedFiles() {
+ bool AllWritten = true;
+ for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
+ const FileEntry *Entry =
+ getSourceMgr().getFileEntryForID(I->first);
+ AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(),
+ AllWritten);
+ if (File.ok()) {
+ I->second.write(File.getStream());
+ }
+ }
+ return !AllWritten;
+}
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index a8e6791..19a7d6f 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -19,6 +19,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Lexer.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprObjC.h"
@@ -27,6 +28,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
@@ -42,7 +44,9 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <algorithm>
+#include <iterator>
#include <vector>
+#include <deque>
using namespace clang;
@@ -185,6 +189,12 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
continue;
}
}
+ if (isa<MSAsmStmt>(S)) {
+ // TODO: Verify this is correct.
+ HasFakeEdge = true;
+ HasLiveReturn = true;
+ continue;
+ }
if (isa<CXXTryStmt>(S)) {
HasAbnormalEdge = true;
continue;
@@ -438,9 +448,14 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
return false;
// Suggest possible initialization (if any).
- const char *Init = S.getFixItZeroInitializerForType(VariableTy);
- if (!Init)
+ std::string Init = S.getFixItZeroInitializerForType(VariableTy);
+ if (Init.empty())
return false;
+
+ // Don't suggest a fixit inside macros.
+ if (VD->getLocEnd().isMacroID())
+ return false;
+
SourceLocation Loc = S.PP.getLocForEndOfToken(VD->getLocEnd());
S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName()
@@ -448,82 +463,428 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
return true;
}
+/// Create a fixit to remove an if-like statement, on the assumption that its
+/// condition is CondVal.
+static void CreateIfFixit(Sema &S, const Stmt *If, const Stmt *Then,
+ const Stmt *Else, bool CondVal,
+ FixItHint &Fixit1, FixItHint &Fixit2) {
+ if (CondVal) {
+ // If condition is always true, remove all but the 'then'.
+ Fixit1 = FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(If->getLocStart(),
+ Then->getLocStart()));
+ if (Else) {
+ SourceLocation ElseKwLoc = Lexer::getLocForEndOfToken(
+ Then->getLocEnd(), 0, S.getSourceManager(), S.getLangOpts());
+ Fixit2 = FixItHint::CreateRemoval(
+ SourceRange(ElseKwLoc, Else->getLocEnd()));
+ }
+ } else {
+ // If condition is always false, remove all but the 'else'.
+ if (Else)
+ Fixit1 = FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(If->getLocStart(),
+ Else->getLocStart()));
+ else
+ Fixit1 = FixItHint::CreateRemoval(If->getSourceRange());
+ }
+}
+
+/// DiagUninitUse -- Helper function to produce a diagnostic for an
+/// uninitialized use of a variable.
+static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
+ bool IsCapturedByBlock) {
+ bool Diagnosed = false;
+
+ // Diagnose each branch which leads to a sometimes-uninitialized use.
+ for (UninitUse::branch_iterator I = Use.branch_begin(), E = Use.branch_end();
+ I != E; ++I) {
+ assert(Use.getKind() == UninitUse::Sometimes);
+
+ const Expr *User = Use.getUser();
+ const Stmt *Term = I->Terminator;
+
+ // Information used when building the diagnostic.
+ unsigned DiagKind;
+ const char *Str;
+ SourceRange Range;
+
+ // FixIts to suppress the diagnosic by removing the dead condition.
+ // For all binary terminators, branch 0 is taken if the condition is true,
+ // and branch 1 is taken if the condition is false.
+ int RemoveDiagKind = -1;
+ const char *FixitStr =
+ S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false")
+ : (I->Output ? "1" : "0");
+ FixItHint Fixit1, Fixit2;
+
+ switch (Term->getStmtClass()) {
+ default:
+ // Don't know how to report this. Just fall back to 'may be used
+ // uninitialized'. This happens for range-based for, which the user
+ // can't explicitly fix.
+ // FIXME: This also happens if the first use of a variable is always
+ // uninitialized, eg "for (int n; n < 10; ++n)". We should report that
+ // with the 'is uninitialized' diagnostic.
+ continue;
+
+ // "condition is true / condition is false".
+ case Stmt::IfStmtClass: {
+ const IfStmt *IS = cast<IfStmt>(Term);
+ DiagKind = 0;
+ Str = "if";
+ Range = IS->getCond()->getSourceRange();
+ RemoveDiagKind = 0;
+ CreateIfFixit(S, IS, IS->getThen(), IS->getElse(),
+ I->Output, Fixit1, Fixit2);
+ break;
+ }
+ case Stmt::ConditionalOperatorClass: {
+ const ConditionalOperator *CO = cast<ConditionalOperator>(Term);
+ DiagKind = 0;
+ Str = "?:";
+ Range = CO->getCond()->getSourceRange();
+ RemoveDiagKind = 0;
+ CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(),
+ I->Output, Fixit1, Fixit2);
+ break;
+ }
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BO = cast<BinaryOperator>(Term);
+ if (!BO->isLogicalOp())
+ continue;
+ DiagKind = 0;
+ Str = BO->getOpcodeStr();
+ Range = BO->getLHS()->getSourceRange();
+ RemoveDiagKind = 0;
+ if ((BO->getOpcode() == BO_LAnd && I->Output) ||
+ (BO->getOpcode() == BO_LOr && !I->Output))
+ // true && y -> y, false || y -> y.
+ Fixit1 = FixItHint::CreateRemoval(SourceRange(BO->getLocStart(),
+ BO->getOperatorLoc()));
+ else
+ // false && y -> false, true || y -> true.
+ Fixit1 = FixItHint::CreateReplacement(BO->getSourceRange(), FixitStr);
+ break;
+ }
+
+ // "loop is entered / loop is exited".
+ case Stmt::WhileStmtClass:
+ DiagKind = 1;
+ Str = "while";
+ Range = cast<WhileStmt>(Term)->getCond()->getSourceRange();
+ RemoveDiagKind = 1;
+ Fixit1 = FixItHint::CreateReplacement(Range, FixitStr);
+ break;
+ case Stmt::ForStmtClass:
+ DiagKind = 1;
+ Str = "for";
+ Range = cast<ForStmt>(Term)->getCond()->getSourceRange();
+ RemoveDiagKind = 1;
+ if (I->Output)
+ Fixit1 = FixItHint::CreateRemoval(Range);
+ else
+ Fixit1 = FixItHint::CreateReplacement(Range, FixitStr);
+ break;
+
+ // "condition is true / loop is exited".
+ case Stmt::DoStmtClass:
+ DiagKind = 2;
+ Str = "do";
+ Range = cast<DoStmt>(Term)->getCond()->getSourceRange();
+ RemoveDiagKind = 1;
+ Fixit1 = FixItHint::CreateReplacement(Range, FixitStr);
+ break;
+
+ // "switch case is taken".
+ case Stmt::CaseStmtClass:
+ DiagKind = 3;
+ Str = "case";
+ Range = cast<CaseStmt>(Term)->getLHS()->getSourceRange();
+ break;
+ case Stmt::DefaultStmtClass:
+ DiagKind = 3;
+ Str = "default";
+ Range = cast<DefaultStmt>(Term)->getDefaultLoc();
+ break;
+ }
+
+ S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var)
+ << VD->getDeclName() << IsCapturedByBlock << DiagKind
+ << Str << I->Output << Range;
+ S.Diag(User->getLocStart(), diag::note_uninit_var_use)
+ << IsCapturedByBlock << User->getSourceRange();
+ if (RemoveDiagKind != -1)
+ S.Diag(Fixit1.RemoveRange.getBegin(), diag::note_uninit_fixit_remove_cond)
+ << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2;
+
+ Diagnosed = true;
+ }
+
+ if (!Diagnosed)
+ S.Diag(Use.getUser()->getLocStart(),
+ Use.getKind() == UninitUse::Always ? diag::warn_uninit_var
+ : diag::warn_maybe_uninit_var)
+ << VD->getDeclName() << IsCapturedByBlock
+ << Use.getUser()->getSourceRange();
+}
+
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
/// uninitialized variable. This manages the different forms of diagnostic
/// emitted for particular types of uses. Returns true if the use was diagnosed
-/// as a warning. If a pariticular use is one we omit warnings for, returns
+/// as a warning. If a particular use is one we omit warnings for, returns
/// false.
static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
- const Expr *E, bool isAlwaysUninit,
+ const UninitUse &Use,
bool alwaysReportSelfInit = false) {
- bool isSelfInit = false;
-
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
- if (isAlwaysUninit) {
- // Inspect the initializer of the variable declaration which is
- // being referenced prior to its initialization. We emit
- // specialized diagnostics for self-initialization, and we
- // specifically avoid warning about self references which take the
- // form of:
- //
- // int x = x;
- //
- // This is used to indicate to GCC that 'x' is intentionally left
- // uninitialized. Proven code paths which access 'x' in
- // an uninitialized state after this will still warn.
- //
- // TODO: Should we suppress maybe-uninitialized warnings for
- // variables initialized in this way?
- if (const Expr *Initializer = VD->getInit()) {
- if (!alwaysReportSelfInit && DRE == Initializer->IgnoreParenImpCasts())
- return false;
-
- ContainsReference CR(S.Context, DRE);
- CR.Visit(const_cast<Expr*>(Initializer));
- isSelfInit = CR.doesContainReference();
- }
- if (isSelfInit) {
+
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Use.getUser())) {
+ // Inspect the initializer of the variable declaration which is
+ // being referenced prior to its initialization. We emit
+ // specialized diagnostics for self-initialization, and we
+ // specifically avoid warning about self references which take the
+ // form of:
+ //
+ // int x = x;
+ //
+ // This is used to indicate to GCC that 'x' is intentionally left
+ // uninitialized. Proven code paths which access 'x' in
+ // an uninitialized state after this will still warn.
+ if (const Expr *Initializer = VD->getInit()) {
+ if (!alwaysReportSelfInit && DRE == Initializer->IgnoreParenImpCasts())
+ return false;
+
+ ContainsReference CR(S.Context, DRE);
+ CR.Visit(const_cast<Expr*>(Initializer));
+ if (CR.doesContainReference()) {
S.Diag(DRE->getLocStart(),
diag::warn_uninit_self_reference_in_init)
- << VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
- } else {
- S.Diag(DRE->getLocStart(), diag::warn_uninit_var)
- << VD->getDeclName() << DRE->getSourceRange();
+ << VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
+ return true;
}
- } else {
- S.Diag(DRE->getLocStart(), diag::warn_maybe_uninit_var)
- << VD->getDeclName() << DRE->getSourceRange();
}
+
+ DiagUninitUse(S, VD, Use, false);
} else {
- const BlockExpr *BE = cast<BlockExpr>(E);
- if (VD->getType()->isBlockPointerType() &&
- !VD->hasAttr<BlocksAttr>())
- S.Diag(BE->getLocStart(), diag::warn_uninit_byref_blockvar_captured_by_block)
- << VD->getDeclName();
- else
+ const BlockExpr *BE = cast<BlockExpr>(Use.getUser());
+ if (VD->getType()->isBlockPointerType() && !VD->hasAttr<BlocksAttr>())
S.Diag(BE->getLocStart(),
- isAlwaysUninit ? diag::warn_uninit_var_captured_by_block
- : diag::warn_maybe_uninit_var_captured_by_block)
+ diag::warn_uninit_byref_blockvar_captured_by_block)
<< VD->getDeclName();
+ else
+ DiagUninitUse(S, VD, Use, true);
}
// Report where the variable was declared when the use wasn't within
// the initializer of that declaration & we didn't already suggest
// an initialization fixit.
- if (!isSelfInit && !SuggestInitializationFixit(S, VD))
+ if (!SuggestInitializationFixit(S, VD))
S.Diag(VD->getLocStart(), diag::note_uninit_var_def)
<< VD->getDeclName();
return true;
}
-typedef std::pair<const Expr*, bool> UninitUse;
+namespace {
+ class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> {
+ public:
+ FallthroughMapper(Sema &S)
+ : FoundSwitchStatements(false),
+ S(S) {
+ }
+
+ bool foundSwitchStatements() const { return FoundSwitchStatements; }
+
+ void markFallthroughVisited(const AttributedStmt *Stmt) {
+ bool Found = FallthroughStmts.erase(Stmt);
+ assert(Found);
+ (void)Found;
+ }
+
+ typedef llvm::SmallPtrSet<const AttributedStmt*, 8> AttrStmts;
+
+ const AttrStmts &getFallthroughStmts() const {
+ return FallthroughStmts;
+ }
+
+ bool checkFallThroughIntoBlock(const CFGBlock &B, int &AnnotatedCnt) {
+ int UnannotatedCnt = 0;
+ AnnotatedCnt = 0;
+
+ std::deque<const CFGBlock*> BlockQueue;
+
+ std::copy(B.pred_begin(), B.pred_end(), std::back_inserter(BlockQueue));
+
+ while (!BlockQueue.empty()) {
+ const CFGBlock *P = BlockQueue.front();
+ BlockQueue.pop_front();
+
+ const Stmt *Term = P->getTerminator();
+ if (Term && isa<SwitchStmt>(Term))
+ continue; // Switch statement, good.
+
+ const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(P->getLabel());
+ if (SW && SW->getSubStmt() == B.getLabel() && P->begin() == P->end())
+ continue; // Previous case label has no statements, good.
+
+ if (P->pred_begin() == P->pred_end()) { // The block is unreachable.
+ // This only catches trivially unreachable blocks.
+ for (CFGBlock::const_iterator ElIt = P->begin(), ElEnd = P->end();
+ ElIt != ElEnd; ++ElIt) {
+ if (const CFGStmt *CS = ElIt->getAs<CFGStmt>()){
+ if (const AttributedStmt *AS = asFallThroughAttr(CS->getStmt())) {
+ S.Diag(AS->getLocStart(),
+ diag::warn_fallthrough_attr_unreachable);
+ markFallthroughVisited(AS);
+ ++AnnotatedCnt;
+ }
+ // Don't care about other unreachable statements.
+ }
+ }
+ // If there are no unreachable statements, this may be a special
+ // case in CFG:
+ // case X: {
+ // A a; // A has a destructor.
+ // break;
+ // }
+ // // <<<< This place is represented by a 'hanging' CFG block.
+ // case Y:
+ continue;
+ }
+
+ const Stmt *LastStmt = getLastStmt(*P);
+ if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) {
+ markFallthroughVisited(AS);
+ ++AnnotatedCnt;
+ continue; // Fallthrough annotation, good.
+ }
+
+ if (!LastStmt) { // This block contains no executable statements.
+ // Traverse its predecessors.
+ std::copy(P->pred_begin(), P->pred_end(),
+ std::back_inserter(BlockQueue));
+ continue;
+ }
+
+ ++UnannotatedCnt;
+ }
+ return !!UnannotatedCnt;
+ }
+
+ // RecursiveASTVisitor setup.
+ bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+ bool VisitAttributedStmt(AttributedStmt *S) {
+ if (asFallThroughAttr(S))
+ FallthroughStmts.insert(S);
+ return true;
+ }
+
+ bool VisitSwitchStmt(SwitchStmt *S) {
+ FoundSwitchStatements = true;
+ return true;
+ }
+
+ private:
+
+ static const AttributedStmt *asFallThroughAttr(const Stmt *S) {
+ if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) {
+ if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs()))
+ return AS;
+ }
+ return 0;
+ }
+
+ static const Stmt *getLastStmt(const CFGBlock &B) {
+ if (const Stmt *Term = B.getTerminator())
+ return Term;
+ for (CFGBlock::const_reverse_iterator ElemIt = B.rbegin(),
+ ElemEnd = B.rend();
+ ElemIt != ElemEnd; ++ElemIt) {
+ if (const CFGStmt *CS = ElemIt->getAs<CFGStmt>())
+ return CS->getStmt();
+ }
+ // Workaround to detect a statement thrown out by CFGBuilder:
+ // case X: {} case Y:
+ // case X: ; case Y:
+ if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
+ if (!isa<SwitchCase>(SW->getSubStmt()))
+ return SW->getSubStmt();
+
+ return 0;
+ }
+
+ bool FoundSwitchStatements;
+ AttrStmts FallthroughStmts;
+ Sema &S;
+ };
+}
+
+static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC,
+ bool PerFunction) {
+ FallthroughMapper FM(S);
+ FM.TraverseStmt(AC.getBody());
+
+ if (!FM.foundSwitchStatements())
+ return;
+
+ if (PerFunction && FM.getFallthroughStmts().empty())
+ return;
+
+ CFG *Cfg = AC.getCFG();
+
+ if (!Cfg)
+ return;
+
+ int AnnotatedCnt;
+
+ for (CFG::reverse_iterator I = Cfg->rbegin(), E = Cfg->rend(); I != E; ++I) {
+ const CFGBlock &B = **I;
+ const Stmt *Label = B.getLabel();
+
+ if (!Label || !isa<SwitchCase>(Label))
+ continue;
+
+ if (!FM.checkFallThroughIntoBlock(B, AnnotatedCnt))
+ continue;
+
+ S.Diag(Label->getLocStart(),
+ PerFunction ? diag::warn_unannotated_fallthrough_per_function
+ : diag::warn_unannotated_fallthrough);
+
+ if (!AnnotatedCnt) {
+ SourceLocation L = Label->getLocStart();
+ if (L.isMacroID())
+ continue;
+ if (S.getLangOpts().CPlusPlus0x) {
+ const Stmt *Term = B.getTerminator();
+ if (!(B.empty() && Term && isa<BreakStmt>(Term))) {
+ S.Diag(L, diag::note_insert_fallthrough_fixit) <<
+ FixItHint::CreateInsertion(L, "[[clang::fallthrough]]; ");
+ }
+ }
+ S.Diag(L, diag::note_insert_break_fixit) <<
+ FixItHint::CreateInsertion(L, "break; ");
+ }
+ }
+
+ const FallthroughMapper::AttrStmts &Fallthroughs = FM.getFallthroughStmts();
+ for (FallthroughMapper::AttrStmts::const_iterator I = Fallthroughs.begin(),
+ E = Fallthroughs.end();
+ I != E; ++I) {
+ S.Diag((*I)->getLocStart(), diag::warn_fallthrough_attr_invalid_placement);
+ }
+
+}
namespace {
struct SLocSort {
bool operator()(const UninitUse &a, const UninitUse &b) {
- SourceLocation aLoc = a.first->getLocStart();
- SourceLocation bLoc = b.first->getLocStart();
+ // Prefer a more confident report over a less confident one.
+ if (a.getKind() != b.getKind())
+ return a.getKind() > b.getKind();
+ SourceLocation aLoc = a.getUser()->getLocStart();
+ SourceLocation bLoc = b.getUser()->getLocStart();
return aLoc.getRawEncoding() < bLoc.getRawEncoding();
}
};
@@ -552,9 +913,8 @@ public:
return V;
}
- void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd,
- bool isAlwaysUninit) {
- getUses(vd).first->push_back(std::make_pair(ex, isAlwaysUninit));
+ void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use) {
+ getUses(vd).first->push_back(use);
}
void handleSelfInit(const VarDecl *vd) {
@@ -565,6 +925,8 @@ public:
if (!uses)
return;
+ // FIXME: This iteration order, and thus the resulting diagnostic order,
+ // is nondeterministic.
for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) {
const VarDecl *vd = i->first;
const UsesMap::mapped_type &V = i->second;
@@ -576,8 +938,9 @@ public:
// variable, but the root cause is an idiomatic self-init. We want
// to report the diagnostic at the self-init since that is the root cause.
if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
- DiagnoseUninitializedUse(S, vd, vd->getInit()->IgnoreParenCasts(),
- /* isAlwaysUninit */ true,
+ DiagnoseUninitializedUse(S, vd,
+ UninitUse(vd->getInit()->IgnoreParenCasts(),
+ /* isAlwaysUninit */ true),
/* alwaysReportSelfInit */ true);
else {
// Sort the uses by their SourceLocations. While not strictly
@@ -587,8 +950,10 @@ public:
for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve;
++vi) {
- if (DiagnoseUninitializedUse(S, vd, vi->first,
- /*isAlwaysUninit=*/vi->second))
+ // If we have self-init, downgrade all uses to 'may be uninitialized'.
+ UninitUse Use = hasSelfInit ? UninitUse(vi->getUser(), false) : *vi;
+
+ if (DiagnoseUninitializedUse(S, vd, Use))
// Skip further diagnostics for this variable. We try to warn only
// on the first point at which a variable is used uninitialized.
break;
@@ -604,7 +969,7 @@ public:
private:
static bool hasAlwaysUninitializedUse(const UsesVec* vec) {
for (UsesVec::const_iterator i = vec->begin(), e = vec->end(); i != e; ++i) {
- if (i->second) {
+ if (i->getKind() == UninitUse::Always) {
return true;
}
}
@@ -696,6 +1061,9 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
case LEK_LockedAtEndOfFunction:
DiagID = diag::warn_no_unlock;
break;
+ case LEK_NotLockedAtEndOfFunction:
+ DiagID = diag::warn_expecting_locked;
+ break;
}
if (LocEndOfScope.isInvalid())
LocEndOfScope = FunEndLocation;
@@ -830,7 +1198,7 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
const Stmt *Body = D->getBody();
assert(Body);
- AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D, 0);
+ AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D);
// Don't generate EH edges for CallExprs as we'd like to avoid the n^2
// explosion for destrutors that can result and the compile time hit.
@@ -852,11 +1220,13 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
else {
AC.getCFGBuildOptions()
.setAlwaysAdd(Stmt::BinaryOperatorClass)
+ .setAlwaysAdd(Stmt::CompoundAssignOperatorClass)
.setAlwaysAdd(Stmt::BlockExprClass)
.setAlwaysAdd(Stmt::CStyleCastExprClass)
.setAlwaysAdd(Stmt::DeclRefExprClass)
.setAlwaysAdd(Stmt::ImplicitCastExprClass)
- .setAlwaysAdd(Stmt::UnaryOperatorClass);
+ .setAlwaysAdd(Stmt::UnaryOperatorClass)
+ .setAlwaysAdd(Stmt::AttributedStmtClass);
}
// Construct the analysis context with the specified CFG build options.
@@ -945,6 +1315,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
!= DiagnosticsEngine::Ignored ||
+ Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())
+ != DiagnosticsEngine::Ignored ||
Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
!= DiagnosticsEngine::Ignored) {
if (CFG *cfg = AC.getCFG()) {
@@ -968,6 +1340,16 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
}
}
+ bool FallThroughDiagFull =
+ Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough,
+ D->getLocStart()) != DiagnosticsEngine::Ignored;
+ bool FallThroughDiagPerFunction =
+ Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough_per_function,
+ D->getLocStart()) != DiagnosticsEngine::Ignored;
+ if (FallThroughDiagFull || FallThroughDiagPerFunction) {
+ DiagnoseSwitchLabelsFallthrough(S, AC, !FallThroughDiagFull);
+ }
+
// Collect statistics about the CFG if it was built.
if (S.CollectStats && AC.isCFGBuilt()) {
++NumFunctionsAnalyzed;
diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp
index f142ab4..0f209fd 100644
--- a/lib/Sema/AttributeList.cpp
+++ b/lib/Sema/AttributeList.cpp
@@ -12,9 +12,11 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/AttributeList.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/IdentifierTable.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/SmallString.h"
using namespace clang;
size_t AttributeList::allocated_size() const {
@@ -94,10 +96,15 @@ AttributePool::createIntegerAttribute(ASTContext &C, IdentifierInfo *Name,
SourceLocation TokLoc, int Arg) {
Expr *IArg = IntegerLiteral::Create(C, llvm::APInt(32, (uint64_t) Arg),
C.IntTy, TokLoc);
- return create(Name, TokLoc, 0, TokLoc, 0, TokLoc, &IArg, 1, 0);
+ return create(Name, TokLoc, 0, TokLoc, 0, TokLoc, &IArg, 1,
+ AttributeList::AS_GNU);
}
-AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
+#include "clang/Sema/AttrParsedAttrKinds.inc"
+
+AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name,
+ const IdentifierInfo *ScopeName,
+ Syntax SyntaxUsed) {
StringRef AttrName = Name->getName();
// Normalize the attribute name, __foo__ becomes foo.
@@ -105,22 +112,14 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
AttrName.size() >= 4)
AttrName = AttrName.substr(2, AttrName.size() - 4);
- return llvm::StringSwitch<AttributeList::Kind>(AttrName)
- #include "clang/Sema/AttrParsedAttrKinds.inc"
- .Case("address_space", AT_address_space)
- .Case("align", AT_aligned) // FIXME - should it be "aligned"?
- .Case("base_check", AT_base_check)
- .Case("bounded", IgnoredAttribute) // OpenBSD
- .Case("__const", AT_const) // some GCC headers do contain this spelling
- .Case("cf_returns_autoreleased", AT_cf_returns_autoreleased)
- .Case("mode", AT_mode)
- .Case("vec_type_hint", IgnoredAttribute)
- .Case("ext_vector_type", AT_ext_vector_type)
- .Case("neon_vector_type", AT_neon_vector_type)
- .Case("neon_polyvector_type", AT_neon_polyvector_type)
- .Case("opencl_image_access", AT_opencl_image_access)
- .Case("objc_gc", AT_objc_gc)
- .Case("objc_ownership", AT_objc_ownership)
- .Case("vector_size", AT_vector_size)
- .Default(UnknownAttribute);
+ SmallString<64> Buf;
+ if (ScopeName)
+ Buf += ScopeName->getName();
+ // Ensure that in the case of C++11 attributes, we look for '::foo' if it is
+ // unscoped.
+ if (ScopeName || SyntaxUsed == AS_CXX11)
+ Buf += "::";
+ Buf += AttrName;
+
+ return ::getAttrKind(Buf);
}
diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt
index 07734c7..46dfa05 100644
--- a/lib/Sema/CMakeLists.txt
+++ b/lib/Sema/CMakeLists.txt
@@ -1,9 +1,8 @@
-set(LLVM_USED_LIBS
- clangAST
- clangAnalysis
- clangBasic
- clangEdit
- clangLex
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
)
add_clang_library(clangSema
@@ -50,9 +49,27 @@ add_clang_library(clangSema
TargetAttributesSema.cpp
)
-add_dependencies(clangSema ClangARMNeon ClangAttrClasses ClangAttrList
- ClangDiagnosticSema ClangDeclNodes ClangStmtNodes
- ClangAttrTemplateInstantiate ClangAttrParsedAttrList
- ClangAttrParsedAttrKinds)
-
+add_dependencies(clangSema
+ ClangARMNeon
+ ClangAttrClasses
+ ClangAttrList
+ ClangAttrParsedAttrList
+ ClangAttrParsedAttrKinds
+ ClangAttrTemplateInstantiate
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticAST
+ ClangDiagnosticComment
+ ClangDiagnosticCommon
+ ClangDiagnosticParse
+ ClangDiagnosticSema
+ ClangStmtNodes
+ )
+target_link_libraries(clangSema
+ clangAST
+ clangAnalysis
+ clangBasic
+ clangEdit
+ clangLex
+ )
diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp
index ce9bbb9..a835725 100644
--- a/lib/Sema/CodeCompleteConsumer.cpp
+++ b/lib/Sema/CodeCompleteConsumer.cpp
@@ -194,10 +194,11 @@ CodeCompletionString::CodeCompletionString(const Chunk *Chunks,
const char **Annotations,
unsigned NumAnnotations,
CXCursorKind ParentKind,
- StringRef ParentName)
+ StringRef ParentName,
+ const char *BriefComment)
: NumChunks(NumChunks), NumAnnotations(NumAnnotations),
Priority(Priority), Availability(Availability), ParentKind(ParentKind),
- ParentName(ParentName)
+ ParentName(ParentName), BriefComment(BriefComment)
{
assert(NumChunks <= 0xffff);
assert(NumAnnotations <= 0xffff);
@@ -338,7 +339,7 @@ CodeCompletionString *CodeCompletionBuilder::TakeString() {
= new (Mem) CodeCompletionString(Chunks.data(), Chunks.size(),
Priority, Availability,
Annotations.data(), Annotations.size(),
- ParentKind, ParentName);
+ ParentKind, ParentName, BriefComment);
Chunks.clear();
return Result;
}
@@ -394,6 +395,10 @@ void CodeCompletionBuilder::addParentContext(DeclContext *DC) {
ParentName = getCodeCompletionTUInfo().getParentName(DC);
}
+void CodeCompletionBuilder::addBriefComment(StringRef Comment) {
+ BriefComment = Allocator.CopyString(Comment);
+}
+
unsigned CodeCompletionResult::getPriorityFromDecl(NamedDecl *ND) {
if (!ND)
return CCP_Unlikely;
@@ -474,8 +479,11 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
OS << " (Hidden)";
if (CodeCompletionString *CCS
= Results[I].CreateCodeCompletionString(SemaRef, getAllocator(),
- CCTUInfo)) {
+ CCTUInfo,
+ includeBriefComments())) {
OS << " : " << CCS->getAsString();
+ if (const char *BriefComment = CCS->getBriefComment())
+ OS << " : " << BriefComment;
}
OS << '\n';
@@ -489,7 +497,8 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
OS << Results[I].Macro->getName();
if (CodeCompletionString *CCS
= Results[I].CreateCodeCompletionString(SemaRef, getAllocator(),
- CCTUInfo)) {
+ CCTUInfo,
+ includeBriefComments())) {
OS << " : " << CCS->getAsString();
}
OS << '\n';
@@ -573,14 +582,8 @@ void CodeCompletionResult::computeCursorKindAndAvailability(bool Accessible) {
}
case RK_Macro:
- Availability = CXAvailability_Available;
- CursorKind = CXCursor_MacroDefinition;
- break;
-
case RK_Keyword:
- Availability = CXAvailability_Available;
- CursorKind = CXCursor_NotImplemented;
- break;
+ llvm_unreachable("Macro and keyword kinds are handled by the constructors");
}
if (!Accessible)
diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp
index b531acc..d12ca78 100644
--- a/lib/Sema/DeclSpec.cpp
+++ b/lib/Sema/DeclSpec.cpp
@@ -145,6 +145,7 @@ CXXScopeSpec::getWithLocInContext(ASTContext &Context) const {
/// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function.
/// "TheDeclarator" is the declarator that this will be added to.
DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
+ bool isAmbiguous,
SourceLocation EllipsisLoc,
ParamInfo *ArgInfo,
unsigned NumArgs,
@@ -165,7 +166,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
SourceLocation LocalRangeBegin,
SourceLocation LocalRangeEnd,
Declarator &TheDeclarator,
- ParsedType TrailingReturnType) {
+ TypeResult TrailingReturnType) {
DeclaratorChunk I;
I.Kind = Function;
I.Loc = LocalRangeBegin;
@@ -173,6 +174,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
I.Fun.AttrList = 0;
I.Fun.hasPrototype = hasProto;
I.Fun.isVariadic = isVariadic;
+ I.Fun.isAmbiguous = isAmbiguous;
I.Fun.EllipsisLoc = EllipsisLoc.getRawEncoding();
I.Fun.DeleteArgInfo = false;
I.Fun.TypeQuals = TypeQuals;
@@ -188,7 +190,9 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
I.Fun.NumExceptions = 0;
I.Fun.Exceptions = 0;
I.Fun.NoexceptExpr = 0;
- I.Fun.TrailingReturnType = TrailingReturnType.getAsOpaquePtr();
+ I.Fun.HasTrailingReturnType = TrailingReturnType.isUsable() ||
+ TrailingReturnType.isInvalid();
+ I.Fun.TrailingReturnType = TrailingReturnType.get();
// new[] an argument array if needed.
if (NumArgs) {
@@ -418,19 +422,27 @@ const char *DeclSpec::getSpecifierName(TQ T) {
bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc,
const char *&PrevSpec,
unsigned &DiagID) {
- // OpenCL 1.1 6.8g: "The extern, static, auto and register storage-class
- // specifiers are not supported."
+ // OpenCL v1.1 s6.8g: "The extern, static, auto and register storage-class
+ // specifiers are not supported.
// It seems sensible to prohibit private_extern too
// The cl_clang_storage_class_specifiers extension enables support for
// these storage-class specifiers.
+ // OpenCL v1.2 s6.8 changes this to "The auto and register storage-class
+ // specifiers are not supported."
if (S.getLangOpts().OpenCL &&
!S.getOpenCLOptions().cl_clang_storage_class_specifiers) {
switch (SC) {
case SCS_extern:
case SCS_private_extern:
+ case SCS_static:
+ if (S.getLangOpts().OpenCLVersion < 120) {
+ DiagID = diag::err_not_opencl_storage_class_specifier;
+ PrevSpec = getSpecifierName(SC);
+ return true;
+ }
+ break;
case SCS_auto:
case SCS_register:
- case SCS_static:
DiagID = diag::err_not_opencl_storage_class_specifier;
PrevSpec = getSpecifierName(SC);
return true;
@@ -658,9 +670,11 @@ bool DeclSpec::SetTypeSpecError() {
}
bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec,
- unsigned &DiagID, const LangOptions &Lang) {
- // Duplicates turn into warnings pre-C99.
- if ((TypeQualifiers & T) && !Lang.C99)
+ unsigned &DiagID, const LangOptions &Lang,
+ bool IsTypeSpec) {
+ // Duplicates are permitted in C99, and are permitted in C++11 unless the
+ // cv-qualifier appears as a type-specifier.
+ if ((TypeQualifiers & T) && !Lang.C99 && (!Lang.CPlusPlus0x || IsTypeSpec))
return BadSpecifier(T, T, PrevSpec, DiagID);
TypeQualifiers |= T;
@@ -751,7 +765,7 @@ void DeclSpec::SaveWrittenBuiltinSpecs() {
writtenBS.ModeAttr = false;
AttributeList* attrs = getAttributes().getList();
while (attrs) {
- if (attrs->getKind() == AttributeList::AT_mode) {
+ if (attrs->getKind() == AttributeList::AT_Mode) {
writtenBS.ModeAttr = true;
break;
}
@@ -935,13 +949,6 @@ bool DeclSpec::isMissingDeclaratorOk() {
StorageClassSpec != DeclSpec::SCS_typedef;
}
-void UnqualifiedId::clear() {
- Kind = IK_Identifier;
- Identifier = 0;
- StartLocation = SourceLocation();
- EndLocation = SourceLocation();
-}
-
void UnqualifiedId::setOperatorFunctionId(SourceLocation OperatorLoc,
OverloadedOperatorKind Op,
SourceLocation SymbolLocations[3]) {
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 30a9cd7..7f79f0c 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -18,6 +18,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/APFloat.h"
+#include "llvm/Support/CrashRecoveryContext.h"
#include "clang/Sema/CXXFieldCollector.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/ExternalSemaSource.h"
@@ -29,6 +30,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
@@ -90,13 +92,13 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
PackContext(0), MSStructPragmaOn(false), VisContext(0),
ExprNeedsCleanups(false), LateTemplateParser(0), OpaqueParser(0),
IdResolver(pp), StdInitializerList(0), CXXTypeInfoDecl(0), MSVCGuidDecl(0),
- NSNumberDecl(0), NSArrayDecl(0), ArrayWithObjectsMethod(0),
+ NSNumberDecl(0),
+ NSStringDecl(0), StringWithUTF8StringMethod(0),
+ NSArrayDecl(0), ArrayWithObjectsMethod(0),
NSDictionaryDecl(0), DictionaryWithObjectsMethod(0),
GlobalNewDeleteDeclared(false),
- ObjCShouldCallSuperDealloc(false),
- ObjCShouldCallSuperFinalize(false),
TUKind(TUKind),
- NumSFINAEErrors(0), InFunctionDeclarator(0), SuppressAccessChecking(false),
+ NumSFINAEErrors(0), InFunctionDeclarator(0),
AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
CurrentInstantiationScope(0), TyposCorrected(0),
@@ -176,6 +178,10 @@ void Sema::Initialize() {
if (IdResolver.begin(Protocol) == IdResolver.end())
PushOnScopeChains(Context.getObjCProtocolDecl(), TUScope);
}
+
+ DeclarationName BuiltinVaList = &Context.Idents.get("__builtin_va_list");
+ if (IdResolver.begin(BuiltinVaList) == IdResolver.end())
+ PushOnScopeChains(Context.getBuiltinVaListDecl(), TUScope);
}
Sema::~Sema() {
@@ -199,7 +205,6 @@ Sema::~Sema() {
ExternalSema->ForgetSema();
}
-
/// makeUnavailableInSystemHeader - There is an error in the current
/// context. If we're still in a system header, and we can plausibly
/// make the relevant declaration unavailable instead of erroring, do
@@ -420,10 +425,88 @@ void Sema::LoadExternalWeakUndeclaredIdentifiers() {
}
}
+
+typedef llvm::DenseMap<const CXXRecordDecl*, bool> RecordCompleteMap;
+
+/// \brief Returns true, if all methods and nested classes of the given
+/// CXXRecordDecl are defined in this translation unit.
+///
+/// Should only be called from ActOnEndOfTranslationUnit so that all
+/// definitions are actually read.
+static bool MethodsAndNestedClassesComplete(const CXXRecordDecl *RD,
+ RecordCompleteMap &MNCComplete) {
+ RecordCompleteMap::iterator Cache = MNCComplete.find(RD);
+ if (Cache != MNCComplete.end())
+ return Cache->second;
+ if (!RD->isCompleteDefinition())
+ return false;
+ bool Complete = true;
+ for (DeclContext::decl_iterator I = RD->decls_begin(),
+ E = RD->decls_end();
+ I != E && Complete; ++I) {
+ if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(*I))
+ Complete = M->isDefined() || (M->isPure() && !isa<CXXDestructorDecl>(M));
+ else if (const FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(*I))
+ Complete = F->getTemplatedDecl()->isDefined();
+ else if (const CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(*I)) {
+ if (R->isInjectedClassName())
+ continue;
+ if (R->hasDefinition())
+ Complete = MethodsAndNestedClassesComplete(R->getDefinition(),
+ MNCComplete);
+ else
+ Complete = false;
+ }
+ }
+ MNCComplete[RD] = Complete;
+ return Complete;
+}
+
+/// \brief Returns true, if the given CXXRecordDecl is fully defined in this
+/// translation unit, i.e. all methods are defined or pure virtual and all
+/// friends, friend functions and nested classes are fully defined in this
+/// translation unit.
+///
+/// Should only be called from ActOnEndOfTranslationUnit so that all
+/// definitions are actually read.
+static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
+ RecordCompleteMap &RecordsComplete,
+ RecordCompleteMap &MNCComplete) {
+ RecordCompleteMap::iterator Cache = RecordsComplete.find(RD);
+ if (Cache != RecordsComplete.end())
+ return Cache->second;
+ bool Complete = MethodsAndNestedClassesComplete(RD, MNCComplete);
+ for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
+ E = RD->friend_end();
+ I != E && Complete; ++I) {
+ // Check if friend classes and methods are complete.
+ if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
+ // Friend classes are available as the TypeSourceInfo of the FriendDecl.
+ if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
+ Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
+ else
+ Complete = false;
+ } else {
+ // Friend functions are available through the NamedDecl of FriendDecl.
+ if (const FunctionDecl *FD =
+ dyn_cast<FunctionDecl>((*I)->getFriendDecl()))
+ Complete = FD->isDefined();
+ else
+ // This is a template friend, give up.
+ Complete = false;
+ }
+ }
+ RecordsComplete[RD] = Complete;
+ return Complete;
+}
+
/// ActOnEndOfTranslationUnit - This is called at the very end of the
/// translation unit when EOF is reached and all but the top-level scope is
/// popped.
void Sema::ActOnEndOfTranslationUnit() {
+ assert(DelayedDiagnostics.getCurrentPool() == NULL
+ && "reached end of translation unit with a pool attached?");
+
// Only complete translation units define vtables and perform implicit
// instantiations.
if (TUKind == TU_Complete) {
@@ -597,9 +680,17 @@ void Sema::ActOnEndOfTranslationUnit() {
if (isa<CXXMethodDecl>(DiagD))
Diag(DiagD->getLocation(), diag::warn_unneeded_member_function)
<< DiagD->getDeclName();
- else
- Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl)
- << /*function*/0 << DiagD->getDeclName();
+ else {
+ if (FD->getStorageClassAsWritten() == SC_Static &&
+ !FD->isInlineSpecified() &&
+ !SourceMgr.isFromMainFile(
+ SourceMgr.getExpansionLoc(FD->getLocation())))
+ Diag(DiagD->getLocation(), diag::warn_unneeded_static_internal_decl)
+ << DiagD->getDeclName();
+ else
+ Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl)
+ << /*function*/0 << DiagD->getDeclName();
+ }
} else {
Diag(DiagD->getLocation(),
isa<CXXMethodDecl>(DiagD) ? diag::warn_unused_member_function
@@ -623,6 +714,23 @@ void Sema::ActOnEndOfTranslationUnit() {
checkUndefinedInternals(*this);
}
+ if (Diags.getDiagnosticLevel(diag::warn_unused_private_field,
+ SourceLocation())
+ != DiagnosticsEngine::Ignored) {
+ RecordCompleteMap RecordsComplete;
+ RecordCompleteMap MNCComplete;
+ for (NamedDeclSetType::iterator I = UnusedPrivateFields.begin(),
+ E = UnusedPrivateFields.end(); I != E; ++I) {
+ const NamedDecl *D = *I;
+ const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext());
+ if (RD && !RD->isUnion() &&
+ IsRecordFullyDefined(RD, RecordsComplete, MNCComplete)) {
+ Diag(D->getLocation(), diag::warn_unused_private_field)
+ << D->getDeclName();
+ }
+ }
+ }
+
// Check we've noticed that we're no longer parsing the initializer for every
// variable. If we miss cases, then at best we have a performance issue and
// at worst a rejects-valid bug.
@@ -693,6 +801,15 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
// Count this failure so that we know that template argument deduction
// has failed.
++NumSFINAEErrors;
+
+ // Make a copy of this suppressed diagnostic and store it with the
+ // template-deduction information.
+ if (*Info && !(*Info)->hasSFINAEDiagnostic()) {
+ Diagnostic DiagInfo(&Diags);
+ (*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(),
+ PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
+ }
+
Diags.setLastDiagnosticIgnored();
Diags.Clear();
return;
@@ -709,6 +826,15 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
// Suppress this diagnostic.
++NumSFINAEErrors;
+
+ // Make a copy of this suppressed diagnostic and store it with the
+ // template-deduction information.
+ if (*Info && !(*Info)->hasSFINAEDiagnostic()) {
+ Diagnostic DiagInfo(&Diags);
+ (*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(),
+ PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
+ }
+
Diags.setLastDiagnosticIgnored();
Diags.Clear();
@@ -725,13 +851,13 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
case DiagnosticIDs::SFINAE_Suppress:
// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information;
- Diagnostic DiagInfo(&Diags);
-
- if (*Info)
+ if (*Info) {
+ Diagnostic DiagInfo(&Diags);
(*Info)->addSuppressedDiagnostic(DiagInfo.getLocation(),
- PartialDiagnostic(DiagInfo,Context.getDiagAllocator()));
-
- // Suppress this diagnostic.
+ PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
+ }
+
+ // Suppress this diagnostic.
Diags.setLastDiagnosticIgnored();
Diags.Clear();
return;
@@ -894,6 +1020,29 @@ LambdaScopeInfo *Sema::getCurLambda() {
return dyn_cast<LambdaScopeInfo>(FunctionScopes.back());
}
+void Sema::ActOnComment(SourceRange Comment) {
+ RawComment RC(SourceMgr, Comment);
+ if (RC.isAlmostTrailingComment()) {
+ SourceRange MagicMarkerRange(Comment.getBegin(),
+ Comment.getBegin().getLocWithOffset(3));
+ StringRef MagicMarkerText;
+ switch (RC.getKind()) {
+ case RawComment::RCK_OrdinaryBCPL:
+ MagicMarkerText = "///<";
+ break;
+ case RawComment::RCK_OrdinaryC:
+ MagicMarkerText = "/**<";
+ break;
+ default:
+ llvm_unreachable("if this is an almost Doxygen comment, "
+ "it should be ordinary");
+ }
+ Diag(Comment.getBegin(), diag::warn_not_a_doxygen_trailing_member_comment) <<
+ FixItHint::CreateReplacement(MagicMarkerRange, MagicMarkerText);
+ }
+ Context.addComment(RC);
+}
+
// Pin this vtable to this file.
ExternalSemaSource::~ExternalSemaSource() {}
diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp
index 01c141e..3481171 100644
--- a/lib/Sema/SemaAccess.cpp
+++ b/lib/Sema/SemaAccess.cpp
@@ -152,7 +152,8 @@ struct AccessTarget : public AccessedEntity {
CXXRecordDecl *NamingClass,
DeclAccessPair FoundDecl,
QualType BaseObjectType)
- : AccessedEntity(Context, Member, NamingClass, FoundDecl, BaseObjectType) {
+ : AccessedEntity(Context.getDiagAllocator(), Member, NamingClass,
+ FoundDecl, BaseObjectType) {
initialize();
}
@@ -161,7 +162,8 @@ struct AccessTarget : public AccessedEntity {
CXXRecordDecl *BaseClass,
CXXRecordDecl *DerivedClass,
AccessSpecifier Access)
- : AccessedEntity(Context, Base, BaseClass, DerivedClass, Access) {
+ : AccessedEntity(Context.getDiagAllocator(), Base, BaseClass, DerivedClass,
+ Access) {
initialize();
}
@@ -781,7 +783,7 @@ static AccessResult HasAccess(Sema &S,
// Emulate a MSVC bug where the creation of pointer-to-member
// to protected member of base class is allowed but only from
- // a static function member functions.
+ // static member functions.
if (S.getLangOpts().MicrosoftMode && !EC.Functions.empty())
if (CXXMethodDecl* MD = dyn_cast<CXXMethodDecl>(EC.Functions.front()))
if (MD->isStatic()) return AR_accessible;
@@ -1391,9 +1393,6 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
if (Entity.getAccess() == AS_public)
return Sema::AR_accessible;
- if (S.SuppressAccessChecking)
- return Sema::AR_accessible;
-
// If we're currently parsing a declaration, we may need to delay
// access control checking, because our effective context might be
// different based on what the declaration comes out as.
@@ -1633,25 +1632,6 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
return CheckAccess(*this, UseLoc, AccessEntity);
}
-/// Checks direct (i.e. non-inherited) access to an arbitrary class
-/// member.
-Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc,
- NamedDecl *Target,
- const PartialDiagnostic &Diag) {
- AccessSpecifier Access = Target->getAccess();
- if (!getLangOpts().AccessControl ||
- Access == AS_public)
- return AR_accessible;
-
- CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Target->getDeclContext());
- AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
- DeclAccessPair::make(Target, Access),
- QualType());
- Entity.setDiag(Diag);
- return CheckAccess(*this, UseLoc, Entity);
-}
-
-
/// Checks access to an overloaded operator new or delete.
Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc,
SourceRange PlacementRange,
@@ -1694,6 +1674,44 @@ Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
return CheckAccess(*this, OpLoc, Entity);
}
+/// Checks access to the target of a friend declaration.
+Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) {
+ assert(isa<CXXMethodDecl>(target) ||
+ (isa<FunctionTemplateDecl>(target) &&
+ isa<CXXMethodDecl>(cast<FunctionTemplateDecl>(target)
+ ->getTemplatedDecl())));
+
+ // Friendship lookup is a redeclaration lookup, so there's never an
+ // inheritance path modifying access.
+ AccessSpecifier access = target->getAccess();
+
+ if (!getLangOpts().AccessControl || access == AS_public)
+ return AR_accessible;
+
+ CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(target);
+ if (!method)
+ method = cast<CXXMethodDecl>(
+ cast<FunctionTemplateDecl>(target)->getTemplatedDecl());
+ assert(method->getQualifier());
+
+ AccessTarget entity(Context, AccessTarget::Member,
+ cast<CXXRecordDecl>(target->getDeclContext()),
+ DeclAccessPair::make(target, access),
+ /*no instance context*/ QualType());
+ entity.setDiag(diag::err_access_friend_function)
+ << method->getQualifierLoc().getSourceRange();
+
+ // We need to bypass delayed-diagnostics because we might be called
+ // while the ParsingDeclarator is active.
+ EffectiveContext EC(CurContext);
+ switch (CheckEffectiveAccess(*this, EC, target->getLocation(), entity)) {
+ case AR_accessible: return Sema::AR_accessible;
+ case AR_inaccessible: return Sema::AR_inaccessible;
+ case AR_dependent: return Sema::AR_dependent;
+ }
+ llvm_unreachable("falling off end");
+}
+
Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr,
DeclAccessPair Found) {
if (!getLangOpts().AccessControl ||
@@ -1714,13 +1732,10 @@ Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr,
/// Checks access for a hierarchy conversion.
///
-/// \param IsBaseToDerived whether this is a base-to-derived conversion (true)
-/// or a derived-to-base conversion (false)
/// \param ForceCheck true if this check should be performed even if access
/// control is disabled; some things rely on this for semantics
/// \param ForceUnprivileged true if this check should proceed as if the
/// context had no special privileges
-/// \param ADK controls the kind of diagnostics that are used
Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
QualType Base,
QualType Derived,
@@ -1836,15 +1851,3 @@ bool Sema::IsSimplyAccessible(NamedDecl *Decl, DeclContext *Ctx) {
return true;
}
-
-void Sema::ActOnStartSuppressingAccessChecks() {
- assert(!SuppressAccessChecking &&
- "Tried to start access check suppression when already started.");
- SuppressAccessChecking = true;
-}
-
-void Sema::ActOnStopSuppressingAccessChecks() {
- assert(SuppressAccessChecking &&
- "Tried to stop access check suprression when already stopped.");
- SuppressAccessChecking = false;
-}
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 5a0fcec..0de9dd5 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -227,9 +227,8 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
if (loc.isInvalid()) loc = SS.getRange().getBegin();
// The type must be complete.
- if (RequireCompleteType(loc, type,
- PDiag(diag::err_incomplete_nested_name_spec)
- << SS.getRange())) {
+ if (RequireCompleteType(loc, type, diag::err_incomplete_nested_name_spec,
+ SS.getRange())) {
SS.SetInvalid(SS.getRange());
return true;
}
@@ -539,8 +538,9 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
NamedDecl *SD = Found.getAsSingle<NamedDecl>();
if (isAcceptableNestedNameSpecifier(SD)) {
- if (!ObjectType.isNull() && !ObjectTypeSearchedInScope) {
- // C++ [basic.lookup.classref]p4:
+ if (!ObjectType.isNull() && !ObjectTypeSearchedInScope &&
+ !getLangOpts().CPlusPlus0x) {
+ // C++03 [basic.lookup.classref]p4:
// [...] If the name is found in both contexts, the
// class-name-or-namespace-name shall refer to the same entity.
//
@@ -548,6 +548,8 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
// into the current scope (the scope of the postfix-expression) to
// see if we can find the same name there. As above, if there is no
// scope, reconstruct the result from the template instantiation itself.
+ //
+ // Note that C++11 does *not* perform this redundant lookup.
NamedDecl *OuterDecl;
if (S) {
LookupResult FoundOuter(*this, &Identifier, IdentifierLoc,
diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp
index 54683e1..8199751 100644
--- a/lib/Sema/SemaCast.cpp
+++ b/lib/Sema/SemaCast.cpp
@@ -561,8 +561,8 @@ void CastOperation::CheckDynamicCast() {
assert(DestPointer && "Reference to void is not possible");
} else if (DestRecord) {
if (Self.RequireCompleteType(OpRange.getBegin(), DestPointee,
- Self.PDiag(diag::err_bad_dynamic_cast_incomplete)
- << DestRange))
+ diag::err_bad_dynamic_cast_incomplete,
+ DestRange))
return;
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
@@ -597,8 +597,8 @@ void CastOperation::CheckDynamicCast() {
const RecordType *SrcRecord = SrcPointee->getAs<RecordType>();
if (SrcRecord) {
if (Self.RequireCompleteType(OpRange.getBegin(), SrcPointee,
- Self.PDiag(diag::err_bad_dynamic_cast_incomplete)
- << SrcExpr.get()->getSourceRange()))
+ diag::err_bad_dynamic_cast_incomplete,
+ SrcExpr.get()))
return;
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
@@ -1075,8 +1075,8 @@ TryStaticDowncast(Sema &Self, CanQualType SrcType, CanQualType DestType,
QualType OrigDestType, unsigned &msg,
CastKind &Kind, CXXCastPath &BasePath) {
// We can only work with complete types. But don't complain if it doesn't work
- if (Self.RequireCompleteType(OpRange.getBegin(), SrcType, Self.PDiag(0)) ||
- Self.RequireCompleteType(OpRange.getBegin(), DestType, Self.PDiag(0)))
+ if (Self.RequireCompleteType(OpRange.getBegin(), SrcType, 0) ||
+ Self.RequireCompleteType(OpRange.getBegin(), DestType, 0))
return TC_NotApplicable;
// Downcast can only happen in class hierarchies, so we need classes.
@@ -1302,7 +1302,9 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType,
CastKind &Kind, bool ListInitialization) {
if (DestType->isRecordType()) {
if (Self.RequireCompleteType(OpRange.getBegin(), DestType,
- diag::err_bad_dynamic_cast_incomplete)) {
+ diag::err_bad_dynamic_cast_incomplete) ||
+ Self.RequireNonAbstractType(OpRange.getBegin(), DestType,
+ diag::err_allocation_of_abstract_type)) {
msg = 0;
return TC_Failed;
}
@@ -1504,10 +1506,9 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
}
if (const ReferenceType *DestTypeTmp = DestType->getAs<ReferenceType>()) {
- bool LValue = DestTypeTmp->isLValueReferenceType();
- if (LValue && !SrcExpr.get()->isLValue()) {
- // Cannot cast non-lvalue to lvalue reference type. See the similar
- // comment in const_cast.
+ if (!SrcExpr.get()->isGLValue()) {
+ // Cannot cast non-glvalue to (lvalue or rvalue) reference type. See the
+ // similar comment in const_cast.
msg = diag::err_bad_cxx_cast_rvalue;
return TC_NotApplicable;
}
@@ -1915,10 +1916,6 @@ void CastOperation::CheckCStyleCast() {
return;
QualType SrcType = SrcExpr.get()->getType();
- // You can cast an _Atomic(T) to anything you can cast a T to.
- if (const AtomicType *AtomicSrcType = SrcType->getAs<AtomicType>())
- SrcType = AtomicSrcType->getValueType();
-
assert(!SrcType->isPlaceholderType());
if (Self.RequireCompleteType(OpRange.getBegin(), DestType,
@@ -2105,6 +2102,9 @@ ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo,
Op.CheckCXXCStyleCast(/*FunctionalStyle=*/true, /*ListInit=*/false);
if (Op.SrcExpr.isInvalid())
return ExprError();
+
+ if (CXXConstructExpr *ConstructExpr = dyn_cast<CXXConstructExpr>(Op.SrcExpr.get()))
+ ConstructExpr->setParenRange(SourceRange(LPLoc, RPLoc));
return Op.complete(CXXFunctionalCastExpr::Create(Context, Op.ResultType,
Op.ValueKind, CastTypeInfo, Op.DestRange.getBegin(),
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 0d15ce2..2594648 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -16,12 +16,14 @@
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Initialization.h"
+#include "clang/Sema/Lookup.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Analysis/Analyses/FormatString.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/EvaluatedExprVisitor.h"
@@ -66,16 +68,31 @@ static bool checkArgCount(Sema &S, CallExpr *call, unsigned desiredArgCount) {
<< call->getArg(1)->getSourceRange();
}
-/// CheckBuiltinAnnotationString - Checks that string argument to the builtin
-/// annotation is a non wide string literal.
-static bool CheckBuiltinAnnotationString(Sema &S, Expr *Arg) {
- Arg = Arg->IgnoreParenCasts();
- StringLiteral *Literal = dyn_cast<StringLiteral>(Arg);
+/// Check that the first argument to __builtin_annotation is an integer
+/// and the second argument is a non-wide string literal.
+static bool SemaBuiltinAnnotation(Sema &S, CallExpr *TheCall) {
+ if (checkArgCount(S, TheCall, 2))
+ return true;
+
+ // First argument should be an integer.
+ Expr *ValArg = TheCall->getArg(0);
+ QualType Ty = ValArg->getType();
+ if (!Ty->isIntegerType()) {
+ S.Diag(ValArg->getLocStart(), diag::err_builtin_annotation_first_arg)
+ << ValArg->getSourceRange();
+ return true;
+ }
+
+ // Second argument should be a constant string.
+ Expr *StrArg = TheCall->getArg(1)->IgnoreParenCasts();
+ StringLiteral *Literal = dyn_cast<StringLiteral>(StrArg);
if (!Literal || !Literal->isAscii()) {
- S.Diag(Arg->getLocStart(), diag::err_builtin_annotation_not_string_constant)
- << Arg->getSourceRange();
+ S.Diag(StrArg->getLocStart(), diag::err_builtin_annotation_second_arg)
+ << StrArg->getSourceRange();
return true;
}
+
+ TheCall->setType(Ty);
return false;
}
@@ -256,7 +273,7 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return SemaAtomicOpsOverloaded(move(TheCallResult), AtomicExpr::AO##ID);
#include "clang/Basic/Builtins.def"
case Builtin::BI__builtin_annotation:
- if (CheckBuiltinAnnotationString(*this, TheCall->getArg(1)))
+ if (SemaBuiltinAnnotation(*this, TheCall))
return ExprError();
break;
}
@@ -270,6 +287,13 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
if (CheckARMBuiltinFunctionCall(BuiltinID, TheCall))
return ExprError();
break;
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ if (CheckMipsBuiltinFunctionCall(BuiltinID, TheCall))
+ return ExprError();
+ break;
default:
break;
}
@@ -331,7 +355,7 @@ static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context) {
bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
llvm::APSInt Result;
- unsigned mask = 0;
+ uint64_t mask = 0;
unsigned TV = 0;
int PtrArgNum = -1;
bool HasConstPtr = false;
@@ -349,7 +373,7 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
TV = Result.getLimitedValue(64);
- if ((TV > 63) || (mask & (1 << TV)) == 0)
+ if ((TV > 63) || (mask & (1ULL << TV)) == 0)
return Diag(TheCall->getLocStart(), diag::err_invalid_neon_type_code)
<< TheCall->getArg(ImmArg)->getSourceRange();
}
@@ -388,6 +412,11 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
#undef GET_NEON_IMMEDIATE_CHECK
};
+ // We can't check the value of a dependent argument.
+ if (TheCall->getArg(i)->isTypeDependent() ||
+ TheCall->getArg(i)->isValueDependent())
+ return false;
+
// Check that the immediate argument is actually a constant.
if (SemaBuiltinConstantArg(TheCall, i, Result))
return true;
@@ -402,34 +431,119 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return false;
}
-/// CheckFunctionCall - Check a direct function call for various correctness
-/// and safety properties not strictly enforced by the C type system.
-bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
- // Get the IdentifierInfo* for the called function.
- IdentifierInfo *FnInfo = FDecl->getIdentifier();
+bool Sema::CheckMipsBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
+ unsigned i = 0, l = 0, u = 0;
+ switch (BuiltinID) {
+ default: return false;
+ case Mips::BI__builtin_mips_wrdsp: i = 1; l = 0; u = 63; break;
+ case Mips::BI__builtin_mips_rddsp: i = 0; l = 0; u = 63; break;
+ };
- // None of the checks below are needed for functions that don't have
- // simple names (e.g., C++ conversion functions).
- if (!FnInfo)
+ // We can't check the value of a dependent argument.
+ if (TheCall->getArg(i)->isTypeDependent() ||
+ TheCall->getArg(i)->isValueDependent())
return false;
+ // Check that the immediate argument is actually a constant.
+ llvm::APSInt Result;
+ if (SemaBuiltinConstantArg(TheCall, i, Result))
+ return true;
+
+ // Range check against the upper/lower values for this instruction.
+ unsigned Val = Result.getZExtValue();
+ if (Val < l || Val > u)
+ return Diag(TheCall->getLocStart(), diag::err_argument_invalid_range)
+ << l << u << TheCall->getArg(i)->getSourceRange();
+
+ return false;
+}
+
+/// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo
+/// parameter with the FormatAttr's correct format_idx and firstDataArg.
+/// Returns true when the format fits the function and the FormatStringInfo has
+/// been populated.
+bool Sema::getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember,
+ FormatStringInfo *FSI) {
+ FSI->HasVAListArg = Format->getFirstArg() == 0;
+ FSI->FormatIdx = Format->getFormatIdx() - 1;
+ FSI->FirstDataArg = FSI->HasVAListArg ? 0 : Format->getFirstArg() - 1;
+
+ // The way the format attribute works in GCC, the implicit this argument
+ // of member functions is counted. However, it doesn't appear in our own
+ // lists, so decrement format_idx in that case.
+ if (IsCXXMember) {
+ if(FSI->FormatIdx == 0)
+ return false;
+ --FSI->FormatIdx;
+ if (FSI->FirstDataArg != 0)
+ --FSI->FirstDataArg;
+ }
+ return true;
+}
+
+/// Handles the checks for format strings, non-POD arguments to vararg
+/// functions, and NULL arguments passed to non-NULL parameters.
+void Sema::checkCall(NamedDecl *FDecl, Expr **Args,
+ unsigned NumArgs,
+ unsigned NumProtoArgs,
+ bool IsMemberFunction,
+ SourceLocation Loc,
+ SourceRange Range,
+ VariadicCallType CallType) {
// FIXME: This mechanism should be abstracted to be less fragile and
// more efficient. For example, just map function ids to custom
// handlers.
// Printf and scanf checking.
+ bool HandledFormatString = false;
for (specific_attr_iterator<FormatAttr>
- i = FDecl->specific_attr_begin<FormatAttr>(),
- e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) {
- CheckFormatArguments(*i, TheCall);
- }
+ I = FDecl->specific_attr_begin<FormatAttr>(),
+ E = FDecl->specific_attr_end<FormatAttr>(); I != E ; ++I)
+ if (CheckFormatArguments(*I, Args, NumArgs, IsMemberFunction, CallType,
+ Loc, Range))
+ HandledFormatString = true;
+
+ // Refuse POD arguments that weren't caught by the format string
+ // checks above.
+ if (!HandledFormatString && CallType != VariadicDoesNotApply)
+ for (unsigned ArgIdx = NumProtoArgs; ArgIdx < NumArgs; ++ArgIdx)
+ variadicArgumentPODCheck(Args[ArgIdx], CallType);
for (specific_attr_iterator<NonNullAttr>
- i = FDecl->specific_attr_begin<NonNullAttr>(),
- e = FDecl->specific_attr_end<NonNullAttr>(); i != e; ++i) {
- CheckNonNullArguments(*i, TheCall->getArgs(),
- TheCall->getCallee()->getLocStart());
- }
+ I = FDecl->specific_attr_begin<NonNullAttr>(),
+ E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I)
+ CheckNonNullArguments(*I, Args, Loc);
+}
+
+/// CheckConstructorCall - Check a constructor call for correctness and safety
+/// properties not enforced by the C type system.
+void Sema::CheckConstructorCall(FunctionDecl *FDecl, Expr **Args,
+ unsigned NumArgs,
+ const FunctionProtoType *Proto,
+ SourceLocation Loc) {
+ VariadicCallType CallType =
+ Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply;
+ checkCall(FDecl, Args, NumArgs, Proto->getNumArgs(),
+ /*IsMemberFunction=*/true, Loc, SourceRange(), CallType);
+}
+
+/// CheckFunctionCall - Check a direct function call for various correctness
+/// and safety properties not strictly enforced by the C type system.
+bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
+ const FunctionProtoType *Proto) {
+ bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall);
+ VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
+ TheCall->getCallee());
+ unsigned NumProtoArgs = Proto ? Proto->getNumArgs() : 0;
+ checkCall(FDecl, TheCall->getArgs(), TheCall->getNumArgs(), NumProtoArgs,
+ IsMemberFunction, TheCall->getRParenLoc(),
+ TheCall->getCallee()->getSourceRange(), CallType);
+
+ IdentifierInfo *FnInfo = FDecl->getIdentifier();
+ // None of the checks below are needed for functions that don't have
+ // simple names (e.g., C++ conversion functions).
+ if (!FnInfo)
+ return false;
unsigned CMId = FDecl->getMemoryFunctionKind();
if (CMId == 0)
@@ -448,25 +562,18 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac,
Expr **Args, unsigned NumArgs) {
- for (specific_attr_iterator<FormatAttr>
- i = Method->specific_attr_begin<FormatAttr>(),
- e = Method->specific_attr_end<FormatAttr>(); i != e ; ++i) {
-
- CheckFormatArguments(*i, Args, NumArgs, false, lbrac,
- Method->getSourceRange());
- }
+ VariadicCallType CallType =
+ Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply;
- // diagnose nonnull arguments.
- for (specific_attr_iterator<NonNullAttr>
- i = Method->specific_attr_begin<NonNullAttr>(),
- e = Method->specific_attr_end<NonNullAttr>(); i != e; ++i) {
- CheckNonNullArguments(*i, Args, lbrac);
- }
+ checkCall(Method, Args, NumArgs, Method->param_size(),
+ /*IsMemberFunction=*/false,
+ lbrac, Method->getSourceRange(), CallType);
return false;
}
-bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) {
+bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall,
+ const FunctionProtoType *Proto) {
const VarDecl *V = dyn_cast<VarDecl>(NDecl);
if (!V)
return false;
@@ -475,13 +582,15 @@ bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) {
if (!Ty->isBlockPointerType())
return false;
- // format string checking.
- for (specific_attr_iterator<FormatAttr>
- i = NDecl->specific_attr_begin<FormatAttr>(),
- e = NDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) {
- CheckFormatArguments(*i, TheCall);
- }
+ VariadicCallType CallType =
+ Proto && Proto->isVariadic() ? VariadicBlock : VariadicDoesNotApply ;
+ unsigned NumProtoArgs = Proto ? Proto->getNumArgs() : 0;
+ checkCall(NDecl, TheCall->getArgs(), TheCall->getNumArgs(),
+ NumProtoArgs, /*IsMemberFunction=*/false,
+ TheCall->getRParenLoc(),
+ TheCall->getCallee()->getSourceRange(), CallType);
+
return false;
}
@@ -1260,7 +1369,7 @@ bool Sema::SemaBuiltinUnorderedCompare(CallExpr *TheCall) {
// If the common type isn't a real floating type, then the arguments were
// invalid for this operation.
- if (!Res->isRealFloatingType())
+ if (Res.isNull() || !Res->isRealFloatingType())
return Diag(OrigArg0.get()->getLocStart(),
diag::err_typecheck_call_invalid_ordered_compare)
<< OrigArg0.get()->getType() << OrigArg1.get()->getType()
@@ -1409,7 +1518,11 @@ bool Sema::SemaBuiltinPrefetch(CallExpr *TheCall) {
// constant integers.
for (unsigned i = 1; i != NumArgs; ++i) {
Expr *Arg = TheCall->getArg(i);
-
+
+ // We can't check the value of a dependent argument.
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ continue;
+
llvm::APSInt Result;
if (SemaBuiltinConstantArg(TheCall, i, Result))
return true;
@@ -1454,7 +1567,12 @@ bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
// For compatibility check 0-3, llvm only handles 0 and 2.
bool Sema::SemaBuiltinObjectSize(CallExpr *TheCall) {
llvm::APSInt Result;
-
+
+ // We can't check the value of a dependent argument.
+ if (TheCall->getArg(1)->isTypeDependent() ||
+ TheCall->getArg(1)->isValueDependent())
+ return false;
+
// Check constant-ness first.
if (SemaBuiltinConstantArg(TheCall, 1, Result))
return true;
@@ -1485,14 +1603,19 @@ bool Sema::SemaBuiltinLongjmp(CallExpr *TheCall) {
return false;
}
-// Handle i > 1 ? "x" : "y", recursively.
-bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
- unsigned NumArgs, bool HasVAListArg,
- unsigned format_idx, unsigned firstDataArg,
- FormatStringType Type, bool inFunctionCall) {
+// Determine if an expression is a string literal or constant string.
+// If this function returns false on the arguments to a function expecting a
+// format string, we will usually need to emit a warning.
+// True string literals are then checked by CheckFormatString.
+Sema::StringLiteralCheckType
+Sema::checkFormatStringExpr(const Expr *E, Expr **Args,
+ unsigned NumArgs, bool HasVAListArg,
+ unsigned format_idx, unsigned firstDataArg,
+ FormatStringType Type, VariadicCallType CallType,
+ bool inFunctionCall) {
tryAgain:
if (E->isTypeDependent() || E->isValueDependent())
- return false;
+ return SLCT_NotALiteral;
E = E->IgnoreParenCasts();
@@ -1501,18 +1624,26 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
// The behavior of printf and friends in this case is implementation
// dependent. Ideally if the format string cannot be null then
// it should have a 'nonnull' attribute in the function prototype.
- return true;
+ return SLCT_CheckedLiteral;
switch (E->getStmtClass()) {
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass: {
- const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(E);
- return SemaCheckStringLiteral(C->getTrueExpr(), Args, NumArgs, HasVAListArg,
- format_idx, firstDataArg, Type,
- inFunctionCall)
- && SemaCheckStringLiteral(C->getFalseExpr(), Args, NumArgs, HasVAListArg,
- format_idx, firstDataArg, Type,
- inFunctionCall);
+ // The expression is a literal if both sub-expressions were, and it was
+ // completely checked only if both sub-expressions were checked.
+ const AbstractConditionalOperator *C =
+ cast<AbstractConditionalOperator>(E);
+ StringLiteralCheckType Left =
+ checkFormatStringExpr(C->getTrueExpr(), Args, NumArgs,
+ HasVAListArg, format_idx, firstDataArg,
+ Type, CallType, inFunctionCall);
+ if (Left == SLCT_NotALiteral)
+ return SLCT_NotALiteral;
+ StringLiteralCheckType Right =
+ checkFormatStringExpr(C->getFalseExpr(), Args, NumArgs,
+ HasVAListArg, format_idx, firstDataArg,
+ Type, CallType, inFunctionCall);
+ return Left < Right ? Left : Right;
}
case Stmt::ImplicitCastExprClass: {
@@ -1525,13 +1656,13 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
E = src;
goto tryAgain;
}
- return false;
+ return SLCT_NotALiteral;
case Stmt::PredefinedExprClass:
// While __func__, etc., are technically not string literals, they
// cannot contain format specifiers and thus are not a security
// liability.
- return true;
+ return SLCT_UncheckedLiteral;
case Stmt::DeclRefExprClass: {
const DeclRefExpr *DR = cast<DeclRefExpr>(E);
@@ -1554,10 +1685,17 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
}
if (isConstant) {
- if (const Expr *Init = VD->getAnyInitializer())
- return SemaCheckStringLiteral(Init, Args, NumArgs,
- HasVAListArg, format_idx, firstDataArg,
- Type, /*inFunctionCall*/false);
+ if (const Expr *Init = VD->getAnyInitializer()) {
+ // Look through initializers like const char c[] = { "foo" }
+ if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
+ if (InitList->isStringLiteralInit())
+ Init = InitList->getInit(0)->IgnoreParenImpCasts();
+ }
+ return checkFormatStringExpr(Init, Args, NumArgs,
+ HasVAListArg, format_idx,
+ firstDataArg, Type, CallType,
+ /*inFunctionCall*/false);
+ }
}
// For vprintf* functions (i.e., HasVAListArg==true), we add a
@@ -1590,14 +1728,14 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
// We can't pass a 'scanf' string to a 'printf' function.
if (PVIndex == PVFormat->getFormatIdx() &&
Type == GetFormatStringType(PVFormat))
- return true;
+ return SLCT_UncheckedLiteral;
}
}
}
}
}
- return false;
+ return SLCT_NotALiteral;
}
case Stmt::CallExprClass:
@@ -1611,13 +1749,23 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
--ArgIndex;
const Expr *Arg = CE->getArg(ArgIndex - 1);
- return SemaCheckStringLiteral(Arg, Args, NumArgs, HasVAListArg,
- format_idx, firstDataArg, Type,
- inFunctionCall);
+ return checkFormatStringExpr(Arg, Args, NumArgs,
+ HasVAListArg, format_idx, firstDataArg,
+ Type, CallType, inFunctionCall);
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
+ unsigned BuiltinID = FD->getBuiltinID();
+ if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
+ BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
+ const Expr *Arg = CE->getArg(0);
+ return checkFormatStringExpr(Arg, Args, NumArgs,
+ HasVAListArg, format_idx,
+ firstDataArg, Type, CallType,
+ inFunctionCall);
+ }
}
}
- return false;
+ return SLCT_NotALiteral;
}
case Stmt::ObjCStringLiteralClass:
case Stmt::StringLiteralClass: {
@@ -1630,15 +1778,15 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
if (StrE) {
CheckFormatString(StrE, E, Args, NumArgs, HasVAListArg, format_idx,
- firstDataArg, Type, inFunctionCall);
- return true;
+ firstDataArg, Type, inFunctionCall, CallType);
+ return SLCT_CheckedLiteral;
}
- return false;
+ return SLCT_NotALiteral;
}
default:
- return false;
+ return SLCT_NotALiteral;
}
}
@@ -1667,44 +1815,30 @@ Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
.Default(FST_Unknown);
}
-/// CheckPrintfScanfArguments - Check calls to printf and scanf (and similar
+/// CheckFormatArguments - Check calls to printf and scanf (and similar
/// functions) for correct use of format strings.
-void Sema::CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall) {
- bool IsCXXMember = false;
- // The way the format attribute works in GCC, the implicit this argument
- // of member functions is counted. However, it doesn't appear in our own
- // lists, so decrement format_idx in that case.
- IsCXXMember = isa<CXXMemberCallExpr>(TheCall);
- CheckFormatArguments(Format, TheCall->getArgs(), TheCall->getNumArgs(),
- IsCXXMember, TheCall->getRParenLoc(),
- TheCall->getCallee()->getSourceRange());
-}
-
-void Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args,
+/// Returns true if a format string has been fully checked.
+bool Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args,
unsigned NumArgs, bool IsCXXMember,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange Range) {
- bool HasVAListArg = Format->getFirstArg() == 0;
- unsigned format_idx = Format->getFormatIdx() - 1;
- unsigned firstDataArg = HasVAListArg ? 0 : Format->getFirstArg() - 1;
- if (IsCXXMember) {
- if (format_idx == 0)
- return;
- --format_idx;
- if(firstDataArg != 0)
- --firstDataArg;
- }
- CheckFormatArguments(Args, NumArgs, HasVAListArg, format_idx,
- firstDataArg, GetFormatStringType(Format), Loc, Range);
+ FormatStringInfo FSI;
+ if (getFormatStringInfo(Format, IsCXXMember, &FSI))
+ return CheckFormatArguments(Args, NumArgs, FSI.HasVAListArg, FSI.FormatIdx,
+ FSI.FirstDataArg, GetFormatStringType(Format),
+ CallType, Loc, Range);
+ return false;
}
-void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs,
+bool Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs,
bool HasVAListArg, unsigned format_idx,
unsigned firstDataArg, FormatStringType Type,
+ VariadicCallType CallType,
SourceLocation Loc, SourceRange Range) {
// CHECK: printf/scanf-like function is called with no format string.
if (format_idx >= NumArgs) {
Diag(Loc, diag::warn_missing_format_string) << Range;
- return;
+ return false;
}
const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts();
@@ -1721,21 +1855,25 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs,
// C string (e.g. "%d")
// ObjC string uses the same format specifiers as C string, so we can use
// the same format string checking logic for both ObjC and C strings.
- if (SemaCheckStringLiteral(OrigFormatExpr, Args, NumArgs, HasVAListArg,
- format_idx, firstDataArg, Type))
- return; // Literal format string found, check done!
+ StringLiteralCheckType CT =
+ checkFormatStringExpr(OrigFormatExpr, Args, NumArgs, HasVAListArg,
+ format_idx, firstDataArg, Type, CallType);
+ if (CT != SLCT_NotALiteral)
+ // Literal format string found, check done!
+ return CT == SLCT_CheckedLiteral;
// Strftime is particular as it always uses a single 'time' argument,
// so it is safe to pass a non-literal string.
if (Type == FST_Strftime)
- return;
+ return false;
// Do not emit diag when the string param is a macro expansion and the
// format is either NSString or CFString. This is a hack to prevent
// diag when using the NSLocalizedString and CFCopyLocalizedString macros
// which are usually used in place of NS and CF string literals.
- if (Type == FST_NSString && Args[format_idx]->getLocStart().isMacroID())
- return;
+ if (Type == FST_NSString &&
+ SourceMgr.isInSystemMacro(Args[format_idx]->getLocStart()))
+ return false;
// If there are no arguments specified, warn with -Wformat-security, otherwise
// warn only with -Wformat-nonliteral.
@@ -1747,6 +1885,7 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs,
Diag(Args[format_idx]->getLocStart(),
diag::warn_format_nonliteral)
<< OrigFormatExpr->getSourceRange();
+ return false;
}
namespace {
@@ -1757,7 +1896,6 @@ protected:
const Expr *OrigFormatExpr;
const unsigned FirstDataArg;
const unsigned NumDataArgs;
- const bool IsObjCLiteral;
const char *Beg; // Start of format string.
const bool HasVAListArg;
const Expr * const *Args;
@@ -1767,21 +1905,20 @@ protected:
bool usesPositionalArgs;
bool atFirstArg;
bool inFunctionCall;
+ Sema::VariadicCallType CallType;
public:
CheckFormatHandler(Sema &s, const StringLiteral *fexpr,
const Expr *origFormatExpr, unsigned firstDataArg,
- unsigned numDataArgs, bool isObjCLiteral,
- const char *beg, bool hasVAListArg,
+ unsigned numDataArgs, const char *beg, bool hasVAListArg,
Expr **args, unsigned numArgs,
- unsigned formatIdx, bool inFunctionCall)
+ unsigned formatIdx, bool inFunctionCall,
+ Sema::VariadicCallType callType)
: S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr),
- FirstDataArg(firstDataArg),
- NumDataArgs(numDataArgs),
- IsObjCLiteral(isObjCLiteral), Beg(beg),
- HasVAListArg(hasVAListArg),
+ FirstDataArg(firstDataArg), NumDataArgs(numDataArgs),
+ Beg(beg), HasVAListArg(hasVAListArg),
Args(args), NumArgs(numArgs), FormatIdx(formatIdx),
usesPositionalArgs(false), atFirstArg(true),
- inFunctionCall(inFunctionCall) {
+ inFunctionCall(inFunctionCall), CallType(callType) {
CoveredArgs.resize(numDataArgs);
CoveredArgs.reset();
}
@@ -1938,7 +2075,7 @@ void CheckFormatHandler::HandleZeroPosition(const char *startPos,
}
void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
- if (!IsObjCLiteral) {
+ if (!isa<ObjCStringLiteral>(OrigFormatExpr)) {
// The presence of a null character is likely an error.
EmitFormatDiagnostic(
S.PDiag(diag::warn_printf_format_string_contains_null_char),
@@ -1947,6 +2084,8 @@ void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
}
}
+// Note that this may return NULL if there was an error parsing or building
+// one of the argument expressions.
const Expr *CheckFormatHandler::getDataArg(unsigned i) const {
return Args[FirstDataArg + i];
}
@@ -1960,9 +2099,14 @@ void CheckFormatHandler::DoneProcessing() {
signed notCoveredArg = CoveredArgs.find_first();
if (notCoveredArg >= 0) {
assert((unsigned)notCoveredArg < NumDataArgs);
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_data_arg_not_used),
- getDataArg((unsigned) notCoveredArg)->getLocStart(),
- /*IsStringLocation*/false, getFormatStringRange());
+ if (const Expr *E = getDataArg((unsigned) notCoveredArg)) {
+ SourceLocation Loc = E->getLocStart();
+ if (!S.getSourceManager().isInSystemMacro(Loc)) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_data_arg_not_used),
+ Loc, /*IsStringLocation*/false,
+ getFormatStringRange());
+ }
+ }
}
}
}
@@ -2086,17 +2230,20 @@ void CheckFormatHandler::EmitFormatDiagnostic(Sema &S, bool InFunctionCall,
namespace {
class CheckPrintfHandler : public CheckFormatHandler {
+ bool ObjCContext;
public:
CheckPrintfHandler(Sema &s, const StringLiteral *fexpr,
const Expr *origFormatExpr, unsigned firstDataArg,
- unsigned numDataArgs, bool isObjCLiteral,
+ unsigned numDataArgs, bool isObjC,
const char *beg, bool hasVAListArg,
Expr **Args, unsigned NumArgs,
- unsigned formatIdx, bool inFunctionCall)
+ unsigned formatIdx, bool inFunctionCall,
+ Sema::VariadicCallType CallType)
: CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
- numDataArgs, isObjCLiteral, beg, hasVAListArg,
- Args, NumArgs, formatIdx, inFunctionCall) {}
-
+ numDataArgs, beg, hasVAListArg, Args, NumArgs,
+ formatIdx, inFunctionCall, CallType), ObjCContext(isObjC)
+ {}
+
bool HandleInvalidPrintfConversionSpecifier(
const analyze_printf::PrintfSpecifier &FS,
@@ -2106,7 +2253,11 @@ public:
bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
const char *startSpecifier,
unsigned specifierLen);
-
+ bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
+ const char *StartSpecifier,
+ unsigned SpecifierLen,
+ const Expr *E);
+
bool HandleAmount(const analyze_format_string::OptionalAmount &Amt, unsigned k,
const char *startSpecifier, unsigned specifierLen);
void HandleInvalidAmount(const analyze_printf::PrintfSpecifier &FS,
@@ -2120,6 +2271,9 @@ public:
const analyze_printf::OptionalFlag &ignoredFlag,
const analyze_printf::OptionalFlag &flag,
const char *startSpecifier, unsigned specifierLen);
+ bool checkForCStrMembers(const analyze_printf::ArgType &AT,
+ const Expr *E, const CharSourceRange &CSR);
+
};
}
@@ -2161,14 +2315,17 @@ bool CheckPrintfHandler::HandleAmount(
// doesn't emit a warning for that case.
CoveredArgs.set(argIndex);
const Expr *Arg = getDataArg(argIndex);
+ if (!Arg)
+ return false;
+
QualType T = Arg->getType();
- const analyze_printf::ArgTypeResult &ATR = Amt.getArgType(S.Context);
- assert(ATR.isValid());
+ const analyze_printf::ArgType &AT = Amt.getArgType(S.Context);
+ assert(AT.isValid());
- if (!ATR.matchesType(S.Context, T)) {
+ if (!AT.matchesType(S.Context, T)) {
EmitFormatDiagnostic(S.PDiag(diag::warn_printf_asterisk_wrong_type)
- << k << ATR.getRepresentativeTypeName(S.Context)
+ << k << AT.getRepresentativeTypeName(S.Context)
<< T << Arg->getSourceRange(),
getLocationOfByte(Amt.getStart()),
/*IsStringLocation*/true,
@@ -2237,6 +2394,64 @@ void CheckPrintfHandler::HandleIgnoredFlag(
getSpecifierRange(ignoredFlag.getPosition(), 1)));
}
+// Determines if the specified is a C++ class or struct containing
+// a member with the specified name and kind (e.g. a CXXMethodDecl named
+// "c_str()").
+template<typename MemberKind>
+static llvm::SmallPtrSet<MemberKind*, 1>
+CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
+ const RecordType *RT = Ty->getAs<RecordType>();
+ llvm::SmallPtrSet<MemberKind*, 1> Results;
+
+ if (!RT)
+ return Results;
+ const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+ if (!RD)
+ return Results;
+
+ LookupResult R(S, &S.PP.getIdentifierTable().get(Name), SourceLocation(),
+ Sema::LookupMemberName);
+
+ // We just need to include all members of the right kind turned up by the
+ // filter, at this point.
+ if (S.LookupQualifiedName(R, RT->getDecl()))
+ for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
+ NamedDecl *decl = (*I)->getUnderlyingDecl();
+ if (MemberKind *FK = dyn_cast<MemberKind>(decl))
+ Results.insert(FK);
+ }
+ return Results;
+}
+
+// Check if a (w)string was passed when a (w)char* was needed, and offer a
+// better diagnostic if so. AT is assumed to be valid.
+// Returns true when a c_str() conversion method is found.
+bool CheckPrintfHandler::checkForCStrMembers(
+ const analyze_printf::ArgType &AT, const Expr *E,
+ const CharSourceRange &CSR) {
+ typedef llvm::SmallPtrSet<CXXMethodDecl*, 1> MethodSet;
+
+ MethodSet Results =
+ CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, E->getType());
+
+ for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
+ MI != ME; ++MI) {
+ const CXXMethodDecl *Method = *MI;
+ if (Method->getNumParams() == 0 &&
+ AT.matchesType(S.Context, Method->getResultType())) {
+ // FIXME: Suggest parens if the expression needs them.
+ SourceLocation EndLoc =
+ S.getPreprocessor().getLocForEndOfToken(E->getLocEnd());
+ S.Diag(E->getLocStart(), diag::note_printf_c_str)
+ << "c_str()"
+ << FixItHint::CreateInsertion(EndLoc, ".c_str()");
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool
CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
&FS,
@@ -2288,7 +2503,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
// Check for using an Objective-C specific conversion specifier
// in a non-ObjC literal.
- if (!IsObjCLiteral && CS.isObjCArg()) {
+ if (!ObjCContext && CS.isObjCArg()) {
return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
specifierLen);
}
@@ -2346,17 +2561,6 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
HandleNonStandardConversionSpecification(LM, CS, startSpecifier,
specifierLen);
- // Are we using '%n'?
- if (CS.getKind() == ConversionSpecifier::nArg) {
- // Issue a warning about this being a possible security issue.
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_write_back),
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
- // Continue checking the other format specifiers.
- return true;
- }
-
// The remaining checks depend on the data arguments.
if (HasVAListArg)
return true;
@@ -2364,54 +2568,98 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex))
return false;
+ const Expr *Arg = getDataArg(argIndex);
+ if (!Arg)
+ return true;
+
+ return checkFormatExpr(FS, startSpecifier, specifierLen, Arg);
+}
+
+bool
+CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
+ const char *StartSpecifier,
+ unsigned SpecifierLen,
+ const Expr *E) {
+ using namespace analyze_format_string;
+ using namespace analyze_printf;
// Now type check the data expression that matches the
// format specifier.
- const Expr *Ex = getDataArg(argIndex);
- const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context,
- IsObjCLiteral);
- if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) {
- // Check if we didn't match because of an implicit cast from a 'char'
- // or 'short' to an 'int'. This is done because printf is a varargs
- // function.
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Ex))
- if (ICE->getType() == S.Context.IntTy) {
- // All further checking is done on the subexpression.
- Ex = ICE->getSubExpr();
- if (ATR.matchesType(S.Context, Ex->getType()))
- return true;
+ const analyze_printf::ArgType &AT = FS.getArgType(S.Context,
+ ObjCContext);
+ if (AT.isValid() && !AT.matchesType(S.Context, E->getType())) {
+ // Look through argument promotions for our error message's reported type.
+ // This includes the integral and floating promotions, but excludes array
+ // and function pointer decay; seeing that an argument intended to be a
+ // string has type 'char [6]' is probably more confusing than 'char *'.
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ if (ICE->getCastKind() == CK_IntegralCast ||
+ ICE->getCastKind() == CK_FloatingCast) {
+ E = ICE->getSubExpr();
+
+ // Check if we didn't match because of an implicit cast from a 'char'
+ // or 'short' to an 'int'. This is done because printf is a varargs
+ // function.
+ if (ICE->getType() == S.Context.IntTy ||
+ ICE->getType() == S.Context.UnsignedIntTy) {
+ // All further checking is done on the subexpression.
+ if (AT.matchesType(S.Context, E->getType()))
+ return true;
+ }
}
+ }
// We may be able to offer a FixItHint if it is a supported type.
PrintfSpecifier fixedFS = FS;
- bool success = fixedFS.fixType(Ex->getType(), S.getLangOpts(),
- S.Context, IsObjCLiteral);
+ bool success = fixedFS.fixType(E->getType(), S.getLangOpts(),
+ S.Context, ObjCContext);
if (success) {
// Get the fix string from the fixed format specifier
- SmallString<128> buf;
+ SmallString<16> buf;
llvm::raw_svector_ostream os(buf);
fixedFS.toString(os);
EmitFormatDiagnostic(
S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
- << ATR.getRepresentativeTypeName(S.Context) << Ex->getType()
- << Ex->getSourceRange(),
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen),
+ << AT.getRepresentativeTypeName(S.Context) << E->getType()
+ << E->getSourceRange(),
+ E->getLocStart(),
+ /*IsStringLocation*/false,
+ getSpecifierRange(StartSpecifier, SpecifierLen),
FixItHint::CreateReplacement(
- getSpecifierRange(startSpecifier, specifierLen),
+ getSpecifierRange(StartSpecifier, SpecifierLen),
os.str()));
- }
- else {
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
- << ATR.getRepresentativeTypeName(S.Context) << Ex->getType()
- << getSpecifierRange(startSpecifier, specifierLen)
- << Ex->getSourceRange(),
- getLocationOfByte(CS.getStart()),
- true,
- getSpecifierRange(startSpecifier, specifierLen));
+ } else {
+ const CharSourceRange &CSR = getSpecifierRange(StartSpecifier,
+ SpecifierLen);
+ // Since the warning for passing non-POD types to variadic functions
+ // was deferred until now, we emit a warning for non-POD
+ // arguments here.
+ if (S.isValidVarArgType(E->getType()) == Sema::VAK_Invalid) {
+ unsigned DiagKind;
+ if (E->getType()->isObjCObjectType())
+ DiagKind = diag::err_cannot_pass_objc_interface_to_vararg_format;
+ else
+ DiagKind = diag::warn_non_pod_vararg_with_format_string;
+
+ EmitFormatDiagnostic(
+ S.PDiag(DiagKind)
+ << S.getLangOpts().CPlusPlus0x
+ << E->getType()
+ << CallType
+ << AT.getRepresentativeTypeName(S.Context)
+ << CSR
+ << E->getSourceRange(),
+ E->getLocStart(), /*IsStringLocation*/false, CSR);
+
+ checkForCStrMembers(AT, E, CSR);
+ } else
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
+ << AT.getRepresentativeTypeName(S.Context) << E->getType()
+ << CSR
+ << E->getSourceRange(),
+ E->getLocStart(), /*IsStringLocation*/false, CSR);
}
}
@@ -2425,13 +2673,14 @@ class CheckScanfHandler : public CheckFormatHandler {
public:
CheckScanfHandler(Sema &s, const StringLiteral *fexpr,
const Expr *origFormatExpr, unsigned firstDataArg,
- unsigned numDataArgs, bool isObjCLiteral,
- const char *beg, bool hasVAListArg,
+ unsigned numDataArgs, const char *beg, bool hasVAListArg,
Expr **Args, unsigned NumArgs,
- unsigned formatIdx, bool inFunctionCall)
+ unsigned formatIdx, bool inFunctionCall,
+ Sema::VariadicCallType CallType)
: CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
- numDataArgs, isObjCLiteral, beg, hasVAListArg,
- Args, NumArgs, formatIdx, inFunctionCall) {}
+ numDataArgs, beg, hasVAListArg,
+ Args, NumArgs, formatIdx, inFunctionCall, CallType)
+ {}
bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
const char *startSpecifier,
@@ -2548,8 +2797,11 @@ bool CheckScanfHandler::HandleScanfSpecifier(
// Check that the argument type matches the format specifier.
const Expr *Ex = getDataArg(argIndex);
- const analyze_scanf::ScanfArgTypeResult &ATR = FS.getArgType(S.Context);
- if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) {
+ if (!Ex)
+ return true;
+
+ const analyze_format_string::ArgType &AT = FS.getArgType(S.Context);
+ if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType())) {
ScanfSpecifier fixedFS = FS;
bool success = fixedFS.fixType(Ex->getType(), S.getLangOpts(),
S.Context);
@@ -2562,10 +2814,10 @@ bool CheckScanfHandler::HandleScanfSpecifier(
EmitFormatDiagnostic(
S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
- << ATR.getRepresentativeTypeName(S.Context) << Ex->getType()
+ << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
<< Ex->getSourceRange(),
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/true,
+ Ex->getLocStart(),
+ /*IsStringLocation*/false,
getSpecifierRange(startSpecifier, specifierLen),
FixItHint::CreateReplacement(
getSpecifierRange(startSpecifier, specifierLen),
@@ -2573,10 +2825,10 @@ bool CheckScanfHandler::HandleScanfSpecifier(
} else {
EmitFormatDiagnostic(
S.PDiag(diag::warn_printf_conversion_argument_type_mismatch)
- << ATR.getRepresentativeTypeName(S.Context) << Ex->getType()
+ << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
<< Ex->getSourceRange(),
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/true,
+ Ex->getLocStart(),
+ /*IsStringLocation*/false,
getSpecifierRange(startSpecifier, specifierLen));
}
}
@@ -2589,10 +2841,10 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
Expr **Args, unsigned NumArgs,
bool HasVAListArg, unsigned format_idx,
unsigned firstDataArg, FormatStringType Type,
- bool inFunctionCall) {
+ bool inFunctionCall, VariadicCallType CallType) {
// CHECK: is the format string a wide literal?
- if (!FExpr->isAscii()) {
+ if (!FExpr->isAscii() && !FExpr->isUTF8()) {
CheckFormatHandler::EmitFormatDiagnostic(
*this, inFunctionCall, Args[format_idx],
PDiag(diag::warn_format_string_is_wide_literal), FExpr->getLocStart(),
@@ -2617,18 +2869,17 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
if (Type == FST_Printf || Type == FST_NSString) {
CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
- numDataArgs, isa<ObjCStringLiteral>(OrigFormatExpr),
+ numDataArgs, (Type == FST_NSString),
Str, HasVAListArg, Args, NumArgs, format_idx,
- inFunctionCall);
+ inFunctionCall, CallType);
if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
getLangOpts()))
H.DoneProcessing();
} else if (Type == FST_Scanf) {
- CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
- numDataArgs, isa<ObjCStringLiteral>(OrigFormatExpr),
+ CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
Str, HasVAListArg, Args, NumArgs, format_idx,
- inFunctionCall);
+ inFunctionCall, CallType);
if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
getLangOpts()))
@@ -2728,19 +2979,43 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
// TODO: For strncpy() and friends, this could suggest sizeof(dst)
// over sizeof(src) as well.
unsigned ActionIdx = 0; // Default is to suggest dereferencing.
+ StringRef ReadableName = FnName->getName();
+
if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
if (UnaryOp->getOpcode() == UO_AddrOf)
ActionIdx = 1; // If its an address-of operator, just remove it.
if (Context.getTypeSize(PointeeTy) == Context.getCharWidth())
ActionIdx = 2; // If the pointee's size is sizeof(char),
// suggest an explicit length.
- unsigned DestSrcSelect =
- (BId == Builtin::BIstrndup ? 1 : ArgIdx);
- DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest,
+
+ // If the function is defined as a builtin macro, do not show macro
+ // expansion.
+ SourceLocation SL = SizeOfArg->getExprLoc();
+ SourceRange DSR = Dest->getSourceRange();
+ SourceRange SSR = SizeOfArg->getSourceRange();
+ SourceManager &SM = PP.getSourceManager();
+
+ if (SM.isMacroArgExpansion(SL)) {
+ ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
+ SL = SM.getSpellingLoc(SL);
+ DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
+ SM.getSpellingLoc(DSR.getEnd()));
+ SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
+ SM.getSpellingLoc(SSR.getEnd()));
+ }
+
+ DiagRuntimeBehavior(SL, SizeOfArg,
PDiag(diag::warn_sizeof_pointer_expr_memaccess)
- << FnName << DestSrcSelect << ActionIdx
- << Dest->getSourceRange()
- << SizeOfArg->getSourceRange());
+ << ReadableName
+ << PointeeTy
+ << DestTy
+ << DSR
+ << SSR);
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
+ << ActionIdx
+ << SSR);
+
break;
}
}
@@ -2826,6 +3101,19 @@ static const Expr *ignoreLiteralAdditions(const Expr *Ex, ASTContext &Ctx) {
return Ex;
}
+static bool isConstantSizeArrayWithMoreThanOneElement(QualType Ty,
+ ASTContext &Context) {
+ // Only handle constant-sized or VLAs, but not flexible members.
+ if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(Ty)) {
+ // Only issue the FIXIT for arrays of size > 1.
+ if (CAT->getSize().getSExtValue() <= 1)
+ return false;
+ } else if (!Ty->isVariableArrayType()) {
+ return false;
+ }
+ return true;
+}
+
// Warn if the user has made the 'size' argument to strlcpy or strlcat
// be the size of the source, instead of the destination.
void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
@@ -2876,16 +3164,8 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
// pointers if we know the actual size, like if DstArg is 'array+2'
// we could say 'sizeof(array)-2'.
const Expr *DstArg = Call->getArg(0)->IgnoreParenImpCasts();
- QualType DstArgTy = DstArg->getType();
-
- // Only handle constant-sized or VLAs, but not flexible members.
- if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(DstArgTy)) {
- // Only issue the FIXIT for arrays of size > 1.
- if (CAT->getSize().getSExtValue() <= 1)
- return;
- } else if (!DstArgTy->isVariableArrayType()) {
+ if (!isConstantSizeArrayWithMoreThanOneElement(DstArg->getType(), Context))
return;
- }
SmallString<128> sizeString;
llvm::raw_svector_ostream OS(sizeString);
@@ -2967,26 +3247,23 @@ void Sema::CheckStrncatArguments(const CallExpr *CE,
SM.getSpellingLoc(SR.getEnd()));
}
+ // Check if the destination is an array (rather than a pointer to an array).
+ QualType DstTy = DstArg->getType();
+ bool isKnownSizeArray = isConstantSizeArrayWithMoreThanOneElement(DstTy,
+ Context);
+ if (!isKnownSizeArray) {
+ if (PatternType == 1)
+ Diag(SL, diag::warn_strncat_wrong_size) << SR;
+ else
+ Diag(SL, diag::warn_strncat_src_size) << SR;
+ return;
+ }
+
if (PatternType == 1)
Diag(SL, diag::warn_strncat_large_size) << SR;
else
Diag(SL, diag::warn_strncat_src_size) << SR;
- // Output a FIXIT hint if the destination is an array (rather than a
- // pointer to an array). This could be enhanced to handle some
- // pointers if we know the actual size, like if DstArg is 'array+2'
- // we could say 'sizeof(array)-2'.
- QualType DstArgTy = DstArg->getType();
-
- // Only handle constant-sized or VLAs, but not flexible members.
- if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(DstArgTy)) {
- // Only issue the FIXIT for arrays of size > 1.
- if (CAT->getSize().getSExtValue() <= 1)
- return;
- } else if (!DstArgTy->isVariableArrayType()) {
- return;
- }
-
SmallString<128> sizeString;
llvm::raw_svector_ostream OS(sizeString);
OS << "sizeof(";
@@ -3002,8 +3279,10 @@ void Sema::CheckStrncatArguments(const CallExpr *CE,
//===--- CHECK: Return Address of Stack Variable --------------------------===//
-static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars);
-static Expr *EvalAddr(Expr* E, SmallVectorImpl<DeclRefExpr *> &refVars);
+static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars,
+ Decl *ParentDecl);
+static Expr *EvalAddr(Expr* E, SmallVectorImpl<DeclRefExpr *> &refVars,
+ Decl *ParentDecl);
/// CheckReturnStackAddr - Check if a return statement returns the address
/// of a stack variable.
@@ -3018,9 +3297,9 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType,
// label addresses or references to temporaries.
if (lhsType->isPointerType() ||
(!getLangOpts().ObjCAutoRefCount && lhsType->isBlockPointerType())) {
- stackE = EvalAddr(RetValExp, refVars);
+ stackE = EvalAddr(RetValExp, refVars, /*ParentDecl=*/0);
} else if (lhsType->isReferenceType()) {
- stackE = EvalVal(RetValExp, refVars);
+ stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/0);
}
if (stackE == 0)
@@ -3094,7 +3373,8 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType,
/// * arbitrary interplay between "&" and "*" operators
/// * pointer arithmetic from an address of a stack variable
/// * taking the address of an array element where the array is on the stack
-static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
+static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars,
+ Decl *ParentDecl) {
if (E->isTypeDependent())
return NULL;
@@ -3120,7 +3400,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
V->getType()->isReferenceType() && V->hasInit()) {
// Add the reference variable to the "trail".
refVars.push_back(DR);
- return EvalAddr(V->getInit(), refVars);
+ return EvalAddr(V->getInit(), refVars, ParentDecl);
}
return NULL;
@@ -3132,7 +3412,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
UnaryOperator *U = cast<UnaryOperator>(E);
if (U->getOpcode() == UO_AddrOf)
- return EvalVal(U->getSubExpr(), refVars);
+ return EvalVal(U->getSubExpr(), refVars, ParentDecl);
else
return NULL;
}
@@ -3153,7 +3433,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
if (!Base->getType()->isPointerType()) Base = B->getRHS();
assert (Base->getType()->isPointerType());
- return EvalAddr(Base, refVars);
+ return EvalAddr(Base, refVars, ParentDecl);
}
// For conditional operators we need to see if either the LHS or RHS are
@@ -3165,7 +3445,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
if (Expr *lhsExpr = C->getLHS()) {
// In C++, we can have a throw-expression, which has 'void' type.
if (!lhsExpr->getType()->isVoidType())
- if (Expr* LHS = EvalAddr(lhsExpr, refVars))
+ if (Expr* LHS = EvalAddr(lhsExpr, refVars, ParentDecl))
return LHS;
}
@@ -3173,7 +3453,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
if (C->getRHS()->getType()->isVoidType())
return NULL;
- return EvalAddr(C->getRHS(), refVars);
+ return EvalAddr(C->getRHS(), refVars, ParentDecl);
}
case Stmt::BlockExprClass:
@@ -3185,7 +3465,8 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
return E; // address of label.
case Stmt::ExprWithCleanupsClass:
- return EvalAddr(cast<ExprWithCleanups>(E)->getSubExpr(), refVars);
+ return EvalAddr(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,
+ ParentDecl);
// For casts, we need to handle conversions from arrays to
// pointer values, and pointer-to-pointer conversions.
@@ -3209,10 +3490,10 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
- return EvalAddr(SubExpr, refVars);
+ return EvalAddr(SubExpr, refVars, ParentDecl);
case CK_ArrayToPointerDecay:
- return EvalVal(SubExpr, refVars);
+ return EvalVal(SubExpr, refVars, ParentDecl);
default:
return 0;
@@ -3222,7 +3503,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
case Stmt::MaterializeTemporaryExprClass:
if (Expr *Result = EvalAddr(
cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
- refVars))
+ refVars, ParentDecl))
return Result;
return E;
@@ -3236,7 +3517,8 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
/// EvalVal - This function is complements EvalAddr in the mutual recursion.
/// See the comments for EvalAddr for more details.
-static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) {
+static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars,
+ Decl *ParentDecl) {
do {
// We should only be called for evaluating non-pointer expressions, or
// expressions with a pointer type that are not used as references but instead
@@ -3258,7 +3540,7 @@ do {
}
case Stmt::ExprWithCleanupsClass:
- return EvalVal(cast<ExprWithCleanups>(E)->getSubExpr(), refVars);
+ return EvalVal(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,ParentDecl);
case Stmt::DeclRefExprClass: {
// When we hit a DeclRefExpr we are looking at code that refers to a
@@ -3266,7 +3548,11 @@ do {
// local storage within the function, and if so, return the expression.
DeclRefExpr *DR = cast<DeclRefExpr>(E);
- if (VarDecl *V = dyn_cast<VarDecl>(DR->getDecl()))
+ if (VarDecl *V = dyn_cast<VarDecl>(DR->getDecl())) {
+ // Check if it refers to itself, e.g. "int& i = i;".
+ if (V == ParentDecl)
+ return DR;
+
if (V->hasLocalStorage()) {
if (!V->getType()->isReferenceType())
return DR;
@@ -3276,9 +3562,10 @@ do {
if (V->hasInit()) {
// Add the reference variable to the "trail".
refVars.push_back(DR);
- return EvalVal(V->getInit(), refVars);
+ return EvalVal(V->getInit(), refVars, V);
}
}
+ }
return NULL;
}
@@ -3290,7 +3577,7 @@ do {
UnaryOperator *U = cast<UnaryOperator>(E);
if (U->getOpcode() == UO_Deref)
- return EvalAddr(U->getSubExpr(), refVars);
+ return EvalAddr(U->getSubExpr(), refVars, ParentDecl);
return NULL;
}
@@ -3299,7 +3586,7 @@ do {
// Array subscripts are potential references to data on the stack. We
// retrieve the DeclRefExpr* for the array variable if it indeed
// has local storage.
- return EvalAddr(cast<ArraySubscriptExpr>(E)->getBase(), refVars);
+ return EvalAddr(cast<ArraySubscriptExpr>(E)->getBase(), refVars,ParentDecl);
}
case Stmt::ConditionalOperatorClass: {
@@ -3309,10 +3596,10 @@ do {
// Handle the GNU extension for missing LHS.
if (Expr *lhsExpr = C->getLHS())
- if (Expr *LHS = EvalVal(lhsExpr, refVars))
+ if (Expr *LHS = EvalVal(lhsExpr, refVars, ParentDecl))
return LHS;
- return EvalVal(C->getRHS(), refVars);
+ return EvalVal(C->getRHS(), refVars, ParentDecl);
}
// Accesses to members are potential references to data on the stack.
@@ -3328,13 +3615,13 @@ do {
if (M->getMemberDecl()->getType()->isReferenceType())
return NULL;
- return EvalVal(M->getBase(), refVars);
+ return EvalVal(M->getBase(), refVars, ParentDecl);
}
case Stmt::MaterializeTemporaryExprClass:
if (Expr *Result = EvalVal(
cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
- refVars))
+ refVars, ParentDecl))
return Result;
return E;
@@ -3357,8 +3644,6 @@ do {
/// Issue a warning if these are no self-comparisons, as they are not likely
/// to do what the programmer intended.
void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) {
- bool EmitWarning = true;
-
Expr* LeftExprSansParen = LHS->IgnoreParenImpCasts();
Expr* RightExprSansParen = RHS->IgnoreParenImpCasts();
@@ -3367,7 +3652,7 @@ void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) {
if (DeclRefExpr* DRL = dyn_cast<DeclRefExpr>(LeftExprSansParen))
if (DeclRefExpr* DRR = dyn_cast<DeclRefExpr>(RightExprSansParen))
if (DRL->getDecl() == DRR->getDecl())
- EmitWarning = false;
+ return;
// Special case: check for comparisons against literals that can be exactly
@@ -3375,32 +3660,26 @@ void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) {
// is a heuristic: often comparison against such literals are used to
// detect if a value in a variable has not changed. This clearly can
// lead to false negatives.
- if (EmitWarning) {
- if (FloatingLiteral* FLL = dyn_cast<FloatingLiteral>(LeftExprSansParen)) {
- if (FLL->isExact())
- EmitWarning = false;
- } else
- if (FloatingLiteral* FLR = dyn_cast<FloatingLiteral>(RightExprSansParen)){
- if (FLR->isExact())
- EmitWarning = false;
- }
- }
+ if (FloatingLiteral* FLL = dyn_cast<FloatingLiteral>(LeftExprSansParen)) {
+ if (FLL->isExact())
+ return;
+ } else
+ if (FloatingLiteral* FLR = dyn_cast<FloatingLiteral>(RightExprSansParen))
+ if (FLR->isExact())
+ return;
// Check for comparisons with builtin types.
- if (EmitWarning)
- if (CallExpr* CL = dyn_cast<CallExpr>(LeftExprSansParen))
- if (CL->isBuiltinCall())
- EmitWarning = false;
+ if (CallExpr* CL = dyn_cast<CallExpr>(LeftExprSansParen))
+ if (CL->isBuiltinCall())
+ return;
- if (EmitWarning)
- if (CallExpr* CR = dyn_cast<CallExpr>(RightExprSansParen))
- if (CR->isBuiltinCall())
- EmitWarning = false;
+ if (CallExpr* CR = dyn_cast<CallExpr>(RightExprSansParen))
+ if (CR->isBuiltinCall())
+ return;
// Emit the diagnostic.
- if (EmitWarning)
- Diag(Loc, diag::warn_floatingpoint_eq)
- << LHS->getSourceRange() << RHS->getSourceRange();
+ Diag(Loc, diag::warn_floatingpoint_eq)
+ << LHS->getSourceRange() << RHS->getSourceRange();
}
//===--- CHECK: Integer mixed-sign comparisons (-Wsign-compare) --------===//
@@ -3927,9 +4206,10 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
return;
}
- S.Diag(E->getOperatorLoc(), diag::warn_mixed_sign_comparison)
- << LHS->getType() << RHS->getType()
- << LHS->getSourceRange() << RHS->getSourceRange();
+ S.DiagRuntimeBehavior(E->getOperatorLoc(), E,
+ S.PDiag(diag::warn_mixed_sign_comparison)
+ << LHS->getType() << RHS->getType()
+ << LHS->getSourceRange() << RHS->getSourceRange());
}
/// Analyzes an attempt to assign the given value to a bitfield.
@@ -3970,7 +4250,7 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
// Check whether the stored value is equal to the original value.
TruncatedValue = TruncatedValue.extend(OriginalWidth);
- if (Value == TruncatedValue)
+ if (llvm::APSInt::isSameValue(Value, TruncatedValue))
return false;
// Special-case bitfields of width 1: booleans are naturally 0/1, and
@@ -4044,8 +4324,17 @@ void DiagnoseFloatingLiteralImpCast(Sema &S, FloatingLiteral *FL, QualType T,
== llvm::APFloat::opOK && isExact)
return;
+ SmallString<16> PrettySourceValue;
+ Value.toString(PrettySourceValue);
+ SmallString<16> PrettyTargetValue;
+ if (T->isSpecificBuiltinType(BuiltinType::Bool))
+ PrettyTargetValue = IntegerValue == 0 ? "false" : "true";
+ else
+ IntegerValue.toString(PrettyTargetValue);
+
S.Diag(FL->getExprLoc(), diag::warn_impcast_literal_float_to_integer)
- << FL->getType() << T << FL->getSourceRange() << SourceRange(CContext);
+ << FL->getType() << T.getUnqualifiedType() << PrettySourceValue
+ << PrettyTargetValue << FL->getSourceRange() << SourceRange(CContext);
}
std::string PrettyPrintInRange(const llvm::APSInt &Value, IntRange Range) {
@@ -4112,7 +4401,6 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
}
}
}
- return; // Other casts to bool are not checked.
}
// Strip vector types.
@@ -4176,7 +4464,7 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
}
// If the target is integral, always warn.
- if ((TargetBT && TargetBT->isInteger())) {
+ if (TargetBT && TargetBT->isInteger()) {
if (S.SourceMgr.isInSystemMacro(CC))
return;
@@ -4196,19 +4484,26 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
return;
}
- if (!Source->isIntegerType() || !Target->isIntegerType())
- return;
-
if ((E->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull)
- == Expr::NPCK_GNUNull) && Target->isIntegerType()) {
+ == Expr::NPCK_GNUNull) && !Target->isAnyPointerType()
+ && !Target->isBlockPointerType() && !Target->isMemberPointerType()) {
SourceLocation Loc = E->getSourceRange().getBegin();
if (Loc.isMacroID())
Loc = S.SourceMgr.getImmediateExpansionRange(Loc).first;
- S.Diag(Loc, diag::warn_impcast_null_pointer_to_integer)
- << T << Loc << clang::SourceRange(CC);
- return;
+ if (!Loc.isMacroID() || CC.isMacroID())
+ S.Diag(Loc, diag::warn_impcast_null_pointer_to_integer)
+ << T << clang::SourceRange(CC)
+ << FixItHint::CreateReplacement(Loc, S.getFixItZeroLiteralForType(T));
}
+ if (!Source->isIntegerType() || !Target->isIntegerType())
+ return;
+
+ // TODO: remove this early return once the false positives for constant->bool
+ // in templates, macros, etc, are reduced or removed.
+ if (Target->isSpecificBuiltinType(BuiltinType::Bool))
+ return;
+
IntRange SourceRange = GetExprRange(S.Context, E);
IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target);
@@ -4293,14 +4588,15 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
return;
}
-void CheckConditionalOperator(Sema &S, ConditionalOperator *E, QualType T);
+void CheckConditionalOperator(Sema &S, ConditionalOperator *E,
+ SourceLocation CC, QualType T);
void CheckConditionalOperand(Sema &S, Expr *E, QualType T,
SourceLocation CC, bool &ICContext) {
E = E->IgnoreParenImpCasts();
if (isa<ConditionalOperator>(E))
- return CheckConditionalOperator(S, cast<ConditionalOperator>(E), T);
+ return CheckConditionalOperator(S, cast<ConditionalOperator>(E), CC, T);
AnalyzeImplicitConversions(S, E, CC);
if (E->getType() != T)
@@ -4308,9 +4604,8 @@ void CheckConditionalOperand(Sema &S, Expr *E, QualType T,
return;
}
-void CheckConditionalOperator(Sema &S, ConditionalOperator *E, QualType T) {
- SourceLocation CC = E->getQuestionLoc();
-
+void CheckConditionalOperator(Sema &S, ConditionalOperator *E,
+ SourceLocation CC, QualType T) {
AnalyzeImplicitConversions(S, E->getCond(), CC);
bool Suspicious = false;
@@ -4352,7 +4647,7 @@ void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC) {
// were being fed directly into the output.
if (isa<ConditionalOperator>(E)) {
ConditionalOperator *CO = cast<ConditionalOperator>(E);
- CheckConditionalOperator(S, CO, T);
+ CheckConditionalOperator(S, CO, CC, T);
return;
}
@@ -4417,7 +4712,7 @@ void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC) {
/// conversion
void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) {
// Don't diagnose in unevaluated contexts.
- if (ExprEvalContexts.back().Context == Sema::Unevaluated)
+ if (isUnevaluatedContext())
return;
// Don't diagnose for value- or type-dependent expressions.
@@ -4457,7 +4752,7 @@ bool Sema::CheckParmsForFunctionDef(ParmVarDecl **P, ParmVarDecl **PEnd,
// This is also C++ [dcl.fct]p6.
if (!Param->isInvalidDecl() &&
RequireCompleteType(Param->getLocation(), Param->getType(),
- diag::err_typecheck_decl_incomplete_type)) {
+ diag::err_typecheck_decl_incomplete_type)) {
Param->setInvalidDecl();
HasInvalidParm = true;
}
@@ -4478,7 +4773,7 @@ bool Sema::CheckParmsForFunctionDef(ParmVarDecl **P, ParmVarDecl **PEnd,
QualType PType = Param->getOriginalType();
if (const ArrayType *AT = Context.getAsArrayType(PType)) {
if (AT->getSizeModifier() == ArrayType::Star) {
- // FIXME: This diagnosic should point the the '[*]' if source-location
+ // FIXME: This diagnosic should point the '[*]' if source-location
// information is added for it.
Diag(Param->getLocation(), diag::err_array_star_in_function_definition);
}
@@ -4556,11 +4851,23 @@ static bool IsTailPaddedMemberArray(Sema &S, llvm::APInt Size,
// Don't consider sizes resulting from macro expansions or template argument
// substitution to form C89 tail-padded arrays.
- ConstantArrayTypeLoc TL =
- cast<ConstantArrayTypeLoc>(FD->getTypeSourceInfo()->getTypeLoc());
- const Expr *SizeExpr = dyn_cast<IntegerLiteral>(TL.getSizeExpr());
- if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
- return false;
+
+ TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
+ while (TInfo) {
+ TypeLoc TL = TInfo->getTypeLoc();
+ // Look through typedefs.
+ const TypedefTypeLoc *TTL = dyn_cast<TypedefTypeLoc>(&TL);
+ if (TTL) {
+ const TypedefNameDecl *TDL = TTL->getTypedefNameDecl();
+ TInfo = TDL->getTypeSourceInfo();
+ continue;
+ }
+ ConstantArrayTypeLoc CTL = cast<ConstantArrayTypeLoc>(TL);
+ const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr());
+ if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
+ return false;
+ break;
+ }
const RecordDecl *RD = dyn_cast<RecordDecl>(FD->getDeclContext());
if (!RD) return false;
@@ -4966,7 +5273,7 @@ bool Sema::checkUnsafeAssigns(SourceLocation Loc,
while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
if (cast->getCastKind() == CK_ARCConsumeObject) {
Diag(Loc, diag::warn_arc_retained_assign)
- << (LT == Qualifiers::OCL_ExplicitNone)
+ << (LT == Qualifiers::OCL_ExplicitNone) << 1
<< RHS->getSourceRange();
return true;
}
@@ -5023,6 +5330,16 @@ void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
RHS = cast->getSubExpr();
}
}
+ else if (Attributes & ObjCPropertyDecl::OBJC_PR_weak) {
+ while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
+ if (cast->getCastKind() == CK_ARCConsumeObject) {
+ Diag(Loc, diag::warn_arc_retained_assign)
+ << 0 << 0<< RHS->getSourceRange();
+ return;
+ }
+ RHS = cast->getSubExpr();
+ }
+ }
}
}
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index 1ee7532..9fa757d 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -158,7 +158,7 @@ namespace {
/// \brief The completion context in which we are gathering results.
CodeCompletionContext CompletionContext;
- /// \brief If we are in an instance method definition, the @implementation
+ /// \brief If we are in an instance method definition, the \@implementation
/// object.
ObjCImplementationDecl *ObjCImplementation;
@@ -1181,7 +1181,7 @@ bool ResultBuilder::IsImpossibleToSatisfy(NamedDecl *ND) const {
return false;
}
-/// \rief Determines whether the given declaration is an Objective-C
+/// \brief Determines whether the given declaration is an Objective-C
/// instance variable.
bool ResultBuilder::IsObjCIvar(NamedDecl *ND) const {
return isa<ObjCIvarDecl>(ND);
@@ -1414,7 +1414,7 @@ static const char *GetCompletionTypeString(QualType T,
if (!T.getLocalQualifiers()) {
// Built-in type names are constant strings.
if (const BuiltinType *BT = dyn_cast<BuiltinType>(T))
- return BT->getName(Policy);
+ return BT->getNameAsCString(Policy);
// Anonymous tag types are constant strings.
if (const TagType *TagT = dyn_cast<TagType>(T))
@@ -1955,6 +1955,19 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC,
AddObjCExpressionResults(Results, true);
}
+ if (SemaRef.getLangOpts().C11) {
+ // _Alignof
+ Builder.AddResultTypeChunk("size_t");
+ if (SemaRef.getASTContext().Idents.get("alignof").hasMacroDefinition())
+ Builder.AddTypedTextChunk("alignof");
+ else
+ Builder.AddTypedTextChunk("_Alignof");
+ Builder.AddChunk(CodeCompletionString::CK_LeftParen);
+ Builder.AddPlaceholderChunk("type");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Results.AddResult(Result(Builder.TakeString()));
+ }
+
// sizeof expression
Builder.AddResultTypeChunk("size_t");
Builder.AddTypedTextChunk("sizeof");
@@ -2356,11 +2369,11 @@ AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
// Handle multiple qualifiers.
std::string QualsStr;
- if (Proto->getTypeQuals() & Qualifiers::Const)
+ if (Proto->isConst())
QualsStr += " const";
- if (Proto->getTypeQuals() & Qualifiers::Volatile)
+ if (Proto->isVolatile())
QualsStr += " volatile";
- if (Proto->getTypeQuals() & Qualifiers::Restrict)
+ if (Proto->isRestrict())
QualsStr += " restrict";
Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
}
@@ -2440,8 +2453,10 @@ static void AddTypedNameChunk(ASTContext &Context, const PrintingPolicy &Policy,
CodeCompletionString *CodeCompletionResult::CreateCodeCompletionString(Sema &S,
CodeCompletionAllocator &Allocator,
- CodeCompletionTUInfo &CCTUInfo) {
- return CreateCodeCompletionString(S.Context, S.PP, Allocator, CCTUInfo);
+ CodeCompletionTUInfo &CCTUInfo,
+ bool IncludeBriefComments) {
+ return CreateCodeCompletionString(S.Context, S.PP, Allocator, CCTUInfo,
+ IncludeBriefComments);
}
/// \brief If possible, create a new code completion string for the given
@@ -2454,7 +2469,8 @@ CodeCompletionString *
CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx,
Preprocessor &PP,
CodeCompletionAllocator &Allocator,
- CodeCompletionTUInfo &CCTUInfo) {
+ CodeCompletionTUInfo &CCTUInfo,
+ bool IncludeBriefComments) {
CodeCompletionBuilder Result(Allocator, CCTUInfo, Priority, Availability);
PrintingPolicy Policy = getCompletionPrintingPolicy(Ctx, PP);
@@ -2524,7 +2540,14 @@ CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx,
assert(Kind == RK_Declaration && "Missed a result kind?");
NamedDecl *ND = Declaration;
Result.addParentContext(ND->getDeclContext());
-
+
+ if (IncludeBriefComments) {
+ // Add documentation comment, if it exists.
+ if (const RawComment *RC = Ctx.getRawCommentForAnyRedecl(ND)) {
+ Result.addBriefComment(RC->getBriefText(Ctx));
+ }
+ }
+
if (StartsNestedNameSpecifier) {
Result.AddTypedTextChunk(
Result.getAllocator().CopyString(ND->getNameAsString()));
@@ -2842,6 +2865,7 @@ CXCursorKind clang::getCursorKindForDecl(Decl *D) {
case Decl::ClassTemplatePartialSpecialization:
return CXCursor_ClassTemplatePartialSpecialization;
case Decl::UsingDirective: return CXCursor_UsingDirective;
+ case Decl::TranslationUnit: return CXCursor_TranslationUnit;
case Decl::Using:
case Decl::UnresolvedUsingValue:
@@ -3270,9 +3294,6 @@ struct Sema::CodeCompleteExpressionData {
/// \brief Perform code-completion in an expression context when we know what
/// type we're looking for.
-///
-/// \param IntegralConstantExpression Only permit integral constant
-/// expressions.
void Sema::CodeCompleteExpression(Scope *S,
const CodeCompleteExpressionData &Data) {
typedef CodeCompletionResult Result;
@@ -3333,7 +3354,25 @@ void Sema::CodeCompletePostfixExpression(Scope *S, ExprResult E) {
/// property name.
typedef llvm::SmallPtrSet<IdentifierInfo*, 16> AddedPropertiesSet;
-static void AddObjCProperties(ObjCContainerDecl *Container,
+/// \brief Retrieve the container definition, if any?
+static ObjCContainerDecl *getContainerDef(ObjCContainerDecl *Container) {
+ if (ObjCInterfaceDecl *Interface = dyn_cast<ObjCInterfaceDecl>(Container)) {
+ if (Interface->hasDefinition())
+ return Interface->getDefinition();
+
+ return Interface;
+ }
+
+ if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) {
+ if (Protocol->hasDefinition())
+ return Protocol->getDefinition();
+
+ return Protocol;
+ }
+ return Container;
+}
+
+static void AddObjCProperties(ObjCContainerDecl *Container,
bool AllowCategories,
bool AllowNullaryMethods,
DeclContext *CurContext,
@@ -3341,6 +3380,9 @@ static void AddObjCProperties(ObjCContainerDecl *Container,
ResultBuilder &Results) {
typedef CodeCompletionResult Result;
+ // Retrieve the definition.
+ Container = getContainerDef(Container);
+
// Add properties in this container.
for (ObjCContainerDecl::prop_iterator P = Container->prop_begin(),
PEnd = Container->prop_end();
@@ -3616,6 +3658,8 @@ void Sema::CodeCompleteCase(Scope *S) {
// Code-complete the cases of a switch statement over an enumeration type
// by providing the list of
EnumDecl *Enum = type->castAs<EnumType>()->getDecl();
+ if (EnumDecl *Def = Enum->getDefinition())
+ Enum = Def;
// Determine which enumerators we have already seen in the switch statement.
// FIXME: Ideally, we would also be able to look *past* the code-completion
@@ -4273,27 +4317,28 @@ void Sema::CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro,
Results.data(), Results.size());
}
-// Macro that expands to @Keyword or Keyword, depending on whether NeedAt is
-// true or false.
-#define OBJC_AT_KEYWORD_NAME(NeedAt,Keyword) NeedAt? "@" #Keyword : #Keyword
+/// Macro that optionally prepends an "@" to the string literal passed in via
+/// Keyword, depending on whether NeedAt is true or false.
+#define OBJC_AT_KEYWORD_NAME(NeedAt,Keyword) ((NeedAt)? "@" Keyword : Keyword)
+
static void AddObjCImplementationResults(const LangOptions &LangOpts,
ResultBuilder &Results,
bool NeedAt) {
typedef CodeCompletionResult Result;
// Since we have an implementation, we can end it.
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,end)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"end")));
CodeCompletionBuilder Builder(Results.getAllocator(),
Results.getCodeCompletionTUInfo());
if (LangOpts.ObjC2) {
// @dynamic
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,dynamic));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"dynamic"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("property");
Results.AddResult(Result(Builder.TakeString()));
// @synthesize
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,synthesize));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"synthesize"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("property");
Results.AddResult(Result(Builder.TakeString()));
@@ -4306,17 +4351,17 @@ static void AddObjCInterfaceResults(const LangOptions &LangOpts,
typedef CodeCompletionResult Result;
// Since we have an interface or protocol, we can end it.
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,end)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"end")));
if (LangOpts.ObjC2) {
// @property
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,property)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"property")));
// @required
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,required)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"required")));
// @optional
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,optional)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"optional")));
}
}
@@ -4326,7 +4371,7 @@ static void AddObjCTopLevelResults(ResultBuilder &Results, bool NeedAt) {
Results.getCodeCompletionTUInfo());
// @class name ;
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,class));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"class"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("name");
Results.AddResult(Result(Builder.TakeString()));
@@ -4335,26 +4380,26 @@ static void AddObjCTopLevelResults(ResultBuilder &Results, bool NeedAt) {
// @interface name
// FIXME: Could introduce the whole pattern, including superclasses and
// such.
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,interface));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"interface"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("class");
Results.AddResult(Result(Builder.TakeString()));
// @protocol name
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,protocol));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"protocol"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("protocol");
Results.AddResult(Result(Builder.TakeString()));
// @implementation name
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,implementation));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"implementation"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("class");
Results.AddResult(Result(Builder.TakeString()));
}
// @compatibility_alias name
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,compatibility_alias));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"compatibility_alias"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("alias");
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
@@ -4389,9 +4434,9 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) {
const char *EncodeType = "char[]";
if (Results.getSema().getLangOpts().CPlusPlus ||
Results.getSema().getLangOpts().ConstStrings)
- EncodeType = " const char[]";
+ EncodeType = "const char[]";
Builder.AddResultTypeChunk(EncodeType);
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,encode));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"encode"));
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("type-name");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
@@ -4399,7 +4444,7 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) {
// @protocol ( protocol-name )
Builder.AddResultTypeChunk("Protocol *");
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,protocol));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"protocol"));
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("protocol-name");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
@@ -4407,31 +4452,43 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) {
// @selector ( selector )
Builder.AddResultTypeChunk("SEL");
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,selector));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"selector"));
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("selector");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
Results.AddResult(Result(Builder.TakeString()));
-
- // @[ objects, ... ]
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,[));
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+
+ // @"string"
+ Builder.AddResultTypeChunk("NSString *");
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"\""));
+ Builder.AddPlaceholderChunk("string");
+ Builder.AddTextChunk("\"");
+ Results.AddResult(Result(Builder.TakeString()));
+
+ // @[objects, ...]
+ Builder.AddResultTypeChunk("NSArray *");
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"["));
Builder.AddPlaceholderChunk("objects, ...");
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddChunk(CodeCompletionString::CK_RightBracket);
Results.AddResult(Result(Builder.TakeString()));
- // @{ key : object, ... }
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,{));
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ // @{key : object, ...}
+ Builder.AddResultTypeChunk("NSDictionary *");
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"{"));
Builder.AddPlaceholderChunk("key");
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddChunk(CodeCompletionString::CK_Colon);
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("object, ...");
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddChunk(CodeCompletionString::CK_RightBrace);
Results.AddResult(Result(Builder.TakeString()));
+
+ // @(expression)
+ Builder.AddResultTypeChunk("id");
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt, "("));
+ Builder.AddPlaceholderChunk("expression");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Results.AddResult(Result(Builder.TakeString()));
}
static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) {
@@ -4442,7 +4499,7 @@ static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) {
if (Results.includeCodePatterns()) {
// @try { statements } @catch ( declaration ) { statements } @finally
// { statements }
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,try));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"try"));
Builder.AddChunk(CodeCompletionString::CK_LeftBrace);
Builder.AddPlaceholderChunk("statements");
Builder.AddChunk(CodeCompletionString::CK_RightBrace);
@@ -4461,14 +4518,14 @@ static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) {
}
// @throw
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,throw));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"throw"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("expression");
Results.AddResult(Result(Builder.TakeString()));
if (Results.includeCodePatterns()) {
// @synchronized ( expression ) { statements }
- Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,synchronized));
+ Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"synchronized"));
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("expression");
@@ -4484,11 +4541,11 @@ static void AddObjCVisibilityResults(const LangOptions &LangOpts,
ResultBuilder &Results,
bool NeedAt) {
typedef CodeCompletionResult Result;
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,private)));
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,protected)));
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,public)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"private")));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"protected")));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"public")));
if (LangOpts.ObjC2)
- Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,package)));
+ Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"package")));
}
void Sema::CodeCompleteObjCAtVisibility(Scope *S) {
@@ -4616,12 +4673,12 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) {
Results.data(),Results.size());
}
-/// \brief Descripts the kind of Objective-C method that we want to find
+/// \brief Describes the kind of Objective-C method that we want to find
/// via code completion.
enum ObjCMethodKind {
- MK_Any, //< Any kind of method, provided it means other specified criteria.
- MK_ZeroArgSelector, //< Zero-argument (unary) selector.
- MK_OneArgSelector //< One-argument selector.
+ MK_Any, ///< Any kind of method, provided it means other specified criteria.
+ MK_ZeroArgSelector, ///< Zero-argument (unary) selector.
+ MK_OneArgSelector ///< One-argument selector.
};
static bool isAcceptableObjCSelector(Selector Sel,
@@ -4673,8 +4730,8 @@ namespace {
///
/// \param Container the container in which we'll look to find methods.
///
-/// \param WantInstance whether to add instance methods (only); if false, this
-/// routine will add factory methods (only).
+/// \param WantInstanceMethods Whether to add instance methods (only); if
+/// false, this routine will add factory methods (only).
///
/// \param CurContext the context in which we're performing the lookup that
/// finds methods.
@@ -4694,17 +4751,18 @@ static void AddObjCMethods(ObjCContainerDecl *Container,
ResultBuilder &Results,
bool InOriginalClass = true) {
typedef CodeCompletionResult Result;
+ Container = getContainerDef(Container);
for (ObjCContainerDecl::method_iterator M = Container->meth_begin(),
MEnd = Container->meth_end();
M != MEnd; ++M) {
- if ((*M)->isInstanceMethod() == WantInstanceMethods) {
+ if (M->isInstanceMethod() == WantInstanceMethods) {
// Check whether the selector identifiers we've been given are a
// subset of the identifiers for this particular method.
if (!isAcceptableObjCMethod(*M, WantKind, SelIdents, NumSelIdents,
AllowSameLength))
continue;
- if (!Selectors.insert((*M)->getSelector()))
+ if (!Selectors.insert(M->getSelector()))
continue;
Result R = Result(*M, 0);
@@ -5825,7 +5883,8 @@ void Sema::CodeCompleteObjCPropertyDefinition(Scope *S) {
return;
// Ignore any properties that have already been implemented.
- for (DeclContext::decl_iterator D = Container->decls_begin(),
+ Container = getContainerDef(Container);
+ for (DeclContext::decl_iterator D = Container->decls_begin(),
DEnd = Container->decls_end();
D != DEnd; ++D)
if (ObjCPropertyImplDecl *PropertyImpl = dyn_cast<ObjCPropertyImplDecl>(*D))
@@ -5958,9 +6017,12 @@ static void FindImplementableMethods(ASTContext &Context,
KnownMethodsMap &KnownMethods,
bool InOriginalClass = true) {
if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Container)) {
- // Recurse into protocols.
+ // Make sure we have a definition; that's what we'll walk.
if (!IFace->hasDefinition())
return;
+
+ IFace = IFace->getDefinition();
+ Container = IFace;
const ObjCList<ObjCProtocolDecl> &Protocols
= IFace->getReferencedProtocols();
@@ -6002,16 +6064,20 @@ static void FindImplementableMethods(ASTContext &Context,
}
if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) {
- if (Protocol->hasDefinition()) {
- // Recurse into protocols.
- const ObjCList<ObjCProtocolDecl> &Protocols
- = Protocol->getReferencedProtocols();
- for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
- E = Protocols.end();
- I != E; ++I)
- FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType,
- KnownMethods, false);
- }
+ // Make sure we have a definition; that's what we'll walk.
+ if (!Protocol->hasDefinition())
+ return;
+ Protocol = Protocol->getDefinition();
+ Container = Protocol;
+
+ // Recurse into protocols.
+ const ObjCList<ObjCProtocolDecl> &Protocols
+ = Protocol->getReferencedProtocols();
+ for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+ E = Protocols.end();
+ I != E; ++I)
+ FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType,
+ KnownMethods, false);
}
// Add methods in this container. This operation occurs last because
@@ -6020,12 +6086,12 @@ static void FindImplementableMethods(ASTContext &Context,
for (ObjCContainerDecl::method_iterator M = Container->meth_begin(),
MEnd = Container->meth_end();
M != MEnd; ++M) {
- if ((*M)->isInstanceMethod() == WantInstanceMethods) {
+ if (M->isInstanceMethod() == WantInstanceMethods) {
if (!ReturnType.isNull() &&
- !Context.hasSameUnqualifiedType(ReturnType, (*M)->getResultType()))
+ !Context.hasSameUnqualifiedType(ReturnType, M->getResultType()))
continue;
- KnownMethods[(*M)->getSelector()] = std::make_pair(*M, InOriginalClass);
+ KnownMethods[M->getSelector()] = std::make_pair(*M, InOriginalClass);
}
}
}
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 1227e92..3aae99a 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/CommentDiagnostic.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@@ -60,7 +61,8 @@ namespace {
class TypeNameValidatorCCC : public CorrectionCandidateCallback {
public:
- TypeNameValidatorCCC(bool AllowInvalid) : AllowInvalidDecl(AllowInvalid) {
+ TypeNameValidatorCCC(bool AllowInvalid, bool WantClass=false)
+ : AllowInvalidDecl(AllowInvalid), WantClassName(WantClass) {
WantExpressionKeywords = false;
WantCXXNamedCasts = false;
WantRemainingKeywords = false;
@@ -71,15 +73,52 @@ class TypeNameValidatorCCC : public CorrectionCandidateCallback {
return (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND)) &&
(AllowInvalidDecl || !ND->isInvalidDecl());
else
- return candidate.isKeyword();
+ return !WantClassName && candidate.isKeyword();
}
private:
bool AllowInvalidDecl;
+ bool WantClassName;
};
}
+/// \brief Determine whether the token kind starts a simple-type-specifier.
+bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const {
+ switch (Kind) {
+ // FIXME: Take into account the current language when deciding whether a
+ // token kind is a valid type specifier
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_int:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw_wchar_t:
+ case tok::kw_bool:
+ case tok::kw___underlying_type:
+ return true;
+
+ case tok::annot_typename:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_typeof:
+ case tok::kw_decltype:
+ return getLangOpts().CPlusPlus;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
/// \brief If the identifier refers to a type name within this scope,
/// return the declaration of that type.
///
@@ -173,7 +212,7 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
case LookupResult::NotFound:
case LookupResult::NotFoundInCurrentInstantiation:
if (CorrectedII) {
- TypeNameValidatorCCC Validator(true);
+ TypeNameValidatorCCC Validator(true, isClassName);
TypoCorrection Correction = CorrectTypo(Result.getLookupNameInfo(),
Kind, S, SS, Validator);
IdentifierInfo *NewII = Correction.getCorrectionAsIdentifierInfo();
@@ -202,8 +241,8 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
std::string CorrectedStr(Correction.getAsString(getLangOpts()));
std::string CorrectedQuotedStr(
Correction.getQuoted(getLangOpts()));
- Diag(NameLoc, diag::err_unknown_typename_suggest)
- << Result.getLookupName() << CorrectedQuotedStr
+ Diag(NameLoc, diag::err_unknown_type_or_class_name_suggest)
+ << Result.getLookupName() << CorrectedQuotedStr << isClassName
<< FixItHint::CreateReplacement(SourceRange(NameLoc),
CorrectedStr);
if (NamedDecl *FirstDecl = Correction.getCorrectionDecl())
@@ -359,7 +398,7 @@ bool Sema::isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S) {
return CurContext->isFunctionOrMethod() || S->isFunctionPrototypeScope();
}
-bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II,
+bool Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
SourceLocation IILoc,
Scope *S,
CXXScopeSpec *SS,
@@ -370,7 +409,7 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II,
// There may have been a typo in the name of the type. Look up typo
// results, in case we have something that we can suggest.
TypeNameValidatorCCC Validator(false);
- if (TypoCorrection Corrected = CorrectTypo(DeclarationNameInfo(&II, IILoc),
+ if (TypoCorrection Corrected = CorrectTypo(DeclarationNameInfo(II, IILoc),
LookupOrdinaryName, S, SS,
Validator)) {
std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
@@ -378,19 +417,23 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II,
if (Corrected.isKeyword()) {
// We corrected to a keyword.
- // FIXME: Actually recover with the keyword we suggest, and emit a fix-it.
+ IdentifierInfo *NewII = Corrected.getCorrectionAsIdentifierInfo();
+ if (!isSimpleTypeSpecifier(NewII->getTokenID()))
+ CorrectedQuotedStr = "the keyword " + CorrectedQuotedStr;
Diag(IILoc, diag::err_unknown_typename_suggest)
- << &II << CorrectedQuotedStr;
+ << II << CorrectedQuotedStr
+ << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr);
+ II = NewII;
} else {
NamedDecl *Result = Corrected.getCorrectionDecl();
// We found a similarly-named type or interface; suggest that.
if (!SS || !SS->isSet())
Diag(IILoc, diag::err_unknown_typename_suggest)
- << &II << CorrectedQuotedStr
+ << II << CorrectedQuotedStr
<< FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr);
else if (DeclContext *DC = computeDeclContext(*SS, false))
Diag(IILoc, diag::err_unknown_nested_typename_suggest)
- << &II << DC << CorrectedQuotedStr << SS->getRange()
+ << II << DC << CorrectedQuotedStr << SS->getRange()
<< FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr);
else
llvm_unreachable("could not have corrected a typo here");
@@ -409,7 +452,7 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II,
if (getLangOpts().CPlusPlus) {
// See if II is a class template that the user forgot to pass arguments to.
UnqualifiedId Name;
- Name.setIdentifier(&II, IILoc);
+ Name.setIdentifier(II, IILoc);
CXXScopeSpec EmptySS;
TemplateTy TemplateResult;
bool MemberOfUnknownSpecialization;
@@ -430,21 +473,21 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II,
// (struct, union, enum) from Parser::ParseImplicitInt here, instead?
if (!SS || (!SS->isSet() && !SS->isInvalid()))
- Diag(IILoc, diag::err_unknown_typename) << &II;
+ Diag(IILoc, diag::err_unknown_typename) << II;
else if (DeclContext *DC = computeDeclContext(*SS, false))
Diag(IILoc, diag::err_typename_nested_not_found)
- << &II << DC << SS->getRange();
+ << II << DC << SS->getRange();
else if (isDependentScopeSpecifier(*SS)) {
unsigned DiagID = diag::err_typename_missing;
if (getLangOpts().MicrosoftMode && isMicrosoftMissingTypename(SS, S))
DiagID = diag::warn_typename_missing;
Diag(SS->getRange().getBegin(), DiagID)
- << (NestedNameSpecifier *)SS->getScopeRep() << II.getName()
+ << (NestedNameSpecifier *)SS->getScopeRep() << II->getName()
<< SourceRange(SS->getRange().getBegin(), IILoc)
<< FixItHint::CreateInsertion(SS->getRange().getBegin(), "typename ");
- SuggestedType = ActOnTypenameType(S, SourceLocation(), *SS, II, IILoc)
- .get();
+ SuggestedType = ActOnTypenameType(S, SourceLocation(),
+ *SS, *II, IILoc).get();
} else {
assert(SS && SS->isInvalid() &&
"Invalid scope specifier has already been diagnosed");
@@ -470,6 +513,55 @@ static bool isResultTypeOrTemplate(LookupResult &R, const Token &NextToken) {
return false;
}
+static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result,
+ Scope *S, CXXScopeSpec &SS,
+ IdentifierInfo *&Name,
+ SourceLocation NameLoc) {
+ Result.clear(Sema::LookupTagName);
+ SemaRef.LookupParsedName(Result, S, &SS);
+ if (TagDecl *Tag = Result.getAsSingle<TagDecl>()) {
+ const char *TagName = 0;
+ const char *FixItTagName = 0;
+ switch (Tag->getTagKind()) {
+ case TTK_Class:
+ TagName = "class";
+ FixItTagName = "class ";
+ break;
+
+ case TTK_Enum:
+ TagName = "enum";
+ FixItTagName = "enum ";
+ break;
+
+ case TTK_Struct:
+ TagName = "struct";
+ FixItTagName = "struct ";
+ break;
+
+ case TTK_Union:
+ TagName = "union";
+ FixItTagName = "union ";
+ break;
+ }
+
+ SemaRef.Diag(NameLoc, diag::err_use_of_tag_name_without_tag)
+ << Name << TagName << SemaRef.getLangOpts().CPlusPlus
+ << FixItHint::CreateInsertion(NameLoc, FixItTagName);
+
+ LookupResult R(SemaRef, Name, NameLoc, Sema::LookupOrdinaryName);
+ if (SemaRef.LookupParsedName(R, S, &SS)) {
+ for (LookupResult::iterator I = R.begin(), IEnd = R.end();
+ I != IEnd; ++I)
+ SemaRef.Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type)
+ << Name << TagName;
+ }
+ return true;
+ }
+
+ Result.clear(Sema::LookupOrdinaryName);
+ return false;
+}
+
Sema::NameClassification Sema::ClassifyName(Scope *S,
CXXScopeSpec &SS,
IdentifierInfo *&Name,
@@ -533,41 +625,9 @@ Corrected:
// In C, we first see whether there is a tag type by the same name, in
// which case it's likely that the user just forget to write "enum",
// "struct", or "union".
- if (!getLangOpts().CPlusPlus && !SecondTry) {
- Result.clear(LookupTagName);
- LookupParsedName(Result, S, &SS);
- if (TagDecl *Tag = Result.getAsSingle<TagDecl>()) {
- const char *TagName = 0;
- const char *FixItTagName = 0;
- switch (Tag->getTagKind()) {
- case TTK_Class:
- TagName = "class";
- FixItTagName = "class ";
- break;
-
- case TTK_Enum:
- TagName = "enum";
- FixItTagName = "enum ";
- break;
-
- case TTK_Struct:
- TagName = "struct";
- FixItTagName = "struct ";
- break;
-
- case TTK_Union:
- TagName = "union";
- FixItTagName = "union ";
- break;
- }
-
- Diag(NameLoc, diag::err_use_of_tag_name_without_tag)
- << Name << TagName << getLangOpts().CPlusPlus
- << FixItHint::CreateInsertion(NameLoc, FixItTagName);
- break;
- }
-
- Result.clear(LookupOrdinaryName);
+ if (!getLangOpts().CPlusPlus && !SecondTry &&
+ isTagTypeWithMissingTag(*this, Result, S, SS, Name, NameLoc)) {
+ break;
}
// Perform typo correction to determine if there is another name that is
@@ -575,6 +635,19 @@ Corrected:
if (!SecondTry) {
SecondTry = true;
CorrectionCandidateCallback DefaultValidator;
+ // Try to limit which sets of keywords should be included in typo
+ // correction based on what the next token is.
+ DefaultValidator.WantTypeSpecifiers =
+ NextToken.is(tok::l_paren) || NextToken.is(tok::less) ||
+ NextToken.is(tok::identifier) || NextToken.is(tok::star) ||
+ NextToken.is(tok::amp) || NextToken.is(tok::l_square);
+ DefaultValidator.WantExpressionKeywords =
+ NextToken.is(tok::l_paren) || NextToken.is(tok::identifier) ||
+ NextToken.is(tok::arrow) || NextToken.is(tok::period);
+ DefaultValidator.WantRemainingKeywords =
+ NextToken.is(tok::l_paren) || NextToken.is(tok::semi) ||
+ NextToken.is(tok::identifier) || NextToken.is(tok::l_brace);
+ DefaultValidator.WantCXXNamedCasts = false;
if (TypoCorrection Corrected = CorrectTypo(Result.getLookupNameInfo(),
Result.getLookupKind(), S,
&SS, DefaultValidator)) {
@@ -740,7 +813,7 @@ Corrected:
if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) {
DiagnoseUseOfDecl(Type, NameLoc);
QualType T = Context.getTypeDeclType(Type);
- return ParsedType::make(T);
+ return ParsedType::make(T);
}
ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(FirstDecl);
@@ -764,6 +837,23 @@ Corrected:
QualType T = Context.getObjCInterfaceType(Class);
return ParsedType::make(T);
}
+
+ // Check for a tag type hidden by a non-type decl in a few cases where it
+ // seems likely a type is wanted instead of the non-type that was found.
+ if (!getLangOpts().ObjC1 && FirstDecl && !isa<ClassTemplateDecl>(FirstDecl) &&
+ !isa<TypeAliasTemplateDecl>(FirstDecl)) {
+ bool NextIsOp = NextToken.is(tok::amp) || NextToken.is(tok::star);
+ if ((NextToken.is(tok::identifier) ||
+ (NextIsOp && FirstDecl->isFunctionOrFunctionTemplate())) &&
+ isTagTypeWithMissingTag(*this, Result, S, SS, Name, NameLoc)) {
+ FirstDecl = (*Result.begin())->getUnderlyingDecl();
+ if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) {
+ DiagnoseUseOfDecl(Type, NameLoc);
+ QualType T = Context.getTypeDeclType(Type);
+ return ParsedType::make(T);
+ }
+ }
+ }
if (!Result.empty() && (*Result.begin())->isCXXClassMember())
return BuildPossibleImplicitMemberExpr(SS, SourceLocation(), Result, 0);
@@ -1132,9 +1222,9 @@ void Sema::MarkUnusedFileScopedDecl(const DeclaratorDecl *D) {
return; // First should already be in the vector.
}
- if (ShouldWarnIfUnusedFileScopedDecl(D))
- UnusedFileScopedDecls.push_back(D);
- }
+ if (ShouldWarnIfUnusedFileScopedDecl(D))
+ UnusedFileScopedDecls.push_back(D);
+}
static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
if (D->isInvalidDecl())
@@ -1281,7 +1371,7 @@ void Sema::ActOnEndFunctionDeclarator() {
///
/// \param IdLoc The location of the name in the translation unit.
///
-/// \param TypoCorrection If true, this routine will attempt typo correction
+/// \param DoTypoCorrection If true, this routine will attempt typo correction
/// if there is no class with the given name.
///
/// \returns The declaration of the named Objective-C class, or NULL if the
@@ -1484,12 +1574,22 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) {
switch (TypeID->getLength()) {
default: break;
case 2:
- if (!TypeID->isStr("id"))
- break;
- Context.setObjCIdRedefinitionType(New->getUnderlyingType());
- // Install the built-in type for 'id', ignoring the current definition.
- New->setTypeForDecl(Context.getObjCIdType().getTypePtr());
- return;
+ {
+ if (!TypeID->isStr("id"))
+ break;
+ QualType T = New->getUnderlyingType();
+ if (!T->isPointerType())
+ break;
+ if (!T->isVoidPointerType()) {
+ QualType PT = T->getAs<PointerType>()->getPointeeType();
+ if (!PT->isStructureType())
+ break;
+ }
+ Context.setObjCIdRedefinitionType(T);
+ // Install the built-in type for 'id', ignoring the current definition.
+ New->setTypeForDecl(Context.getObjCIdType().getTypePtr());
+ return;
+ }
case 5:
if (!TypeID->isStr("Class"))
break;
@@ -1599,6 +1699,13 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) {
/// attribute.
static bool
DeclHasAttr(const Decl *D, const Attr *A) {
+ // There can be multiple AvailabilityAttr in a Decl. Make sure we copy
+ // all of them. It is mergeAvailabilityAttr in SemaDeclAttr.cpp that is
+ // responsible for making sure they are consistent.
+ const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(A);
+ if (AA)
+ return false;
+
const OwnershipAttr *OA = dyn_cast<OwnershipAttr>(A);
const AnnotateAttr *Ann = dyn_cast<AnnotateAttr>(A);
for (Decl::attr_iterator i = D->attr_begin(), e = D->attr_end(); i != e; ++i)
@@ -1617,9 +1724,90 @@ DeclHasAttr(const Decl *D, const Attr *A) {
return false;
}
+bool Sema::mergeDeclAttribute(Decl *D, InheritableAttr *Attr) {
+ InheritableAttr *NewAttr = NULL;
+ if (AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attr))
+ NewAttr = mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(),
+ AA->getIntroduced(), AA->getDeprecated(),
+ AA->getObsoleted(), AA->getUnavailable(),
+ AA->getMessage());
+ else if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr))
+ NewAttr = mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility());
+ else if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr))
+ NewAttr = mergeDLLImportAttr(D, ImportA->getRange());
+ else if (DLLExportAttr *ExportA = dyn_cast<DLLExportAttr>(Attr))
+ NewAttr = mergeDLLExportAttr(D, ExportA->getRange());
+ else if (FormatAttr *FA = dyn_cast<FormatAttr>(Attr))
+ NewAttr = mergeFormatAttr(D, FA->getRange(), FA->getType(),
+ FA->getFormatIdx(), FA->getFirstArg());
+ else if (SectionAttr *SA = dyn_cast<SectionAttr>(Attr))
+ NewAttr = mergeSectionAttr(D, SA->getRange(), SA->getName());
+ else if (!DeclHasAttr(D, Attr))
+ NewAttr = cast<InheritableAttr>(Attr->clone(Context));
+
+ if (NewAttr) {
+ NewAttr->setInherited(true);
+ D->addAttr(NewAttr);
+ return true;
+ }
+
+ return false;
+}
+
+static const Decl *getDefinition(const Decl *D) {
+ if (const TagDecl *TD = dyn_cast<TagDecl>(D))
+ return TD->getDefinition();
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D))
+ return VD->getDefinition();
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ const FunctionDecl* Def;
+ if (FD->hasBody(Def))
+ return Def;
+ }
+ return NULL;
+}
+
+static bool hasAttribute(const Decl *D, attr::Kind Kind) {
+ for (Decl::attr_iterator I = D->attr_begin(), E = D->attr_end();
+ I != E; ++I) {
+ Attr *Attribute = *I;
+ if (Attribute->getKind() == Kind)
+ return true;
+ }
+ return false;
+}
+
+/// checkNewAttributesAfterDef - If we already have a definition, check that
+/// there are no new attributes in this declaration.
+static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
+ if (!New->hasAttrs())
+ return;
+
+ const Decl *Def = getDefinition(Old);
+ if (!Def || Def == New)
+ return;
+
+ AttrVec &NewAttributes = New->getAttrs();
+ for (unsigned I = 0, E = NewAttributes.size(); I != E;) {
+ const Attr *NewAttribute = NewAttributes[I];
+ if (hasAttribute(Def, NewAttribute->getKind())) {
+ ++I;
+ continue; // regular attr merging will take care of validating this.
+ }
+ S.Diag(NewAttribute->getLocation(),
+ diag::warn_attribute_precede_definition);
+ S.Diag(Def->getLocation(), diag::note_previous_definition);
+ NewAttributes.erase(NewAttributes.begin() + I);
+ --E;
+ }
+}
+
/// mergeDeclAttributes - Copy attributes from the Old decl to the New one.
void Sema::mergeDeclAttributes(Decl *New, Decl *Old,
bool MergeDeprecation) {
+ // attributes declared post-definition are currently ignored
+ checkNewAttributesAfterDef(*this, New, Old);
+
if (!Old->hasAttrs())
return;
@@ -1640,12 +1828,8 @@ void Sema::mergeDeclAttributes(Decl *New, Decl *Old,
isa<AvailabilityAttr>(*i)))
continue;
- if (!DeclHasAttr(New, *i)) {
- InheritableAttr *newAttr = cast<InheritableAttr>((*i)->clone(Context));
- newAttr->setInherited(true);
- New->addAttr(newAttr);
+ if (mergeDeclAttribute(New, *i))
foundAny = true;
- }
}
if (!foundAny) New->dropAttrs();
@@ -1909,22 +2093,27 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) {
Diag(Old->getLocation(), PrevDiag) << Old << Old->getType();
return true;
}
-
+
// C++ [class.mem]p1:
// [...] A member shall not be declared twice in the
// member-specification, except that a nested class or member
// class template can be declared and then later defined.
- unsigned NewDiag;
- if (isa<CXXConstructorDecl>(OldMethod))
- NewDiag = diag::err_constructor_redeclared;
- else if (isa<CXXDestructorDecl>(NewMethod))
- NewDiag = diag::err_destructor_redeclared;
- else if (isa<CXXConversionDecl>(NewMethod))
- NewDiag = diag::err_conv_function_redeclared;
- else
- NewDiag = diag::err_member_redeclared;
+ if (ActiveTemplateInstantiations.empty()) {
+ unsigned NewDiag;
+ if (isa<CXXConstructorDecl>(OldMethod))
+ NewDiag = diag::err_constructor_redeclared;
+ else if (isa<CXXDestructorDecl>(NewMethod))
+ NewDiag = diag::err_destructor_redeclared;
+ else if (isa<CXXConversionDecl>(NewMethod))
+ NewDiag = diag::err_conv_function_redeclared;
+ else
+ NewDiag = diag::err_member_redeclared;
- Diag(New->getLocation(), NewDiag);
+ Diag(New->getLocation(), NewDiag);
+ } else {
+ Diag(New->getLocation(), diag::err_member_redeclared_in_instantiation)
+ << New << New->getType();
+ }
Diag(Old->getLocation(), PrevDiag) << Old << Old->getType();
// Complain if this is an explicit declaration of a special
@@ -1941,7 +2130,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) {
<< New << getSpecialMember(OldMethod);
return true;
}
- } else if (OldMethod->isExplicitlyDefaulted()) {
+ } else if (OldMethod->isExplicitlyDefaulted() && !isFriend) {
Diag(NewMethod->getLocation(),
diag::err_definition_of_explicitly_defaulted_member)
<< getSpecialMember(OldMethod);
@@ -2142,18 +2331,16 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
ObjCMethodDecl *oldMethod) {
- // We don't want to merge unavailable and deprecated attributes
- // except from interface to implementation.
- bool mergeDeprecation = isa<ObjCImplDecl>(newMethod->getDeclContext());
- // Merge the attributes.
- mergeDeclAttributes(newMethod, oldMethod, mergeDeprecation);
+ // Merge the attributes, including deprecated/unavailable
+ mergeDeclAttributes(newMethod, oldMethod, /* mergeDeprecation */true);
// Merge attributes from the parameters.
- ObjCMethodDecl::param_const_iterator oi = oldMethod->param_begin();
+ ObjCMethodDecl::param_const_iterator oi = oldMethod->param_begin(),
+ oe = oldMethod->param_end();
for (ObjCMethodDecl::param_iterator
ni = newMethod->param_begin(), ne = newMethod->param_end();
- ni != ne; ++ni, ++oi)
+ ni != ne && oi != oe; ++ni, ++oi)
mergeParamDeclAttributes(*ni, *oi, Context);
CheckObjCMethodOverride(newMethod, oldMethod, true);
@@ -2552,6 +2739,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
}
}
+ ActOnDocumentableDecl(TagD);
+
return TagD;
}
@@ -2873,7 +3062,7 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
Context.getTypeDeclType(Record),
TInfo,
/*BitWidth=*/0, /*Mutable=*/false,
- /*HasInit=*/false);
+ /*InitStyle=*/ICIS_NoInit);
Anon->setAccess(AS);
if (getLangOpts().CPlusPlus)
FieldCollector->Add(cast<FieldDecl>(Anon));
@@ -2970,7 +3159,7 @@ Decl *Sema::BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS,
Context.getTypeDeclType(Record),
TInfo,
/*BitWidth=*/0, /*Mutable=*/false,
- /*HasInit=*/false);
+ /*InitStyle=*/ICIS_NoInit);
Anon->setImplicit();
// Add the anonymous struct object to the current context.
@@ -3225,7 +3414,7 @@ Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
Decl *Dcl = HandleDeclarator(S, D, MultiTemplateParamsArg(*this));
if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer() &&
- Dcl->getDeclContext()->isFileContext())
+ Dcl && Dcl->getDeclContext()->isFileContext())
Dcl->setTopLevelDeclInObjCContainer();
return Dcl;
@@ -3794,8 +3983,6 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD,
Context.setsigjmp_bufDecl(NewTD);
else if (II->isStr("ucontext_t"))
Context.setucontext_tDecl(NewTD);
- else if (II->isStr("__builtin_va_list"))
- Context.setBuiltinVaListType(Context.getTypedefType(NewTD));
}
return NewTD;
@@ -4173,18 +4360,6 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
CheckMemberSpecialization(NewVD, Previous))
NewVD->setInvalidDecl();
}
-
- // attributes declared post-definition are currently ignored
- // FIXME: This should be handled in attribute merging, not
- // here.
- if (Previous.isSingleResult()) {
- VarDecl *Def = dyn_cast<VarDecl>(Previous.getFoundDecl());
- if (Def && (Def = Def->getDefinition()) &&
- Def != NewVD && D.hasAttributes()) {
- Diag(NewVD->getLocation(), diag::warn_attribute_precede_definition);
- Diag(Def->getLocation(), diag::note_previous_definition);
- }
- }
// If this is a locally-scoped extern C variable, update the map of
// such variables.
@@ -4334,6 +4509,15 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD,
return false;
}
+ // OpenCL v1.2 s6.8 -- The static qualifier is valid only in program
+ // scope.
+ if ((getLangOpts().OpenCLVersion >= 120)
+ && NewVD->isStaticLocal()) {
+ Diag(NewVD->getLocation(), diag::err_static_function_scope);
+ NewVD->setInvalidDecl();
+ return false;
+ }
+
if (NewVD->hasLocalStorage() && T.isObjCGCWeak()
&& !NewVD->hasAttr<BlocksAttr>()) {
if (getLangOpts().getGC() != LangOptions::NonGC)
@@ -4418,7 +4602,7 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD,
if (NewVD->isConstexpr() && !T->isDependentType() &&
RequireLiteralType(NewVD->getLocation(), T,
- PDiag(diag::err_constexpr_var_non_literal))) {
+ diag::err_constexpr_var_non_literal)) {
NewVD->setInvalidDecl();
return false;
}
@@ -4471,11 +4655,6 @@ static bool FindOverriddenMethod(const CXXBaseSpecifier *Specifier,
return false;
}
-static bool hasDelayedExceptionSpec(CXXMethodDecl *Method) {
- const FunctionProtoType *Proto =Method->getType()->getAs<FunctionProtoType>();
- return Proto && Proto->getExceptionSpecType() == EST_Delayed;
-}
-
/// AddOverriddenMethods - See if a method overrides any in the base classes,
/// and if so, check that it's a valid override and remember it.
bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
@@ -4491,8 +4670,7 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) {
MD->addOverriddenMethod(OldMD->getCanonicalDecl());
if (!CheckOverridingFunctionReturnType(MD, OldMD) &&
- (hasDelayedExceptionSpec(MD) ||
- !CheckOverridingFunctionExceptionSpec(MD, OldMD)) &&
+ !CheckOverridingFunctionExceptionSpec(MD, OldMD) &&
!CheckIfOverriddenFunctionIsMarkedFinal(MD, OldMD)) {
AddedAny = true;
}
@@ -4520,22 +4698,39 @@ namespace {
// Also only accept corrections that have the same parent decl.
class DifferentNameValidatorCCC : public CorrectionCandidateCallback {
public:
- DifferentNameValidatorCCC(CXXRecordDecl *Parent)
- : ExpectedParent(Parent ? Parent->getCanonicalDecl() : 0) {}
+ DifferentNameValidatorCCC(ASTContext &Context, FunctionDecl *TypoFD,
+ CXXRecordDecl *Parent)
+ : Context(Context), OriginalFD(TypoFD),
+ ExpectedParent(Parent ? Parent->getCanonicalDecl() : 0) {}
virtual bool ValidateCandidate(const TypoCorrection &candidate) {
if (candidate.getEditDistance() == 0)
return false;
- if (CXXMethodDecl *MD = candidate.getCorrectionDeclAs<CXXMethodDecl>()) {
- CXXRecordDecl *Parent = MD->getParent();
- return Parent && Parent->getCanonicalDecl() == ExpectedParent;
+ llvm::SmallVector<unsigned, 1> MismatchedParams;
+ for (TypoCorrection::const_decl_iterator CDecl = candidate.begin(),
+ CDeclEnd = candidate.end();
+ CDecl != CDeclEnd; ++CDecl) {
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(*CDecl);
+
+ if (FD && !FD->hasBody() &&
+ hasSimilarParameters(Context, FD, OriginalFD, MismatchedParams)) {
+ if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ CXXRecordDecl *Parent = MD->getParent();
+ if (Parent && Parent->getCanonicalDecl() == ExpectedParent)
+ return true;
+ } else if (!ExpectedParent) {
+ return true;
+ }
+ }
}
- return !ExpectedParent;
+ return false;
}
private:
+ ASTContext &Context;
+ FunctionDecl *OriginalFD;
CXXRecordDecl *ExpectedParent;
};
@@ -4571,7 +4766,8 @@ static NamedDecl* DiagnoseInvalidRedeclaration(
assert(!Prev.isAmbiguous() &&
"Cannot have an ambiguity in previous-declaration lookup");
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
- DifferentNameValidatorCCC Validator(MD ? MD->getParent() : 0);
+ DifferentNameValidatorCCC Validator(SemaRef.Context, NewFD,
+ MD ? MD->getParent() : 0);
if (!Prev.empty()) {
for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
Func != FuncEnd; ++Func) {
@@ -4601,8 +4797,8 @@ static NamedDecl* DiagnoseInvalidRedeclaration(
CDeclEnd = Correction.end();
CDecl != CDeclEnd; ++CDecl) {
FunctionDecl *FD = dyn_cast<FunctionDecl>(*CDecl);
- if (FD && hasSimilarParameters(SemaRef.Context, FD, NewFD,
- MismatchedParams)) {
+ if (FD && !FD->hasBody() &&
+ hasSimilarParameters(SemaRef.Context, FD, NewFD, MismatchedParams)) {
Previous.addDecl(FD);
}
}
@@ -4640,19 +4836,23 @@ static NamedDecl* DiagnoseInvalidRedeclaration(
}
}
- if (Correction)
- SemaRef.Diag(NewFD->getLocation(), DiagMsg)
+ if (Correction) {
+ SourceRange FixItLoc(NewFD->getLocation());
+ CXXScopeSpec &SS = ExtraArgs.D.getCXXScopeSpec();
+ if (Correction.getCorrectionSpecifier() && SS.isValid())
+ FixItLoc.setBegin(SS.getBeginLoc());
+ SemaRef.Diag(NewFD->getLocStart(), DiagMsg)
<< Name << NewDC << Correction.getQuoted(SemaRef.getLangOpts())
<< FixItHint::CreateReplacement(
- NewFD->getLocation(),
- Correction.getAsString(SemaRef.getLangOpts()));
- else
+ FixItLoc, Correction.getAsString(SemaRef.getLangOpts()));
+ } else {
SemaRef.Diag(NewFD->getLocation(), DiagMsg)
<< Name << NewDC << NewFD->getLocation();
+ }
bool NewFDisConst = false;
if (CXXMethodDecl *NewMD = dyn_cast<CXXMethodDecl>(NewFD))
- NewFDisConst = NewMD->getTypeQualifiers() & Qualifiers::Const;
+ NewFDisConst = NewMD->isConst();
for (llvm::SmallVector<std::pair<FunctionDecl*, unsigned>, 1>::iterator
NearMatch = NearMatches.begin(), NearMatchEnd = NearMatches.end();
@@ -4660,7 +4860,7 @@ static NamedDecl* DiagnoseInvalidRedeclaration(
FunctionDecl *FD = NearMatch->first;
bool FDisConst = false;
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD))
- FDisConst = MD->getTypeQualifiers() & Qualifiers::Const;
+ FDisConst = MD->isConst();
if (unsigned Idx = NearMatch->second) {
ParmVarDecl *FDParam = FD->getParamDecl(Idx-1);
@@ -4911,7 +5111,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
FunctionTemplateDecl *FunctionTemplate = 0;
bool isExplicitSpecialization = false;
bool isFunctionTemplateSpecialization = false;
+
bool isDependentClassScopeExplicitSpecialization = false;
+ bool HasExplicitTemplateArgs = false;
+ TemplateArgumentListInfo TemplateArgs;
+
bool isVirtualOkay = false;
FunctionDecl *NewFD = CreateNewFunctionDecl(*this, D, DC, R, TInfo, SC,
@@ -5041,56 +5245,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
FunctionTemplate->setInvalidDecl();
}
- // If we see "T var();" at block scope, where T is a class type, it is
- // probably an attempt to initialize a variable, not a function declaration.
- // We don't catch this case earlier, since there is no ambiguity here.
- if (!FunctionTemplate && D.getFunctionDefinitionKind() == FDK_Declaration &&
- CurContext->isFunctionOrMethod() &&
- D.getNumTypeObjects() == 1 && D.isFunctionDeclarator() &&
- D.getDeclSpec().getStorageClassSpecAsWritten()
- == DeclSpec::SCS_unspecified) {
- QualType T = R->getAs<FunctionType>()->getResultType();
- DeclaratorChunk &C = D.getTypeObject(0);
- if (!T->isVoidType() && C.Fun.NumArgs == 0 && !C.Fun.isVariadic &&
- !C.Fun.TrailingReturnType &&
- C.Fun.getExceptionSpecType() == EST_None) {
- SourceRange ParenRange(C.Loc, C.EndLoc);
- Diag(C.Loc, diag::warn_empty_parens_are_function_decl) << ParenRange;
-
- // If the declaration looks like:
- // T var1,
- // f();
- // and name lookup finds a function named 'f', then the ',' was
- // probably intended to be a ';'.
- if (!D.isFirstDeclarator() && D.getIdentifier()) {
- FullSourceLoc Comma(D.getCommaLoc(), SourceMgr);
- FullSourceLoc Name(D.getIdentifierLoc(), SourceMgr);
- if (Comma.getFileID() != Name.getFileID() ||
- Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) {
- LookupResult Result(*this, D.getIdentifier(), SourceLocation(),
- LookupOrdinaryName);
- if (LookupName(Result, S))
- Diag(D.getCommaLoc(), diag::note_empty_parens_function_call)
- << FixItHint::CreateReplacement(D.getCommaLoc(), ";") << NewFD;
- }
- }
- const CXXRecordDecl *RD = T->getAsCXXRecordDecl();
- // Empty parens mean value-initialization, and no parens mean default
- // initialization. These are equivalent if the default constructor is
- // user-provided, or if zero-initialization is a no-op.
- if (RD && RD->hasDefinition() &&
- (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor()))
- Diag(C.Loc, diag::note_empty_parens_default_ctor)
- << FixItHint::CreateRemoval(ParenRange);
- else if (const char *Init = getFixItZeroInitializerForType(T))
- Diag(C.Loc, diag::note_empty_parens_zero_initialize)
- << FixItHint::CreateReplacement(ParenRange, Init);
- else if (LangOpts.CPlusPlus0x)
- Diag(C.Loc, diag::note_empty_parens_zero_initialize)
- << FixItHint::CreateReplacement(ParenRange, "{}");
- }
- }
-
// C++ [dcl.fct.spec]p5:
// The virtual specifier shall only be used in declarations of
// nonstatic class member functions that appear within a
@@ -5333,6 +5487,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
NewFD->setInvalidDecl();
}
+ // Handle attributes.
+ ProcessDeclAttributes(S, NewFD, D,
+ /*NonInheritable=*/false, /*Inheritable=*/true);
+
if (!getLangOpts().CPlusPlus) {
// Perform semantic checking on the function declaration.
bool isExplicitSpecialization=false;
@@ -5348,8 +5506,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
} else {
// If the declarator is a template-id, translate the parser's template
// argument list into our AST format.
- bool HasExplicitTemplateArgs = false;
- TemplateArgumentListInfo TemplateArgs;
if (D.getName().getKind() == UnqualifiedId::IK_TemplateId) {
TemplateIdAnnotation *TemplateId = D.getName().TemplateId;
TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc);
@@ -5580,25 +5736,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
<< D.getCXXScopeSpec().getRange();
}
}
-
-
- // Handle attributes. We need to have merged decls when handling attributes
- // (for example to check for conflicts, etc).
- // FIXME: This needs to happen before we merge declarations. Then,
- // let attribute merging cope with attribute conflicts.
- ProcessDeclAttributes(S, NewFD, D,
- /*NonInheritable=*/false, /*Inheritable=*/true);
-
- // attributes declared post-definition are currently ignored
- // FIXME: This should happen during attribute merging
- if (D.isRedeclaration() && Previous.isSingleResult()) {
- const FunctionDecl *Def;
- FunctionDecl *PrevFD = dyn_cast<FunctionDecl>(Previous.getFoundDecl());
- if (PrevFD && PrevFD->isDefined(Def) && D.hasAttributes()) {
- Diag(NewFD->getLocation(), diag::warn_attribute_precede_definition);
- Diag(Def->getLocation(), diag::note_previous_definition);
- }
- }
AddKnownFunctionAttributes(NewFD);
@@ -5644,6 +5781,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
}
+ // OpenCL v1.2 s6.8 static is invalid for kernel functions.
+ if ((getLangOpts().OpenCLVersion >= 120)
+ && NewFD->hasAttr<OpenCLKernelAttr>()
+ && (SC == SC_Static)) {
+ Diag(D.getIdentifierLoc(), diag::err_static_kernel);
+ D.setInvalidType();
+ }
+
MarkUnusedFileScopedDecl(NewFD);
if (getLangOpts().CUDA)
@@ -5664,8 +5809,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (isDependentClassScopeExplicitSpecialization) {
ClassScopeFunctionSpecializationDecl *NewSpec =
ClassScopeFunctionSpecializationDecl::Create(
- Context, CurContext, SourceLocation(),
- cast<CXXMethodDecl>(NewFD));
+ Context, CurContext, SourceLocation(),
+ cast<CXXMethodDecl>(NewFD),
+ HasExplicitTemplateArgs, TemplateArgs);
CurContext->addDecl(NewSpec);
AddToScope = false;
}
@@ -5683,12 +5829,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
/// that have been instantiated via C++ template instantiation (called
/// via InstantiateDecl).
///
-/// \param IsExplicitSpecialiation whether this new function declaration is
+/// \param IsExplicitSpecialization whether this new function declaration is
/// an explicit specialization of the previous declaration.
///
/// This sets NewFD->isInvalidDecl() to true if there was an error.
///
-/// Returns true if the function declaration is a redeclaration.
+/// \returns true if the function declaration is a redeclaration.
bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
LookupResult &Previous,
bool IsExplicitSpecialization) {
@@ -5882,10 +6028,12 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
// compatible, and if it does, warn the user.
if (NewFD->isExternC()) {
QualType R = NewFD->getResultType();
- if (!R.isPODType(Context) &&
- !R->isVoidType())
- Diag( NewFD->getLocation(), diag::warn_return_value_udt )
- << NewFD << R;
+ if (R->isIncompleteType() && !R->isVoidType())
+ Diag(NewFD->getLocation(), diag::warn_return_value_udt_incomplete)
+ << NewFD << R;
+ else if (!R.isPODType(Context) && !R->isVoidType() &&
+ !R->isObjCObjectPointerType())
+ Diag(NewFD->getLocation(), diag::warn_return_value_udt) << NewFD << R;
}
}
return Redeclaration;
@@ -6040,55 +6188,73 @@ namespace {
}
}
- void VisitExpr(Expr *E) {
- if (isa<ObjCMessageExpr>(*E)) return;
- if (isRecordType) {
- Expr *expr = E;
- if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
- ValueDecl *VD = ME->getMemberDecl();
- if (isa<EnumConstantDecl>(VD) || isa<VarDecl>(VD)) return;
- expr = ME->getBase();
- }
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(expr)) {
+ // Sometimes, the expression passed in lacks the casts that are used
+ // to determine which DeclRefExpr's to check. Assume that the casts
+ // are present and continue visiting the expression.
+ void HandleExpr(Expr *E) {
+ // Skip checking T a = a where T is not a record type. Doing so is a
+ // way to silence uninitialized warnings.
+ if (isRecordType)
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
HandleDeclRefExpr(DRE);
- return;
- }
+
+ if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ HandleValue(CO->getTrueExpr());
+ HandleValue(CO->getFalseExpr());
+ }
+
+ Visit(E);
+ }
+
+ // For most expressions, the cast is directly above the DeclRefExpr.
+ // For conditional operators, the cast can be outside the conditional
+ // operator if both expressions are DeclRefExpr's.
+ void HandleValue(Expr *E) {
+ E = E->IgnoreParenImpCasts();
+ if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(E)) {
+ HandleDeclRefExpr(DRE);
+ return;
+ }
+
+ if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ HandleValue(CO->getTrueExpr());
+ HandleValue(CO->getFalseExpr());
}
- Inherited::VisitExpr(E);
+ }
+
+ void VisitImplicitCastExpr(ImplicitCastExpr *E) {
+ if ((!isRecordType && E->getCastKind() == CK_LValueToRValue) ||
+ (isRecordType && E->getCastKind() == CK_NoOp))
+ HandleValue(E->getSubExpr());
+
+ Inherited::VisitImplicitCastExpr(E);
}
void VisitMemberExpr(MemberExpr *E) {
+ // Don't warn on arrays since they can be treated as pointers.
if (E->getType()->canDecayToPointerType()) return;
+
ValueDecl *VD = E->getMemberDecl();
- if (isa<FieldDecl>(VD) || isa<CXXMethodDecl>(VD))
+ CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(VD);
+ if (isa<FieldDecl>(VD) || (MD && !MD->isStatic()))
if (DeclRefExpr *DRE
= dyn_cast<DeclRefExpr>(E->getBase()->IgnoreParenImpCasts())) {
HandleDeclRefExpr(DRE);
return;
}
- Inherited::VisitMemberExpr(E);
- }
- void VisitImplicitCastExpr(ImplicitCastExpr *E) {
- if ((!isRecordType &&E->getCastKind() == CK_LValueToRValue) ||
- (isRecordType && E->getCastKind() == CK_NoOp)) {
- Expr* SubExpr = E->getSubExpr()->IgnoreParenImpCasts();
- if (MemberExpr *ME = dyn_cast<MemberExpr>(SubExpr))
- SubExpr = ME->getBase()->IgnoreParenImpCasts();
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SubExpr)) {
- HandleDeclRefExpr(DRE);
- return;
- }
- }
- Inherited::VisitImplicitCastExpr(E);
+ Inherited::VisitMemberExpr(E);
}
void VisitUnaryOperator(UnaryOperator *E) {
// For POD record types, addresses of its own members are well-defined.
- if (isRecordType && isPODType) return;
+ if (E->getOpcode() == UO_AddrOf && isRecordType && isPODType &&
+ isa<MemberExpr>(E->getSubExpr()->IgnoreParens())) return;
Inherited::VisitUnaryOperator(E);
}
-
+
+ void VisitObjCMessageExpr(ObjCMessageExpr *E) { return; }
+
void HandleDeclRefExpr(DeclRefExpr *DRE) {
Decl* ReferenceDecl = DRE->getDecl();
if (OrigDecl != ReferenceDecl) return;
@@ -6105,7 +6271,7 @@ namespace {
/// CheckSelfReference - Warns if OrigDecl is used in expression E.
void Sema::CheckSelfReference(Decl* OrigDecl, Expr *E) {
- SelfReferenceChecker(*this, OrigDecl).VisitExpr(E);
+ SelfReferenceChecker(*this, OrigDecl).HandleExpr(E);
}
/// AddInitializerToDecl - Adds the initializer Init to the
@@ -6145,13 +6311,19 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
// Check for self-references within variable initializers.
// Variables declared within a function/method body are handled
// by a dataflow analysis.
- if (!VDecl->hasLocalStorage() && !VDecl->isStaticLocal())
+ // Record types initialized by initializer list are handled here.
+ // Initialization by constructors are handled in TryConstructorInitialization.
+ if (!VDecl->hasLocalStorage() && !VDecl->isStaticLocal() &&
+ (isa<InitListExpr>(Init) || !VDecl->getType()->isRecordType()))
CheckSelfReference(RealDecl, Init);
ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
- if (TypeMayContainAuto && VDecl->getType()->getContainedAutoType()) {
+ AutoType *Auto = 0;
+ if (TypeMayContainAuto &&
+ (Auto = VDecl->getType()->getContainedAutoType()) &&
+ !Auto->isDeduced()) {
Expr *DeduceInit = Init;
// Initializer could be a C++ direct-initializer. Deduction only works if it
// contains exactly one expression.
@@ -6192,6 +6364,17 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl))
VDecl->setInvalidDecl();
+ // Warn if we deduced 'id'. 'auto' usually implies type-safety, but using
+ // 'id' instead of a specific object type prevents most of our usual checks.
+ // We only want to warn outside of template instantiations, though:
+ // inside a template, the 'id' could have come from a parameter.
+ if (ActiveTemplateInstantiations.empty() &&
+ DeducedType->getType()->isObjCIdType()) {
+ SourceLocation Loc = DeducedType->getTypeLoc().getBeginLoc();
+ Diag(Loc, diag::warn_auto_var_is_id)
+ << VDecl->getDeclName() << DeduceInit->getSourceRange();
+ }
+
// If this is a redeclaration, check that the type we just deduced matches
// the previously declared type.
if (VarDecl *Old = VDecl->getPreviousDecl())
@@ -6906,9 +7089,55 @@ Sema::BuildDeclaratorGroup(Decl **Group, unsigned NumDecls,
}
}
+ ActOnDocumentableDecls(Group, NumDecls);
+
return DeclGroupPtrTy::make(DeclGroupRef::Create(Context, Group, NumDecls));
}
+void Sema::ActOnDocumentableDecl(Decl *D) {
+ ActOnDocumentableDecls(&D, 1);
+}
+
+void Sema::ActOnDocumentableDecls(Decl **Group, unsigned NumDecls) {
+ // Don't parse the comment if Doxygen diagnostics are ignored.
+ if (NumDecls == 0 || !Group[0])
+ return;
+
+ if (Diags.getDiagnosticLevel(diag::warn_doc_param_not_found,
+ Group[0]->getLocation())
+ == DiagnosticsEngine::Ignored)
+ return;
+
+ if (NumDecls >= 2) {
+ // This is a decl group. Normally it will contain only declarations
+ // procuded from declarator list. But in case we have any definitions or
+ // additional declaration references:
+ // 'typedef struct S {} S;'
+ // 'typedef struct S *S;'
+ // 'struct S *pS;'
+ // FinalizeDeclaratorGroup adds these as separate declarations.
+ Decl *MaybeTagDecl = Group[0];
+ if (MaybeTagDecl && isa<TagDecl>(MaybeTagDecl)) {
+ Group++;
+ NumDecls--;
+ }
+ }
+
+ // See if there are any new comments that are not attached to a decl.
+ ArrayRef<RawComment *> Comments = Context.getRawCommentList().getComments();
+ if (!Comments.empty() &&
+ !Comments.back()->isAttached()) {
+ // There is at least one comment that not attached to a decl.
+ // Maybe it should be attached to one of these decls?
+ //
+ // Note that this way we pick up not only comments that precede the
+ // declaration, but also comments that *follow* the declaration -- thanks to
+ // the lookahead in the lexer: we've consumed the semicolon and looked
+ // ahead through comments.
+ for (unsigned i = 0; i != NumDecls; ++i)
+ Context.getCommentForDecl(Group[i]);
+ }
+}
/// ActOnParamDeclarator - Called from Parser::ParseFunctionDeclarator()
/// to introduce parameters into function prototype scope.
@@ -7132,9 +7361,10 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
// Parameter declarators cannot be interface types. All ObjC objects are
// passed by reference.
if (T->isObjCObjectType()) {
+ SourceLocation TypeEndLoc = TSInfo->getTypeLoc().getLocEnd();
Diag(NameLoc,
diag::err_object_cannot_be_passed_returned_by_value) << 1 << T
- << FixItHint::CreateInsertion(NameLoc, "*");
+ << FixItHint::CreateInsertion(TypeEndLoc, "*");
T = Context.getObjCObjectPointerType(T);
New->setType(T);
}
@@ -7225,6 +7455,10 @@ static bool ShouldWarnAboutMissingPrototype(const FunctionDecl *FD) {
if (FD->isFunctionTemplateSpecialization())
return false;
+ // Don't warn for OpenCL kernels.
+ if (FD->hasAttr<OpenCLKernelAttr>())
+ return false;
+
bool MissingPrototype = true;
for (const FunctionDecl *Prev = FD->getPreviousDecl();
Prev; Prev = Prev->getPreviousDecl()) {
@@ -7253,6 +7487,7 @@ void Sema::CheckForFunctionRedefinition(FunctionDecl *FD) {
else
Diag(FD->getLocation(), diag::err_redefinition) << FD->getDeclName();
Diag(Definition->getLocation(), diag::note_previous_definition);
+ FD->setInvalidDecl();
}
}
@@ -7388,6 +7623,7 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) {
<< FD->getName() << "dllimport";
}
}
+ ActOnDocumentableDecl(FD);
return FD;
}
@@ -7463,7 +7699,12 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(FD))
MarkVTableUsed(FD->getLocation(), Constructor->getParent());
- computeNRVO(Body, getCurFunction());
+ // Try to apply the named return value optimization. We have to check
+ // if we can do this here because lambdas keep return statements around
+ // to deduce an implicit return type.
+ if (getLangOpts().CPlusPlus && FD->getResultType()->isRecordType() &&
+ !FD->isDependentContext())
+ computeNRVO(Body, getCurFunction());
}
assert((FD == getCurFunctionDecl() || getCurLambda()->CallOperator == FD) &&
@@ -7471,8 +7712,6 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
} else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(dcl)) {
assert(MD == getCurMethodDecl() && "Method parsing confused");
MD->setBody(Body);
- if (Body)
- MD->setEndLoc(Body->getLocEnd());
if (!MD->isInvalidDecl()) {
DiagnoseUnusedParameters(MD->param_begin(), MD->param_end());
DiagnoseSizeOfParametersAndReturnValue(MD->param_begin(), MD->param_end(),
@@ -7481,22 +7720,24 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
if (Body)
computeNRVO(Body, getCurFunction());
}
- if (ObjCShouldCallSuperDealloc) {
+ if (getCurFunction()->ObjCShouldCallSuperDealloc) {
Diag(MD->getLocEnd(), diag::warn_objc_missing_super_dealloc);
- ObjCShouldCallSuperDealloc = false;
+ getCurFunction()->ObjCShouldCallSuperDealloc = false;
}
- if (ObjCShouldCallSuperFinalize) {
+ if (getCurFunction()->ObjCShouldCallSuperFinalize) {
Diag(MD->getLocEnd(), diag::warn_objc_missing_super_finalize);
- ObjCShouldCallSuperFinalize = false;
+ getCurFunction()->ObjCShouldCallSuperFinalize = false;
}
} else {
return 0;
}
- assert(!ObjCShouldCallSuperDealloc && "This should only be set for "
- "ObjC methods, which should have been handled in the block above.");
- assert(!ObjCShouldCallSuperFinalize && "This should only be set for "
- "ObjC methods, which should have been handled in the block above.");
+ assert(!getCurFunction()->ObjCShouldCallSuperDealloc &&
+ "This should only be set for ObjC methods, which should have been "
+ "handled in the block above.");
+ assert(!getCurFunction()->ObjCShouldCallSuperFinalize &&
+ "This should only be set for ObjC methods, which should have been "
+ "handled in the block above.");
// Verify and clean out per-function state.
if (Body) {
@@ -7630,10 +7871,10 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
(void)Error; // Silence warning.
assert(!Error && "Error setting up implicit decl!");
Declarator D(DS, Declarator::BlockContext);
- D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0,
- 0, 0, true, SourceLocation(),
+ D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, false,
+ SourceLocation(), 0, 0, 0, true,
+ SourceLocation(), SourceLocation(),
SourceLocation(), SourceLocation(),
- SourceLocation(),
EST_None, SourceLocation(),
0, 0, 0, 0, Loc, Loc, D),
DS.getAttributes(),
@@ -7733,6 +7974,13 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
"printf", 2,
Name->isStr("vasprintf") ? 0 : 3));
}
+
+ if (Name->isStr("__CFStringMakeConstantString")) {
+ // We already have a __builtin___CFStringMakeConstantString,
+ // but builds that use -fno-constant-cfstrings don't go through that.
+ if (!FD->getAttr<FormatArgAttr>())
+ FD->addAttr(::new (Context) FormatArgAttr(FD->getLocation(), Context, 1));
+ }
}
TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
@@ -8566,9 +8814,10 @@ CreateNewDecl:
// many points during the parsing of a struct declaration (because
// the #pragma tokens are effectively skipped over during the
// parsing of the struct).
- AddAlignmentAttributesForRecord(RD);
-
- AddMsStructLayoutForRecord(RD);
+ if (TUK == TUK_Definition) {
+ AddAlignmentAttributesForRecord(RD);
+ AddMsStructLayoutForRecord(RD);
+ }
}
if (ModulePrivateLoc.isValid()) {
@@ -8653,6 +8902,13 @@ CreateNewDecl:
InFunctionDeclarator && Name)
DeclsInPrototypeScope.push_back(New);
+ if (PrevDecl)
+ mergeDeclAttributes(New, PrevDecl);
+
+ // If there's a #pragma GCC visibility in scope, set the visibility of this
+ // record.
+ AddPushedVisibilityAttribute(New);
+
OwnedDecl = true;
return New;
}
@@ -8663,6 +8919,12 @@ void Sema::ActOnTagStartDefinition(Scope *S, Decl *TagD) {
// Enter the tag context.
PushDeclContext(S, Tag);
+
+ ActOnDocumentableDecl(TagD);
+
+ // If there's a #pragma GCC visibility in scope, set the visibility of this
+ // record.
+ AddPushedVisibilityAttribute(Tag);
}
Decl *Sema::ActOnObjCContainerStartDefinition(Decl *IDecl) {
@@ -8849,7 +9111,7 @@ Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart,
Declarator &D, Expr *BitfieldWidth) {
FieldDecl *Res = HandleField(S, cast_or_null<RecordDecl>(TagD),
DeclStart, D, static_cast<Expr*>(BitfieldWidth),
- /*HasInit=*/false, AS_public);
+ /*InitStyle=*/ICIS_NoInit, AS_public);
return Res;
}
@@ -8857,7 +9119,8 @@ Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart,
///
FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
SourceLocation DeclStart,
- Declarator &D, Expr *BitWidth, bool HasInit,
+ Declarator &D, Expr *BitWidth,
+ InClassInitStyle InitStyle,
AccessSpecifier AS) {
IdentifierInfo *II = D.getIdentifier();
SourceLocation Loc = DeclStart;
@@ -8919,7 +9182,7 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
= (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_mutable);
SourceLocation TSSL = D.getLocStart();
FieldDecl *NewFD
- = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, HasInit,
+ = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, InitStyle,
TSSL, AS, PrevDecl, &D);
if (NewFD->isInvalidDecl())
@@ -8952,7 +9215,8 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
TypeSourceInfo *TInfo,
RecordDecl *Record, SourceLocation Loc,
- bool Mutable, Expr *BitWidth, bool HasInit,
+ bool Mutable, Expr *BitWidth,
+ InClassInitStyle InitStyle,
SourceLocation TSSL,
AccessSpecifier AS, NamedDecl *PrevDecl,
Declarator *D) {
@@ -9042,7 +9306,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
}
FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo,
- BitWidth, Mutable, HasInit);
+ BitWidth, Mutable, InitStyle);
if (InvalidDecl)
NewFD->setInvalidDecl();
@@ -9213,10 +9477,9 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) {
case CXXCopyAssignment:
if (RD->hasUserDeclaredCopyAssignment()) {
- // FIXME: this should use the location of the copy
- // assignment, not the type.
- SourceLocation TyLoc = RD->getLocStart();
- Diag(TyLoc, diag::note_nontrivial_user_defined) << QT << member;
+ SourceLocation AssignLoc =
+ RD->getCopyAssignmentOperator(0)->getLocation();
+ Diag(AssignLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
break;
@@ -9295,12 +9558,12 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) {
typedef RecordDecl::field_iterator field_iter;
for (field_iter fi = RD->field_begin(), fe = RD->field_end(); fi != fe;
++fi) {
- QualType EltTy = Context.getBaseElementType((*fi)->getType());
+ QualType EltTy = Context.getBaseElementType(fi->getType());
if (const RecordType *EltRT = EltTy->getAs<RecordType>()) {
CXXRecordDecl* EltRD = cast<CXXRecordDecl>(EltRT->getDecl());
if (!(EltRD->*hasTrivial)()) {
- SourceLocation FLoc = (*fi)->getLocation();
+ SourceLocation FLoc = fi->getLocation();
Diag(FLoc, diag::note_nontrivial_has_nontrivial) << QT << 0 << member;
DiagnoseNontrivial(EltRT, member);
return;
@@ -9316,7 +9579,7 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) {
case Qualifiers::OCL_Autoreleasing:
case Qualifiers::OCL_Weak:
case Qualifiers::OCL_Strong:
- Diag((*fi)->getLocation(), diag::note_nontrivial_objc_ownership)
+ Diag(fi->getLocation(), diag::note_nontrivial_objc_ownership)
<< QT << EltTy.getObjCLifetime();
return;
}
@@ -9390,7 +9653,7 @@ Decl *Sema::ActOnIvar(Scope *S,
ObjCContainerDecl *EnclosingContext;
if (ObjCImplementationDecl *IMPDecl =
dyn_cast<ObjCImplementationDecl>(EnclosingDecl)) {
- if (!LangOpts.ObjCNonFragileABI2) {
+ if (LangOpts.ObjCRuntime.isFragile()) {
// Case of ivar declared in an implementation. Context is that of its class.
EnclosingContext = IMPDecl->getClassInterface();
assert(EnclosingContext && "Implementation has no class interface!");
@@ -9400,7 +9663,7 @@ Decl *Sema::ActOnIvar(Scope *S,
} else {
if (ObjCCategoryDecl *CDecl =
dyn_cast<ObjCCategoryDecl>(EnclosingDecl)) {
- if (!LangOpts.ObjCNonFragileABI2 || !CDecl->IsClassExtension()) {
+ if (LangOpts.ObjCRuntime.isFragile() || !CDecl->IsClassExtension()) {
Diag(Loc, diag::err_misplaced_ivar) << CDecl->IsClassExtension();
return 0;
}
@@ -9443,7 +9706,11 @@ Decl *Sema::ActOnIvar(Scope *S,
S->AddDecl(NewID);
IdResolver.AddDecl(NewID);
}
-
+
+ if (LangOpts.ObjCRuntime.isNonFragile() &&
+ !NewID->isInvalidDecl() && isa<ObjCInterfaceDecl>(EnclosingDecl))
+ Diag(Loc, diag::warn_ivars_in_interface);
+
return NewID;
}
@@ -9453,7 +9720,7 @@ Decl *Sema::ActOnIvar(Scope *S,
/// then add an implicit `char :0` ivar to the end of that interface.
void Sema::ActOnLastBitfield(SourceLocation DeclLoc,
SmallVectorImpl<Decl *> &AllIvarDecls) {
- if (!LangOpts.ObjCNonFragileABI2 || AllIvarDecls.empty())
+ if (LangOpts.ObjCRuntime.isFragile() || AllIvarDecls.empty())
return;
Decl *ivarDecl = AllIvarDecls[AllIvarDecls.size()-1];
@@ -9492,11 +9759,23 @@ void Sema::ActOnFields(Scope* S,
AttributeList *Attr) {
assert(EnclosingDecl && "missing record or interface decl");
- // If the decl this is being inserted into is invalid, then it may be a
- // redeclaration or some other bogus case. Don't try to add fields to it.
- if (EnclosingDecl->isInvalidDecl())
- return;
-
+ // If this is an Objective-C @implementation or category and we have
+ // new fields here we should reset the layout of the interface since
+ // it will now change.
+ if (!Fields.empty() && isa<ObjCContainerDecl>(EnclosingDecl)) {
+ ObjCContainerDecl *DC = cast<ObjCContainerDecl>(EnclosingDecl);
+ switch (DC->getKind()) {
+ default: break;
+ case Decl::ObjCCategory:
+ Context.ResetObjCLayout(cast<ObjCCategoryDecl>(DC)->getClassInterface());
+ break;
+ case Decl::ObjCImplementation:
+ Context.
+ ResetObjCLayout(cast<ObjCImplementationDecl>(DC)->getClassInterface());
+ break;
+ }
+ }
+
RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl);
// Start counting up the number of named members; make sure to include
@@ -9704,7 +9983,7 @@ void Sema::ActOnFields(Scope* S,
// However, here we check whether this particular class is only
// non-POD because of the presence of an Objective-C pointer member.
// If so, objects of this type cannot be shared between code compiled
- // with instant objects and code compiled with manual retain/release.
+ // with ARC and code compiled with manual retain/release.
if (getLangOpts().ObjCAutoRefCount &&
CXXRecord->hasObjectMember() &&
CXXRecord->getLinkage() == ExternalLinkage) {
@@ -9848,11 +10127,6 @@ void Sema::ActOnFields(Scope* S,
if (Attr)
ProcessDeclAttributeList(S, Record, Attr);
-
- // If there's a #pragma GCC visibility in scope, and this isn't a subclass,
- // set the visibility of this record.
- if (Record && !Record->getDeclContext()->isRecord())
- AddPushedVisibilityAttribute(Record);
}
/// \brief Determine whether the given integral value is representable within
@@ -10106,15 +10380,16 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst,
}
}
- // C++ [class.mem]p13:
- // If T is the name of a class, then each of the following shall have a
- // name different from T:
- // - every enumerator of every member of class T that is an enumerated
- // type
+ // C++ [class.mem]p15:
+ // If T is the name of a class, then each of the following shall have a name
+ // different from T:
+ // - every enumerator of every member of class T that is an unscoped
+ // enumerated type
if (CXXRecordDecl *Record
= dyn_cast<CXXRecordDecl>(
TheEnumDecl->getDeclContext()->getRedeclContext()))
- if (Record->getIdentifier() && Record->getIdentifier() == Id)
+ if (!TheEnumDecl->isScoped() &&
+ Record->getIdentifier() && Record->getIdentifier() == Id)
Diag(IdLoc, diag::err_member_name_of_class) << Id;
EnumConstantDecl *New =
@@ -10129,9 +10404,62 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst,
PushOnScopeChains(New, S);
}
+ ActOnDocumentableDecl(New);
+
return New;
}
+// Emits a warning if every element in the enum is the same value and if
+// every element is initialized with a integer or boolean literal.
+static void CheckForUniqueEnumValues(Sema &S, Decl **Elements,
+ unsigned NumElements, EnumDecl *Enum,
+ QualType EnumType) {
+ if (S.Diags.getDiagnosticLevel(diag::warn_identical_enum_values,
+ Enum->getLocation()) ==
+ DiagnosticsEngine::Ignored)
+ return;
+
+ if (NumElements < 2)
+ return;
+
+ if (!Enum->getIdentifier())
+ return;
+
+ llvm::APSInt FirstVal;
+
+ for (unsigned i = 0; i != NumElements; ++i) {
+ EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(Elements[i]);
+ if (!ECD)
+ return;
+
+ Expr *InitExpr = ECD->getInitExpr();
+ if (!InitExpr)
+ return;
+ InitExpr = InitExpr->IgnoreImpCasts();
+ if (!isa<IntegerLiteral>(InitExpr) && !isa<CXXBoolLiteralExpr>(InitExpr))
+ return;
+
+ if (i == 0) {
+ FirstVal = ECD->getInitVal();
+ continue;
+ }
+
+ if (!llvm::APSInt::isSameValue(FirstVal, ECD->getInitVal()))
+ return;
+ }
+
+ S.Diag(Enum->getLocation(), diag::warn_identical_enum_values)
+ << EnumType << FirstVal.toString(10)
+ << Enum->getSourceRange();
+
+ EnumConstantDecl *Last = cast<EnumConstantDecl>(Elements[NumElements - 1]),
+ *Next = cast<EnumConstantDecl>(Elements[NumElements - 2]);
+
+ S.Diag(Last->getLocation(), diag::note_identical_enum_values)
+ << FixItHint::CreateReplacement(Last->getInitExpr()->getSourceRange(),
+ Next->getName());
+}
+
void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
SourceLocation RBraceLoc, Decl *EnumDeclX,
Decl **Elements, unsigned NumElements,
@@ -10355,6 +10683,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
if (InFunctionDeclarator)
DeclsInPrototypeScope.push_back(Enum);
+ CheckForUniqueEnumValues(*this, Elements, NumElements, Enum, EnumType);
}
Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr,
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 5c6ddd2..22bff86 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -14,6 +14,7 @@
#include "clang/Sema/SemaInternal.h"
#include "TargetAttributesSema.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclObjC.h"
@@ -42,7 +43,8 @@ enum AttributeDeclKind {
ExpectedMethod,
ExpectedVariableFunctionOrLabel,
ExpectedFieldOrGlobalVar,
- ExpectedStruct
+ ExpectedStruct,
+ ExpectedTLSVar
};
//===----------------------------------------------------------------------===//
@@ -82,7 +84,7 @@ static bool isFunction(const Decl *D) {
/// type (function or function-typed variable) or an Objective-C
/// method.
static bool isFunctionOrMethod(const Decl *D) {
- return isFunction(D)|| isa<ObjCMethodDecl>(D);
+ return isFunction(D) || isa<ObjCMethodDecl>(D);
}
/// isFunctionOrMethodOrBlock - Return true if the given decl has function
@@ -238,17 +240,45 @@ static bool isIntOrBool(Expr *Exp) {
return QT->isBooleanType() || QT->isIntegerType();
}
-///
+
+// Check to see if the type is a smart pointer of some kind. We assume
+// it's a smart pointer if it defines both operator-> and operator*.
+static bool threadSafetyCheckIsSmartPointer(Sema &S, const RecordType* RT) {
+ DeclContextLookupConstResult Res1 = RT->getDecl()->lookup(
+ S.Context.DeclarationNames.getCXXOperatorName(OO_Star));
+ if (Res1.first == Res1.second)
+ return false;
+
+ DeclContextLookupConstResult Res2 = RT->getDecl()->lookup(
+ S.Context.DeclarationNames.getCXXOperatorName(OO_Arrow));
+ if (Res2.first == Res2.second)
+ return false;
+
+ return true;
+}
+
/// \brief Check if passed in Decl is a pointer type.
/// Note that this function may produce an error message.
/// \return true if the Decl is a pointer type; false otherwise
-///
-static bool checkIsPointer(Sema &S, const Decl *D, const AttributeList &Attr) {
+static bool threadSafetyCheckIsPointer(Sema &S, const Decl *D,
+ const AttributeList &Attr) {
if (const ValueDecl *vd = dyn_cast<ValueDecl>(D)) {
QualType QT = vd->getType();
if (QT->isAnyPointerType())
return true;
- S.Diag(Attr.getLoc(), diag::warn_pointer_attribute_wrong_type)
+
+ if (const RecordType *RT = QT->getAs<RecordType>()) {
+ // If it's an incomplete type, it could be a smart pointer; skip it.
+ // (We don't want to force template instantiation if we can avoid it,
+ // since that would alter the order in which templates are instantiated.)
+ if (RT->isIncompleteType())
+ return true;
+
+ if (threadSafetyCheckIsSmartPointer(S, RT))
+ return true;
+ }
+
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_decl_not_pointer)
<< Attr.getName()->getName() << QT;
} else {
S.Diag(Attr.getLoc(), diag::err_attribute_can_be_applied_only_to_value_decl)
@@ -270,35 +300,60 @@ static const RecordType *getRecordType(QualType QT) {
return 0;
}
+
+static bool checkBaseClassIsLockableCallback(const CXXBaseSpecifier *Specifier,
+ CXXBasePath &Path, void *Unused) {
+ const RecordType *RT = Specifier->getType()->getAs<RecordType>();
+ if (RT->getDecl()->getAttr<LockableAttr>())
+ return true;
+ return false;
+}
+
+
/// \brief Thread Safety Analysis: Checks that the passed in RecordType
-/// resolves to a lockable object. May flag an error.
+/// resolves to a lockable object.
static void checkForLockableRecord(Sema &S, Decl *D, const AttributeList &Attr,
QualType Ty) {
const RecordType *RT = getRecordType(Ty);
-
+
// Warn if could not get record type for this argument.
if (!RT) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_argument_not_class)
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_argument_not_class)
<< Attr.getName() << Ty.getAsString();
return;
}
- // Don't check for lockable if the class hasn't been defined yet.
+
+ // Don't check for lockable if the class hasn't been defined yet.
if (RT->isIncompleteType())
return;
- // Warn if the type is not lockable.
- if (!RT->getDecl()->getAttr<LockableAttr>()) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_argument_not_lockable)
- << Attr.getName() << Ty.getAsString();
+
+ // Allow smart pointers to be used as lockable objects.
+ // FIXME -- Check the type that the smart pointer points to.
+ if (threadSafetyCheckIsSmartPointer(S, RT))
+ return;
+
+ // Check if the type is lockable.
+ RecordDecl *RD = RT->getDecl();
+ if (RD->getAttr<LockableAttr>())
return;
+
+ // Else check if any base classes are lockable.
+ if (CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) {
+ CXXBasePaths BPaths(false, false);
+ if (CRD->lookupInBases(checkBaseClassIsLockableCallback, 0, BPaths))
+ return;
}
+
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_argument_not_lockable)
+ << Attr.getName() << Ty.getAsString();
}
/// \brief Thread Safety Analysis: Checks that all attribute arguments, starting
-/// from Sidx, resolve to a lockable object. May flag an error.
+/// from Sidx, resolve to a lockable object.
/// \param Sidx The attribute argument index to start checking with.
/// \param ParamIdxOk Whether an argument can be indexing into a function
/// parameter list.
-static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
+static void checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
const AttributeList &Attr,
SmallVectorImpl<Expr*> &Args,
int Sidx = 0,
@@ -307,13 +362,33 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
Expr *ArgExp = Attr.getArg(Idx);
if (ArgExp->isTypeDependent()) {
- // FIXME -- need to processs this again on template instantiation
+ // FIXME -- need to check this again on template instantiation
Args.push_back(ArgExp);
continue;
}
+ if (StringLiteral *StrLit = dyn_cast<StringLiteral>(ArgExp)) {
+ // Ignore empty strings without warnings
+ if (StrLit->getLength() == 0)
+ continue;
+
+ // We allow constant strings to be used as a placeholder for expressions
+ // that are not valid C++ syntax, but warn that they are ignored.
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_ignored) <<
+ Attr.getName();
+ continue;
+ }
+
QualType ArgTy = ArgExp->getType();
+ // A pointer to member expression of the form &MyClass::mu is treated
+ // specially -- we need to look at the type of the member.
+ if (UnaryOperator *UOp = dyn_cast<UnaryOperator>(ArgExp))
+ if (UOp->getOpcode() == UO_AddrOf)
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(UOp->getSubExpr()))
+ if (DRE->getDecl()->isCXXInstanceMember())
+ ArgTy = DRE->getDecl()->getType();
+
// First see if we can just cast to record type, or point to record type.
const RecordType *RT = getRecordType(ArgTy);
@@ -329,7 +404,7 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
if(!ArgValue.isStrictlyPositive() || ParamIdxFromOne > NumParams) {
S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_range)
<< Attr.getName() << Idx + 1 << NumParams;
- return false;
+ continue;
}
ArgTy = FD->getParamDecl(ParamIdxFromZero)->getType();
}
@@ -339,7 +414,6 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
Args.push_back(ArgExp);
}
- return true;
}
//===----------------------------------------------------------------------===//
@@ -350,78 +424,125 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
// least add some helper functions to check most argument patterns (#
// and types of args).
-static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool pointer = false) {
+enum ThreadAttributeDeclKind {
+ ThreadExpectedFieldOrGlobalVar,
+ ThreadExpectedFunctionOrMethod,
+ ThreadExpectedClassOrStruct
+};
+
+static bool checkGuardedVarAttrCommon(Sema &S, Decl *D,
+ const AttributeList &Attr) {
assert(!Attr.isInvalid());
if (!checkAttributeNumArgs(S, Attr, 0))
- return;
+ return false;
// D must be either a member field or global (potentially shared) variable.
if (!mayBeSharedVariable(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFieldOrGlobalVar;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFieldOrGlobalVar;
+ return false;
}
- if (pointer && !checkIsPointer(S, D, Attr))
+ return true;
+}
+
+static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (!checkGuardedVarAttrCommon(S, D, Attr))
return;
- if (pointer)
- D->addAttr(::new (S.Context) PtGuardedVarAttr(Attr.getRange(), S.Context));
- else
- D->addAttr(::new (S.Context) GuardedVarAttr(Attr.getRange(), S.Context));
+ D->addAttr(::new (S.Context) GuardedVarAttr(Attr.getRange(), S.Context));
}
-static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool pointer = false) {
- assert(!Attr.isInvalid());
+static void handlePtGuardedVarAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ if (!checkGuardedVarAttrCommon(S, D, Attr))
+ return;
- if (!checkAttributeNumArgs(S, Attr, 1))
+ if (!threadSafetyCheckIsPointer(S, D, Attr))
return;
- Expr *Arg = Attr.getArg(0);
+ D->addAttr(::new (S.Context) PtGuardedVarAttr(Attr.getRange(), S.Context));
+}
+
+static bool checkGuardedByAttrCommon(Sema &S, Decl *D,
+ const AttributeList &Attr,
+ Expr* &Arg) {
+ assert(!Attr.isInvalid());
+
+ if (!checkAttributeNumArgs(S, Attr, 1))
+ return false;
// D must be either a member field or global (potentially shared) variable.
if (!mayBeSharedVariable(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFieldOrGlobalVar;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFieldOrGlobalVar;
+ return false;
}
- if (pointer && !checkIsPointer(S, D, Attr))
- return;
+ SmallVector<Expr*, 1> Args;
+ // check that all arguments are lockable objects
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args);
+ unsigned Size = Args.size();
+ if (Size != 1)
+ return false;
- if (!Arg->isTypeDependent()) {
- checkForLockableRecord(S, D, Attr, Arg->getType());
- }
+ Arg = Args[0];
- if (pointer)
- D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getRange(),
- S.Context, Arg));
- else
- D->addAttr(::new (S.Context) GuardedByAttr(Attr.getRange(), S.Context, Arg));
+ return true;
+}
+
+static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ Expr *Arg = 0;
+ if (!checkGuardedByAttrCommon(S, D, Attr, Arg))
+ return;
+
+ D->addAttr(::new (S.Context) GuardedByAttr(Attr.getRange(), S.Context, Arg));
}
+static void handlePtGuardedByAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ Expr *Arg = 0;
+ if (!checkGuardedByAttrCommon(S, D, Attr, Arg))
+ return;
+
+ if (!threadSafetyCheckIsPointer(S, D, Attr))
+ return;
+
+ D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getRange(),
+ S.Context, Arg));
+}
-static void handleLockableAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool scoped = false) {
+static bool checkLockableAttrCommon(Sema &S, Decl *D,
+ const AttributeList &Attr) {
assert(!Attr.isInvalid());
if (!checkAttributeNumArgs(S, Attr, 0))
- return;
+ return false;
// FIXME: Lockable structs for C code.
if (!isa<CXXRecordDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedClass;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedClassOrStruct;
+ return false;
}
- if (scoped)
- D->addAttr(::new (S.Context) ScopedLockableAttr(Attr.getRange(), S.Context));
- else
- D->addAttr(::new (S.Context) LockableAttr(Attr.getRange(), S.Context));
+ return true;
+}
+
+static void handleLockableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (!checkLockableAttrCommon(S, D, Attr))
+ return;
+
+ D->addAttr(::new (S.Context) LockableAttr(Attr.getRange(), S.Context));
+}
+
+static void handleScopedLockableAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ if (!checkLockableAttrCommon(S, D, Attr))
+ return;
+
+ D->addAttr(::new (S.Context) ScopedLockableAttr(Attr.getRange(), S.Context));
}
static void handleNoThreadSafetyAttr(Sema &S, Decl *D,
@@ -432,8 +553,8 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D,
return;
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
return;
}
@@ -442,7 +563,7 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D,
}
static void handleNoAddressSafetyAttr(Sema &S, Decl *D,
- const AttributeList &Attr) {
+ const AttributeList &Attr) {
assert(!Attr.isInvalid());
if (!checkAttributeNumArgs(S, Attr, 0))
@@ -455,154 +576,212 @@ static void handleNoAddressSafetyAttr(Sema &S, Decl *D,
}
D->addAttr(::new (S.Context) NoAddressSafetyAnalysisAttr(Attr.getRange(),
- S.Context));
+ S.Context));
}
-static void handleAcquireOrderAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool before) {
+static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D,
+ const AttributeList &Attr,
+ SmallVector<Expr*, 1> &Args) {
assert(!Attr.isInvalid());
if (!checkAttributeAtLeastNumArgs(S, Attr, 1))
- return;
+ return false;
// D must be either a member field or global (potentially shared) variable.
ValueDecl *VD = dyn_cast<ValueDecl>(D);
if (!VD || !mayBeSharedVariable(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFieldOrGlobalVar;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFieldOrGlobalVar;
+ return false;
}
- // Check that this attribute only applies to lockable types
+ // Check that this attribute only applies to lockable types.
QualType QT = VD->getType();
if (!QT->isDependentType()) {
const RecordType *RT = getRecordType(QT);
if (!RT || !RT->getDecl()->getAttr<LockableAttr>()) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_decl_not_lockable)
- << Attr.getName();
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_decl_not_lockable)
+ << Attr.getName();
+ return false;
}
}
+ // Check that all arguments are lockable objects.
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args);
+ if (Args.size() == 0)
+ return false;
+
+ return true;
+}
+
+static void handleAcquiredAfterAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
SmallVector<Expr*, 1> Args;
- // check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
+ if (!checkAcquireOrderAttrCommon(S, D, Attr, Args))
return;
- unsigned Size = Args.size();
- assert(Size == Attr.getNumArgs());
- Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ Expr **StartArg = &Args[0];
+ D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getRange(), S.Context,
+ StartArg, Args.size()));
+}
- if (before)
- D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getRange(), S.Context,
- StartArg, Size));
- else
- D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getRange(), S.Context,
- StartArg, Size));
+static void handleAcquiredBeforeAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 1> Args;
+ if (!checkAcquireOrderAttrCommon(S, D, Attr, Args))
+ return;
+
+ Expr **StartArg = &Args[0];
+ D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getRange(), S.Context,
+ StartArg, Args.size()));
}
-static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool exclusive = false) {
+static bool checkLockFunAttrCommon(Sema &S, Decl *D,
+ const AttributeList &Attr,
+ SmallVector<Expr*, 1> &Args) {
assert(!Attr.isInvalid());
// zero or more arguments ok
// check that the attribute is applied to a function
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
+ return false;
}
// check that all arguments are lockable objects
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true);
+
+ return true;
+}
+
+static void handleSharedLockFunctionAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
SmallVector<Expr*, 1> Args;
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true))
+ if (!checkLockFunAttrCommon(S, D, Attr, Args))
return;
unsigned Size = Args.size();
- assert(Size == Attr.getNumArgs());
Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getRange(),
+ S.Context,
+ StartArg, Size));
+}
- if (exclusive)
- D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getRange(),
- S.Context, StartArg,
- Size));
- else
- D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getRange(),
- S.Context, StartArg,
- Size));
+static void handleExclusiveLockFunctionAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 1> Args;
+ if (!checkLockFunAttrCommon(S, D, Attr, Args))
+ return;
+
+ unsigned Size = Args.size();
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getRange(),
+ S.Context,
+ StartArg, Size));
}
-static void handleTrylockFunAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool exclusive = false) {
+static bool checkTryLockFunAttrCommon(Sema &S, Decl *D,
+ const AttributeList &Attr,
+ SmallVector<Expr*, 2> &Args) {
assert(!Attr.isInvalid());
if (!checkAttributeAtLeastNumArgs(S, Attr, 1))
- return;
-
+ return false;
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
+ return false;
}
if (!isIntOrBool(Attr.getArg(0))) {
S.Diag(Attr.getLoc(), diag::err_attribute_first_argument_not_int_or_bool)
- << Attr.getName();
- return;
+ << Attr.getName();
+ return false;
}
- SmallVector<Expr*, 2> Args;
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 1))
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args, 1);
+
+ return true;
+}
+
+static void handleSharedTrylockFunctionAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 2> Args;
+ if (!checkTryLockFunAttrCommon(S, D, Attr, Args))
return;
unsigned Size = Args.size();
Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getRange(),
+ S.Context,
+ Attr.getArg(0),
+ StartArg, Size));
+}
- if (exclusive)
- D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getRange(),
- S.Context,
- Attr.getArg(0),
- StartArg, Size));
- else
- D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getRange(),
- S.Context,
- Attr.getArg(0),
- StartArg, Size));
+static void handleExclusiveTrylockFunctionAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 2> Args;
+ if (!checkTryLockFunAttrCommon(S, D, Attr, Args))
+ return;
+
+ unsigned Size = Args.size();
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getRange(),
+ S.Context,
+ Attr.getArg(0),
+ StartArg, Size));
}
-static void handleLocksRequiredAttr(Sema &S, Decl *D, const AttributeList &Attr,
- bool exclusive = false) {
+static bool checkLocksRequiredCommon(Sema &S, Decl *D,
+ const AttributeList &Attr,
+ SmallVector<Expr*, 1> &Args) {
assert(!Attr.isInvalid());
if (!checkAttributeAtLeastNumArgs(S, Attr, 1))
- return;
+ return false;
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
- return;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
+ return false;
}
// check that all arguments are lockable objects
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args);
+ if (Args.size() == 0)
+ return false;
+
+ return true;
+}
+
+static void handleExclusiveLocksRequiredAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
SmallVector<Expr*, 1> Args;
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
+ if (!checkLocksRequiredCommon(S, D, Attr, Args))
return;
- unsigned Size = Args.size();
- assert(Size == Attr.getNumArgs());
- Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ Expr **StartArg = &Args[0];
+ D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getRange(),
+ S.Context,
+ StartArg,
+ Args.size()));
+}
- if (exclusive)
- D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getRange(),
- S.Context, StartArg,
- Size));
- else
- D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getRange(),
- S.Context, StartArg,
- Size));
+static void handleSharedLocksRequiredAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 1> Args;
+ if (!checkLocksRequiredCommon(S, D, Attr, Args))
+ return;
+
+ Expr **StartArg = &Args[0];
+ D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getRange(),
+ S.Context,
+ StartArg,
+ Args.size()));
}
static void handleUnlockFunAttr(Sema &S, Decl *D,
@@ -612,18 +791,15 @@ static void handleUnlockFunAttr(Sema &S, Decl *D,
// zero or more arguments ok
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
return;
}
// check that all arguments are lockable objects
SmallVector<Expr*, 1> Args;
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true))
- return;
-
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true);
unsigned Size = Args.size();
- assert(Size == Attr.getNumArgs());
Expr **StartArg = Size == 0 ? 0 : &Args[0];
D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getRange(), S.Context,
@@ -639,8 +815,8 @@ static void handleLockReturnedAttr(Sema &S, Decl *D,
Expr *Arg = Attr.getArg(0);
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
return;
}
@@ -648,9 +824,14 @@ static void handleLockReturnedAttr(Sema &S, Decl *D,
return;
// check that the argument is lockable object
- checkForLockableRecord(S, D, Attr, Arg->getType());
+ SmallVector<Expr*, 1> Args;
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args);
+ unsigned Size = Args.size();
+ if (Size == 0)
+ return;
- D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getRange(), S.Context, Arg));
+ D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getRange(), S.Context,
+ Args[0]));
}
static void handleLocksExcludedAttr(Sema &S, Decl *D,
@@ -661,19 +842,18 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D,
return;
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunctionOrMethod;
+ S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type)
+ << Attr.getName() << ThreadExpectedFunctionOrMethod;
return;
}
// check that all arguments are lockable objects
SmallVector<Expr*, 1> Args;
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
- return;
-
+ checkAttrArgsAreLockableObjs(S, D, Attr, Args);
unsigned Size = Args.size();
- assert(Size == Attr.getNumArgs());
- Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ if (Size == 0)
+ return;
+ Expr **StartArg = &Args[0];
D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getRange(), S.Context,
StartArg, Size));
@@ -698,12 +878,12 @@ static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
SourceLocation TemplateKWLoc;
UnqualifiedId id;
id.setIdentifier(Attr.getParameterName(), Attr.getLoc());
-
+
ExprResult Size = S.ActOnIdExpression(scope, SS, TemplateKWLoc, id,
false, false);
if (Size.isInvalid())
return;
-
+
sizeExpr = Size.get();
} else {
// check the attribute arguments.
@@ -854,6 +1034,75 @@ static void possibleTransparentUnionPointerType(QualType &T) {
}
}
+static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (!isFunctionOrMethod(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+ << "alloc_size" << ExpectedFunctionOrMethod;
+ return;
+ }
+
+ if (!checkAttributeAtLeastNumArgs(S, Attr, 1))
+ return;
+
+ // In C++ the implicit 'this' function parameter also counts, and they are
+ // counted from one.
+ bool HasImplicitThisParam = isInstanceMethod(D);
+ unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam;
+
+ SmallVector<unsigned, 8> SizeArgs;
+
+ for (AttributeList::arg_iterator I = Attr.arg_begin(),
+ E = Attr.arg_end(); I!=E; ++I) {
+ // The argument must be an integer constant expression.
+ Expr *Ex = *I;
+ llvm::APSInt ArgNum;
+ if (Ex->isTypeDependent() || Ex->isValueDependent() ||
+ !Ex->isIntegerConstantExpr(ArgNum, S.Context)) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int)
+ << "alloc_size" << Ex->getSourceRange();
+ return;
+ }
+
+ uint64_t x = ArgNum.getZExtValue();
+
+ if (x < 1 || x > NumArgs) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds)
+ << "alloc_size" << I.getArgNum() << Ex->getSourceRange();
+ return;
+ }
+
+ --x;
+ if (HasImplicitThisParam) {
+ if (x == 0) {
+ S.Diag(Attr.getLoc(),
+ diag::err_attribute_invalid_implicit_this_argument)
+ << "alloc_size" << Ex->getSourceRange();
+ return;
+ }
+ --x;
+ }
+
+ // check if the function argument is of an integer type
+ QualType T = getFunctionOrMethodArgType(D, x).getNonReferenceType();
+ if (!T->isIntegerType()) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int)
+ << "alloc_size" << Ex->getSourceRange();
+ return;
+ }
+
+ SizeArgs.push_back(x);
+ }
+
+ // check if the function returns a pointer
+ if (!getFunctionType(D)->getResultType()->isAnyPointerType()) {
+ S.Diag(Attr.getLoc(), diag::warn_ns_attribute_wrong_return_type)
+ << "alloc_size" << 0 /*function*/<< 1 /*pointer*/ << D->getSourceRange();
+ }
+
+ D->addAttr(::new (S.Context) AllocSizeAttr(Attr.getRange(), S.Context,
+ SizeArgs.data(), SizeArgs.size()));
+}
+
static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) {
// GCC ignores the nonnull attribute on K&R style function prototypes, so we
// ignore it as well
@@ -1226,6 +1475,46 @@ static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) {
Str->getString()));
}
+static void handleColdAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // Check the attribute arguments.
+ if (!checkAttributeNumArgs(S, Attr, 0))
+ return;
+
+ if (!isa<FunctionDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+ << Attr.getName() << ExpectedFunction;
+ return;
+ }
+
+ if (D->hasAttr<HotAttr>()) {
+ S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible)
+ << Attr.getName() << "hot";
+ return;
+ }
+
+ D->addAttr(::new (S.Context) ColdAttr(Attr.getRange(), S.Context));
+}
+
+static void handleHotAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // Check the attribute arguments.
+ if (!checkAttributeNumArgs(S, Attr, 0))
+ return;
+
+ if (!isa<FunctionDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+ << Attr.getName() << ExpectedFunction;
+ return;
+ }
+
+ if (D->hasAttr<ColdAttr>()) {
+ S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible)
+ << Attr.getName() << "cold";
+ return;
+ }
+
+ D->addAttr(::new (S.Context) HotAttr(Attr.getRange(), S.Context));
+}
+
static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
// Check the attribute arguments.
if (!checkAttributeNumArgs(S, Attr, 0))
@@ -1257,6 +1546,42 @@ static void handleAlwaysInlineAttr(Sema &S, Decl *D,
D->addAttr(::new (S.Context) AlwaysInlineAttr(Attr.getRange(), S.Context));
}
+static void handleTLSModelAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ // Check the attribute arguments.
+ if (Attr.getNumArgs() != 1) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1;
+ return;
+ }
+
+ Expr *Arg = Attr.getArg(0);
+ Arg = Arg->IgnoreParenCasts();
+ StringLiteral *Str = dyn_cast<StringLiteral>(Arg);
+
+ // Check that it is a string.
+ if (!Str) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_not_string) << "tls_model";
+ return;
+ }
+
+ if (!isa<VarDecl>(D) || !cast<VarDecl>(D)->isThreadSpecified()) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
+ << Attr.getName() << ExpectedTLSVar;
+ return;
+ }
+
+ // Check that the value.
+ StringRef Model = Str->getString();
+ if (Model != "global-dynamic" && Model != "local-dynamic"
+ && Model != "initial-exec" && Model != "local-exec") {
+ S.Diag(Attr.getLoc(), diag::err_attr_tlsmodel_arg);
+ return;
+ }
+
+ D->addAttr(::new (S.Context) TLSModelAttr(Attr.getRange(), S.Context,
+ Model));
+}
+
static void handleMallocAttr(Sema &S, Decl *D, const AttributeList &Attr) {
// Check the attribute arguments.
if (Attr.hasParameterOrArguments()) {
@@ -1427,7 +1752,7 @@ static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}
if (!isa<VarDecl>(D) && !isa<ObjCIvarDecl>(D) && !isFunctionOrMethod(D) &&
- !isa<TypeDecl>(D) && !isa<LabelDecl>(D)) {
+ !isa<TypeDecl>(D) && !isa<LabelDecl>(D) && !isa<FieldDecl>(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
<< Attr.getName() << ExpectedVariableFunctionOrLabel;
return;
@@ -1534,47 +1859,28 @@ static void handleDestructorAttr(Sema &S, Decl *D, const AttributeList &Attr) {
priority));
}
-static void handleDeprecatedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+template <typename AttrTy>
+static void handleAttrWithMessage(Sema &S, Decl *D, const AttributeList &Attr,
+ const char *Name) {
unsigned NumArgs = Attr.getNumArgs();
if (NumArgs > 1) {
S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1;
return;
}
-
- // Handle the case where deprecated attribute has a text message.
+
+ // Handle the case where the attribute has a text message.
StringRef Str;
if (NumArgs == 1) {
StringLiteral *SE = dyn_cast<StringLiteral>(Attr.getArg(0));
if (!SE) {
S.Diag(Attr.getArg(0)->getLocStart(), diag::err_attribute_not_string)
- << "deprecated";
+ << Name;
return;
}
Str = SE->getString();
}
- D->addAttr(::new (S.Context) DeprecatedAttr(Attr.getRange(), S.Context, Str));
-}
-
-static void handleUnavailableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
- unsigned NumArgs = Attr.getNumArgs();
- if (NumArgs > 1) {
- S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1;
- return;
- }
-
- // Handle the case where unavailable attribute has a text message.
- StringRef Str;
- if (NumArgs == 1) {
- StringLiteral *SE = dyn_cast<StringLiteral>(Attr.getArg(0));
- if (!SE) {
- S.Diag(Attr.getArg(0)->getLocStart(),
- diag::err_attribute_not_string) << "unavailable";
- return;
- }
- Str = SE->getString();
- }
- D->addAttr(::new (S.Context) UnavailableAttr(Attr.getRange(), S.Context, Str));
+ D->addAttr(::new (S.Context) AttrTy(Attr.getRange(), S.Context, Str));
}
static void handleArcWeakrefUnavailableAttr(Sema &S, Decl *D,
@@ -1622,64 +1928,180 @@ static void handleObjCRequiresPropertyDefsAttr(Sema &S, Decl *D,
Attr.getRange(), S.Context));
}
-static void handleAvailabilityAttr(Sema &S, Decl *D,
- const AttributeList &Attr) {
- IdentifierInfo *Platform = Attr.getParameterName();
- SourceLocation PlatformLoc = Attr.getParameterLoc();
-
+static bool checkAvailabilityAttr(Sema &S, SourceRange Range,
+ IdentifierInfo *Platform,
+ VersionTuple Introduced,
+ VersionTuple Deprecated,
+ VersionTuple Obsoleted) {
StringRef PlatformName
= AvailabilityAttr::getPrettyPlatformName(Platform->getName());
- if (PlatformName.empty()) {
- S.Diag(PlatformLoc, diag::warn_availability_unknown_platform)
- << Platform;
-
+ if (PlatformName.empty())
PlatformName = Platform->getName();
- }
-
- AvailabilityChange Introduced = Attr.getAvailabilityIntroduced();
- AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated();
- AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted();
- bool IsUnavailable = Attr.getUnavailableLoc().isValid();
// Ensure that Introduced <= Deprecated <= Obsoleted (although not all
// of these steps are needed).
- if (Introduced.isValid() && Deprecated.isValid() &&
- !(Introduced.Version <= Deprecated.Version)) {
- S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
- << 1 << PlatformName << Deprecated.Version.getAsString()
- << 0 << Introduced.Version.getAsString();
- return;
+ if (!Introduced.empty() && !Deprecated.empty() &&
+ !(Introduced <= Deprecated)) {
+ S.Diag(Range.getBegin(), diag::warn_availability_version_ordering)
+ << 1 << PlatformName << Deprecated.getAsString()
+ << 0 << Introduced.getAsString();
+ return true;
}
- if (Introduced.isValid() && Obsoleted.isValid() &&
- !(Introduced.Version <= Obsoleted.Version)) {
- S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
- << 2 << PlatformName << Obsoleted.Version.getAsString()
- << 0 << Introduced.Version.getAsString();
- return;
+ if (!Introduced.empty() && !Obsoleted.empty() &&
+ !(Introduced <= Obsoleted)) {
+ S.Diag(Range.getBegin(), diag::warn_availability_version_ordering)
+ << 2 << PlatformName << Obsoleted.getAsString()
+ << 0 << Introduced.getAsString();
+ return true;
}
- if (Deprecated.isValid() && Obsoleted.isValid() &&
- !(Deprecated.Version <= Obsoleted.Version)) {
- S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering)
- << 2 << PlatformName << Obsoleted.Version.getAsString()
- << 1 << Deprecated.Version.getAsString();
- return;
+ if (!Deprecated.empty() && !Obsoleted.empty() &&
+ !(Deprecated <= Obsoleted)) {
+ S.Diag(Range.getBegin(), diag::warn_availability_version_ordering)
+ << 2 << PlatformName << Obsoleted.getAsString()
+ << 1 << Deprecated.getAsString();
+ return true;
}
+ return false;
+}
+
+AvailabilityAttr *Sema::mergeAvailabilityAttr(Decl *D, SourceRange Range,
+ IdentifierInfo *Platform,
+ VersionTuple Introduced,
+ VersionTuple Deprecated,
+ VersionTuple Obsoleted,
+ bool IsUnavailable,
+ StringRef Message) {
+ VersionTuple MergedIntroduced = Introduced;
+ VersionTuple MergedDeprecated = Deprecated;
+ VersionTuple MergedObsoleted = Obsoleted;
+ bool FoundAny = false;
+
+ if (D->hasAttrs()) {
+ AttrVec &Attrs = D->getAttrs();
+ for (unsigned i = 0, e = Attrs.size(); i != e;) {
+ const AvailabilityAttr *OldAA = dyn_cast<AvailabilityAttr>(Attrs[i]);
+ if (!OldAA) {
+ ++i;
+ continue;
+ }
+
+ IdentifierInfo *OldPlatform = OldAA->getPlatform();
+ if (OldPlatform != Platform) {
+ ++i;
+ continue;
+ }
+
+ FoundAny = true;
+ VersionTuple OldIntroduced = OldAA->getIntroduced();
+ VersionTuple OldDeprecated = OldAA->getDeprecated();
+ VersionTuple OldObsoleted = OldAA->getObsoleted();
+ bool OldIsUnavailable = OldAA->getUnavailable();
+ StringRef OldMessage = OldAA->getMessage();
+
+ if ((!OldIntroduced.empty() && !Introduced.empty() &&
+ OldIntroduced != Introduced) ||
+ (!OldDeprecated.empty() && !Deprecated.empty() &&
+ OldDeprecated != Deprecated) ||
+ (!OldObsoleted.empty() && !Obsoleted.empty() &&
+ OldObsoleted != Obsoleted) ||
+ (OldIsUnavailable != IsUnavailable) ||
+ (OldMessage != Message)) {
+ Diag(OldAA->getLocation(), diag::warn_mismatched_availability);
+ Diag(Range.getBegin(), diag::note_previous_attribute);
+ Attrs.erase(Attrs.begin() + i);
+ --e;
+ continue;
+ }
+
+ VersionTuple MergedIntroduced2 = MergedIntroduced;
+ VersionTuple MergedDeprecated2 = MergedDeprecated;
+ VersionTuple MergedObsoleted2 = MergedObsoleted;
+
+ if (MergedIntroduced2.empty())
+ MergedIntroduced2 = OldIntroduced;
+ if (MergedDeprecated2.empty())
+ MergedDeprecated2 = OldDeprecated;
+ if (MergedObsoleted2.empty())
+ MergedObsoleted2 = OldObsoleted;
+
+ if (checkAvailabilityAttr(*this, OldAA->getRange(), Platform,
+ MergedIntroduced2, MergedDeprecated2,
+ MergedObsoleted2)) {
+ Attrs.erase(Attrs.begin() + i);
+ --e;
+ continue;
+ }
+
+ MergedIntroduced = MergedIntroduced2;
+ MergedDeprecated = MergedDeprecated2;
+ MergedObsoleted = MergedObsoleted2;
+ ++i;
+ }
+ }
+
+ if (FoundAny &&
+ MergedIntroduced == Introduced &&
+ MergedDeprecated == Deprecated &&
+ MergedObsoleted == Obsoleted)
+ return NULL;
+
+ if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced,
+ MergedDeprecated, MergedObsoleted)) {
+ return ::new (Context) AvailabilityAttr(Range, Context, Platform,
+ Introduced, Deprecated,
+ Obsoleted, IsUnavailable, Message);
+ }
+ return NULL;
+}
+
+static void handleAvailabilityAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ IdentifierInfo *Platform = Attr.getParameterName();
+ SourceLocation PlatformLoc = Attr.getParameterLoc();
+
+ if (AvailabilityAttr::getPrettyPlatformName(Platform->getName()).empty())
+ S.Diag(PlatformLoc, diag::warn_availability_unknown_platform)
+ << Platform;
+
+ AvailabilityChange Introduced = Attr.getAvailabilityIntroduced();
+ AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated();
+ AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted();
+ bool IsUnavailable = Attr.getUnavailableLoc().isValid();
StringRef Str;
const StringLiteral *SE =
dyn_cast_or_null<const StringLiteral>(Attr.getMessageExpr());
if (SE)
Str = SE->getString();
-
- D->addAttr(::new (S.Context) AvailabilityAttr(Attr.getRange(), S.Context,
- Platform,
- Introduced.Version,
- Deprecated.Version,
- Obsoleted.Version,
- IsUnavailable,
- Str));
+
+ AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(D, Attr.getRange(),
+ Platform,
+ Introduced.Version,
+ Deprecated.Version,
+ Obsoleted.Version,
+ IsUnavailable, Str);
+ if (NewAttr)
+ D->addAttr(NewAttr);
+}
+
+VisibilityAttr *Sema::mergeVisibilityAttr(Decl *D, SourceRange Range,
+ VisibilityAttr::VisibilityType Vis) {
+ if (isa<TypedefNameDecl>(D)) {
+ Diag(Range.getBegin(), diag::warn_attribute_ignored) << "visibility";
+ return NULL;
+ }
+ VisibilityAttr *ExistingAttr = D->getAttr<VisibilityAttr>();
+ if (ExistingAttr) {
+ VisibilityAttr::VisibilityType ExistingVis = ExistingAttr->getVisibility();
+ if (ExistingVis == Vis)
+ return NULL;
+ Diag(ExistingAttr->getLocation(), diag::err_mismatched_visibility);
+ Diag(Range.getBegin(), diag::note_previous_attribute);
+ D->dropAttr<VisibilityAttr>();
+ }
+ return ::new (Context) VisibilityAttr(Range, Context, Vis);
}
static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
@@ -1720,7 +2142,9 @@ static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
return;
}
- D->addAttr(::new (S.Context) VisibilityAttr(Attr.getRange(), S.Context, type));
+ VisibilityAttr *NewAttr = S.mergeVisibilityAttr(D, Attr.getRange(), type);
+ if (NewAttr)
+ D->addAttr(NewAttr);
}
static void handleObjCMethodFamilyAttr(Sema &S, Decl *decl,
@@ -1803,7 +2227,15 @@ static void handleObjCNSObject(Sema &S, Decl *D, const AttributeList &Attr) {
return;
}
}
- else if (!isa<ObjCPropertyDecl>(D)) {
+ else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) {
+ QualType T = PD->getType();
+ if (!T->isPointerType() ||
+ !T->getAs<PointerType>()->getPointeeType()->isRecordType()) {
+ S.Diag(PD->getLocation(), diag::err_nsobject_attribute);
+ return;
+ }
+ }
+ else {
// It is okay to include this attribute on properties, e.g.:
//
// @property (retain, nonatomic) struct Bork *Q __attribute__((NSObject));
@@ -2028,11 +2460,14 @@ static void handleWeakImportAttr(Sema &S, Decl *D, const AttributeList &Attr) {
D->addAttr(::new (S.Context) WeakImportAttr(Attr.getRange(), S.Context));
}
-static void handleReqdWorkGroupSize(Sema &S, Decl *D,
- const AttributeList &Attr) {
+// Handles reqd_work_group_size and work_group_size_hint.
+static void handleWorkGroupSize(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ assert(Attr.getKind() == AttributeList::AT_ReqdWorkGroupSize
+ || Attr.getKind() == AttributeList::AT_WorkGroupSizeHint);
+
// Attribute has 3 arguments.
- if (!checkAttributeNumArgs(S, Attr, 3))
- return;
+ if (!checkAttributeNumArgs(S, Attr, 3)) return;
unsigned WGSize[3];
for (unsigned i = 0; i < 3; ++i) {
@@ -2041,14 +2476,54 @@ static void handleReqdWorkGroupSize(Sema &S, Decl *D,
if (E->isTypeDependent() || E->isValueDependent() ||
!E->isIntegerConstantExpr(ArgNum, S.Context)) {
S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int)
- << "reqd_work_group_size" << E->getSourceRange();
+ << Attr.getName()->getName() << E->getSourceRange();
return;
}
WGSize[i] = (unsigned) ArgNum.getZExtValue();
}
- D->addAttr(::new (S.Context) ReqdWorkGroupSizeAttr(Attr.getRange(), S.Context,
- WGSize[0], WGSize[1],
- WGSize[2]));
+
+ if (Attr.getKind() == AttributeList::AT_ReqdWorkGroupSize
+ && D->hasAttr<ReqdWorkGroupSizeAttr>()) {
+ ReqdWorkGroupSizeAttr *A = D->getAttr<ReqdWorkGroupSizeAttr>();
+ if (!(A->getXDim() == WGSize[0] &&
+ A->getYDim() == WGSize[1] &&
+ A->getZDim() == WGSize[2])) {
+ S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) <<
+ Attr.getName();
+ }
+ }
+
+ if (Attr.getKind() == AttributeList::AT_WorkGroupSizeHint
+ && D->hasAttr<WorkGroupSizeHintAttr>()) {
+ WorkGroupSizeHintAttr *A = D->getAttr<WorkGroupSizeHintAttr>();
+ if (!(A->getXDim() == WGSize[0] &&
+ A->getYDim() == WGSize[1] &&
+ A->getZDim() == WGSize[2])) {
+ S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) <<
+ Attr.getName();
+ }
+ }
+
+ if (Attr.getKind() == AttributeList::AT_ReqdWorkGroupSize)
+ D->addAttr(::new (S.Context)
+ ReqdWorkGroupSizeAttr(Attr.getRange(), S.Context,
+ WGSize[0], WGSize[1], WGSize[2]));
+ else
+ D->addAttr(::new (S.Context)
+ WorkGroupSizeHintAttr(Attr.getRange(), S.Context,
+ WGSize[0], WGSize[1], WGSize[2]));
+}
+
+SectionAttr *Sema::mergeSectionAttr(Decl *D, SourceRange Range,
+ StringRef Name) {
+ if (SectionAttr *ExistingAttr = D->getAttr<SectionAttr>()) {
+ if (ExistingAttr->getName() == Name)
+ return NULL;
+ Diag(ExistingAttr->getLocation(), diag::warn_mismatched_section);
+ Diag(Range.getBegin(), diag::note_previous_attribute);
+ return NULL;
+ }
+ return ::new (Context) SectionAttr(Range, Context, Name);
}
static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) {
@@ -2078,9 +2553,10 @@ static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) {
S.Diag(SE->getLocStart(), diag::err_attribute_section_local_variable);
return;
}
-
- D->addAttr(::new (S.Context) SectionAttr(Attr.getRange(), S.Context,
- SE->getString()));
+ SectionAttr *NewAttr = S.mergeSectionAttr(D, Attr.getRange(),
+ SE->getString());
+ if (NewAttr)
+ D->addAttr(NewAttr);
}
@@ -2269,26 +2745,19 @@ enum FormatAttrKind {
/// getFormatAttrKind - Map from format attribute names to supported format
/// types.
static FormatAttrKind getFormatAttrKind(StringRef Format) {
- // Check for formats that get handled specially.
- if (Format == "NSString")
- return NSStringFormat;
- if (Format == "CFString")
- return CFStringFormat;
- if (Format == "strftime")
- return StrftimeFormat;
-
- // Otherwise, check for supported formats.
- if (Format == "scanf" || Format == "printf" || Format == "printf0" ||
- Format == "strfmon" || Format == "cmn_err" || Format == "vcmn_err" ||
- Format == "zcmn_err" ||
- Format == "kprintf") // OpenBSD.
- return SupportedFormat;
-
- if (Format == "gcc_diag" || Format == "gcc_cdiag" ||
- Format == "gcc_cxxdiag" || Format == "gcc_tdiag")
- return IgnoredFormat;
-
- return InvalidFormat;
+ return llvm::StringSwitch<FormatAttrKind>(Format)
+ // Check for formats that get handled specially.
+ .Case("NSString", NSStringFormat)
+ .Case("CFString", CFStringFormat)
+ .Case("strftime", StrftimeFormat)
+
+ // Otherwise, check for supported formats.
+ .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
+ .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
+ .Case("kprintf", SupportedFormat) // OpenBSD.
+
+ .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
+ .Default(InvalidFormat);
}
/// Handle __attribute__((init_priority(priority))) attributes based on
@@ -2340,6 +2809,29 @@ static void handleInitPriorityAttr(Sema &S, Decl *D,
prioritynum));
}
+FormatAttr *Sema::mergeFormatAttr(Decl *D, SourceRange Range, StringRef Format,
+ int FormatIdx, int FirstArg) {
+ // Check whether we already have an equivalent format attribute.
+ for (specific_attr_iterator<FormatAttr>
+ i = D->specific_attr_begin<FormatAttr>(),
+ e = D->specific_attr_end<FormatAttr>();
+ i != e ; ++i) {
+ FormatAttr *f = *i;
+ if (f->getType() == Format &&
+ f->getFormatIdx() == FormatIdx &&
+ f->getFirstArg() == FirstArg) {
+ // If we don't have a valid location for this attribute, adopt the
+ // location.
+ if (f->getLocation().isInvalid())
+ f->setRange(Range);
+ return NULL;
+ }
+ }
+
+ return ::new (Context) FormatAttr(Range, Context, Format, FormatIdx,
+ FirstArg);
+}
+
/// Handle __attribute__((format(type,idx,firstarg))) attributes based on
/// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) {
@@ -2475,26 +2967,11 @@ static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) {
return;
}
- // Check whether we already have an equivalent format attribute.
- for (specific_attr_iterator<FormatAttr>
- i = D->specific_attr_begin<FormatAttr>(),
- e = D->specific_attr_end<FormatAttr>();
- i != e ; ++i) {
- FormatAttr *f = *i;
- if (f->getType() == Format &&
- f->getFormatIdx() == (int)Idx.getZExtValue() &&
- f->getFirstArg() == (int)FirstArg.getZExtValue()) {
- // If we don't have a valid location for this attribute, adopt the
- // location.
- if (f->getLocation().isInvalid())
- f->setRange(Attr.getRange());
- return;
- }
- }
-
- D->addAttr(::new (S.Context) FormatAttr(Attr.getRange(), S.Context, Format,
+ FormatAttr *NewAttr = S.mergeFormatAttr(D, Attr.getRange(), Format,
Idx.getZExtValue(),
- FirstArg.getZExtValue()));
+ FirstArg.getZExtValue());
+ if (NewAttr)
+ D->addAttr(NewAttr);
}
static void handleTransparentUnionAttr(Sema &S, Decl *D,
@@ -2596,37 +3073,41 @@ static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1;
return;
}
-
+
//FIXME: The C++0x version of this attribute has more limited applicabilty
// than GNU's, and should error out when it is used to specify a
// weaker alignment, rather than being silently ignored.
if (Attr.getNumArgs() == 0) {
- D->addAttr(::new (S.Context) AlignedAttr(Attr.getRange(), S.Context, true, 0));
+ D->addAttr(::new (S.Context) AlignedAttr(Attr.getRange(), S.Context,
+ true, 0, Attr.isDeclspecAttribute()));
return;
}
- S.AddAlignedAttr(Attr.getRange(), D, Attr.getArg(0));
+ S.AddAlignedAttr(Attr.getRange(), D, Attr.getArg(0),
+ Attr.isDeclspecAttribute());
}
-void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E) {
+void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E,
+ bool isDeclSpec) {
// FIXME: Handle pack-expansions here.
if (DiagnoseUnexpandedParameterPack(E))
return;
if (E->isTypeDependent() || E->isValueDependent()) {
// Save dependent expressions in the AST to be instantiated.
- D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, E));
+ D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, E,
+ isDeclSpec));
return;
}
-
+
SourceLocation AttrLoc = AttrRange.getBegin();
// FIXME: Cache the number on the Attr object?
llvm::APSInt Alignment(32);
- ExprResult ICE =
- VerifyIntegerConstantExpression(E, &Alignment,
- PDiag(diag::err_attribute_argument_not_int) << "aligned",
- /*AllowFold*/ false);
+ ExprResult ICE
+ = VerifyIntegerConstantExpression(E, &Alignment,
+ diag::err_aligned_attribute_argument_not_int,
+ /*AllowFold*/ false);
if (ICE.isInvalid())
return;
if (!llvm::isPowerOf2_64(Alignment.getZExtValue())) {
@@ -2634,14 +3115,26 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E) {
<< E->getSourceRange();
return;
}
+ if (isDeclSpec) {
+ // We've already verified it's a power of 2, now let's make sure it's
+ // 8192 or less.
+ if (Alignment.getZExtValue() > 8192) {
+ Diag(AttrLoc, diag::err_attribute_aligned_greater_than_8192)
+ << E->getSourceRange();
+ return;
+ }
+ }
- D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, ICE.take()));
+ D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, ICE.take(),
+ isDeclSpec));
}
-void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS) {
+void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS,
+ bool isDeclSpec) {
// FIXME: Cache the number on the Attr object if non-dependent?
// FIXME: Perform checking of type validity
- D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, false, TS));
+ D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, false, TS,
+ isDeclSpec));
return;
}
@@ -2820,9 +3313,15 @@ static void handleNoDebugAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (!checkAttributeNumArgs(S, Attr, 0))
return;
- if (!isFunctionOrMethod(D)) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedFunction;
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ if (!VD->hasGlobalStorage())
+ S.Diag(Attr.getLoc(),
+ diag::warn_attribute_requires_functions_or_static_globals)
+ << Attr.getName();
+ } else if (!isFunctionOrMethod(D)) {
+ S.Diag(Attr.getLoc(),
+ diag::warn_attribute_requires_functions_or_static_globals)
+ << Attr.getName();
return;
}
@@ -3008,22 +3507,22 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}
switch (Attr.getKind()) {
- case AttributeList::AT_fastcall:
+ case AttributeList::AT_FastCall:
D->addAttr(::new (S.Context) FastCallAttr(Attr.getRange(), S.Context));
return;
- case AttributeList::AT_stdcall:
+ case AttributeList::AT_StdCall:
D->addAttr(::new (S.Context) StdCallAttr(Attr.getRange(), S.Context));
return;
- case AttributeList::AT_thiscall:
+ case AttributeList::AT_ThisCall:
D->addAttr(::new (S.Context) ThisCallAttr(Attr.getRange(), S.Context));
return;
- case AttributeList::AT_cdecl:
+ case AttributeList::AT_CDecl:
D->addAttr(::new (S.Context) CDeclAttr(Attr.getRange(), S.Context));
return;
- case AttributeList::AT_pascal:
+ case AttributeList::AT_Pascal:
D->addAttr(::new (S.Context) PascalAttr(Attr.getRange(), S.Context));
return;
- case AttributeList::AT_pcs: {
+ case AttributeList::AT_Pcs: {
Expr *Arg = Attr.getArg(0);
StringLiteral *Str = dyn_cast<StringLiteral>(Arg);
if (!Str || !Str->isAscii()) {
@@ -3062,7 +3561,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) {
return true;
if ((attr.getNumArgs() != 0 &&
- !(attr.getKind() == AttributeList::AT_pcs && attr.getNumArgs() == 1)) ||
+ !(attr.getKind() == AttributeList::AT_Pcs && attr.getNumArgs() == 1)) ||
attr.getParameterName()) {
Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
attr.setInvalid();
@@ -3072,12 +3571,12 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) {
// TODO: diagnose uses of these conventions on the wrong target. Or, better
// move to TargetAttributesSema one day.
switch (attr.getKind()) {
- case AttributeList::AT_cdecl: CC = CC_C; break;
- case AttributeList::AT_fastcall: CC = CC_X86FastCall; break;
- case AttributeList::AT_stdcall: CC = CC_X86StdCall; break;
- case AttributeList::AT_thiscall: CC = CC_X86ThisCall; break;
- case AttributeList::AT_pascal: CC = CC_X86Pascal; break;
- case AttributeList::AT_pcs: {
+ case AttributeList::AT_CDecl: CC = CC_C; break;
+ case AttributeList::AT_FastCall: CC = CC_X86FastCall; break;
+ case AttributeList::AT_StdCall: CC = CC_X86StdCall; break;
+ case AttributeList::AT_ThisCall: CC = CC_X86ThisCall; break;
+ case AttributeList::AT_Pascal: CC = CC_X86Pascal; break;
+ case AttributeList::AT_Pcs: {
Expr *Arg = attr.getArg(0);
StringLiteral *Str = dyn_cast<StringLiteral>(Arg);
if (!Str || !Str->isAscii()) {
@@ -3228,7 +3727,7 @@ static void handleNSConsumedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}
bool typeOK, cf;
- if (Attr.getKind() == AttributeList::AT_ns_consumed) {
+ if (Attr.getKind() == AttributeList::AT_NSConsumed) {
typeOK = isValidSubjectOfNSAttribute(S, param->getType());
cf = false;
} else {
@@ -3269,7 +3768,7 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D))
returnType = PD->getType();
else if (S.getLangOpts().ObjCAutoRefCount && hasDeclarator(D) &&
- (Attr.getKind() == AttributeList::AT_ns_returns_retained))
+ (Attr.getKind() == AttributeList::AT_NSReturnsRetained))
return; // ignore: was handled as a type attribute
else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
returnType = FD->getResultType();
@@ -3284,15 +3783,15 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
bool cf;
switch (Attr.getKind()) {
default: llvm_unreachable("invalid ownership attribute");
- case AttributeList::AT_ns_returns_autoreleased:
- case AttributeList::AT_ns_returns_retained:
- case AttributeList::AT_ns_returns_not_retained:
+ case AttributeList::AT_NSReturnsAutoreleased:
+ case AttributeList::AT_NSReturnsRetained:
+ case AttributeList::AT_NSReturnsNotRetained:
typeOK = isValidSubjectOfNSAttribute(S, returnType);
cf = false;
break;
- case AttributeList::AT_cf_returns_retained:
- case AttributeList::AT_cf_returns_not_retained:
+ case AttributeList::AT_CFReturnsRetained:
+ case AttributeList::AT_CFReturnsNotRetained:
typeOK = isValidSubjectOfCFAttribute(S, returnType);
cf = true;
break;
@@ -3307,23 +3806,23 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
switch (Attr.getKind()) {
default:
llvm_unreachable("invalid ownership attribute");
- case AttributeList::AT_ns_returns_autoreleased:
+ case AttributeList::AT_NSReturnsAutoreleased:
D->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr(Attr.getRange(),
S.Context));
return;
- case AttributeList::AT_cf_returns_not_retained:
+ case AttributeList::AT_CFReturnsNotRetained:
D->addAttr(::new (S.Context) CFReturnsNotRetainedAttr(Attr.getRange(),
S.Context));
return;
- case AttributeList::AT_ns_returns_not_retained:
+ case AttributeList::AT_NSReturnsNotRetained:
D->addAttr(::new (S.Context) NSReturnsNotRetainedAttr(Attr.getRange(),
S.Context));
return;
- case AttributeList::AT_cf_returns_retained:
+ case AttributeList::AT_CFReturnsRetained:
D->addAttr(::new (S.Context) CFReturnsRetainedAttr(Attr.getRange(),
S.Context));
return;
- case AttributeList::AT_ns_returns_retained:
+ case AttributeList::AT_NSReturnsRetained:
D->addAttr(::new (S.Context) NSReturnsRetainedAttr(Attr.getRange(),
S.Context));
return;
@@ -3336,8 +3835,8 @@ static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D,
ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(D);
- if (!isa<ObjCMethodDecl>(method)) {
- S.Diag(method->getLocStart(), diag::err_attribute_wrong_decl_type)
+ if (!method) {
+ S.Diag(D->getLocStart(), diag::err_attribute_wrong_decl_type)
<< SourceRange(loc, loc) << attr.getName() << ExpectedMethod;
return;
}
@@ -3367,7 +3866,7 @@ static void handleCFTransferAttr(Sema &S, Decl *D, const AttributeList &A) {
return;
}
- bool IsAudited = (A.getKind() == AttributeList::AT_cf_audited_transfer);
+ bool IsAudited = (A.getKind() == AttributeList::AT_CFAuditedTransfer);
// Check whether there's a conflicting attribute already present.
Attr *Existing;
@@ -3478,22 +3977,6 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D,
ObjCPreciseLifetimeAttr(Attr.getRange(), S.Context));
}
-static bool isKnownDeclSpecAttr(const AttributeList &Attr) {
- switch (Attr.getKind()) {
- default:
- return false;
- case AttributeList::AT_dllimport:
- case AttributeList::AT_dllexport:
- case AttributeList::AT_uuid:
- case AttributeList::AT_deprecated:
- case AttributeList::AT_noreturn:
- case AttributeList::AT_nothrow:
- case AttributeList::AT_naked:
- case AttributeList::AT_noinline:
- return true;
- }
-}
-
//===----------------------------------------------------------------------===//
// Microsoft specific attribute handlers.
//===----------------------------------------------------------------------===//
@@ -3552,6 +4035,45 @@ static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) {
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "uuid";
}
+static void handleInheritanceAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (S.LangOpts.MicrosoftExt) {
+ AttributeList::Kind Kind = Attr.getKind();
+ if (Kind == AttributeList::AT_SingleInheritance)
+ D->addAttr(
+ ::new (S.Context) SingleInheritanceAttr(Attr.getRange(), S.Context));
+ else if (Kind == AttributeList::AT_MultipleInheritance)
+ D->addAttr(
+ ::new (S.Context) MultipleInheritanceAttr(Attr.getRange(), S.Context));
+ else if (Kind == AttributeList::AT_VirtualInheritance)
+ D->addAttr(
+ ::new (S.Context) VirtualInheritanceAttr(Attr.getRange(), S.Context));
+ } else
+ S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName();
+}
+
+static void handlePortabilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (S.LangOpts.MicrosoftExt) {
+ AttributeList::Kind Kind = Attr.getKind();
+ if (Kind == AttributeList::AT_Ptr32)
+ D->addAttr(
+ ::new (S.Context) Ptr32Attr(Attr.getRange(), S.Context));
+ else if (Kind == AttributeList::AT_Ptr64)
+ D->addAttr(
+ ::new (S.Context) Ptr64Attr(Attr.getRange(), S.Context));
+ else if (Kind == AttributeList::AT_Win64)
+ D->addAttr(
+ ::new (S.Context) Win64Attr(Attr.getRange(), S.Context));
+ } else
+ S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName();
+}
+
+static void handleForceInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (S.LangOpts.MicrosoftExt)
+ D->addAttr(::new (S.Context) ForceInlineAttr(Attr.getRange(), S.Context));
+ else
+ S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName();
+}
+
//===----------------------------------------------------------------------===//
// Top Level Sema Entry Points
//===----------------------------------------------------------------------===//
@@ -3559,9 +4081,9 @@ static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) {
static void ProcessNonInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
const AttributeList &Attr) {
switch (Attr.getKind()) {
- case AttributeList::AT_device: handleDeviceAttr (S, D, Attr); break;
- case AttributeList::AT_host: handleHostAttr (S, D, Attr); break;
- case AttributeList::AT_overloadable:handleOverloadableAttr(S, D, Attr); break;
+ case AttributeList::AT_CUDADevice: handleDeviceAttr (S, D, Attr); break;
+ case AttributeList::AT_CUDAHost: handleHostAttr (S, D, Attr); break;
+ case AttributeList::AT_Overloadable:handleOverloadableAttr(S, D, Attr); break;
default:
break;
}
@@ -3570,227 +4092,254 @@ static void ProcessNonInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
const AttributeList &Attr) {
switch (Attr.getKind()) {
- case AttributeList::AT_ibaction: handleIBAction(S, D, Attr); break;
- case AttributeList::AT_iboutlet: handleIBOutlet(S, D, Attr); break;
- case AttributeList::AT_iboutletcollection:
+ case AttributeList::AT_IBAction: handleIBAction(S, D, Attr); break;
+ case AttributeList::AT_IBOutlet: handleIBOutlet(S, D, Attr); break;
+ case AttributeList::AT_IBOutletCollection:
handleIBOutletCollection(S, D, Attr); break;
- case AttributeList::AT_address_space:
- case AttributeList::AT_opencl_image_access:
- case AttributeList::AT_objc_gc:
- case AttributeList::AT_vector_size:
- case AttributeList::AT_neon_vector_type:
- case AttributeList::AT_neon_polyvector_type:
+ case AttributeList::AT_AddressSpace:
+ case AttributeList::AT_OpenCLImageAccess:
+ case AttributeList::AT_ObjCGC:
+ case AttributeList::AT_VectorSize:
+ case AttributeList::AT_NeonVectorType:
+ case AttributeList::AT_NeonPolyVectorType:
// Ignore these, these are type attributes, handled by
// ProcessTypeAttributes.
break;
- case AttributeList::AT_device:
- case AttributeList::AT_host:
- case AttributeList::AT_overloadable:
+ case AttributeList::AT_CUDADevice:
+ case AttributeList::AT_CUDAHost:
+ case AttributeList::AT_Overloadable:
// Ignore, this is a non-inheritable attribute, handled
// by ProcessNonInheritableDeclAttr.
break;
- case AttributeList::AT_alias: handleAliasAttr (S, D, Attr); break;
- case AttributeList::AT_aligned: handleAlignedAttr (S, D, Attr); break;
- case AttributeList::AT_always_inline:
+ case AttributeList::AT_Alias: handleAliasAttr (S, D, Attr); break;
+ case AttributeList::AT_Aligned: handleAlignedAttr (S, D, Attr); break;
+ case AttributeList::AT_AllocSize: handleAllocSizeAttr (S, D, Attr); break;
+ case AttributeList::AT_AlwaysInline:
handleAlwaysInlineAttr (S, D, Attr); break;
- case AttributeList::AT_analyzer_noreturn:
+ case AttributeList::AT_AnalyzerNoReturn:
handleAnalyzerNoReturnAttr (S, D, Attr); break;
- case AttributeList::AT_annotate: handleAnnotateAttr (S, D, Attr); break;
- case AttributeList::AT_availability:handleAvailabilityAttr(S, D, Attr); break;
- case AttributeList::AT_carries_dependency:
+ case AttributeList::AT_TLSModel: handleTLSModelAttr (S, D, Attr); break;
+ case AttributeList::AT_Annotate: handleAnnotateAttr (S, D, Attr); break;
+ case AttributeList::AT_Availability:handleAvailabilityAttr(S, D, Attr); break;
+ case AttributeList::AT_CarriesDependency:
handleDependencyAttr (S, D, Attr); break;
- case AttributeList::AT_common: handleCommonAttr (S, D, Attr); break;
- case AttributeList::AT_constant: handleConstantAttr (S, D, Attr); break;
- case AttributeList::AT_constructor: handleConstructorAttr (S, D, Attr); break;
- case AttributeList::AT_deprecated: handleDeprecatedAttr (S, D, Attr); break;
- case AttributeList::AT_destructor: handleDestructorAttr (S, D, Attr); break;
- case AttributeList::AT_ext_vector_type:
+ case AttributeList::AT_Common: handleCommonAttr (S, D, Attr); break;
+ case AttributeList::AT_CUDAConstant:handleConstantAttr (S, D, Attr); break;
+ case AttributeList::AT_Constructor: handleConstructorAttr (S, D, Attr); break;
+ case AttributeList::AT_Deprecated:
+ handleAttrWithMessage<DeprecatedAttr>(S, D, Attr, "deprecated");
+ break;
+ case AttributeList::AT_Destructor: handleDestructorAttr (S, D, Attr); break;
+ case AttributeList::AT_ExtVectorType:
handleExtVectorTypeAttr(S, scope, D, Attr);
break;
- case AttributeList::AT_format: handleFormatAttr (S, D, Attr); break;
- case AttributeList::AT_format_arg: handleFormatArgAttr (S, D, Attr); break;
- case AttributeList::AT_global: handleGlobalAttr (S, D, Attr); break;
- case AttributeList::AT_gnu_inline: handleGNUInlineAttr (S, D, Attr); break;
- case AttributeList::AT_launch_bounds:
+ case AttributeList::AT_Format: handleFormatAttr (S, D, Attr); break;
+ case AttributeList::AT_FormatArg: handleFormatArgAttr (S, D, Attr); break;
+ case AttributeList::AT_CUDAGlobal: handleGlobalAttr (S, D, Attr); break;
+ case AttributeList::AT_GNUInline: handleGNUInlineAttr (S, D, Attr); break;
+ case AttributeList::AT_CUDALaunchBounds:
handleLaunchBoundsAttr(S, D, Attr);
break;
- case AttributeList::AT_mode: handleModeAttr (S, D, Attr); break;
- case AttributeList::AT_malloc: handleMallocAttr (S, D, Attr); break;
- case AttributeList::AT_may_alias: handleMayAliasAttr (S, D, Attr); break;
- case AttributeList::AT_nocommon: handleNoCommonAttr (S, D, Attr); break;
- case AttributeList::AT_nonnull: handleNonNullAttr (S, D, Attr); break;
+ case AttributeList::AT_Mode: handleModeAttr (S, D, Attr); break;
+ case AttributeList::AT_Malloc: handleMallocAttr (S, D, Attr); break;
+ case AttributeList::AT_MayAlias: handleMayAliasAttr (S, D, Attr); break;
+ case AttributeList::AT_NoCommon: handleNoCommonAttr (S, D, Attr); break;
+ case AttributeList::AT_NonNull: handleNonNullAttr (S, D, Attr); break;
case AttributeList::AT_ownership_returns:
case AttributeList::AT_ownership_takes:
case AttributeList::AT_ownership_holds:
handleOwnershipAttr (S, D, Attr); break;
- case AttributeList::AT_naked: handleNakedAttr (S, D, Attr); break;
- case AttributeList::AT_noreturn: handleNoReturnAttr (S, D, Attr); break;
- case AttributeList::AT_nothrow: handleNothrowAttr (S, D, Attr); break;
- case AttributeList::AT_shared: handleSharedAttr (S, D, Attr); break;
- case AttributeList::AT_vecreturn: handleVecReturnAttr (S, D, Attr); break;
-
- case AttributeList::AT_objc_ownership:
+ case AttributeList::AT_Cold: handleColdAttr (S, D, Attr); break;
+ case AttributeList::AT_Hot: handleHotAttr (S, D, Attr); break;
+ case AttributeList::AT_Naked: handleNakedAttr (S, D, Attr); break;
+ case AttributeList::AT_NoReturn: handleNoReturnAttr (S, D, Attr); break;
+ case AttributeList::AT_NoThrow: handleNothrowAttr (S, D, Attr); break;
+ case AttributeList::AT_CUDAShared: handleSharedAttr (S, D, Attr); break;
+ case AttributeList::AT_VecReturn: handleVecReturnAttr (S, D, Attr); break;
+
+ case AttributeList::AT_ObjCOwnership:
handleObjCOwnershipAttr(S, D, Attr); break;
- case AttributeList::AT_objc_precise_lifetime:
+ case AttributeList::AT_ObjCPreciseLifetime:
handleObjCPreciseLifetimeAttr(S, D, Attr); break;
- case AttributeList::AT_objc_returns_inner_pointer:
+ case AttributeList::AT_ObjCReturnsInnerPointer:
handleObjCReturnsInnerPointerAttr(S, D, Attr); break;
- case AttributeList::AT_ns_bridged:
+ case AttributeList::AT_NSBridged:
handleNSBridgedAttr(S, scope, D, Attr); break;
- case AttributeList::AT_cf_audited_transfer:
- case AttributeList::AT_cf_unknown_transfer:
+ case AttributeList::AT_CFAuditedTransfer:
+ case AttributeList::AT_CFUnknownTransfer:
handleCFTransferAttr(S, D, Attr); break;
// Checker-specific.
- case AttributeList::AT_cf_consumed:
- case AttributeList::AT_ns_consumed: handleNSConsumedAttr (S, D, Attr); break;
- case AttributeList::AT_ns_consumes_self:
+ case AttributeList::AT_CFConsumed:
+ case AttributeList::AT_NSConsumed: handleNSConsumedAttr (S, D, Attr); break;
+ case AttributeList::AT_NSConsumesSelf:
handleNSConsumesSelfAttr(S, D, Attr); break;
- case AttributeList::AT_ns_returns_autoreleased:
- case AttributeList::AT_ns_returns_not_retained:
- case AttributeList::AT_cf_returns_not_retained:
- case AttributeList::AT_ns_returns_retained:
- case AttributeList::AT_cf_returns_retained:
+ case AttributeList::AT_NSReturnsAutoreleased:
+ case AttributeList::AT_NSReturnsNotRetained:
+ case AttributeList::AT_CFReturnsNotRetained:
+ case AttributeList::AT_NSReturnsRetained:
+ case AttributeList::AT_CFReturnsRetained:
handleNSReturnsRetainedAttr(S, D, Attr); break;
- case AttributeList::AT_reqd_work_group_size:
- handleReqdWorkGroupSize(S, D, Attr); break;
+ case AttributeList::AT_WorkGroupSizeHint:
+ case AttributeList::AT_ReqdWorkGroupSize:
+ handleWorkGroupSize(S, D, Attr); break;
- case AttributeList::AT_init_priority:
+ case AttributeList::AT_InitPriority:
handleInitPriorityAttr(S, D, Attr); break;
- case AttributeList::AT_packed: handlePackedAttr (S, D, Attr); break;
- case AttributeList::AT_ms_struct: handleMsStructAttr (S, D, Attr); break;
- case AttributeList::AT_section: handleSectionAttr (S, D, Attr); break;
- case AttributeList::AT_unavailable: handleUnavailableAttr (S, D, Attr); break;
- case AttributeList::AT_objc_arc_weak_reference_unavailable:
+ case AttributeList::AT_Packed: handlePackedAttr (S, D, Attr); break;
+ case AttributeList::AT_Section: handleSectionAttr (S, D, Attr); break;
+ case AttributeList::AT_Unavailable:
+ handleAttrWithMessage<UnavailableAttr>(S, D, Attr, "unavailable");
+ break;
+ case AttributeList::AT_ArcWeakrefUnavailable:
handleArcWeakrefUnavailableAttr (S, D, Attr);
break;
- case AttributeList::AT_objc_root_class:
+ case AttributeList::AT_ObjCRootClass:
handleObjCRootClassAttr(S, D, Attr);
break;
- case AttributeList::AT_objc_requires_property_definitions:
+ case AttributeList::AT_ObjCRequiresPropertyDefs:
handleObjCRequiresPropertyDefsAttr (S, D, Attr);
break;
- case AttributeList::AT_unused: handleUnusedAttr (S, D, Attr); break;
- case AttributeList::AT_returns_twice:
+ case AttributeList::AT_Unused: handleUnusedAttr (S, D, Attr); break;
+ case AttributeList::AT_ReturnsTwice:
handleReturnsTwiceAttr(S, D, Attr);
break;
- case AttributeList::AT_used: handleUsedAttr (S, D, Attr); break;
- case AttributeList::AT_visibility: handleVisibilityAttr (S, D, Attr); break;
- case AttributeList::AT_warn_unused_result: handleWarnUnusedResult(S, D, Attr);
+ case AttributeList::AT_Used: handleUsedAttr (S, D, Attr); break;
+ case AttributeList::AT_Visibility: handleVisibilityAttr (S, D, Attr); break;
+ case AttributeList::AT_WarnUnusedResult: handleWarnUnusedResult(S, D, Attr);
break;
- case AttributeList::AT_weak: handleWeakAttr (S, D, Attr); break;
- case AttributeList::AT_weakref: handleWeakRefAttr (S, D, Attr); break;
- case AttributeList::AT_weak_import: handleWeakImportAttr (S, D, Attr); break;
- case AttributeList::AT_transparent_union:
+ case AttributeList::AT_Weak: handleWeakAttr (S, D, Attr); break;
+ case AttributeList::AT_WeakRef: handleWeakRefAttr (S, D, Attr); break;
+ case AttributeList::AT_WeakImport: handleWeakImportAttr (S, D, Attr); break;
+ case AttributeList::AT_TransparentUnion:
handleTransparentUnionAttr(S, D, Attr);
break;
- case AttributeList::AT_objc_exception:
+ case AttributeList::AT_ObjCException:
handleObjCExceptionAttr(S, D, Attr);
break;
- case AttributeList::AT_objc_method_family:
+ case AttributeList::AT_ObjCMethodFamily:
handleObjCMethodFamilyAttr(S, D, Attr);
break;
- case AttributeList::AT_NSObject: handleObjCNSObject (S, D, Attr); break;
- case AttributeList::AT_blocks: handleBlocksAttr (S, D, Attr); break;
- case AttributeList::AT_sentinel: handleSentinelAttr (S, D, Attr); break;
- case AttributeList::AT_const: handleConstAttr (S, D, Attr); break;
- case AttributeList::AT_pure: handlePureAttr (S, D, Attr); break;
- case AttributeList::AT_cleanup: handleCleanupAttr (S, D, Attr); break;
- case AttributeList::AT_nodebug: handleNoDebugAttr (S, D, Attr); break;
- case AttributeList::AT_noinline: handleNoInlineAttr (S, D, Attr); break;
- case AttributeList::AT_regparm: handleRegparmAttr (S, D, Attr); break;
+ case AttributeList::AT_ObjCNSObject:handleObjCNSObject (S, D, Attr); break;
+ case AttributeList::AT_Blocks: handleBlocksAttr (S, D, Attr); break;
+ case AttributeList::AT_Sentinel: handleSentinelAttr (S, D, Attr); break;
+ case AttributeList::AT_Const: handleConstAttr (S, D, Attr); break;
+ case AttributeList::AT_Pure: handlePureAttr (S, D, Attr); break;
+ case AttributeList::AT_Cleanup: handleCleanupAttr (S, D, Attr); break;
+ case AttributeList::AT_NoDebug: handleNoDebugAttr (S, D, Attr); break;
+ case AttributeList::AT_NoInline: handleNoInlineAttr (S, D, Attr); break;
+ case AttributeList::AT_Regparm: handleRegparmAttr (S, D, Attr); break;
case AttributeList::IgnoredAttribute:
// Just ignore
break;
- case AttributeList::AT_no_instrument_function: // Interacts with -pg.
+ case AttributeList::AT_NoInstrumentFunction: // Interacts with -pg.
handleNoInstrumentFunctionAttr(S, D, Attr);
break;
- case AttributeList::AT_stdcall:
- case AttributeList::AT_cdecl:
- case AttributeList::AT_fastcall:
- case AttributeList::AT_thiscall:
- case AttributeList::AT_pascal:
- case AttributeList::AT_pcs:
+ case AttributeList::AT_StdCall:
+ case AttributeList::AT_CDecl:
+ case AttributeList::AT_FastCall:
+ case AttributeList::AT_ThisCall:
+ case AttributeList::AT_Pascal:
+ case AttributeList::AT_Pcs:
handleCallConvAttr(S, D, Attr);
break;
- case AttributeList::AT_opencl_kernel_function:
+ case AttributeList::AT_OpenCLKernel:
handleOpenCLKernelAttr(S, D, Attr);
break;
- case AttributeList::AT_uuid:
+
+ // Microsoft attributes:
+ case AttributeList::AT_MsStruct:
+ handleMsStructAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_Uuid:
handleUuidAttr(S, D, Attr);
break;
+ case AttributeList::AT_SingleInheritance:
+ case AttributeList::AT_MultipleInheritance:
+ case AttributeList::AT_VirtualInheritance:
+ handleInheritanceAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_Win64:
+ case AttributeList::AT_Ptr32:
+ case AttributeList::AT_Ptr64:
+ handlePortabilityAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_ForceInline:
+ handleForceInlineAttr(S, D, Attr);
+ break;
// Thread safety attributes:
- case AttributeList::AT_guarded_var:
+ case AttributeList::AT_GuardedVar:
handleGuardedVarAttr(S, D, Attr);
break;
- case AttributeList::AT_pt_guarded_var:
- handleGuardedVarAttr(S, D, Attr, /*pointer = */true);
+ case AttributeList::AT_PtGuardedVar:
+ handlePtGuardedVarAttr(S, D, Attr);
break;
- case AttributeList::AT_scoped_lockable:
- handleLockableAttr(S, D, Attr, /*scoped = */true);
+ case AttributeList::AT_ScopedLockable:
+ handleScopedLockableAttr(S, D, Attr);
break;
- case AttributeList::AT_no_address_safety_analysis:
+ case AttributeList::AT_NoAddressSafetyAnalysis:
handleNoAddressSafetyAttr(S, D, Attr);
break;
- case AttributeList::AT_no_thread_safety_analysis:
+ case AttributeList::AT_NoThreadSafetyAnalysis:
handleNoThreadSafetyAttr(S, D, Attr);
break;
- case AttributeList::AT_lockable:
+ case AttributeList::AT_Lockable:
handleLockableAttr(S, D, Attr);
break;
- case AttributeList::AT_guarded_by:
+ case AttributeList::AT_GuardedBy:
handleGuardedByAttr(S, D, Attr);
break;
- case AttributeList::AT_pt_guarded_by:
- handleGuardedByAttr(S, D, Attr, /*pointer = */true);
+ case AttributeList::AT_PtGuardedBy:
+ handlePtGuardedByAttr(S, D, Attr);
break;
- case AttributeList::AT_exclusive_lock_function:
- handleLockFunAttr(S, D, Attr, /*exclusive = */true);
+ case AttributeList::AT_ExclusiveLockFunction:
+ handleExclusiveLockFunctionAttr(S, D, Attr);
break;
- case AttributeList::AT_exclusive_locks_required:
- handleLocksRequiredAttr(S, D, Attr, /*exclusive = */true);
+ case AttributeList::AT_ExclusiveLocksRequired:
+ handleExclusiveLocksRequiredAttr(S, D, Attr);
break;
- case AttributeList::AT_exclusive_trylock_function:
- handleTrylockFunAttr(S, D, Attr, /*exclusive = */true);
+ case AttributeList::AT_ExclusiveTrylockFunction:
+ handleExclusiveTrylockFunctionAttr(S, D, Attr);
break;
- case AttributeList::AT_lock_returned:
+ case AttributeList::AT_LockReturned:
handleLockReturnedAttr(S, D, Attr);
break;
- case AttributeList::AT_locks_excluded:
+ case AttributeList::AT_LocksExcluded:
handleLocksExcludedAttr(S, D, Attr);
break;
- case AttributeList::AT_shared_lock_function:
- handleLockFunAttr(S, D, Attr);
+ case AttributeList::AT_SharedLockFunction:
+ handleSharedLockFunctionAttr(S, D, Attr);
break;
- case AttributeList::AT_shared_locks_required:
- handleLocksRequiredAttr(S, D, Attr);
+ case AttributeList::AT_SharedLocksRequired:
+ handleSharedLocksRequiredAttr(S, D, Attr);
break;
- case AttributeList::AT_shared_trylock_function:
- handleTrylockFunAttr(S, D, Attr);
+ case AttributeList::AT_SharedTrylockFunction:
+ handleSharedTrylockFunctionAttr(S, D, Attr);
break;
- case AttributeList::AT_unlock_function:
+ case AttributeList::AT_UnlockFunction:
handleUnlockFunAttr(S, D, Attr);
break;
- case AttributeList::AT_acquired_before:
- handleAcquireOrderAttr(S, D, Attr, /*before = */true);
+ case AttributeList::AT_AcquiredBefore:
+ handleAcquiredBeforeAttr(S, D, Attr);
break;
- case AttributeList::AT_acquired_after:
- handleAcquireOrderAttr(S, D, Attr, /*before = */false);
+ case AttributeList::AT_AcquiredAfter:
+ handleAcquiredAfterAttr(S, D, Attr);
break;
default:
// Ask target about the attribute.
const TargetAttributesSema &TargetAttrs = S.getTargetAttributesSema();
if (!TargetAttrs.ProcessDeclAttribute(scope, D, Attr, S))
- S.Diag(Attr.getLoc(), diag::warn_unknown_attribute_ignored)
- << Attr.getName();
+ S.Diag(Attr.getLoc(), Attr.isDeclspecAttribute() ?
+ diag::warn_unhandled_ms_attribute_ignored :
+ diag::warn_unknown_attribute_ignored) << Attr.getName();
break;
}
}
@@ -3805,8 +4354,11 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
if (Attr.isInvalid())
return;
- if (Attr.isDeclspecAttribute() && !isKnownDeclSpecAttr(Attr))
- // FIXME: Try to deal with other __declspec attributes!
+ // Type attributes are still treated as declaration attributes by
+ // ParseMicrosoftTypeAttributes and ParseBorlandTypeAttributes. We don't
+ // want to process them, however, because we will simply warn about ignoring
+ // them. So instead, we will bail out early.
+ if (Attr.isMSTypespecAttribute())
return;
if (NonInheritable)
@@ -3840,7 +4392,7 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
bool Sema::ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl,
const AttributeList *AttrList) {
for (const AttributeList* l = AttrList; l; l = l->getNext()) {
- if (l->getKind() == AttributeList::AT_annotate) {
+ if (l->getKind() == AttributeList::AT_Annotate) {
handleAnnotateAttr(*this, ASDecl, *l);
} else {
Diag(l->getLoc(), diag::err_only_annotate_after_access_spec);
@@ -3880,7 +4432,7 @@ void Sema::checkUnusedDeclAttributes(Declarator &D) {
}
/// DeclClonePragmaWeak - clone existing decl (maybe definition),
-/// #pragma weak needs a non-definition decl and source may not have one
+/// \#pragma weak needs a non-definition decl and source may not have one.
NamedDecl * Sema::DeclClonePragmaWeak(NamedDecl *ND, IdentifierInfo *II,
SourceLocation Loc) {
assert(isa<FunctionDecl>(ND) || isa<VarDecl>(ND));
@@ -3930,7 +4482,7 @@ NamedDecl * Sema::DeclClonePragmaWeak(NamedDecl *ND, IdentifierInfo *II,
return NewD;
}
-/// DeclApplyPragmaWeak - A declaration (maybe definition) needs #pragma weak
+/// DeclApplyPragmaWeak - A declaration (maybe definition) needs \#pragma weak
/// applied to it, possibly with an alias.
void Sema::DeclApplyPragmaWeak(Scope *S, NamedDecl *ND, WeakInfo &W) {
if (W.getUsed()) return; // only do this once
@@ -4018,7 +4570,7 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag,
}
if (S.getLangOpts().ObjCAutoRefCount)
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(decl)) {
- // FIXME. we may want to supress diagnostics for all
+ // FIXME: we may want to suppress diagnostics for all
// kind of forbidden type messages on unavailable functions.
if (FD->hasAttr<UnavailableAttr>() &&
diag.getForbiddenTypeDiagnostic() ==
@@ -4033,57 +4585,29 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag,
diag.Triggered = true;
}
-// This duplicates a vector push_back but hides the need to know the
-// size of the type.
-void Sema::DelayedDiagnostics::add(const DelayedDiagnostic &diag) {
- assert(StackSize <= StackCapacity);
-
- // Grow the stack if necessary.
- if (StackSize == StackCapacity) {
- unsigned newCapacity = 2 * StackCapacity + 2;
- char *newBuffer = new char[newCapacity * sizeof(DelayedDiagnostic)];
- const char *oldBuffer = (const char*) Stack;
-
- if (StackCapacity)
- memcpy(newBuffer, oldBuffer, StackCapacity * sizeof(DelayedDiagnostic));
-
- delete[] oldBuffer;
- Stack = reinterpret_cast<sema::DelayedDiagnostic*>(newBuffer);
- StackCapacity = newCapacity;
- }
-
- assert(StackSize < StackCapacity);
- new (&Stack[StackSize++]) DelayedDiagnostic(diag);
-}
-
-void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state,
- Decl *decl) {
- DelayedDiagnostics &DD = S.DelayedDiagnostics;
-
- // Check the invariants.
- assert(DD.StackSize >= state.SavedStackSize);
- assert(state.SavedStackSize >= DD.ActiveStackBase);
- assert(DD.ParsingDepth > 0);
-
- // Drop the parsing depth.
- DD.ParsingDepth--;
-
- // If there are no active diagnostics, we're done.
- if (DD.StackSize == DD.ActiveStackBase)
- return;
-
- // We only want to actually emit delayed diagnostics when we
- // successfully parsed a decl.
- if (decl) {
- // We emit all the active diagnostics, not just those starting
- // from the saved state. The idea is this: we get one push for a
- // decl spec and another for each declarator; in a decl group like:
- // deprecated_typedef foo, *bar, baz();
- // only the declarator pops will be passed decls. This is correct;
- // we really do need to consider delayed diagnostics from the decl spec
- // for each of the different declarations.
- for (unsigned i = DD.ActiveStackBase, e = DD.StackSize; i != e; ++i) {
- DelayedDiagnostic &diag = DD.Stack[i];
+void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) {
+ assert(DelayedDiagnostics.getCurrentPool());
+ DelayedDiagnosticPool &poppedPool = *DelayedDiagnostics.getCurrentPool();
+ DelayedDiagnostics.popWithoutEmitting(state);
+
+ // When delaying diagnostics to run in the context of a parsed
+ // declaration, we only want to actually emit anything if parsing
+ // succeeds.
+ if (!decl) return;
+
+ // We emit all the active diagnostics in this pool or any of its
+ // parents. In general, we'll get one pool for the decl spec
+ // and a child pool for each declarator; in a decl group like:
+ // deprecated_typedef foo, *bar, baz();
+ // only the declarator pops will be passed decls. This is correct;
+ // we really do need to consider delayed diagnostics from the decl spec
+ // for each of the different declarations.
+ const DelayedDiagnosticPool *pool = &poppedPool;
+ do {
+ for (DelayedDiagnosticPool::pool_iterator
+ i = pool->pool_begin(), e = pool->pool_end(); i != e; ++i) {
+ // This const_cast is a bit lame. Really, Triggered should be mutable.
+ DelayedDiagnostic &diag = const_cast<DelayedDiagnostic&>(*i);
if (diag.Triggered)
continue;
@@ -4091,25 +4615,28 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state,
case DelayedDiagnostic::Deprecation:
// Don't bother giving deprecation diagnostics if the decl is invalid.
if (!decl->isInvalidDecl())
- S.HandleDelayedDeprecationCheck(diag, decl);
+ HandleDelayedDeprecationCheck(diag, decl);
break;
case DelayedDiagnostic::Access:
- S.HandleDelayedAccessCheck(diag, decl);
+ HandleDelayedAccessCheck(diag, decl);
break;
case DelayedDiagnostic::ForbiddenType:
- handleDelayedForbiddenType(S, diag, decl);
+ handleDelayedForbiddenType(*this, diag, decl);
break;
}
}
- }
-
- // Destroy all the delayed diagnostics we're about to pop off.
- for (unsigned i = state.SavedStackSize, e = DD.StackSize; i != e; ++i)
- DD.Stack[i].Destroy();
+ } while ((pool = pool->getParent()));
+}
- DD.StackSize = state.SavedStackSize;
+/// Given a set of delayed diagnostics, re-emit them as if they had
+/// been delayed in the current context instead of in the given pool.
+/// Essentially, this just moves them to the current pool.
+void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) {
+ DelayedDiagnosticPool *curPool = DelayedDiagnostics.getCurrentPool();
+ assert(curPool && "re-emitting in undelayed context not supported");
+ curPool->steal(pool);
}
static bool isDeclDeprecated(Decl *D) {
@@ -4123,24 +4650,36 @@ static bool isDeclDeprecated(Decl *D) {
return false;
}
+static void
+DoEmitDeprecationWarning(Sema &S, const NamedDecl *D, StringRef Message,
+ SourceLocation Loc,
+ const ObjCInterfaceDecl *UnknownObjCClass) {
+ DeclarationName Name = D->getDeclName();
+ if (!Message.empty()) {
+ S.Diag(Loc, diag::warn_deprecated_message) << Name << Message;
+ S.Diag(D->getLocation(),
+ isa<ObjCMethodDecl>(D) ? diag::note_method_declared_at
+ : diag::note_previous_decl) << Name;
+ } else if (!UnknownObjCClass) {
+ S.Diag(Loc, diag::warn_deprecated) << D->getDeclName();
+ S.Diag(D->getLocation(),
+ isa<ObjCMethodDecl>(D) ? diag::note_method_declared_at
+ : diag::note_previous_decl) << Name;
+ } else {
+ S.Diag(Loc, diag::warn_deprecated_fwdclass_message) << Name;
+ S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
+ }
+}
+
void Sema::HandleDelayedDeprecationCheck(DelayedDiagnostic &DD,
Decl *Ctx) {
if (isDeclDeprecated(Ctx))
return;
DD.Triggered = true;
- if (!DD.getDeprecationMessage().empty())
- Diag(DD.Loc, diag::warn_deprecated_message)
- << DD.getDeprecationDecl()->getDeclName()
- << DD.getDeprecationMessage();
- else if (DD.getUnknownObjCClass()) {
- Diag(DD.Loc, diag::warn_deprecated_fwdclass_message)
- << DD.getDeprecationDecl()->getDeclName();
- Diag(DD.getUnknownObjCClass()->getLocation(), diag::note_forward_class);
- }
- else
- Diag(DD.Loc, diag::warn_deprecated)
- << DD.getDeprecationDecl()->getDeclName();
+ DoEmitDeprecationWarning(*this, DD.getDeprecationDecl(),
+ DD.getDeprecationMessage(), DD.Loc,
+ DD.getUnknownObjCClass());
}
void Sema::EmitDeprecationWarning(NamedDecl *D, StringRef Message,
@@ -4157,15 +4696,5 @@ void Sema::EmitDeprecationWarning(NamedDecl *D, StringRef Message,
// Otherwise, don't warn if our current context is deprecated.
if (isDeclDeprecated(cast<Decl>(getCurLexicalContext())))
return;
- if (!Message.empty())
- Diag(Loc, diag::warn_deprecated_message) << D->getDeclName()
- << Message;
- else {
- if (!UnknownObjCClass)
- Diag(Loc, diag::warn_deprecated) << D->getDeclName();
- else {
- Diag(Loc, diag::warn_deprecated_fwdclass_message) << D->getDeclName();
- Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
- }
- }
+ DoEmitDeprecationWarning(*this, D, Message, Loc, UnknownObjCClass);
}
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index c861072..1d45a68 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -23,6 +23,7 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/RecursiveASTVisitor.h"
@@ -127,8 +128,8 @@ namespace {
void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,
CXXMethodDecl *Method) {
- // If we have an MSAny or unknown spec already, don't bother.
- if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed)
+ // If we have an MSAny spec already, don't bother.
+ if (!Method || ComputedEST == EST_MSAny)
return;
const FunctionProtoType *Proto
@@ -140,7 +141,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
// If this function can throw any exceptions, make a note of that.
- if (EST == EST_Delayed || EST == EST_MSAny || EST == EST_None) {
+ if (EST == EST_MSAny || EST == EST_None) {
ClearExceptions();
ComputedEST = EST;
return;
@@ -197,7 +198,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,
}
void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) {
- if (!E || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed)
+ if (!E || ComputedEST == EST_MSAny)
return;
// FIXME:
@@ -667,9 +668,9 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
SourceLocation ParamLoc = PD->getLocation();
if (!(*i)->isDependentType() &&
SemaRef.RequireLiteralType(ParamLoc, *i,
- SemaRef.PDiag(diag::err_constexpr_non_literal_param)
- << ArgIndex+1 << PD->getSourceRange()
- << isa<CXXConstructorDecl>(FD)))
+ diag::err_constexpr_non_literal_param,
+ ArgIndex+1, PD->getSourceRange(),
+ isa<CXXConstructorDecl>(FD)))
return false;
}
return true;
@@ -725,7 +726,7 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) {
QualType RT = NewFD->getResultType();
if (!RT->isDependentType() &&
RequireLiteralType(NewFD->getLocation(), RT,
- PDiag(diag::err_constexpr_non_literal_return)))
+ diag::err_constexpr_non_literal_return))
return false;
}
@@ -920,7 +921,7 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) {
unsigned Fields = 0;
for (CXXRecordDecl::field_iterator I = RD->field_begin(),
E = RD->field_end(); I != E; ++I, ++Fields) {
- if ((*I)->isAnonymousStructOrUnion()) {
+ if (I->isAnonymousStructOrUnion()) {
AnyAnonStructUnionMembers = true;
break;
}
@@ -1055,8 +1056,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
// The class-name in a base-specifier shall not be an incompletely
// defined class.
if (RequireCompleteType(BaseLoc, BaseType,
- PDiag(diag::err_incomplete_base_class)
- << SpecifierRange)) {
+ diag::err_incomplete_base_class, SpecifierRange)) {
Class->setInvalidDecl();
return 0;
}
@@ -1119,6 +1119,8 @@ Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange,
Virtual, Access, TInfo,
EllipsisLoc))
return BaseSpec;
+ else
+ Class->setInvalidDecl();
return true;
}
@@ -1403,32 +1405,50 @@ bool Sema::ActOnAccessSpecifier(AccessSpecifier Access,
return ProcessAccessDeclAttributeList(ASDecl, Attrs);
}
-/// CheckOverrideControl - Check C++0x override control semantics.
-void Sema::CheckOverrideControl(const Decl *D) {
+/// CheckOverrideControl - Check C++11 override control semantics.
+void Sema::CheckOverrideControl(Decl *D) {
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D);
- if (!MD || !MD->isVirtual())
+
+ // Do we know which functions this declaration might be overriding?
+ bool OverridesAreKnown = !MD ||
+ (!MD->getParent()->hasAnyDependentBases() &&
+ !MD->getType()->isDependentType());
+
+ if (!MD || !MD->isVirtual()) {
+ if (OverridesAreKnown) {
+ if (OverrideAttr *OA = D->getAttr<OverrideAttr>()) {
+ Diag(OA->getLocation(),
+ diag::override_keyword_only_allowed_on_virtual_member_functions)
+ << "override" << FixItHint::CreateRemoval(OA->getLocation());
+ D->dropAttr<OverrideAttr>();
+ }
+ if (FinalAttr *FA = D->getAttr<FinalAttr>()) {
+ Diag(FA->getLocation(),
+ diag::override_keyword_only_allowed_on_virtual_member_functions)
+ << "final" << FixItHint::CreateRemoval(FA->getLocation());
+ D->dropAttr<FinalAttr>();
+ }
+ }
return;
+ }
- if (MD->isDependentContext())
+ if (!OverridesAreKnown)
return;
- // C++0x [class.virtual]p3:
- // If a virtual function is marked with the virt-specifier override and does
- // not override a member function of a base class,
- // the program is ill-formed.
- bool HasOverriddenMethods =
+ // C++11 [class.virtual]p5:
+ // If a virtual function is marked with the virt-specifier override and
+ // does not override a member function of a base class, the program is
+ // ill-formed.
+ bool HasOverriddenMethods =
MD->begin_overridden_methods() != MD->end_overridden_methods();
- if (MD->hasAttr<OverrideAttr>() && !HasOverriddenMethods) {
- Diag(MD->getLocation(),
- diag::err_function_marked_override_not_overriding)
+ if (MD->hasAttr<OverrideAttr>() && !HasOverriddenMethods)
+ Diag(MD->getLocation(), diag::err_function_marked_override_not_overriding)
<< MD->getDeclName();
- return;
- }
}
-/// CheckIfOverriddenFunctionIsMarkedFinal - Checks whether a virtual member
+/// CheckIfOverriddenFunctionIsMarkedFinal - Checks whether a virtual member
/// function overrides a virtual member function marked 'final', according to
-/// C++0x [class.virtual]p3.
+/// C++11 [class.virtual]p4.
bool Sema::CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New,
const CXXMethodDecl *Old) {
if (!Old->hasAttr<FinalAttr>())
@@ -1440,16 +1460,26 @@ bool Sema::CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New,
return true;
}
+static bool InitializationHasSideEffects(const FieldDecl &FD) {
+ const Type *T = FD.getType()->getBaseElementTypeUnsafe();
+ // FIXME: Destruction of ObjC lifetime types has side-effects.
+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return !RD->isCompleteDefinition() ||
+ !RD->hasTrivialDefaultConstructor() ||
+ !RD->hasTrivialDestructor();
+ return false;
+}
+
/// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
/// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the
/// bitfield width if there is one, 'InitExpr' specifies the initializer if
-/// one has been parsed, and 'HasDeferredInit' is true if an initializer is
-/// present but parsing it has been deferred.
+/// one has been parsed, and 'InitStyle' is set if an in-class initializer is
+/// present (but parsing it has been deferred).
Decl *
Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
Expr *BW, const VirtSpecifiers &VS,
- bool HasDeferredInit) {
+ InClassInitStyle InitStyle) {
const DeclSpec &DS = D.getDeclSpec();
DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
DeclarationName Name = NameInfo.getName();
@@ -1507,12 +1537,12 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
CXXScopeSpec &SS = D.getCXXScopeSpec();
// Data members must have identifiers for names.
- if (Name.getNameKind() != DeclarationName::Identifier) {
+ if (!Name.isIdentifier()) {
Diag(Loc, diag::err_bad_variable_name)
<< Name;
return 0;
}
-
+
IdentifierInfo *II = Name.getAsIdentifierInfo();
// Member field could not be with "template" keyword.
@@ -1553,10 +1583,10 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
}
Member = HandleField(S, cast<CXXRecordDecl>(CurContext), Loc, D, BitWidth,
- HasDeferredInit, AS);
+ InitStyle, AS);
assert(Member && "HandleField never returns null");
} else {
- assert(!HasDeferredInit);
+ assert(InitStyle == ICIS_NoInit);
Member = HandleDeclarator(S, D, move(TemplateParameterLists));
if (!Member) {
@@ -1596,37 +1626,39 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
FunTmpl->getTemplatedDecl()->setAccess(AS);
}
- if (VS.isOverrideSpecified()) {
- CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member);
- if (!MD || !MD->isVirtual()) {
- Diag(Member->getLocStart(),
- diag::override_keyword_only_allowed_on_virtual_member_functions)
- << "override" << FixItHint::CreateRemoval(VS.getOverrideLoc());
- } else
- MD->addAttr(new (Context) OverrideAttr(VS.getOverrideLoc(), Context));
- }
- if (VS.isFinalSpecified()) {
- CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member);
- if (!MD || !MD->isVirtual()) {
- Diag(Member->getLocStart(),
- diag::override_keyword_only_allowed_on_virtual_member_functions)
- << "final" << FixItHint::CreateRemoval(VS.getFinalLoc());
- } else
- MD->addAttr(new (Context) FinalAttr(VS.getFinalLoc(), Context));
- }
+ if (VS.isOverrideSpecified())
+ Member->addAttr(new (Context) OverrideAttr(VS.getOverrideLoc(), Context));
+ if (VS.isFinalSpecified())
+ Member->addAttr(new (Context) FinalAttr(VS.getFinalLoc(), Context));
if (VS.getLastLocation().isValid()) {
// Update the end location of a method that has a virt-specifiers.
if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Member))
MD->setRangeEnd(VS.getLastLocation());
}
-
+
CheckOverrideControl(Member);
assert((Name || isInstField) && "No identifier for non-field ?");
- if (isInstField)
- FieldCollector->Add(cast<FieldDecl>(Member));
+ if (isInstField) {
+ FieldDecl *FD = cast<FieldDecl>(Member);
+ FieldCollector->Add(FD);
+
+ if (Diags.getDiagnosticLevel(diag::warn_unused_private_field,
+ FD->getLocation())
+ != DiagnosticsEngine::Ignored) {
+ // Remember all explicit private FieldDecls that have a name, no side
+ // effects and are not part of a dependent type declaration.
+ if (!FD->isImplicit() && FD->getDeclName() &&
+ FD->getAccess() == AS_private &&
+ !FD->hasAttr<UnusedAttr>() &&
+ !FD->getParent()->isDependentContext() &&
+ !InitializationHasSideEffects(*FD))
+ UnusedPrivateFields.insert(FD);
+ }
+ }
+
return Member;
}
@@ -1635,9 +1667,11 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
/// instantiating an in-class initializer in a class template. Such actions
/// are deferred until the class is complete.
void
-Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc,
+Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation InitLoc,
Expr *InitExpr) {
FieldDecl *FD = cast<FieldDecl>(D);
+ assert(FD->getInClassInitStyle() != ICIS_NoInit &&
+ "must set init style when field is created");
if (!InitExpr) {
FD->setInvalidDecl();
@@ -1660,9 +1694,9 @@ Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc,
Expr **Inits = &InitExpr;
unsigned NumInits = 1;
InitializedEntity Entity = InitializedEntity::InitializeMember(FD);
- InitializationKind Kind = EqualLoc.isInvalid()
+ InitializationKind Kind = FD->getInClassInitStyle() == ICIS_ListInit
? InitializationKind::CreateDirectList(InitExpr->getLocStart())
- : InitializationKind::CreateCopy(InitExpr->getLocStart(), EqualLoc);
+ : InitializationKind::CreateCopy(InitExpr->getLocStart(), InitLoc);
InitializationSequence Seq(*this, Entity, Kind, Inits, NumInits);
Init = Seq.Perform(*this, Entity, Kind, MultiExprArg(Inits, NumInits));
if (Init.isInvalid()) {
@@ -1670,7 +1704,7 @@ Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc,
return;
}
- CheckImplicitConversions(Init.get(), EqualLoc);
+ CheckImplicitConversions(Init.get(), InitLoc);
}
// C++0x [class.base.init]p7:
@@ -2010,73 +2044,95 @@ static void CheckForDanglingReferenceOrPointer(Sema &S, ValueDecl *Member,
<< (unsigned)IsPointer;
}
-/// Checks an initializer expression for use of uninitialized fields, such as
-/// containing the field that is being initialized. Returns true if there is an
-/// uninitialized field was used an updates the SourceLocation parameter; false
-/// otherwise.
-static bool InitExprContainsUninitializedFields(const Stmt *S,
- const ValueDecl *LhsField,
- SourceLocation *L) {
- assert(isa<FieldDecl>(LhsField) || isa<IndirectFieldDecl>(LhsField));
-
- if (isa<CallExpr>(S)) {
- // Do not descend into function calls or constructors, as the use
- // of an uninitialized field may be valid. One would have to inspect
- // the contents of the function/ctor to determine if it is safe or not.
- // i.e. Pass-by-value is never safe, but pass-by-reference and pointers
- // may be safe, depending on what the function/ctor does.
- return false;
- }
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
- const NamedDecl *RhsField = ME->getMemberDecl();
-
- if (const VarDecl *VD = dyn_cast<VarDecl>(RhsField)) {
- // The member expression points to a static data member.
- assert(VD->isStaticDataMember() &&
- "Member points to non-static data member!");
- (void)VD;
- return false;
+namespace {
+ class UninitializedFieldVisitor
+ : public EvaluatedExprVisitor<UninitializedFieldVisitor> {
+ Sema &S;
+ ValueDecl *VD;
+ public:
+ typedef EvaluatedExprVisitor<UninitializedFieldVisitor> Inherited;
+ UninitializedFieldVisitor(Sema &S, ValueDecl *VD) : Inherited(S.Context),
+ S(S), VD(VD) {
}
-
- if (isa<EnumConstantDecl>(RhsField)) {
- // The member expression points to an enum.
- return false;
+
+ void HandleExpr(Expr *E) {
+ if (!E) return;
+
+ // Expressions like x(x) sometimes lack the surrounding expressions
+ // but need to be checked anyways.
+ HandleValue(E);
+ Visit(E);
}
- if (RhsField == LhsField) {
- // Initializing a field with itself. Throw a warning.
- // But wait; there are exceptions!
- // Exception #1: The field may not belong to this record.
- // e.g. Foo(const Foo& rhs) : A(rhs.A) {}
- const Expr *base = ME->getBase();
- if (base != NULL && !isa<CXXThisExpr>(base->IgnoreParenCasts())) {
- // Even though the field matches, it does not belong to this record.
- return false;
+ void HandleValue(Expr *E) {
+ E = E->IgnoreParens();
+
+ if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+ if (isa<EnumConstantDecl>(ME->getMemberDecl()))
+ return;
+ Expr *Base = E;
+ while (isa<MemberExpr>(Base)) {
+ ME = dyn_cast<MemberExpr>(Base);
+ if (VarDecl *VarD = dyn_cast<VarDecl>(ME->getMemberDecl()))
+ if (VarD->hasGlobalStorage())
+ return;
+ Base = ME->getBase();
+ }
+
+ if (VD == ME->getMemberDecl() && isa<CXXThisExpr>(Base)) {
+ S.Diag(ME->getExprLoc(), diag::warn_field_is_uninit);
+ return;
+ }
+ }
+
+ if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ HandleValue(CO->getTrueExpr());
+ HandleValue(CO->getFalseExpr());
+ return;
+ }
+
+ if (BinaryConditionalOperator *BCO =
+ dyn_cast<BinaryConditionalOperator>(E)) {
+ HandleValue(BCO->getCommon());
+ HandleValue(BCO->getFalseExpr());
+ return;
+ }
+
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+ switch (BO->getOpcode()) {
+ default:
+ return;
+ case(BO_PtrMemD):
+ case(BO_PtrMemI):
+ HandleValue(BO->getLHS());
+ return;
+ case(BO_Comma):
+ HandleValue(BO->getRHS());
+ return;
+ }
}
- // None of the exceptions triggered; return true to indicate an
- // uninitialized field was used.
- *L = ME->getMemberLoc();
- return true;
}
- } else if (isa<UnaryExprOrTypeTraitExpr>(S)) {
- // sizeof/alignof doesn't reference contents, do not warn.
- return false;
- } else if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(S)) {
- // address-of doesn't reference contents (the pointer may be dereferenced
- // in the same expression but it would be rare; and weird).
- if (UOE->getOpcode() == UO_AddrOf)
- return false;
- }
- for (Stmt::const_child_range it = S->children(); it; ++it) {
- if (!*it) {
- // An expression such as 'member(arg ?: "")' may trigger this.
- continue;
+
+ void VisitImplicitCastExpr(ImplicitCastExpr *E) {
+ if (E->getCastKind() == CK_LValueToRValue)
+ HandleValue(E->getSubExpr());
+
+ Inherited::VisitImplicitCastExpr(E);
}
- if (InitExprContainsUninitializedFields(*it, LhsField, L))
- return true;
+
+ void VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
+ Expr *Callee = E->getCallee();
+ if (isa<MemberExpr>(Callee))
+ HandleValue(Callee);
+
+ Inherited::VisitCXXMemberCallExpr(E);
+ }
+ };
+ static void CheckInitExprContainsUninitializedFields(Sema &S, Expr *E,
+ ValueDecl *VD) {
+ UninitializedFieldVisitor(S, VD).HandleExpr(E);
}
- return false;
-}
+} // namespace
MemInitResult
Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
@@ -2106,18 +2162,17 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
Args = InitList->getInits();
NumArgs = InitList->getNumInits();
}
- for (unsigned i = 0; i < NumArgs; ++i) {
- SourceLocation L;
- if (InitExprContainsUninitializedFields(Args[i], Member, &L)) {
- // FIXME: Return true in the case when other fields are used before being
+
+ if (getDiagnostics().getDiagnosticLevel(diag::warn_field_is_uninit, IdLoc)
+ != DiagnosticsEngine::Ignored)
+ for (unsigned i = 0; i < NumArgs; ++i)
+ // FIXME: Warn about the case when other fields are used before being
// uninitialized. For example, let this field be the i'th field. When
// initializing the i'th field, throw a warning if any of the >= i'th
// fields are used, as they are not yet initialized.
// Right now we are only handling the case where the i'th field uses
// itself in its initializer.
- Diag(L, diag::warn_field_is_uninit);
- }
- }
+ CheckInitExprContainsUninitializedFields(*this, Args[i], Member);
SourceRange InitRange = Init->getSourceRange();
@@ -2235,6 +2290,16 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, Expr *Init,
if (DelegationInit.isInvalid())
return true;
+ // If we are in a dependent context, template instantiation will
+ // perform this type-checking again. Just save the arguments that we
+ // received in a ParenListExpr.
+ // FIXME: This isn't quite ideal, since our ASTs don't capture all
+ // of the information that we have about the base
+ // initializer. However, deconstructing the ASTs is a dicey process,
+ // and this approach is far more likely to get the corner cases right.
+ if (CurContext->isDependentContext())
+ DelegationInit = Owned(Init);
+
return new (Context) CXXCtorInitializer(Context, TInfo, InitRange.getBegin(),
DelegationInit.takeAs<Expr>(),
InitRange.getEnd());
@@ -2694,7 +2759,7 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor,
FieldBaseElementType->isObjCRetainableType() &&
FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_None &&
FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) {
- // Instant objects:
+ // ARC:
// Default-initialize Objective-C pointers to NULL.
CXXMemberInit
= new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field,
@@ -2741,6 +2806,16 @@ struct BaseAndFieldInfo {
llvm_unreachable("Invalid ImplicitInitializerKind!");
}
+
+ bool addFieldInitializer(CXXCtorInitializer *Init) {
+ AllToInit.push_back(Init);
+
+ // Check whether this initializer makes the field "used".
+ if (Init->getInit() && Init->getInit()->HasSideEffects(S.Context))
+ S.UnusedPrivateFields.remove(Init->getAnyMember());
+
+ return false;
+ }
};
}
@@ -2778,12 +2853,10 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info,
IndirectFieldDecl *Indirect = 0) {
// Overwhelmingly common case: we have a direct initializer for this field.
- if (CXXCtorInitializer *Init = Info.AllBaseFields.lookup(Field)) {
- Info.AllToInit.push_back(Init);
- return false;
- }
+ if (CXXCtorInitializer *Init = Info.AllBaseFields.lookup(Field))
+ return Info.addFieldInitializer(Init);
- // C++0x [class.base.init]p8: if the entity is a non-static data member that
+ // C++11 [class.base.init]p8: if the entity is a non-static data member that
// has a brace-or-equal-initializer, the entity is initialized as specified
// in [dcl.init].
if (Field->hasInClassInitializer() && !Info.isImplicitCopyOrMove()) {
@@ -2798,8 +2871,7 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info,
SourceLocation(),
SourceLocation(), 0,
SourceLocation());
- Info.AllToInit.push_back(Init);
- return false;
+ return Info.addFieldInitializer(Init);
}
// Don't build an implicit initializer for union members if none was
@@ -2823,10 +2895,10 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info,
Indirect, Init))
return true;
- if (Init)
- Info.AllToInit.push_back(Init);
+ if (!Init)
+ return false;
- return false;
+ return Info.addFieldInitializer(Init);
}
bool
@@ -3397,19 +3469,33 @@ void Sema::ActOnDefaultCtorInitializers(Decl *CDtorDecl) {
bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T,
unsigned DiagID, AbstractDiagSelID SelID) {
- if (SelID == -1)
- return RequireNonAbstractType(Loc, T, PDiag(DiagID));
- else
- return RequireNonAbstractType(Loc, T, PDiag(DiagID) << SelID);
+ class NonAbstractTypeDiagnoser : public TypeDiagnoser {
+ unsigned DiagID;
+ AbstractDiagSelID SelID;
+
+ public:
+ NonAbstractTypeDiagnoser(unsigned DiagID, AbstractDiagSelID SelID)
+ : TypeDiagnoser(DiagID == 0), DiagID(DiagID), SelID(SelID) { }
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ if (Suppressed) return;
+ if (SelID == -1)
+ S.Diag(Loc, DiagID) << T;
+ else
+ S.Diag(Loc, DiagID) << SelID << T;
+ }
+ } Diagnoser(DiagID, SelID);
+
+ return RequireNonAbstractType(Loc, T, Diagnoser);
}
bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD) {
+ TypeDiagnoser &Diagnoser) {
if (!getLangOpts().CPlusPlus)
return false;
if (const ArrayType *AT = Context.getAsArrayType(T))
- return RequireNonAbstractType(Loc, AT->getElementType(), PD);
+ return RequireNonAbstractType(Loc, AT->getElementType(), Diagnoser);
if (const PointerType *PT = T->getAs<PointerType>()) {
// Find the innermost pointer type.
@@ -3417,7 +3503,7 @@ bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T,
PT = T;
if (const ArrayType *AT = Context.getAsArrayType(PT->getPointeeType()))
- return RequireNonAbstractType(Loc, AT->getElementType(), PD);
+ return RequireNonAbstractType(Loc, AT->getElementType(), Diagnoser);
}
const RecordType *RT = T->getAs<RecordType>();
@@ -3436,7 +3522,7 @@ bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T,
if (!RD->isAbstract())
return false;
- Diag(Loc, PD) << RD->getDeclName();
+ Diagnoser.diagnose(*this, Loc, T);
DiagnoseAbstractType(RD);
return true;
@@ -3732,7 +3818,7 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
for (CXXRecordDecl::method_iterator M = Record->method_begin(),
MEnd = Record->method_end();
M != MEnd; ++M) {
- if (!(*M)->isStatic())
+ if (!M->isStatic())
DiagnoseHiddenVirtualMethods(Record, *M);
}
}
@@ -3762,8 +3848,8 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
- RequireLiteralType((*M)->getLocation(), Context.getRecordType(Record),
- PDiag(diag::err_constexpr_method_non_literal));
+ RequireLiteralType(M->getLocation(), Context.getRecordType(Record),
+ diag::err_constexpr_method_non_literal);
break;
}
@@ -3781,558 +3867,354 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
// instantiated (e.g. meta-functions). This doesn't apply to classes that
// have inherited constructors.
DeclareInheritedConstructors(Record);
-
- if (!Record->isDependentType())
- CheckExplicitlyDefaultedMethods(Record);
}
void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) {
for (CXXRecordDecl::method_iterator MI = Record->method_begin(),
ME = Record->method_end();
- MI != ME; ++MI) {
- if (!MI->isInvalidDecl() && MI->isExplicitlyDefaulted()) {
- switch (getSpecialMember(*MI)) {
- case CXXDefaultConstructor:
- CheckExplicitlyDefaultedDefaultConstructor(
- cast<CXXConstructorDecl>(*MI));
- break;
-
- case CXXDestructor:
- CheckExplicitlyDefaultedDestructor(cast<CXXDestructorDecl>(*MI));
- break;
-
- case CXXCopyConstructor:
- CheckExplicitlyDefaultedCopyConstructor(cast<CXXConstructorDecl>(*MI));
- break;
-
- case CXXCopyAssignment:
- CheckExplicitlyDefaultedCopyAssignment(*MI);
- break;
-
- case CXXMoveConstructor:
- CheckExplicitlyDefaultedMoveConstructor(cast<CXXConstructorDecl>(*MI));
- break;
-
- case CXXMoveAssignment:
- CheckExplicitlyDefaultedMoveAssignment(*MI);
- break;
-
- case CXXInvalid:
- llvm_unreachable("non-special member explicitly defaulted!");
- }
- }
- }
-
+ MI != ME; ++MI)
+ if (!MI->isInvalidDecl() && MI->isExplicitlyDefaulted())
+ CheckExplicitlyDefaultedSpecialMember(*MI);
+}
+
+/// Is the special member function which would be selected to perform the
+/// specified operation on the specified class type a constexpr constructor?
+static bool specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
+ Sema::CXXSpecialMember CSM,
+ bool ConstArg) {
+ Sema::SpecialMemberOverloadResult *SMOR =
+ S.LookupSpecialMember(ClassDecl, CSM, ConstArg,
+ false, false, false, false);
+ if (!SMOR || !SMOR->getMethod())
+ // A constructor we wouldn't select can't be "involved in initializing"
+ // anything.
+ return true;
+ return SMOR->getMethod()->isConstexpr();
}
-void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) {
- assert(CD->isExplicitlyDefaulted() && CD->isDefaultConstructor());
-
- // Whether this was the first-declared instance of the constructor.
- // This affects whether we implicitly add an exception spec (and, eventually,
- // constexpr). It is also ill-formed to explicitly default a constructor such
- // that it would be deleted. (C++0x [decl.fct.def.default])
- bool First = CD == CD->getCanonicalDecl();
+/// Determine whether the specified special member function would be constexpr
+/// if it were implicitly defined.
+static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
+ Sema::CXXSpecialMember CSM,
+ bool ConstArg) {
+ if (!S.getLangOpts().CPlusPlus0x)
+ return false;
- bool HadError = false;
- if (CD->getNumParams() != 0) {
- Diag(CD->getLocation(), diag::err_defaulted_default_ctor_params)
- << CD->getSourceRange();
- HadError = true;
- }
+ // C++11 [dcl.constexpr]p4:
+ // In the definition of a constexpr constructor [...]
+ switch (CSM) {
+ case Sema::CXXDefaultConstructor:
+ // Since default constructor lookup is essentially trivial (and cannot
+ // involve, for instance, template instantiation), we compute whether a
+ // defaulted default constructor is constexpr directly within CXXRecordDecl.
+ //
+ // This is important for performance; we need to know whether the default
+ // constructor is constexpr to determine whether the type is a literal type.
+ return ClassDecl->defaultedDefaultConstructorIsConstexpr();
- ImplicitExceptionSpecification Spec
- = ComputeDefaultedDefaultCtorExceptionSpec(CD->getParent());
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- if (EPI.ExceptionSpecType == EST_Delayed) {
- // Exception specification depends on some deferred part of the class. We'll
- // try again when the class's definition has been fully processed.
- return;
- }
- const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
- *ExceptionType = Context.getFunctionType(
- Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
+ case Sema::CXXCopyConstructor:
+ case Sema::CXXMoveConstructor:
+ // For copy or move constructors, we need to perform overload resolution.
+ break;
- // C++11 [dcl.fct.def.default]p2:
- // An explicitly-defaulted function may be declared constexpr only if it
- // would have been implicitly declared as constexpr,
- // Do not apply this rule to templates, since core issue 1358 makes such
- // functions always instantiate to constexpr functions.
- if (CD->isConstexpr() &&
- CD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
- if (!CD->getParent()->defaultedDefaultConstructorIsConstexpr()) {
- Diag(CD->getLocStart(), diag::err_incorrect_defaulted_constexpr)
- << CXXDefaultConstructor;
- HadError = true;
- }
- }
- // and may have an explicit exception-specification only if it is compatible
- // with the exception-specification on the implicit declaration.
- if (CtorType->hasExceptionSpec()) {
- if (CheckEquivalentExceptionSpec(
- PDiag(diag::err_incorrect_defaulted_exception_spec)
- << CXXDefaultConstructor,
- PDiag(),
- ExceptionType, SourceLocation(),
- CtorType, CD->getLocation())) {
- HadError = true;
- }
+ case Sema::CXXCopyAssignment:
+ case Sema::CXXMoveAssignment:
+ case Sema::CXXDestructor:
+ case Sema::CXXInvalid:
+ return false;
}
- // If a function is explicitly defaulted on its first declaration,
- if (First) {
- // -- it is implicitly considered to be constexpr if the implicit
- // definition would be,
- CD->setConstexpr(CD->getParent()->defaultedDefaultConstructorIsConstexpr());
+ // -- if the class is a non-empty union, or for each non-empty anonymous
+ // union member of a non-union class, exactly one non-static data member
+ // shall be initialized; [DR1359]
+ //
+ // If we squint, this is guaranteed, since exactly one non-static data member
+ // will be initialized (if the constructor isn't deleted), we just don't know
+ // which one.
+ if (ClassDecl->isUnion())
+ return true;
- // -- it is implicitly considered to have the same
- // exception-specification as if it had been implicitly declared
- //
- // FIXME: a compatible, but different, explicit exception specification
- // will be silently overridden. We should issue a warning if this happens.
- EPI.ExtInfo = CtorType->getExtInfo();
+ // -- the class shall not have any virtual base classes;
+ if (ClassDecl->getNumVBases())
+ return false;
- // Such a function is also trivial if the implicitly-declared function
- // would have been.
- CD->setTrivial(CD->getParent()->hasTrivialDefaultConstructor());
- }
+ // -- every constructor involved in initializing [...] base class
+ // sub-objects shall be a constexpr constructor;
+ for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
+ BEnd = ClassDecl->bases_end();
+ B != BEnd; ++B) {
+ const RecordType *BaseType = B->getType()->getAs<RecordType>();
+ if (!BaseType) continue;
- if (HadError) {
- CD->setInvalidDecl();
- return;
+ CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
+ if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, ConstArg))
+ return false;
}
- if (ShouldDeleteSpecialMember(CD, CXXDefaultConstructor)) {
- if (First) {
- CD->setDeletedAsWritten();
- } else {
- Diag(CD->getLocation(), diag::err_out_of_line_default_deletes)
- << CXXDefaultConstructor;
- CD->setInvalidDecl();
+ // -- every constructor involved in initializing non-static data members
+ // [...] shall be a constexpr constructor;
+ // -- every non-static data member and base class sub-object shall be
+ // initialized
+ for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
+ FEnd = ClassDecl->field_end();
+ F != FEnd; ++F) {
+ if (F->isInvalidDecl())
+ continue;
+ if (const RecordType *RecordTy =
+ S.Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
+ CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
+ if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM, ConstArg))
+ return false;
}
}
-}
-
-void Sema::CheckExplicitlyDefaultedCopyConstructor(CXXConstructorDecl *CD) {
- assert(CD->isExplicitlyDefaulted() && CD->isCopyConstructor());
-
- // Whether this was the first-declared instance of the constructor.
- bool First = CD == CD->getCanonicalDecl();
-
- bool HadError = false;
- if (CD->getNumParams() != 1) {
- Diag(CD->getLocation(), diag::err_defaulted_copy_ctor_params)
- << CD->getSourceRange();
- HadError = true;
- }
-
- ImplicitExceptionSpecification Spec(*this);
- bool Const;
- llvm::tie(Spec, Const) =
- ComputeDefaultedCopyCtorExceptionSpecAndConst(CD->getParent());
-
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
- *ExceptionType = Context.getFunctionType(
- Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
- // Check for parameter type matching.
- // This is a copy ctor so we know it's a cv-qualified reference to T.
- QualType ArgType = CtorType->getArgType(0);
- if (ArgType->getPointeeType().isVolatileQualified()) {
- Diag(CD->getLocation(), diag::err_defaulted_copy_ctor_volatile_param);
- HadError = true;
- }
- if (ArgType->getPointeeType().isConstQualified() && !Const) {
- Diag(CD->getLocation(), diag::err_defaulted_copy_ctor_const_param);
- HadError = true;
- }
+ // All OK, it's constexpr!
+ return true;
+}
- // C++11 [dcl.fct.def.default]p2:
- // An explicitly-defaulted function may be declared constexpr only if it
- // would have been implicitly declared as constexpr,
- // Do not apply this rule to templates, since core issue 1358 makes such
- // functions always instantiate to constexpr functions.
- if (CD->isConstexpr() &&
- CD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
- if (!CD->getParent()->defaultedCopyConstructorIsConstexpr()) {
- Diag(CD->getLocStart(), diag::err_incorrect_defaulted_constexpr)
- << CXXCopyConstructor;
- HadError = true;
- }
- }
- // and may have an explicit exception-specification only if it is compatible
- // with the exception-specification on the implicit declaration.
- if (CtorType->hasExceptionSpec()) {
- if (CheckEquivalentExceptionSpec(
- PDiag(diag::err_incorrect_defaulted_exception_spec)
- << CXXCopyConstructor,
- PDiag(),
- ExceptionType, SourceLocation(),
- CtorType, CD->getLocation())) {
- HadError = true;
- }
+static Sema::ImplicitExceptionSpecification
+computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, CXXMethodDecl *MD) {
+ switch (S.getSpecialMember(MD)) {
+ case Sema::CXXDefaultConstructor:
+ return S.ComputeDefaultedDefaultCtorExceptionSpec(Loc, MD);
+ case Sema::CXXCopyConstructor:
+ return S.ComputeDefaultedCopyCtorExceptionSpec(MD);
+ case Sema::CXXCopyAssignment:
+ return S.ComputeDefaultedCopyAssignmentExceptionSpec(MD);
+ case Sema::CXXMoveConstructor:
+ return S.ComputeDefaultedMoveCtorExceptionSpec(MD);
+ case Sema::CXXMoveAssignment:
+ return S.ComputeDefaultedMoveAssignmentExceptionSpec(MD);
+ case Sema::CXXDestructor:
+ return S.ComputeDefaultedDtorExceptionSpec(MD);
+ case Sema::CXXInvalid:
+ break;
}
+ llvm_unreachable("only special members have implicit exception specs");
+}
- // If a function is explicitly defaulted on its first declaration,
- if (First) {
- // -- it is implicitly considered to be constexpr if the implicit
- // definition would be,
- CD->setConstexpr(CD->getParent()->defaultedCopyConstructorIsConstexpr());
+static void
+updateExceptionSpec(Sema &S, FunctionDecl *FD, const FunctionProtoType *FPT,
+ const Sema::ImplicitExceptionSpecification &ExceptSpec) {
+ FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
+ ExceptSpec.getEPI(EPI);
+ const FunctionProtoType *NewFPT = cast<FunctionProtoType>(
+ S.Context.getFunctionType(FPT->getResultType(), FPT->arg_type_begin(),
+ FPT->getNumArgs(), EPI));
+ FD->setType(QualType(NewFPT, 0));
+}
+
+void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) {
+ const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
+ if (FPT->getExceptionSpecType() != EST_Unevaluated)
+ return;
- // -- it is implicitly considered to have the same
- // exception-specification as if it had been implicitly declared, and
- //
- // FIXME: a compatible, but different, explicit exception specification
- // will be silently overridden. We should issue a warning if this happens.
- EPI.ExtInfo = CtorType->getExtInfo();
+ // Evaluate the exception specification.
+ ImplicitExceptionSpecification ExceptSpec =
+ computeImplicitExceptionSpec(*this, Loc, MD);
- // -- [...] it shall have the same parameter type as if it had been
- // implicitly declared.
- CD->setType(Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI));
+ // Update the type of the special member to use it.
+ updateExceptionSpec(*this, MD, FPT, ExceptSpec);
- // Such a function is also trivial if the implicitly-declared function
- // would have been.
- CD->setTrivial(CD->getParent()->hasTrivialCopyConstructor());
- }
+ // A user-provided destructor can be defined outside the class. When that
+ // happens, be sure to update the exception specification on both
+ // declarations.
+ const FunctionProtoType *CanonicalFPT =
+ MD->getCanonicalDecl()->getType()->castAs<FunctionProtoType>();
+ if (CanonicalFPT->getExceptionSpecType() == EST_Unevaluated)
+ updateExceptionSpec(*this, MD->getCanonicalDecl(),
+ CanonicalFPT, ExceptSpec);
+}
- if (HadError) {
- CD->setInvalidDecl();
- return;
- }
+static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl);
+static bool isImplicitCopyAssignmentArgConst(Sema &S, CXXRecordDecl *ClassDecl);
- if (ShouldDeleteSpecialMember(CD, CXXCopyConstructor)) {
- if (First) {
- CD->setDeletedAsWritten();
- } else {
- Diag(CD->getLocation(), diag::err_out_of_line_default_deletes)
- << CXXCopyConstructor;
- CD->setInvalidDecl();
- }
- }
-}
+void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
+ CXXRecordDecl *RD = MD->getParent();
+ CXXSpecialMember CSM = getSpecialMember(MD);
-void Sema::CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *MD) {
- assert(MD->isExplicitlyDefaulted());
+ assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid &&
+ "not an explicitly-defaulted special member");
- // Whether this was the first-declared instance of the operator
+ // Whether this was the first-declared instance of the constructor.
+ // This affects whether we implicitly add an exception spec and constexpr.
bool First = MD == MD->getCanonicalDecl();
bool HadError = false;
- if (MD->getNumParams() != 1) {
- Diag(MD->getLocation(), diag::err_defaulted_copy_assign_params)
- << MD->getSourceRange();
- HadError = true;
- }
- QualType ReturnType =
- MD->getType()->getAs<FunctionType>()->getResultType();
- if (!ReturnType->isLValueReferenceType() ||
- !Context.hasSameType(
- Context.getCanonicalType(ReturnType->getPointeeType()),
- Context.getCanonicalType(Context.getTypeDeclType(MD->getParent())))) {
- Diag(MD->getLocation(), diag::err_defaulted_copy_assign_return_type);
+ // C++11 [dcl.fct.def.default]p1:
+ // A function that is explicitly defaulted shall
+ // -- be a special member function (checked elsewhere),
+ // -- have the same type (except for ref-qualifiers, and except that a
+ // copy operation can take a non-const reference) as an implicit
+ // declaration, and
+ // -- not have default arguments.
+ unsigned ExpectedParams = 1;
+ if (CSM == CXXDefaultConstructor || CSM == CXXDestructor)
+ ExpectedParams = 0;
+ if (MD->getNumParams() != ExpectedParams) {
+ // This also checks for default arguments: a copy or move constructor with a
+ // default argument is classified as a default constructor, and assignment
+ // operations and destructors can't have default arguments.
+ Diag(MD->getLocation(), diag::err_defaulted_special_member_params)
+ << CSM << MD->getSourceRange();
HadError = true;
}
- ImplicitExceptionSpecification Spec(*this);
- bool Const;
- llvm::tie(Spec, Const) =
- ComputeDefaultedCopyCtorExceptionSpecAndConst(MD->getParent());
-
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- const FunctionProtoType *OperType = MD->getType()->getAs<FunctionProtoType>(),
- *ExceptionType = Context.getFunctionType(
- Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
+ const FunctionProtoType *Type = MD->getType()->getAs<FunctionProtoType>();
- QualType ArgType = OperType->getArgType(0);
- if (!ArgType->isLValueReferenceType()) {
- Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref);
- HadError = true;
- } else {
- if (ArgType->getPointeeType().isVolatileQualified()) {
- Diag(MD->getLocation(), diag::err_defaulted_copy_assign_volatile_param);
+ // Compute argument constness, constexpr, and triviality.
+ bool CanHaveConstParam = false;
+ bool Trivial;
+ switch (CSM) {
+ case CXXDefaultConstructor:
+ Trivial = RD->hasTrivialDefaultConstructor();
+ break;
+ case CXXCopyConstructor:
+ CanHaveConstParam = isImplicitCopyCtorArgConst(*this, RD);
+ Trivial = RD->hasTrivialCopyConstructor();
+ break;
+ case CXXCopyAssignment:
+ CanHaveConstParam = isImplicitCopyAssignmentArgConst(*this, RD);
+ Trivial = RD->hasTrivialCopyAssignment();
+ break;
+ case CXXMoveConstructor:
+ Trivial = RD->hasTrivialMoveConstructor();
+ break;
+ case CXXMoveAssignment:
+ Trivial = RD->hasTrivialMoveAssignment();
+ break;
+ case CXXDestructor:
+ Trivial = RD->hasTrivialDestructor();
+ break;
+ case CXXInvalid:
+ llvm_unreachable("non-special member explicitly defaulted!");
+ }
+
+ QualType ReturnType = Context.VoidTy;
+ if (CSM == CXXCopyAssignment || CSM == CXXMoveAssignment) {
+ // Check for return type matching.
+ ReturnType = Type->getResultType();
+ QualType ExpectedReturnType =
+ Context.getLValueReferenceType(Context.getTypeDeclType(RD));
+ if (!Context.hasSameType(ReturnType, ExpectedReturnType)) {
+ Diag(MD->getLocation(), diag::err_defaulted_special_member_return_type)
+ << (CSM == CXXMoveAssignment) << ExpectedReturnType;
HadError = true;
}
- if (ArgType->getPointeeType().isConstQualified() && !Const) {
- Diag(MD->getLocation(), diag::err_defaulted_copy_assign_const_param);
+
+ // A defaulted special member cannot have cv-qualifiers.
+ if (Type->getTypeQuals()) {
+ Diag(MD->getLocation(), diag::err_defaulted_special_member_quals)
+ << (CSM == CXXMoveAssignment);
HadError = true;
}
}
- if (OperType->getTypeQuals()) {
- Diag(MD->getLocation(), diag::err_defaulted_copy_assign_quals);
- HadError = true;
- }
-
- if (OperType->hasExceptionSpec()) {
- if (CheckEquivalentExceptionSpec(
- PDiag(diag::err_incorrect_defaulted_exception_spec)
- << CXXCopyAssignment,
- PDiag(),
- ExceptionType, SourceLocation(),
- OperType, MD->getLocation())) {
+ // Check for parameter type matching.
+ QualType ArgType = ExpectedParams ? Type->getArgType(0) : QualType();
+ bool HasConstParam = false;
+ if (ExpectedParams && ArgType->isReferenceType()) {
+ // Argument must be reference to possibly-const T.
+ QualType ReferentType = ArgType->getPointeeType();
+ HasConstParam = ReferentType.isConstQualified();
+
+ if (ReferentType.isVolatileQualified()) {
+ Diag(MD->getLocation(),
+ diag::err_defaulted_special_member_volatile_param) << CSM;
HadError = true;
}
- }
- if (First) {
- // We set the declaration to have the computed exception spec here.
- // We duplicate the one parameter type.
- EPI.RefQualifier = OperType->getRefQualifier();
- EPI.ExtInfo = OperType->getExtInfo();
- MD->setType(Context.getFunctionType(ReturnType, &ArgType, 1, EPI));
- // Such a function is also trivial if the implicitly-declared function
- // would have been.
- MD->setTrivial(MD->getParent()->hasTrivialCopyAssignment());
- }
-
- if (HadError) {
- MD->setInvalidDecl();
- return;
- }
-
- if (ShouldDeleteSpecialMember(MD, CXXCopyAssignment)) {
- if (First) {
- MD->setDeletedAsWritten();
- } else {
- Diag(MD->getLocation(), diag::err_out_of_line_default_deletes)
- << CXXCopyAssignment;
- MD->setInvalidDecl();
+ if (HasConstParam && !CanHaveConstParam) {
+ if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment) {
+ Diag(MD->getLocation(),
+ diag::err_defaulted_special_member_copy_const_param)
+ << (CSM == CXXCopyAssignment);
+ // FIXME: Explain why this special member can't be const.
+ } else {
+ Diag(MD->getLocation(),
+ diag::err_defaulted_special_member_move_const_param)
+ << (CSM == CXXMoveAssignment);
+ }
+ HadError = true;
}
- }
-}
-
-void Sema::CheckExplicitlyDefaultedMoveConstructor(CXXConstructorDecl *CD) {
- assert(CD->isExplicitlyDefaulted() && CD->isMoveConstructor());
- // Whether this was the first-declared instance of the constructor.
- bool First = CD == CD->getCanonicalDecl();
-
- bool HadError = false;
- if (CD->getNumParams() != 1) {
- Diag(CD->getLocation(), diag::err_defaulted_move_ctor_params)
- << CD->getSourceRange();
+ // If a function is explicitly defaulted on its first declaration, it shall
+ // have the same parameter type as if it had been implicitly declared.
+ // (Presumably this is to prevent it from being trivial?)
+ if (!HasConstParam && CanHaveConstParam && First)
+ Diag(MD->getLocation(),
+ diag::err_defaulted_special_member_copy_non_const_param)
+ << (CSM == CXXCopyAssignment);
+ } else if (ExpectedParams) {
+ // A copy assignment operator can take its argument by value, but a
+ // defaulted one cannot.
+ assert(CSM == CXXCopyAssignment && "unexpected non-ref argument");
+ Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref);
HadError = true;
}
- ImplicitExceptionSpecification Spec(
- ComputeDefaultedMoveCtorExceptionSpec(CD->getParent()));
-
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
- *ExceptionType = Context.getFunctionType(
- Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
-
- // Check for parameter type matching.
- // This is a move ctor so we know it's a cv-qualified rvalue reference to T.
- QualType ArgType = CtorType->getArgType(0);
- if (ArgType->getPointeeType().isVolatileQualified()) {
- Diag(CD->getLocation(), diag::err_defaulted_move_ctor_volatile_param);
- HadError = true;
- }
- if (ArgType->getPointeeType().isConstQualified()) {
- Diag(CD->getLocation(), diag::err_defaulted_move_ctor_const_param);
- HadError = true;
+ // Rebuild the type with the implicit exception specification added, if we
+ // are going to need it.
+ const FunctionProtoType *ImplicitType = 0;
+ if (First || Type->hasExceptionSpec()) {
+ FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo();
+ computeImplicitExceptionSpec(*this, MD->getLocation(), MD).getEPI(EPI);
+ ImplicitType = cast<FunctionProtoType>(
+ Context.getFunctionType(ReturnType, &ArgType, ExpectedParams, EPI));
}
// C++11 [dcl.fct.def.default]p2:
// An explicitly-defaulted function may be declared constexpr only if it
// would have been implicitly declared as constexpr,
- // Do not apply this rule to templates, since core issue 1358 makes such
- // functions always instantiate to constexpr functions.
- if (CD->isConstexpr() &&
- CD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
- if (!CD->getParent()->defaultedMoveConstructorIsConstexpr()) {
- Diag(CD->getLocStart(), diag::err_incorrect_defaulted_constexpr)
- << CXXMoveConstructor;
- HadError = true;
- }
+ // Do not apply this rule to members of class templates, since core issue 1358
+ // makes such functions always instantiate to constexpr functions. For
+ // non-constructors, this is checked elsewhere.
+ bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM,
+ HasConstParam);
+ if (isa<CXXConstructorDecl>(MD) && MD->isConstexpr() && !Constexpr &&
+ MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
+ Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM;
+ // FIXME: Explain why the constructor can't be constexpr.
+ HadError = true;
}
// and may have an explicit exception-specification only if it is compatible
// with the exception-specification on the implicit declaration.
- if (CtorType->hasExceptionSpec()) {
- if (CheckEquivalentExceptionSpec(
- PDiag(diag::err_incorrect_defaulted_exception_spec)
- << CXXMoveConstructor,
- PDiag(),
- ExceptionType, SourceLocation(),
- CtorType, CD->getLocation())) {
- HadError = true;
- }
- }
+ if (Type->hasExceptionSpec() &&
+ CheckEquivalentExceptionSpec(
+ PDiag(diag::err_incorrect_defaulted_exception_spec) << CSM,
+ PDiag(), ImplicitType, SourceLocation(), Type, MD->getLocation()))
+ HadError = true;
// If a function is explicitly defaulted on its first declaration,
if (First) {
// -- it is implicitly considered to be constexpr if the implicit
// definition would be,
- CD->setConstexpr(CD->getParent()->defaultedMoveConstructorIsConstexpr());
-
- // -- it is implicitly considered to have the same
- // exception-specification as if it had been implicitly declared, and
- //
- // FIXME: a compatible, but different, explicit exception specification
- // will be silently overridden. We should issue a warning if this happens.
- EPI.ExtInfo = CtorType->getExtInfo();
+ MD->setConstexpr(Constexpr);
- // -- [...] it shall have the same parameter type as if it had been
- // implicitly declared.
- CD->setType(Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI));
+ // -- it is implicitly considered to have the same exception-specification
+ // as if it had been implicitly declared,
+ MD->setType(QualType(ImplicitType, 0));
// Such a function is also trivial if the implicitly-declared function
// would have been.
- CD->setTrivial(CD->getParent()->hasTrivialMoveConstructor());
+ MD->setTrivial(Trivial);
}
- if (HadError) {
- CD->setInvalidDecl();
- return;
- }
-
- if (ShouldDeleteSpecialMember(CD, CXXMoveConstructor)) {
+ if (ShouldDeleteSpecialMember(MD, CSM)) {
if (First) {
- CD->setDeletedAsWritten();
+ MD->setDeletedAsWritten();
} else {
- Diag(CD->getLocation(), diag::err_out_of_line_default_deletes)
- << CXXMoveConstructor;
- CD->setInvalidDecl();
- }
- }
-}
-
-void Sema::CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *MD) {
- assert(MD->isExplicitlyDefaulted());
-
- // Whether this was the first-declared instance of the operator
- bool First = MD == MD->getCanonicalDecl();
-
- bool HadError = false;
- if (MD->getNumParams() != 1) {
- Diag(MD->getLocation(), diag::err_defaulted_move_assign_params)
- << MD->getSourceRange();
- HadError = true;
- }
-
- QualType ReturnType =
- MD->getType()->getAs<FunctionType>()->getResultType();
- if (!ReturnType->isLValueReferenceType() ||
- !Context.hasSameType(
- Context.getCanonicalType(ReturnType->getPointeeType()),
- Context.getCanonicalType(Context.getTypeDeclType(MD->getParent())))) {
- Diag(MD->getLocation(), diag::err_defaulted_move_assign_return_type);
- HadError = true;
- }
-
- ImplicitExceptionSpecification Spec(
- ComputeDefaultedMoveCtorExceptionSpec(MD->getParent()));
-
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- const FunctionProtoType *OperType = MD->getType()->getAs<FunctionProtoType>(),
- *ExceptionType = Context.getFunctionType(
- Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
-
- QualType ArgType = OperType->getArgType(0);
- if (!ArgType->isRValueReferenceType()) {
- Diag(MD->getLocation(), diag::err_defaulted_move_assign_not_ref);
- HadError = true;
- } else {
- if (ArgType->getPointeeType().isVolatileQualified()) {
- Diag(MD->getLocation(), diag::err_defaulted_move_assign_volatile_param);
- HadError = true;
- }
- if (ArgType->getPointeeType().isConstQualified()) {
- Diag(MD->getLocation(), diag::err_defaulted_move_assign_const_param);
+ // C++11 [dcl.fct.def.default]p4:
+ // [For a] user-provided explicitly-defaulted function [...] if such a
+ // function is implicitly defined as deleted, the program is ill-formed.
+ Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) << CSM;
HadError = true;
}
}
- if (OperType->getTypeQuals()) {
- Diag(MD->getLocation(), diag::err_defaulted_move_assign_quals);
- HadError = true;
- }
-
- if (OperType->hasExceptionSpec()) {
- if (CheckEquivalentExceptionSpec(
- PDiag(diag::err_incorrect_defaulted_exception_spec)
- << CXXMoveAssignment,
- PDiag(),
- ExceptionType, SourceLocation(),
- OperType, MD->getLocation())) {
- HadError = true;
- }
- }
- if (First) {
- // We set the declaration to have the computed exception spec here.
- // We duplicate the one parameter type.
- EPI.RefQualifier = OperType->getRefQualifier();
- EPI.ExtInfo = OperType->getExtInfo();
- MD->setType(Context.getFunctionType(ReturnType, &ArgType, 1, EPI));
-
- // Such a function is also trivial if the implicitly-declared function
- // would have been.
- MD->setTrivial(MD->getParent()->hasTrivialMoveAssignment());
- }
-
- if (HadError) {
+ if (HadError)
MD->setInvalidDecl();
- return;
- }
-
- if (ShouldDeleteSpecialMember(MD, CXXMoveAssignment)) {
- if (First) {
- MD->setDeletedAsWritten();
- } else {
- Diag(MD->getLocation(), diag::err_out_of_line_default_deletes)
- << CXXMoveAssignment;
- MD->setInvalidDecl();
- }
- }
-}
-
-void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) {
- assert(DD->isExplicitlyDefaulted());
-
- // Whether this was the first-declared instance of the destructor.
- bool First = DD == DD->getCanonicalDecl();
-
- ImplicitExceptionSpecification Spec
- = ComputeDefaultedDtorExceptionSpec(DD->getParent());
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- const FunctionProtoType *DtorType = DD->getType()->getAs<FunctionProtoType>(),
- *ExceptionType = Context.getFunctionType(
- Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
-
- if (DtorType->hasExceptionSpec()) {
- if (CheckEquivalentExceptionSpec(
- PDiag(diag::err_incorrect_defaulted_exception_spec)
- << CXXDestructor,
- PDiag(),
- ExceptionType, SourceLocation(),
- DtorType, DD->getLocation())) {
- DD->setInvalidDecl();
- return;
- }
- }
- if (First) {
- // We set the declaration to have the computed exception spec here.
- // There are no parameters.
- EPI.ExtInfo = DtorType->getExtInfo();
- DD->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
-
- // Such a function is also trivial if the implicitly-declared function
- // would have been.
- DD->setTrivial(DD->getParent()->hasTrivialDestructor());
- }
-
- if (ShouldDeleteSpecialMember(DD, CXXDestructor)) {
- if (First) {
- DD->setDeletedAsWritten();
- } else {
- Diag(DD->getLocation(), diag::err_out_of_line_default_deletes)
- << CXXDestructor;
- DD->setInvalidDecl();
- }
- }
}
namespace {
@@ -4385,9 +4267,15 @@ struct SpecialMemberDeletionInfo {
bool inUnion() const { return MD->getParent()->isUnion(); }
/// Look up the corresponding special member in the given class.
- Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class) {
+ Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class,
+ unsigned Quals) {
unsigned TQ = MD->getTypeQualifiers();
- return S.LookupSpecialMember(Class, CSM, ConstArg, VolatileArg,
+ // cv-qualifiers on class members don't affect default ctor / dtor calls.
+ if (CSM == Sema::CXXDefaultConstructor || CSM == Sema::CXXDestructor)
+ Quals = 0;
+ return S.LookupSpecialMember(Class, CSM,
+ ConstArg || (Quals & Qualifiers::Const),
+ VolatileArg || (Quals & Qualifiers::Volatile),
MD->getRefQualifier() == RQ_RValue,
TQ & Qualifiers::Const,
TQ & Qualifiers::Volatile);
@@ -4399,7 +4287,8 @@ struct SpecialMemberDeletionInfo {
bool shouldDeleteForField(FieldDecl *FD);
bool shouldDeleteForAllConstMembers();
- bool shouldDeleteForClassSubobject(CXXRecordDecl *Class, Subobject Subobj);
+ bool shouldDeleteForClassSubobject(CXXRecordDecl *Class, Subobject Subobj,
+ unsigned Quals);
bool shouldDeleteForSubobjectCall(Subobject Subobj,
Sema::SpecialMemberOverloadResult *SMOR,
bool IsDtorCallInCtor);
@@ -4480,9 +4369,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
}
/// Check whether we should delete a special member function due to having a
-/// direct or virtual base class or static data member of class type M.
+/// direct or virtual base class or non-static data member of class type M.
bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
- CXXRecordDecl *Class, Subobject Subobj) {
+ CXXRecordDecl *Class, Subobject Subobj, unsigned Quals) {
FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>();
// C++11 [class.ctor]p5:
@@ -4501,7 +4390,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
// that is deleted or inaccessible
if (!(CSM == Sema::CXXDefaultConstructor &&
Field && Field->hasInClassInitializer()) &&
- shouldDeleteForSubobjectCall(Subobj, lookupIn(Class), false))
+ shouldDeleteForSubobjectCall(Subobj, lookupIn(Class, Quals), false))
return true;
// C++11 [class.ctor]p5, C++11 [class.copy]p11:
@@ -4522,7 +4411,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
/// having a particular direct or virtual base class.
bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) {
CXXRecordDecl *BaseClass = Base->getType()->getAsCXXRecordDecl();
- return shouldDeleteForClassSubobject(BaseClass, Base);
+ return shouldDeleteForClassSubobject(BaseClass, Base, 0);
}
/// Check whether we should delete a special member function due to the class
@@ -4549,7 +4438,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
(!FieldRecord || !FieldRecord->hasUserProvidedDefaultConstructor())) {
if (Diagnose)
S.Diag(FD->getLocation(), diag::note_deleted_default_ctor_uninit_field)
- << MD->getParent() << FD << FieldType << /*Const*/1;
+ << MD->getParent() << FD << FD->getType() << /*Const*/1;
return true;
}
@@ -4577,7 +4466,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
// -- a non-static data member of const non-class type (or array thereof)
if (Diagnose)
S.Diag(FD->getLocation(), diag::note_deleted_assign_field)
- << IsMove << MD->getParent() << FD << FieldType << /*Const*/1;
+ << IsMove << MD->getParent() << FD << FD->getType() << /*Const*/1;
return true;
}
}
@@ -4599,7 +4488,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
CXXRecordDecl *UnionFieldRecord = UnionFieldType->getAsCXXRecordDecl();
if (UnionFieldRecord &&
- shouldDeleteForClassSubobject(UnionFieldRecord, *UI))
+ shouldDeleteForClassSubobject(UnionFieldRecord, *UI,
+ UnionFieldType.getCVRQualifiers()))
return true;
}
@@ -4618,7 +4508,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
return false;
}
- if (shouldDeleteForClassSubobject(FieldRecord, FD))
+ if (shouldDeleteForClassSubobject(FieldRecord, FD,
+ FieldType.getCVRQualifiers()))
return true;
}
@@ -4647,7 +4538,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() {
/// C++11 [class.copy]p23, and C++11 [class.dtor]p5.
bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,
bool Diagnose) {
- assert(!MD->isInvalidDecl());
+ if (MD->isInvalidDecl())
+ return false;
CXXRecordDecl *RD = MD->getParent();
assert(!RD->isDependentType() && "do deletion after instantiation");
if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl())
@@ -4803,7 +4695,7 @@ void Sema::DiagnoseHiddenVirtualMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
if (Diags.getDiagnosticLevel(diag::warn_overloaded_virtual,
MD->getLocation()) == DiagnosticsEngine::Ignored)
return;
- if (MD->getDeclName().getNameKind() != DeclarationName::Identifier)
+ if (!MD->getDeclName().isIdentifier())
return;
CXXBasePaths Paths(/*FindAmbiguities=*/true, // true to look in all bases.
@@ -4850,6 +4742,14 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
AdjustDeclIfTemplate(TagDecl);
+ for (const AttributeList* l = AttrList; l; l = l->getNext()) {
+ if (l->getKind() != AttributeList::AT_Visibility)
+ continue;
+ l->setInvalid();
+ Diag(l->getLoc(), diag::warn_attribute_after_definition_ignored) <<
+ l->getName();
+ }
+
ActOnFields(S, RLoc, TagDecl, llvm::makeArrayRef(
// strict aliasing violation!
reinterpret_cast<Decl**>(FieldCollector->getCurFields()),
@@ -5572,6 +5472,8 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
}
}
+ ActOnDocumentableDecl(Namespc);
+
// Although we could have an invalid decl (i.e. the namespace name is a
// redefinition), push it as current DeclContext and try to continue parsing.
// FIXME: We should be able to push Namespc here, so that the each DeclContext
@@ -6734,6 +6636,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S,
if (!Redeclaration)
PushOnScopeChains(NewND, S);
+ ActOnDocumentableDecl(NewND);
return NewND;
}
@@ -6816,7 +6719,10 @@ namespace {
}
Sema::ImplicitExceptionSpecification
-Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
+Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc,
+ CXXMethodDecl *MD) {
+ CXXRecordDecl *ClassDecl = MD->getParent();
+
// C++ [except.spec]p14:
// An implicitly declared special member function (Clause 12) shall have an
// exception-specification. [...]
@@ -6863,7 +6769,21 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
if (Expr *E = F->getInClassInitializer())
ExceptSpec.CalledExpr(E);
else if (!F->isInvalidDecl())
- ExceptSpec.SetDelayed();
+ // DR1351:
+ // If the brace-or-equal-initializer of a non-static data member
+ // invokes a defaulted default constructor of its class or of an
+ // enclosing class in a potentially evaluated subexpression, the
+ // program is ill-formed.
+ //
+ // This resolution is unworkable: the exception specification of the
+ // default constructor can be needed in an unevaluated context, in
+ // particular, in the operand of a noexcept-expression, and we can be
+ // unable to compute an exception specification for an enclosed class.
+ //
+ // We do not allow an in-class initializer to require the evaluation
+ // of the exception specification for any in-class initializer whose
+ // definition is not lexically complete.
+ Diag(Loc, diag::err_in_class_initializer_references_def_ctor) << MD;
} else if (const RecordType *RecordTy
= Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
@@ -6892,9 +6812,9 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
assert(!ClassDecl->hasUserDeclaredConstructor() &&
"Should not build implicit default constructor!");
- ImplicitExceptionSpecification Spec =
- ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl);
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
+ bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
+ CXXDefaultConstructor,
+ false);
// Create the actual constructor declaration.
CanQualType ClassType
@@ -6904,16 +6824,20 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
= Context.DeclarationNames.getCXXConstructorName(ClassType);
DeclarationNameInfo NameInfo(Name, ClassLoc);
CXXConstructorDecl *DefaultCon = CXXConstructorDecl::Create(
- Context, ClassDecl, ClassLoc, NameInfo,
- Context.getFunctionType(Context.VoidTy, 0, 0, EPI), /*TInfo=*/0,
+ Context, ClassDecl, ClassLoc, NameInfo, /*Type*/QualType(), /*TInfo=*/0,
/*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true,
- /*isConstexpr=*/ClassDecl->defaultedDefaultConstructorIsConstexpr() &&
- getLangOpts().CPlusPlus0x);
+ Constexpr);
DefaultCon->setAccess(AS_public);
DefaultCon->setDefaulted();
DefaultCon->setImplicit();
DefaultCon->setTrivial(ClassDecl->hasTrivialDefaultConstructor());
-
+
+ // Build an exception specification pointing back at this constructor.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = DefaultCon;
+ DefaultCon->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
+
// Note that we have declared this constructor.
++ASTContext::NumImplicitDefaultConstructorsDeclared;
@@ -6948,7 +6872,7 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
}
SourceLocation Loc = Constructor->getLocation();
- Constructor->setBody(new (Context) CompoundStmt(Context, 0, 0, Loc, Loc));
+ Constructor->setBody(new (Context) CompoundStmt(Loc));
Constructor->setUsed();
MarkVTableUsed(CurrentLocation, ClassDecl);
@@ -6958,58 +6882,14 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
}
}
-/// Get any existing defaulted default constructor for the given class. Do not
-/// implicitly define one if it does not exist.
-static CXXConstructorDecl *getDefaultedDefaultConstructorUnsafe(Sema &Self,
- CXXRecordDecl *D) {
- ASTContext &Context = Self.Context;
- QualType ClassType = Context.getTypeDeclType(D);
- DeclarationName ConstructorName
- = Context.DeclarationNames.getCXXConstructorName(
- Context.getCanonicalType(ClassType.getUnqualifiedType()));
-
- DeclContext::lookup_const_iterator Con, ConEnd;
- for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName);
- Con != ConEnd; ++Con) {
- // A function template cannot be defaulted.
- if (isa<FunctionTemplateDecl>(*Con))
- continue;
-
- CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con);
- if (Constructor->isDefaultConstructor())
- return Constructor->isDefaulted() ? Constructor : 0;
- }
- return 0;
-}
-
void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
if (!D) return;
AdjustDeclIfTemplate(D);
CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(D);
- CXXConstructorDecl *CtorDecl
- = getDefaultedDefaultConstructorUnsafe(*this, ClassDecl);
-
- if (!CtorDecl) return;
-
- // Compute the exception specification for the default constructor.
- const FunctionProtoType *CtorTy =
- CtorDecl->getType()->castAs<FunctionProtoType>();
- if (CtorTy->getExceptionSpecType() == EST_Delayed) {
- // FIXME: Don't do this unless the exception spec is needed.
- ImplicitExceptionSpecification Spec =
- ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl);
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
- assert(EPI.ExceptionSpecType != EST_Delayed);
- CtorDecl->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
- }
-
- // If the default constructor is explicitly defaulted, checking the exception
- // specification is deferred until now.
- if (!CtorDecl->isInvalidDecl() && CtorDecl->isExplicitlyDefaulted() &&
- !ClassDecl->isDependentType())
- CheckExplicitlyDefaultedDefaultConstructor(CtorDecl);
+ if (!ClassDecl->isDependentType())
+ CheckExplicitlyDefaultedMethods(ClassDecl);
}
void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
@@ -7193,7 +7073,9 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
}
Sema::ImplicitExceptionSpecification
-Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) {
+Sema::ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD) {
+ CXXRecordDecl *ClassDecl = MD->getParent();
+
// C++ [except.spec]p14:
// An implicitly declared special member function (Clause 12) shall have
// an exception-specification.
@@ -7240,14 +7122,8 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
// If a class has no user-declared destructor, a destructor is
// declared implicitly. An implicitly-declared destructor is an
// inline public member of its class.
-
- ImplicitExceptionSpecification Spec =
- ComputeDefaultedDtorExceptionSpec(ClassDecl);
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
// Create the actual destructor declaration.
- QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI);
-
CanQualType ClassType
= Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
SourceLocation ClassLoc = ClassDecl->getLocation();
@@ -7255,24 +7131,27 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
= Context.DeclarationNames.getCXXDestructorName(ClassType);
DeclarationNameInfo NameInfo(Name, ClassLoc);
CXXDestructorDecl *Destructor
- = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0,
- /*isInline=*/true,
+ = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo,
+ QualType(), 0, /*isInline=*/true,
/*isImplicitlyDeclared=*/true);
Destructor->setAccess(AS_public);
Destructor->setDefaulted();
Destructor->setImplicit();
Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
-
+
+ // Build an exception specification pointing back at this destructor.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = Destructor;
+ Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
+
// Note that we have declared this destructor.
++ASTContext::NumImplicitDestructorsDeclared;
-
+
// Introduce this destructor into its scope.
if (Scope *S = getScopeForContext(ClassDecl))
PushOnScopeChains(Destructor, S, false);
ClassDecl->addDecl(Destructor);
-
- // This could be uniqued if it ever proves significant.
- Destructor->setTypeSourceInfo(Context.getTrivialTypeSourceInfo(Ty));
AddOverriddenMethods(ClassDecl, Destructor);
@@ -7309,7 +7188,7 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
}
SourceLocation Loc = Destructor->getLocation();
- Destructor->setBody(new (Context) CompoundStmt(Context, 0, 0, Loc, Loc));
+ Destructor->setBody(new (Context) CompoundStmt(Loc));
Destructor->setImplicitlyDefined(true);
Destructor->setUsed();
MarkVTableUsed(CurrentLocation, ClassDecl);
@@ -7322,15 +7201,6 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
/// \brief Perform any semantic analysis which needs to be delayed until all
/// pending class member declarations have been parsed.
void Sema::ActOnFinishCXXMemberDecls() {
- // Now we have parsed all exception specifications, determine the implicit
- // exception specifications for destructors.
- for (unsigned i = 0, e = DelayedDestructorExceptionSpecs.size();
- i != e; ++i) {
- CXXDestructorDecl *Dtor = DelayedDestructorExceptionSpecs[i];
- AdjustDestructorExceptionSpec(Dtor->getParent(), Dtor, true);
- }
- DelayedDestructorExceptionSpecs.clear();
-
// Perform any deferred checking of exception specifications for virtual
// destructors.
for (unsigned i = 0, e = DelayedDestructorExceptionSpecChecks.size();
@@ -7345,44 +7215,33 @@ void Sema::ActOnFinishCXXMemberDecls() {
DelayedDestructorExceptionSpecChecks.clear();
}
-void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *classDecl,
- CXXDestructorDecl *destructor,
- bool WasDelayed) {
+void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl,
+ CXXDestructorDecl *Destructor) {
+ assert(getLangOpts().CPlusPlus0x &&
+ "adjusting dtor exception specs was introduced in c++11");
+
// C++11 [class.dtor]p3:
// A declaration of a destructor that does not have an exception-
// specification is implicitly considered to have the same exception-
// specification as an implicit declaration.
- const FunctionProtoType *dtorType = destructor->getType()->
+ const FunctionProtoType *DtorType = Destructor->getType()->
getAs<FunctionProtoType>();
- if (!WasDelayed && dtorType->hasExceptionSpec())
+ if (DtorType->hasExceptionSpec())
return;
- ImplicitExceptionSpecification exceptSpec =
- ComputeDefaultedDtorExceptionSpec(classDecl);
-
// Replace the destructor's type, building off the existing one. Fortunately,
// the only thing of interest in the destructor type is its extended info.
// The return and arguments are fixed.
- FunctionProtoType::ExtProtoInfo epi = dtorType->getExtProtoInfo();
- epi.ExceptionSpecType = exceptSpec.getExceptionSpecType();
- epi.NumExceptions = exceptSpec.size();
- epi.Exceptions = exceptSpec.data();
- QualType ty = Context.getFunctionType(Context.VoidTy, 0, 0, epi);
-
- destructor->setType(ty);
-
- // If we can't compute the exception specification for this destructor yet
- // (because it depends on an exception specification which we have not parsed
- // yet), make a note that we need to try again when the class is complete.
- if (epi.ExceptionSpecType == EST_Delayed) {
- assert(!WasDelayed && "couldn't compute destructor exception spec");
- DelayedDestructorExceptionSpecs.push_back(destructor);
- }
+ FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo();
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = Destructor;
+ Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
// FIXME: If the destructor has a body that could throw, and the newly created
// spec doesn't allow exceptions, we should emit a warning, because this
// change in behavior can break conforming C++03 programs at runtime.
- // However, we don't have a body yet, so it needs to be done somewhere else.
+ // However, we don't have a body or an exception specification yet, so it
+ // needs to be done somewhere else.
}
/// \brief Builds a statement that copies/moves the given entity from \p From to
@@ -7584,11 +7443,13 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T,
Loc, Copy.take());
}
-std::pair<Sema::ImplicitExceptionSpecification, bool>
-Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst(
- CXXRecordDecl *ClassDecl) {
+/// Determine whether an implicit copy assignment operator for ClassDecl has a
+/// const argument.
+/// FIXME: It ought to be possible to store this on the record.
+static bool isImplicitCopyAssignmentArgConst(Sema &S,
+ CXXRecordDecl *ClassDecl) {
if (ClassDecl->isInvalidDecl())
- return std::make_pair(ImplicitExceptionSpecification(*this), false);
+ return true;
// C++ [class.copy]p10:
// If the class definition does not explicitly declare a copy
@@ -7599,37 +7460,34 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst(
// X& X::operator=(const X&)
//
// if
- bool HasConstCopyAssignment = true;
-
// -- each direct base class B of X has a copy assignment operator
// whose parameter is of type const B&, const volatile B& or B,
// and
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
BaseEnd = ClassDecl->bases_end();
- HasConstCopyAssignment && Base != BaseEnd; ++Base) {
+ Base != BaseEnd; ++Base) {
// We'll handle this below
- if (LangOpts.CPlusPlus0x && Base->isVirtual())
+ if (S.getLangOpts().CPlusPlus0x && Base->isVirtual())
continue;
assert(!Base->getType()->isDependentType() &&
"Cannot generate implicit members for class with dependent bases.");
CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl();
- HasConstCopyAssignment &=
- (bool)LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const,
- false, 0);
+ if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, false, 0))
+ return false;
}
// In C++11, the above citation has "or virtual" added
- if (LangOpts.CPlusPlus0x) {
+ if (S.getLangOpts().CPlusPlus0x) {
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(),
BaseEnd = ClassDecl->vbases_end();
- HasConstCopyAssignment && Base != BaseEnd; ++Base) {
+ Base != BaseEnd; ++Base) {
assert(!Base->getType()->isDependentType() &&
"Cannot generate implicit members for class with dependent bases.");
CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl();
- HasConstCopyAssignment &=
- (bool)LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const,
- false, 0);
+ if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const,
+ false, 0))
+ return false;
}
}
@@ -7639,23 +7497,36 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst(
// const volatile M& or M.
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
FieldEnd = ClassDecl->field_end();
- HasConstCopyAssignment && Field != FieldEnd;
- ++Field) {
- QualType FieldType = Context.getBaseElementType((*Field)->getType());
- if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) {
- HasConstCopyAssignment &=
- (bool)LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const,
- false, 0);
- }
+ Field != FieldEnd; ++Field) {
+ QualType FieldType = S.Context.getBaseElementType(Field->getType());
+ if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl())
+ if (!S.LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const,
+ false, 0))
+ return false;
}
// Otherwise, the implicitly declared copy assignment operator will
// have the form
//
// X& X::operator=(X&)
-
+
+ return true;
+}
+
+Sema::ImplicitExceptionSpecification
+Sema::ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD) {
+ CXXRecordDecl *ClassDecl = MD->getParent();
+
+ ImplicitExceptionSpecification ExceptSpec(*this);
+ if (ClassDecl->isInvalidDecl())
+ return ExceptSpec;
+
+ const FunctionProtoType *T = MD->getType()->castAs<FunctionProtoType>();
+ assert(T->getNumArgs() == 1 && "not a copy assignment op");
+ unsigned ArgQuals = T->getArgType(0).getNonReferenceType().getCVRQualifiers();
+
// C++ [except.spec]p14:
- // An implicitly declared special member function (Clause 12) shall have an
+ // An implicitly declared special member function (Clause 12) shall have an
// exception-specification. [...]
// It is unspecified whether or not an implicit copy assignment operator
@@ -7664,8 +7535,6 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst(
// Based on a similar decision made for constness in C++0x, we're erring on
// the side of assuming such calls to be made regardless of whether they
// actually happen.
- ImplicitExceptionSpecification ExceptSpec(*this);
- unsigned ArgQuals = HasConstCopyAssignment ? Qualifiers::Const : 0;
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
BaseEnd = ClassDecl->bases_end();
Base != BaseEnd; ++Base) {
@@ -7693,15 +7562,17 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst(
FieldEnd = ClassDecl->field_end();
Field != FieldEnd;
++Field) {
- QualType FieldType = Context.getBaseElementType((*Field)->getType());
+ QualType FieldType = Context.getBaseElementType(Field->getType());
if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) {
if (CXXMethodDecl *CopyAssign =
- LookupCopyingAssignment(FieldClassDecl, ArgQuals, false, 0))
+ LookupCopyingAssignment(FieldClassDecl,
+ ArgQuals | FieldType.getCVRQualifiers(),
+ false, 0))
ExceptSpec.CalledDecl(Field->getLocation(), CopyAssign);
}
}
- return std::make_pair(ExceptSpec, HasConstCopyAssignment);
+ return ExceptSpec;
}
CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
@@ -7710,26 +7581,19 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
// for determining the argument type of the operator. Note also that
// operators taking an object instead of a reference are allowed.
- ImplicitExceptionSpecification Spec(*this);
- bool Const;
- llvm::tie(Spec, Const) =
- ComputeDefaultedCopyAssignmentExceptionSpecAndConst(ClassDecl);
-
QualType ArgType = Context.getTypeDeclType(ClassDecl);
QualType RetType = Context.getLValueReferenceType(ArgType);
- if (Const)
+ if (isImplicitCopyAssignmentArgConst(*this, ClassDecl))
ArgType = ArgType.withConst();
ArgType = Context.getLValueReferenceType(ArgType);
// An implicitly-declared copy assignment operator is an inline public
// member of its class.
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
SourceLocation ClassLoc = ClassDecl->getLocation();
DeclarationNameInfo NameInfo(Name, ClassLoc);
CXXMethodDecl *CopyAssignment
- = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo,
- Context.getFunctionType(RetType, &ArgType, 1, EPI),
+ = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
/*TInfo=*/0, /*isStatic=*/false,
/*StorageClassAsWritten=*/SC_None,
/*isInline=*/true, /*isConstexpr=*/false,
@@ -7738,7 +7602,13 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
CopyAssignment->setDefaulted();
CopyAssignment->setImplicit();
CopyAssignment->setTrivial(ClassDecl->hasTrivialCopyAssignment());
-
+
+ // Build an exception specification pointing back at this member.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = CopyAssignment;
+ CopyAssignment->setType(Context.getFunctionType(RetType, &ArgType, 1, EPI));
+
// Add the parameter to the operator.
ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment,
ClassLoc, ClassLoc, /*Id=*/0,
@@ -8076,9 +7946,10 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
}
Sema::ImplicitExceptionSpecification
-Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) {
- ImplicitExceptionSpecification ExceptSpec(*this);
+Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXMethodDecl *MD) {
+ CXXRecordDecl *ClassDecl = MD->getParent();
+ ImplicitExceptionSpecification ExceptSpec(*this);
if (ClassDecl->isInvalidDecl())
return ExceptSpec;
@@ -8103,7 +7974,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) {
CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl,
- false, 0))
+ 0, false, 0))
ExceptSpec.CalledDecl(Base->getLocStart(), MoveAssign);
}
@@ -8113,7 +7984,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) {
CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl,
- false, 0))
+ 0, false, 0))
ExceptSpec.CalledDecl(Base->getLocStart(), MoveAssign);
}
@@ -8121,10 +7992,12 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) {
FieldEnd = ClassDecl->field_end();
Field != FieldEnd;
++Field) {
- QualType FieldType = Context.getBaseElementType((*Field)->getType());
+ QualType FieldType = Context.getBaseElementType(Field->getType());
if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) {
- if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(FieldClassDecl,
- false, 0))
+ if (CXXMethodDecl *MoveAssign =
+ LookupMovingAssignment(FieldClassDecl,
+ FieldType.getCVRQualifiers(),
+ false, 0))
ExceptSpec.CalledDecl(Field->getLocation(), MoveAssign);
}
}
@@ -8167,7 +8040,7 @@ hasMoveOrIsTriviallyCopyable(Sema &S, QualType Type, bool IsConstructor) {
// reference types, are supposed to return false here, but that appears
// to be a standard defect.
CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl();
- if (!ClassDecl)
+ if (!ClassDecl || !ClassDecl->getDefinition())
return true;
if (Type.isTriviallyCopyableType(S.Context))
@@ -8209,7 +8082,7 @@ static bool subobjectsHaveMoveOrTrivialCopy(Sema &S, CXXRecordDecl *ClassDecl,
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
FieldEnd = ClassDecl->field_end();
Field != FieldEnd; ++Field) {
- if (!hasMoveOrIsTriviallyCopyable(S, (*Field)->getType(), IsConstructor))
+ if (!hasMoveOrIsTriviallyCopyable(S, Field->getType(), IsConstructor))
return false;
}
@@ -8244,22 +8117,17 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {
// Note: The following rules are largely analoguous to the move
// constructor rules.
- ImplicitExceptionSpecification Spec(
- ComputeDefaultedMoveAssignmentExceptionSpec(ClassDecl));
-
QualType ArgType = Context.getTypeDeclType(ClassDecl);
QualType RetType = Context.getLValueReferenceType(ArgType);
ArgType = Context.getRValueReferenceType(ArgType);
// An implicitly-declared move assignment operator is an inline public
// member of its class.
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
SourceLocation ClassLoc = ClassDecl->getLocation();
DeclarationNameInfo NameInfo(Name, ClassLoc);
CXXMethodDecl *MoveAssignment
- = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo,
- Context.getFunctionType(RetType, &ArgType, 1, EPI),
+ = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
/*TInfo=*/0, /*isStatic=*/false,
/*StorageClassAsWritten=*/SC_None,
/*isInline=*/true,
@@ -8270,6 +8138,12 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {
MoveAssignment->setImplicit();
MoveAssignment->setTrivial(ClassDecl->hasTrivialMoveAssignment());
+ // Build an exception specification pointing back at this member.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = MoveAssignment;
+ MoveAssignment->setType(Context.getFunctionType(RetType, &ArgType, 1, EPI));
+
// Add the parameter to the operator.
ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveAssignment,
ClassLoc, ClassLoc, /*Id=*/0,
@@ -8620,10 +8494,12 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
}
}
-std::pair<Sema::ImplicitExceptionSpecification, bool>
-Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
+/// Determine whether an implicit copy constructor for ClassDecl has a const
+/// argument.
+/// FIXME: It ought to be possible to store this on the record.
+static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl) {
if (ClassDecl->isInvalidDecl())
- return std::make_pair(ImplicitExceptionSpecification(*this), false);
+ return true;
// C++ [class.copy]p5:
// The implicitly-declared copy constructor for a class X will
@@ -8632,60 +8508,71 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
// X::X(const X&)
//
// if
- // FIXME: It ought to be possible to store this on the record.
- bool HasConstCopyConstructor = true;
-
// -- each direct or virtual base class B of X has a copy
// constructor whose first parameter is of type const B& or
// const volatile B&, and
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
BaseEnd = ClassDecl->bases_end();
- HasConstCopyConstructor && Base != BaseEnd;
- ++Base) {
+ Base != BaseEnd; ++Base) {
// Virtual bases are handled below.
if (Base->isVirtual())
continue;
-
+
CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
- HasConstCopyConstructor &=
- (bool)LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const);
+ // FIXME: This lookup is wrong. If the copy ctor for a member or base is
+ // ambiguous, we should still produce a constructor with a const-qualified
+ // parameter.
+ if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const))
+ return false;
}
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(),
BaseEnd = ClassDecl->vbases_end();
- HasConstCopyConstructor && Base != BaseEnd;
- ++Base) {
+ Base != BaseEnd; ++Base) {
CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
- HasConstCopyConstructor &=
- (bool)LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const);
+ if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const))
+ return false;
}
-
+
// -- for all the nonstatic data members of X that are of a
// class type M (or array thereof), each such class type
// has a copy constructor whose first parameter is of type
// const M& or const volatile M&.
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
FieldEnd = ClassDecl->field_end();
- HasConstCopyConstructor && Field != FieldEnd;
- ++Field) {
- QualType FieldType = Context.getBaseElementType((*Field)->getType());
+ Field != FieldEnd; ++Field) {
+ QualType FieldType = S.Context.getBaseElementType(Field->getType());
if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) {
- HasConstCopyConstructor &=
- (bool)LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const);
+ if (!S.LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const))
+ return false;
}
}
+
// Otherwise, the implicitly declared copy constructor will have
// the form
//
// X::X(X&)
-
+
+ return true;
+}
+
+Sema::ImplicitExceptionSpecification
+Sema::ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD) {
+ CXXRecordDecl *ClassDecl = MD->getParent();
+
+ ImplicitExceptionSpecification ExceptSpec(*this);
+ if (ClassDecl->isInvalidDecl())
+ return ExceptSpec;
+
+ const FunctionProtoType *T = MD->getType()->castAs<FunctionProtoType>();
+ assert(T->getNumArgs() >= 1 && "not a copy ctor");
+ unsigned Quals = T->getArgType(0).getNonReferenceType().getCVRQualifiers();
+
// C++ [except.spec]p14:
// An implicitly declared special member function (Clause 12) shall have an
// exception-specification. [...]
- ImplicitExceptionSpecification ExceptSpec(*this);
- unsigned Quals = HasConstCopyConstructor? Qualifiers::Const : 0;
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
BaseEnd = ClassDecl->bases_end();
Base != BaseEnd;
@@ -8714,15 +8601,16 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
FieldEnd = ClassDecl->field_end();
Field != FieldEnd;
++Field) {
- QualType FieldType = Context.getBaseElementType((*Field)->getType());
+ QualType FieldType = Context.getBaseElementType(Field->getType());
if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) {
if (CXXConstructorDecl *CopyConstructor =
- LookupCopyingConstructor(FieldClassDecl, Quals))
+ LookupCopyingConstructor(FieldClassDecl,
+ Quals | FieldType.getCVRQualifiers()))
ExceptSpec.CalledDecl(Field->getLocation(), CopyConstructor);
}
}
- return std::make_pair(ExceptSpec, HasConstCopyConstructor);
+ return ExceptSpec;
}
CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
@@ -8731,18 +8619,16 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
// If the class definition does not explicitly declare a copy
// constructor, one is declared implicitly.
- ImplicitExceptionSpecification Spec(*this);
- bool Const;
- llvm::tie(Spec, Const) =
- ComputeDefaultedCopyCtorExceptionSpecAndConst(ClassDecl);
-
QualType ClassType = Context.getTypeDeclType(ClassDecl);
QualType ArgType = ClassType;
+ bool Const = isImplicitCopyCtorArgConst(*this, ClassDecl);
if (Const)
ArgType = ArgType.withConst();
ArgType = Context.getLValueReferenceType(ArgType);
-
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
+
+ bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
+ CXXCopyConstructor,
+ Const);
DeclarationName Name
= Context.DeclarationNames.getCXXConstructorName(
@@ -8753,15 +8639,20 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
// An implicitly-declared copy constructor is an inline public
// member of its class.
CXXConstructorDecl *CopyConstructor = CXXConstructorDecl::Create(
- Context, ClassDecl, ClassLoc, NameInfo,
- Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0,
+ Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0,
/*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true,
- /*isConstexpr=*/ClassDecl->defaultedCopyConstructorIsConstexpr() &&
- getLangOpts().CPlusPlus0x);
+ Constexpr);
CopyConstructor->setAccess(AS_public);
CopyConstructor->setDefaulted();
CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor());
+ // Build an exception specification pointing back at this member.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = CopyConstructor;
+ CopyConstructor->setType(
+ Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI));
+
// Note that we have declared this constructor.
++ASTContext::NumImplicitCopyConstructorsDeclared;
@@ -8825,7 +8716,9 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,
}
Sema::ImplicitExceptionSpecification
-Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
+Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXMethodDecl *MD) {
+ CXXRecordDecl *ClassDecl = MD->getParent();
+
// C++ [except.spec]p14:
// An implicitly declared special member function (Clause 12) shall have an
// exception-specification. [...]
@@ -8842,7 +8735,8 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
if (const RecordType *BaseType = B->getType()->getAs<RecordType>()) {
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
- CXXConstructorDecl *Constructor = LookupMovingConstructor(BaseClassDecl);
+ CXXConstructorDecl *Constructor =
+ LookupMovingConstructor(BaseClassDecl, 0);
// If this is a deleted function, add it anyway. This might be conformant
// with the standard. This might not. I'm not sure. It might not matter.
if (Constructor)
@@ -8856,7 +8750,8 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
B != BEnd; ++B) {
if (const RecordType *BaseType = B->getType()->getAs<RecordType>()) {
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
- CXXConstructorDecl *Constructor = LookupMovingConstructor(BaseClassDecl);
+ CXXConstructorDecl *Constructor =
+ LookupMovingConstructor(BaseClassDecl, 0);
// If this is a deleted function, add it anyway. This might be conformant
// with the standard. This might not. I'm not sure. It might not matter.
if (Constructor)
@@ -8868,10 +8763,10 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
FEnd = ClassDecl->field_end();
F != FEnd; ++F) {
- if (const RecordType *RecordTy
- = Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
- CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
- CXXConstructorDecl *Constructor = LookupMovingConstructor(FieldRecDecl);
+ QualType FieldType = Context.getBaseElementType(F->getType());
+ if (CXXRecordDecl *FieldRecDecl = FieldType->getAsCXXRecordDecl()) {
+ CXXConstructorDecl *Constructor =
+ LookupMovingConstructor(FieldRecDecl, FieldType.getCVRQualifiers());
// If this is a deleted function, add it anyway. This might be conformant
// with the standard. This might not. I'm not sure. It might not matter.
// In particular, the problem is that this function never gets called. It
@@ -8906,13 +8801,12 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
return 0;
}
- ImplicitExceptionSpecification Spec(
- ComputeDefaultedMoveCtorExceptionSpec(ClassDecl));
-
QualType ClassType = Context.getTypeDeclType(ClassDecl);
QualType ArgType = Context.getRValueReferenceType(ClassType);
-
- FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
+
+ bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
+ CXXMoveConstructor,
+ false);
DeclarationName Name
= Context.DeclarationNames.getCXXConstructorName(
@@ -8924,15 +8818,20 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
// An implicitly-declared copy/move constructor is an inline public
// member of its class.
CXXConstructorDecl *MoveConstructor = CXXConstructorDecl::Create(
- Context, ClassDecl, ClassLoc, NameInfo,
- Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0,
+ Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0,
/*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true,
- /*isConstexpr=*/ClassDecl->defaultedMoveConstructorIsConstexpr() &&
- getLangOpts().CPlusPlus0x);
+ Constexpr);
MoveConstructor->setAccess(AS_public);
MoveConstructor->setDefaulted();
MoveConstructor->setTrivial(ClassDecl->hasTrivialMoveConstructor());
+ // Build an exception specification pointing back at this member.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.ExceptionSpecType = EST_Unevaluated;
+ EPI.ExceptionSpecDecl = MoveConstructor;
+ MoveConstructor->setType(
+ Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI));
+
// Add the parameter to the constructor.
ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveConstructor,
ClassLoc, ClassLoc,
@@ -9046,8 +8945,7 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
// will fill in the actual details.
Invoke->setUsed();
Invoke->setReferenced();
- Invoke->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(),
- Conv->getLocation()));
+ Invoke->setBody(new (Context) CompoundStmt(Conv->getLocation()));
if (ASTMutationListener *L = getASTMutationListener()) {
L->CompletedImplicitDefinition(Conv);
@@ -9171,13 +9069,6 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
unsigned NumExprs = ExprArgs.size();
Expr **Exprs = (Expr **)ExprArgs.release();
- for (specific_attr_iterator<NonNullAttr>
- i = Constructor->specific_attr_begin<NonNullAttr>(),
- e = Constructor->specific_attr_end<NonNullAttr>(); i != e; ++i) {
- const NonNullAttr *NonNull = *i;
- CheckNonNullArguments(NonNull, ExprArgs.get(), ConstructLoc);
- }
-
MarkFunctionReferenced(ConstructLoc, Constructor);
return Owned(CXXConstructExpr::Create(Context, DeclInitType, ConstructLoc,
Constructor, Elidable, Exprs, NumExprs,
@@ -9243,7 +9134,7 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) {
bool
Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
MultiExprArg ArgsPtr,
- SourceLocation Loc,
+ SourceLocation Loc,
ASTOwningVector<Expr*> &ConvertedArgs,
bool AllowExplicit) {
// FIXME: This duplicates a lot of code from Sema::ConvertArgumentsForCall.
@@ -9271,7 +9162,8 @@ Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
DiagnoseSentinelCalls(Constructor, Loc, AllArgs.data(), AllArgs.size());
- // FIXME: Missing call to CheckFunctionCall or equivalent
+ CheckConstructorCall(Constructor, AllArgs.data(), AllArgs.size(),
+ Proto, Loc);
return Invalid;
}
@@ -9329,7 +9221,7 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
diag::err_operator_new_delete_too_few_parameters)
<< FnDecl->getDeclName();
- // Check the the first parameter type is not dependent.
+ // Check the first parameter type is not dependent.
QualType FirstParamType = FnDecl->getParamDecl(0)->getType();
if (FirstParamType->isDependentType())
return SemaRef.Diag(FnDecl->getLocation(), DependentParamTypeDiag)
@@ -9568,7 +9460,7 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) {
TemplateParameterList *Params = TpDecl->getTemplateParameters();
if (Params->size() == 1) {
NonTypeTemplateParmDecl *PmDecl =
- cast<NonTypeTemplateParmDecl>(Params->getParam(0));
+ dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(0));
// The template parameter must be a char parameter pack.
if (PmDecl && PmDecl->isTemplateParameterPack() &&
@@ -9769,7 +9661,8 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S,
Diag(Loc, diag::err_objc_object_catch);
Invalid = true;
} else if (T->isObjCObjectPointerType()) {
- if (!getLangOpts().ObjCNonFragileABI)
+ // FIXME: should this be a test for macosx-fragile specifically?
+ if (getLangOpts().ObjCRuntime.isFragile())
Diag(Loc, diag::warn_objc_pointer_cxx_catch_fragile);
}
}
@@ -9881,37 +9774,49 @@ Decl *Sema::ActOnExceptionDeclarator(Scope *S, Declarator &D) {
Decl *Sema::ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc,
Expr *AssertExpr,
- Expr *AssertMessageExpr_,
+ Expr *AssertMessageExpr,
SourceLocation RParenLoc) {
- StringLiteral *AssertMessage = cast<StringLiteral>(AssertMessageExpr_);
+ StringLiteral *AssertMessage = cast<StringLiteral>(AssertMessageExpr);
+
+ if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression))
+ return 0;
- if (!AssertExpr->isTypeDependent() && !AssertExpr->isValueDependent()) {
+ return BuildStaticAssertDeclaration(StaticAssertLoc, AssertExpr,
+ AssertMessage, RParenLoc, false);
+}
+
+Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
+ Expr *AssertExpr,
+ StringLiteral *AssertMessage,
+ SourceLocation RParenLoc,
+ bool Failed) {
+ if (!AssertExpr->isTypeDependent() && !AssertExpr->isValueDependent() &&
+ !Failed) {
// In a static_assert-declaration, the constant-expression shall be a
// constant expression that can be contextually converted to bool.
ExprResult Converted = PerformContextuallyConvertToBool(AssertExpr);
if (Converted.isInvalid())
- return 0;
+ Failed = true;
llvm::APSInt Cond;
- if (VerifyIntegerConstantExpression(Converted.get(), &Cond,
- PDiag(diag::err_static_assert_expression_is_not_constant),
+ if (!Failed && VerifyIntegerConstantExpression(Converted.get(), &Cond,
+ diag::err_static_assert_expression_is_not_constant,
/*AllowFold=*/false).isInvalid())
- return 0;
+ Failed = true;
- if (!Cond) {
+ if (!Failed && !Cond) {
llvm::SmallString<256> MsgBuffer;
llvm::raw_svector_ostream Msg(MsgBuffer);
AssertMessage->printPretty(Msg, Context, 0, getPrintingPolicy());
Diag(StaticAssertLoc, diag::err_static_assert_failed)
<< Msg.str() << AssertExpr->getSourceRange();
+ Failed = true;
}
}
- if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression))
- return 0;
-
Decl *Decl = StaticAssertDecl::Create(Context, CurContext, StaticAssertLoc,
- AssertExpr, AssertMessage, RParenLoc);
+ AssertExpr, AssertMessage, RParenLoc,
+ Failed);
CurContext->addDecl(Decl);
return Decl;
@@ -10116,7 +10021,7 @@ Decl *Sema::ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
/// friend class A<T>::B<unsigned>;
/// We permit this as a special case; if there are any template
/// parameters present at all, require proper matching, i.e.
-/// template <> template <class T> friend class A<int>::B;
+/// template <> template \<class T> friend class A<int>::B;
Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
MultiTemplateParamsArg TempParams) {
SourceLocation Loc = DS.getLocStart();
@@ -10438,9 +10343,11 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
FrD->setAccess(AS_public);
CurContext->addDecl(FrD);
- if (ND->isInvalidDecl())
+ if (ND->isInvalidDecl()) {
FrD->setInvalidDecl();
- else {
+ } else {
+ if (DC->isRecord()) CheckFriendAccess(ND);
+
FunctionDecl *FD;
if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
FD = FTD->getTemplatedDecl();
@@ -10464,8 +10371,13 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {
return;
}
if (const FunctionDecl *Prev = Fn->getPreviousDecl()) {
- Diag(DelLoc, diag::err_deleted_decl_not_first);
- Diag(Prev->getLocation(), diag::note_previous_declaration);
+ // Don't consider the implicit declaration we generate for explicit
+ // specializations. FIXME: Do not generate these implicit declarations.
+ if ((Prev->getTemplateSpecializationKind() != TSK_ExplicitSpecialization
+ || Prev->getPreviousDecl()) && !Prev->isDefined()) {
+ Diag(DelLoc, diag::err_deleted_decl_not_first);
+ Diag(Prev->getLocation(), diag::note_previous_declaration);
+ }
// If the declaration wasn't the first, we delete the function anyway for
// recovery.
}
@@ -10531,10 +10443,11 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
if (Primary == Primary->getCanonicalDecl())
return;
+ CheckExplicitlyDefaultedSpecialMember(MD);
+
switch (Member) {
case CXXDefaultConstructor: {
CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD);
- CheckExplicitlyDefaultedDefaultConstructor(CD);
if (!CD->isInvalidDecl())
DefineImplicitDefaultConstructor(DefaultLoc, CD);
break;
@@ -10542,14 +10455,12 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
case CXXCopyConstructor: {
CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD);
- CheckExplicitlyDefaultedCopyConstructor(CD);
if (!CD->isInvalidDecl())
DefineImplicitCopyConstructor(DefaultLoc, CD);
break;
}
case CXXCopyAssignment: {
- CheckExplicitlyDefaultedCopyAssignment(MD);
if (!MD->isInvalidDecl())
DefineImplicitCopyAssignment(DefaultLoc, MD);
break;
@@ -10557,7 +10468,6 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
case CXXDestructor: {
CXXDestructorDecl *DD = cast<CXXDestructorDecl>(MD);
- CheckExplicitlyDefaultedDestructor(DD);
if (!DD->isInvalidDecl())
DefineImplicitDestructor(DefaultLoc, DD);
break;
@@ -10565,14 +10475,12 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
case CXXMoveConstructor: {
CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD);
- CheckExplicitlyDefaultedMoveConstructor(CD);
if (!CD->isInvalidDecl())
DefineImplicitMoveConstructor(DefaultLoc, CD);
break;
}
case CXXMoveAssignment: {
- CheckExplicitlyDefaultedMoveAssignment(MD);
if (!MD->isInvalidDecl())
DefineImplicitMoveAssignment(DefaultLoc, MD);
break;
@@ -10650,8 +10558,8 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
if (const RecordType *RT = NewClassTy->getAs<RecordType>()) {
if (!RT->isBeingDefined() &&
RequireCompleteType(New->getLocation(), NewClassTy,
- PDiag(diag::err_covariant_return_incomplete)
- << New->getDeclName()))
+ diag::err_covariant_return_incomplete,
+ New->getDeclName()))
return true;
}
@@ -10857,7 +10765,7 @@ bool Sema::DefineUsedVTables() {
// Note: The VTableUses vector could grow as a result of marking
// the members of a class as "used", so we check the size each
- // time through the loop and prefer indices (with are stable) to
+ // time through the loop and prefer indices (which are stable) to
// iterators (which are not).
bool DefinedAnything = false;
for (unsigned I = 0; I != VTableUses.size(); ++I) {
@@ -10867,6 +10775,8 @@ bool Sema::DefineUsedVTables() {
SourceLocation Loc = VTableUses[I].second;
+ bool DefineVTable = true;
+
// If this class has a key function, but that key function is
// defined in another translation unit, we don't need to emit the
// vtable even though we're using it.
@@ -10877,7 +10787,8 @@ bool Sema::DefineUsedVTables() {
case TSK_ExplicitSpecialization:
case TSK_ExplicitInstantiationDeclaration:
// The key function is in another translation unit.
- continue;
+ DefineVTable = false;
+ break;
case TSK_ExplicitInstantiationDefinition:
case TSK_ImplicitInstantiation:
@@ -10906,7 +10817,15 @@ bool Sema::DefineUsedVTables() {
}
if (IsExplicitInstantiationDeclaration)
- continue;
+ DefineVTable = false;
+ }
+
+ // The exception specifications for all virtual members may be needed even
+ // if we are not providing an authoritative form of the vtable in this TU.
+ // We may choose to emit it available_externally anyway.
+ if (!DefineVTable) {
+ MarkVirtualMemberExceptionSpecsNeeded(Loc, Class);
+ continue;
}
// Mark all of the virtual members of this class as referenced, so
@@ -10935,16 +10854,33 @@ bool Sema::DefineUsedVTables() {
return DefinedAnything;
}
+void Sema::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
+ const CXXRecordDecl *RD) {
+ for (CXXRecordDecl::method_iterator I = RD->method_begin(),
+ E = RD->method_end(); I != E; ++I)
+ if ((*I)->isVirtual() && !(*I)->isPure())
+ ResolveExceptionSpec(Loc, (*I)->getType()->castAs<FunctionProtoType>());
+}
+
void Sema::MarkVirtualMembersReferenced(SourceLocation Loc,
const CXXRecordDecl *RD) {
- for (CXXRecordDecl::method_iterator i = RD->method_begin(),
- e = RD->method_end(); i != e; ++i) {
- CXXMethodDecl *MD = *i;
+ // Mark all functions which will appear in RD's vtable as used.
+ CXXFinalOverriderMap FinalOverriders;
+ RD->getFinalOverriders(FinalOverriders);
+ for (CXXFinalOverriderMap::const_iterator I = FinalOverriders.begin(),
+ E = FinalOverriders.end();
+ I != E; ++I) {
+ for (OverridingMethods::const_iterator OI = I->second.begin(),
+ OE = I->second.end();
+ OI != OE; ++OI) {
+ assert(OI->second.size() > 0 && "no final overrider");
+ CXXMethodDecl *Overrider = OI->second.front().Method;
- // C++ [basic.def.odr]p2:
- // [...] A virtual member function is used if it is not pure. [...]
- if (MD->isVirtual() && !MD->isPure())
- MarkFunctionReferenced(Loc, MD);
+ // C++ [basic.def.odr]p2:
+ // [...] A virtual member function is used if it is not pure. [...]
+ if (!Overrider->isPure())
+ MarkFunctionReferenced(Loc, Overrider);
+ }
}
// Only classes that have virtual bases need a VTT.
@@ -11162,8 +11098,8 @@ bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) {
switch (Proto->getExceptionSpecType()) {
case EST_Uninstantiated:
+ case EST_Unevaluated:
case EST_BasicNoexcept:
- case EST_Delayed:
case EST_DynamicNone:
case EST_MSAny:
case EST_None:
@@ -11290,7 +11226,7 @@ Sema::checkExceptionSpecification(ExceptionSpecificationType EST,
if (!NoexceptExpr->isValueDependent())
NoexceptExpr = VerifyIntegerConstantExpression(NoexceptExpr, 0,
- PDiag(diag::err_noexcept_needs_constant_expression),
+ diag::err_noexcept_needs_constant_expression,
/*AllowFold*/ false).take();
EPI.NoexceptExpr = NoexceptExpr;
}
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index a942d49..9da4d69 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -173,10 +173,11 @@ void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod,
Diag(Overridden->getLocation(), diag::note_previous_decl)
<< "method";
}
- ObjCMethodDecl::param_const_iterator oi = Overridden->param_begin();
+ ObjCMethodDecl::param_const_iterator oi = Overridden->param_begin(),
+ oe = Overridden->param_end();
for (ObjCMethodDecl::param_iterator
ni = NewMethod->param_begin(), ne = NewMethod->param_end();
- ni != ne; ++ni, ++oi) {
+ ni != ne && oi != oe; ++ni, ++oi) {
const ParmVarDecl *oldDecl = (*oi);
ParmVarDecl *newDecl = (*ni);
if (newDecl->hasAttr<NSConsumedAttr>() !=
@@ -196,7 +197,6 @@ static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) {
ObjCMethodFamily family = method->getMethodFamily();
switch (family) {
case OMF_None:
- case OMF_dealloc:
case OMF_finalize:
case OMF_retain:
case OMF_release:
@@ -206,6 +206,24 @@ static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) {
case OMF_performSelector:
return false;
+ case OMF_dealloc:
+ if (!S.Context.hasSameType(method->getResultType(), S.Context.VoidTy)) {
+ SourceRange ResultTypeRange;
+ if (const TypeSourceInfo *ResultTypeInfo
+ = method->getResultTypeSourceInfo())
+ ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange();
+ if (ResultTypeRange.isInvalid())
+ S.Diag(method->getLocation(), diag::error_dealloc_bad_result_type)
+ << method->getResultType()
+ << FixItHint::CreateInsertion(method->getSelectorLoc(0), "(void)");
+ else
+ S.Diag(method->getLocation(), diag::error_dealloc_bad_result_type)
+ << method->getResultType()
+ << FixItHint::CreateReplacement(ResultTypeRange, "void");
+ return true;
+ }
+ return false;
+
case OMF_init:
// If the method doesn't obey the init rules, don't bother annotating it.
if (S.checkInitMethod(method, QualType()))
@@ -267,9 +285,9 @@ void Sema::AddAnyMethodToGlobalPool(Decl *D) {
/// ActOnStartOfObjCMethodDef - This routine sets up parameters; invisible
/// and user declared, in the method definition's AST.
void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
- assert(getCurMethodDecl() == 0 && "Method parsing confused");
+ assert((getCurMethodDecl() == 0) && "Methodparsing confused");
ObjCMethodDecl *MDecl = dyn_cast_or_null<ObjCMethodDecl>(D);
-
+
// If we don't have a valid method decl, simply return.
if (!MDecl)
return;
@@ -338,11 +356,11 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
// Finally, in ActOnFinishFunctionBody() (SemaDecl), warn if flag is set.
// Only do this if the current class actually has a superclass.
if (IC->getSuperClass()) {
- ObjCShouldCallSuperDealloc =
+ getCurFunction()->ObjCShouldCallSuperDealloc =
!(Context.getLangOpts().ObjCAutoRefCount ||
Context.getLangOpts().getGC() == LangOptions::GCOnly) &&
MDecl->getMethodFamily() == OMF_dealloc;
- ObjCShouldCallSuperFinalize =
+ getCurFunction()->ObjCShouldCallSuperFinalize =
Context.getLangOpts().getGC() != LangOptions::NonGC &&
MDecl->getMethodFamily() == OMF_finalize;
}
@@ -474,11 +492,11 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
Diag(SuperLoc, diag::err_undef_superclass)
<< SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc);
else if (RequireCompleteType(SuperLoc,
- Context.getObjCInterfaceType(SuperClassDecl),
- PDiag(diag::err_forward_superclass)
- << SuperClassDecl->getDeclName()
- << ClassName
- << SourceRange(AtInterfaceLoc, ClassLoc))) {
+ Context.getObjCInterfaceType(SuperClassDecl),
+ diag::err_forward_superclass,
+ SuperClassDecl->getDeclName(),
+ ClassName,
+ SourceRange(AtInterfaceLoc, ClassLoc))) {
SuperClassDecl = 0;
}
}
@@ -501,13 +519,13 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
return ActOnObjCContainerStartDefinition(IDecl);
}
-/// ActOnCompatiblityAlias - this action is called after complete parsing of
-/// @compatibility_alias declaration. It sets up the alias relationships.
-Decl *Sema::ActOnCompatiblityAlias(SourceLocation AtLoc,
- IdentifierInfo *AliasName,
- SourceLocation AliasLocation,
- IdentifierInfo *ClassName,
- SourceLocation ClassLocation) {
+/// ActOnCompatibilityAlias - this action is called after complete parsing of
+/// a \@compatibility_alias declaration. It sets up the alias relationships.
+Decl *Sema::ActOnCompatibilityAlias(SourceLocation AtLoc,
+ IdentifierInfo *AliasName,
+ SourceLocation AliasLocation,
+ IdentifierInfo *ClassName,
+ SourceLocation ClassLocation) {
// Look for previous declaration of alias name
NamedDecl *ADecl = LookupSingleName(TUScope, AliasName, AliasLocation,
LookupOrdinaryName, ForRedeclaration);
@@ -712,7 +730,7 @@ void Sema::DiagnoseClassExtensionDupMethods(ObjCCategoryDecl *CAT,
}
}
-/// ActOnForwardProtocolDeclaration - Handle @protocol foo;
+/// ActOnForwardProtocolDeclaration - Handle \@protocol foo;
Sema::DeclGroupPtrTy
Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc,
const IdentifierLocPair *IdentList,
@@ -759,8 +777,8 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc,
if (!IDecl
|| RequireCompleteType(ClassLoc, Context.getObjCInterfaceType(IDecl),
- PDiag(diag::err_category_forward_interface)
- << (CategoryName == 0))) {
+ diag::err_category_forward_interface,
+ CategoryName == 0)) {
// Create an invalid ObjCCategoryDecl to serve as context for
// the enclosing method declarations. We mark the decl invalid
// to make it clear that this isn't a valid AST.
@@ -1019,8 +1037,8 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl,
ObjCInterfaceDecl* IDecl = ImpDecl->getClassInterface();
if (!IDecl)
return;
- /// Check case of non-existing @interface decl.
- /// (legacy objective-c @implementation decl without an @interface decl).
+ /// Check case of non-existing \@interface decl.
+ /// (legacy objective-c \@implementation decl without an \@interface decl).
/// Add implementations's ivar to the synthesize class's ivar list.
if (IDecl->isImplicitInterfaceDecl()) {
IDecl->setEndOfDefinitionLoc(RBrace);
@@ -1038,7 +1056,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl,
return;
assert(ivars && "missing @implementation ivars");
- if (LangOpts.ObjCNonFragileABI2) {
+ if (LangOpts.ObjCRuntime.isNonFragile()) {
if (ImpDecl->getSuperClass())
Diag(ImpDecl->getLocation(), diag::warn_on_superclass_use);
for (unsigned i = 0; i < numIvars; i++) {
@@ -1094,7 +1112,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl,
if (numIvars > 0)
Diag(ivars[j]->getLocation(), diag::err_inconsistant_ivar_count);
else if (IVI != IVE)
- Diag((*IVI)->getLocation(), diag::err_inconsistant_ivar_count);
+ Diag(IVI->getLocation(), diag::err_inconsistant_ivar_count);
}
void Sema::WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method,
@@ -1399,8 +1417,9 @@ void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl,
true);
for (ObjCMethodDecl::param_iterator IM = ImpMethodDecl->param_begin(),
- IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end();
- IM != EM; ++IM, ++IF) {
+ IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end(),
+ EF = MethodDecl->param_end();
+ IM != EM && IF != EF; ++IM, ++IF) {
CheckMethodOverrideParam(*this, ImpMethodDecl, MethodDecl, *IM, *IF,
IsProtocolMethodDecl, false, true);
}
@@ -1421,8 +1440,9 @@ void Sema::CheckConflictingOverridingMethod(ObjCMethodDecl *Method,
true);
for (ObjCMethodDecl::param_iterator IM = Method->param_begin(),
- IF = Overridden->param_begin(), EM = Method->param_end();
- IM != EM; ++IM, ++IF) {
+ IF = Overridden->param_begin(), EM = Method->param_end(),
+ EF = Overridden->param_end();
+ IM != EM && IF != EF; ++IM, ++IF) {
CheckMethodOverrideParam(*this, Method, Overridden, *IM, *IF,
IsProtocolMethodDecl, true, true);
}
@@ -1454,8 +1474,9 @@ void Sema::WarnExactTypedMethods(ObjCMethodDecl *ImpMethodDecl,
IsProtocolMethodDecl, false, false);
if (match)
for (ObjCMethodDecl::param_iterator IM = ImpMethodDecl->param_begin(),
- IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end();
- IM != EM; ++IM, ++IF) {
+ IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end(),
+ EF = MethodDecl->param_end();
+ IM != EM && IF != EF; ++IM, ++IF) {
match = CheckMethodOverrideParam(*this, ImpMethodDecl, MethodDecl,
*IM, *IF,
IsProtocolMethodDecl, false, false);
@@ -1487,8 +1508,8 @@ void Sema::WarnExactTypedMethods(ObjCMethodDecl *ImpMethodDecl,
void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
ObjCProtocolDecl *PDecl,
bool& IncompleteImpl,
- const llvm::DenseSet<Selector> &InsMap,
- const llvm::DenseSet<Selector> &ClsMap,
+ const SelectorSet &InsMap,
+ const SelectorSet &ClsMap,
ObjCContainerDecl *CDecl) {
ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl);
ObjCInterfaceDecl *IDecl = C ? C->getClassInterface()
@@ -1497,7 +1518,7 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
ObjCInterfaceDecl *Super = IDecl->getSuperClass();
ObjCInterfaceDecl *NSIDecl = 0;
- if (getLangOpts().NeXTRuntime) {
+ if (getLangOpts().ObjCRuntime.isNeXTFamily()) {
// check to see if class implements forwardInvocation method and objects
// of this class are derived from 'NSProxy' so that to forward requests
// from one object to another.
@@ -1584,10 +1605,10 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
/// MatchAllMethodDeclarations - Check methods declared in interface
/// or protocol against those declared in their implementations.
///
-void Sema::MatchAllMethodDeclarations(const llvm::DenseSet<Selector> &InsMap,
- const llvm::DenseSet<Selector> &ClsMap,
- llvm::DenseSet<Selector> &InsMapSeen,
- llvm::DenseSet<Selector> &ClsMapSeen,
+void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap,
+ const SelectorSet &ClsMap,
+ SelectorSet &InsMapSeen,
+ SelectorSet &ClsMapSeen,
ObjCImplDecl* IMPDecl,
ObjCContainerDecl* CDecl,
bool &IncompleteImpl,
@@ -1683,7 +1704,7 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet<Selector> &InsMap,
/// warns each time an exact match is found.
void Sema::CheckCategoryVsClassMethodMatches(
ObjCCategoryImplDecl *CatIMPDecl) {
- llvm::DenseSet<Selector> InsMap, ClsMap;
+ SelectorSet InsMap, ClsMap;
for (ObjCImplementationDecl::instmeth_iterator
I = CatIMPDecl->instmeth_begin(),
@@ -1704,7 +1725,7 @@ void Sema::CheckCategoryVsClassMethodMatches(
ObjCInterfaceDecl *IDecl = CatDecl->getClassInterface();
if (!IDecl)
return;
- llvm::DenseSet<Selector> InsMapSeen, ClsMapSeen;
+ SelectorSet InsMapSeen, ClsMapSeen;
bool IncompleteImpl = false;
MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen,
CatIMPDecl, IDecl,
@@ -1715,7 +1736,7 @@ void Sema::CheckCategoryVsClassMethodMatches(
void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
ObjCContainerDecl* CDecl,
bool IncompleteImpl) {
- llvm::DenseSet<Selector> InsMap;
+ SelectorSet InsMap;
// Check and see if instance methods in class interface have been
// implemented in the implementation class.
for (ObjCImplementationDecl::instmeth_iterator
@@ -1726,11 +1747,12 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
// an implementation or 2) there is a @synthesize/@dynamic implementation
// of the property in the @implementation.
if (const ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl))
- if (!(LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCNonFragileABI2) ||
- IDecl->isObjCRequiresPropertyDefs())
+ if (!(LangOpts.ObjCDefaultSynthProperties &&
+ LangOpts.ObjCRuntime.isNonFragile()) ||
+ IDecl->isObjCRequiresPropertyDefs())
DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, InsMap);
- llvm::DenseSet<Selector> ClsMap;
+ SelectorSet ClsMap;
for (ObjCImplementationDecl::classmeth_iterator
I = IMPDecl->classmeth_begin(),
E = IMPDecl->classmeth_end(); I != E; ++I)
@@ -1738,7 +1760,7 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
// Check for type conflict of methods declared in a class/protocol and
// its implementation; if any.
- llvm::DenseSet<Selector> InsMapSeen, ClsMapSeen;
+ SelectorSet InsMapSeen, ClsMapSeen;
MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen,
IMPDecl, CDecl,
IncompleteImpl, true);
@@ -1954,9 +1976,10 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left,
return false;
ObjCMethodDecl::param_const_iterator
- li = left->param_begin(), le = left->param_end(), ri = right->param_begin();
+ li = left->param_begin(), le = left->param_end(), ri = right->param_begin(),
+ re = right->param_end();
- for (; li != le; ++li, ++ri) {
+ for (; li != le && ri != re; ++li, ++ri) {
assert(ri != right->param_end() && "Param mismatch");
const ParmVarDecl *lparm = *li, *rparm = *ri;
@@ -2140,53 +2163,16 @@ ObjCMethodDecl *Sema::LookupImplementedMethodInGlobalPool(Selector Sel) {
return 0;
}
-/// CompareMethodParamsInBaseAndSuper - This routine compares methods with
-/// identical selector names in current and its super classes and issues
-/// a warning if any of their argument types are incompatible.
-void Sema::CompareMethodParamsInBaseAndSuper(Decl *ClassDecl,
- ObjCMethodDecl *Method,
- bool IsInstance) {
- ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ClassDecl);
- if (ID == 0) return;
-
- while (ObjCInterfaceDecl *SD = ID->getSuperClass()) {
- ObjCMethodDecl *SuperMethodDecl =
- SD->lookupMethod(Method->getSelector(), IsInstance);
- if (SuperMethodDecl == 0) {
- ID = SD;
- continue;
- }
- ObjCMethodDecl::param_iterator ParamI = Method->param_begin(),
- E = Method->param_end();
- ObjCMethodDecl::param_iterator PrevI = SuperMethodDecl->param_begin();
- for (; ParamI != E; ++ParamI, ++PrevI) {
- // Number of parameters are the same and is guaranteed by selector match.
- assert(PrevI != SuperMethodDecl->param_end() && "Param mismatch");
- QualType T1 = Context.getCanonicalType((*ParamI)->getType());
- QualType T2 = Context.getCanonicalType((*PrevI)->getType());
- // If type of argument of method in this class does not match its
- // respective argument type in the super class method, issue warning;
- if (!Context.typesAreCompatible(T1, T2)) {
- Diag((*ParamI)->getLocation(), diag::ext_typecheck_base_super)
- << T1 << T2;
- Diag(SuperMethodDecl->getLocation(), diag::note_previous_declaration);
- return;
- }
- }
- ID = SD;
- }
-}
-
/// DiagnoseDuplicateIvars -
/// Check for duplicate ivars in the entire class at the start of
-/// @implementation. This becomes necesssary because class extension can
+/// \@implementation. This becomes necesssary because class extension can
/// add ivars to a class in random order which will not be known until
-/// class's @implementation is seen.
+/// class's \@implementation is seen.
void Sema::DiagnoseDuplicateIvars(ObjCInterfaceDecl *ID,
ObjCInterfaceDecl *SID) {
for (ObjCInterfaceDecl::ivar_iterator IVI = ID->ivar_begin(),
IVE = ID->ivar_end(); IVI != IVE; ++IVI) {
- ObjCIvarDecl* Ivar = (*IVI);
+ ObjCIvarDecl* Ivar = *IVI;
if (Ivar->isInvalidDecl())
continue;
if (IdentifierInfo *II = Ivar->getIdentifier()) {
@@ -2273,9 +2259,6 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd,
InsMap[Method->getSelector()] = Method;
/// The following allows us to typecheck messages to "id".
AddInstanceMethodToGlobalPool(Method);
- // verify that the instance method conforms to the same definition of
- // parent methods if it shadows one.
- CompareMethodParamsInBaseAndSuper(ClassDecl, Method, true);
}
} else {
/// Check for class method of the same name with incompatible types
@@ -2298,11 +2281,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd,
Diag(PrevMethod->getLocation(), diag::note_previous_declaration);
}
ClsMap[Method->getSelector()] = Method;
- /// The following allows us to typecheck messages to "Class".
AddFactoryMethodToGlobalPool(Method);
- // verify that the class method conforms to the same definition of
- // parent methods if it shadows one.
- CompareMethodParamsInBaseAndSuper(ClassDecl, Method, false);
}
}
}
@@ -2347,7 +2326,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd,
ClsExtDecl; ClsExtDecl = ClsExtDecl->getNextClassExtension()) {
for (ObjCContainerDecl::prop_iterator I = ClsExtDecl->prop_begin(),
E = ClsExtDecl->prop_end(); I != E; ++I) {
- ObjCPropertyDecl *Property = (*I);
+ ObjCPropertyDecl *Property = *I;
// Skip over properties declared @dynamic
if (const ObjCPropertyImplDecl *PIDecl
= IC->FindPropertyImplDecl(Property->getIdentifier()))
@@ -2399,7 +2378,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd,
Diag(IDecl->getLocation(), diag::err_objc_root_class_subclass);
}
- if (LangOpts.ObjCNonFragileABI2) {
+ if (LangOpts.ObjCRuntime.isNonFragile()) {
while (IDecl->getSuperClass()) {
DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass());
IDecl = IDecl->getSuperClass();
@@ -2443,6 +2422,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd,
Consumer.HandleTopLevelDeclInObjCContainer(DG);
}
+ ActOnDocumentableDecl(ClassDecl);
return ClassDecl;
}
@@ -2488,19 +2468,10 @@ bool containsInvalidMethodImplAttribute(ObjCMethodDecl *IMD,
return false;
}
-namespace {
- /// \brief Describes the compatibility of a result type with its method.
- enum ResultTypeCompatibilityKind {
- RTC_Compatible,
- RTC_Incompatible,
- RTC_Unknown
- };
-}
-
/// \brief Check whether the declared result type of the given Objective-C
/// method declaration is compatible with the method's class.
///
-static ResultTypeCompatibilityKind
+static Sema::ResultTypeCompatibilityKind
CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method,
ObjCInterfaceDecl *CurrentClass) {
QualType ResultType = Method->getResultType();
@@ -2513,27 +2484,27 @@ CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method,
// - it is id or qualified id, or
if (ResultObjectType->isObjCIdType() ||
ResultObjectType->isObjCQualifiedIdType())
- return RTC_Compatible;
+ return Sema::RTC_Compatible;
if (CurrentClass) {
if (ObjCInterfaceDecl *ResultClass
= ResultObjectType->getInterfaceDecl()) {
// - it is the same as the method's class type, or
if (declaresSameEntity(CurrentClass, ResultClass))
- return RTC_Compatible;
+ return Sema::RTC_Compatible;
// - it is a superclass of the method's class type
if (ResultClass->isSuperClassOf(CurrentClass))
- return RTC_Compatible;
+ return Sema::RTC_Compatible;
}
} else {
// Any Objective-C pointer type might be acceptable for a protocol
// method; we just don't know.
- return RTC_Unknown;
+ return Sema::RTC_Unknown;
}
}
- return RTC_Incompatible;
+ return Sema::RTC_Incompatible;
}
namespace {
@@ -2543,7 +2514,6 @@ class OverrideSearch {
public:
Sema &S;
ObjCMethodDecl *Method;
- llvm::SmallPtrSet<ObjCContainerDecl*, 128> Searched;
llvm::SmallPtrSet<ObjCMethodDecl*, 4> Overridden;
bool Recursive;
@@ -2572,8 +2542,13 @@ public:
// Prevent the search from reaching this container again. This is
// important with categories, which override methods from the
// interface and each other.
- Searched.insert(container);
- searchFromContainer(container);
+ if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(container)) {
+ searchFromContainer(container);
+ if (ObjCInterfaceDecl *Interface = Category->getClassInterface())
+ searchFromContainer(Interface);
+ } else {
+ searchFromContainer(container);
+ }
}
typedef llvm::SmallPtrSet<ObjCMethodDecl*, 128>::iterator iterator;
@@ -2609,7 +2584,7 @@ private:
void searchFrom(ObjCCategoryDecl *category) {
// A method in a category declaration overrides declarations from
// the main class and from protocols the category references.
- search(category->getClassInterface());
+ // The main class is handled in the constructor.
search(category->getReferencedProtocols());
}
@@ -2619,10 +2594,12 @@ private:
// declaration.
if (ObjCCategoryDecl *category = impl->getCategoryDecl()) {
search(category);
+ if (ObjCInterfaceDecl *Interface = category->getClassInterface())
+ search(Interface);
// Otherwise it overrides declarations from the class.
- } else {
- search(impl->getClassInterface());
+ } else if (ObjCInterfaceDecl *Interface = impl->getClassInterface()) {
+ search(Interface);
}
}
@@ -2647,7 +2624,8 @@ private:
void searchFrom(ObjCImplementationDecl *impl) {
// A method in a class implementation overrides declarations from
// the class interface.
- search(impl->getClassInterface());
+ if (ObjCInterfaceDecl *Interface = impl->getClassInterface())
+ search(Interface);
}
@@ -2658,9 +2636,6 @@ private:
}
void search(ObjCContainerDecl *container) {
- // Abort if we've already searched this container.
- if (!Searched.insert(container)) return;
-
// Check for a method in this container which matches this selector.
ObjCMethodDecl *meth = container->getMethod(Method->getSelector(),
Method->isInstanceMethod());
@@ -2682,6 +2657,68 @@ private:
};
}
+void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
+ ObjCInterfaceDecl *CurrentClass,
+ ResultTypeCompatibilityKind RTC) {
+ // Search for overridden methods and merge information down from them.
+ OverrideSearch overrides(*this, ObjCMethod);
+ // Keep track if the method overrides any method in the class's base classes,
+ // its protocols, or its categories' protocols; we will keep that info
+ // in the ObjCMethodDecl.
+ // For this info, a method in an implementation is not considered as
+ // overriding the same method in the interface or its categories.
+ bool hasOverriddenMethodsInBaseOrProtocol = false;
+ for (OverrideSearch::iterator
+ i = overrides.begin(), e = overrides.end(); i != e; ++i) {
+ ObjCMethodDecl *overridden = *i;
+
+ if (isa<ObjCProtocolDecl>(overridden->getDeclContext()) ||
+ CurrentClass != overridden->getClassInterface() ||
+ overridden->isOverriding())
+ hasOverriddenMethodsInBaseOrProtocol = true;
+
+ // Propagate down the 'related result type' bit from overridden methods.
+ if (RTC != Sema::RTC_Incompatible && overridden->hasRelatedResultType())
+ ObjCMethod->SetRelatedResultType();
+
+ // Then merge the declarations.
+ mergeObjCMethodDecls(ObjCMethod, overridden);
+
+ if (ObjCMethod->isImplicit() && overridden->isImplicit())
+ continue; // Conflicting properties are detected elsewhere.
+
+ // Check for overriding methods
+ if (isa<ObjCInterfaceDecl>(ObjCMethod->getDeclContext()) ||
+ isa<ObjCImplementationDecl>(ObjCMethod->getDeclContext()))
+ CheckConflictingOverridingMethod(ObjCMethod, overridden,
+ isa<ObjCProtocolDecl>(overridden->getDeclContext()));
+
+ if (CurrentClass && overridden->getDeclContext() != CurrentClass &&
+ isa<ObjCInterfaceDecl>(overridden->getDeclContext()) &&
+ !overridden->isImplicit() /* not meant for properties */) {
+ ObjCMethodDecl::param_iterator ParamI = ObjCMethod->param_begin(),
+ E = ObjCMethod->param_end();
+ ObjCMethodDecl::param_iterator PrevI = overridden->param_begin(),
+ PrevE = overridden->param_end();
+ for (; ParamI != E && PrevI != PrevE; ++ParamI, ++PrevI) {
+ assert(PrevI != overridden->param_end() && "Param mismatch");
+ QualType T1 = Context.getCanonicalType((*ParamI)->getType());
+ QualType T2 = Context.getCanonicalType((*PrevI)->getType());
+ // If type of argument of method in this class does not match its
+ // respective argument type in the super class method, issue warning;
+ if (!Context.typesAreCompatible(T1, T2)) {
+ Diag((*ParamI)->getLocation(), diag::ext_typecheck_base_super)
+ << T1 << T2;
+ Diag(overridden->getLocation(), diag::note_previous_declaration);
+ break;
+ }
+ }
+ }
+ }
+
+ ObjCMethod->setOverriding(hasOverriddenMethodsInBaseOrProtocol);
+}
+
Decl *Sema::ActOnMethodDeclaration(
Scope *S,
SourceLocation MethodLoc, SourceLocation EndLoc,
@@ -2871,32 +2908,14 @@ Decl *Sema::ActOnMethodDeclaration(
ResultTypeCompatibilityKind RTC
= CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass);
- // Search for overridden methods and merge information down from them.
- OverrideSearch overrides(*this, ObjCMethod);
- for (OverrideSearch::iterator
- i = overrides.begin(), e = overrides.end(); i != e; ++i) {
- ObjCMethodDecl *overridden = *i;
-
- // Propagate down the 'related result type' bit from overridden methods.
- if (RTC != RTC_Incompatible && overridden->hasRelatedResultType())
- ObjCMethod->SetRelatedResultType();
+ CheckObjCMethodOverrides(ObjCMethod, CurrentClass, RTC);
- // Then merge the declarations.
- mergeObjCMethodDecls(ObjCMethod, overridden);
-
- // Check for overriding methods
- if (isa<ObjCInterfaceDecl>(ObjCMethod->getDeclContext()) ||
- isa<ObjCImplementationDecl>(ObjCMethod->getDeclContext()))
- CheckConflictingOverridingMethod(ObjCMethod, overridden,
- isa<ObjCProtocolDecl>(overridden->getDeclContext()));
- }
-
bool ARCError = false;
if (getLangOpts().ObjCAutoRefCount)
ARCError = CheckARCMethodDecl(*this, ObjCMethod);
// Infer the related result type when possible.
- if (!ARCError && RTC == RTC_Compatible &&
+ if (!ARCError && RTC == Sema::RTC_Compatible &&
!ObjCMethod->hasRelatedResultType() &&
LangOpts.ObjCInferRelatedResultType) {
bool InferRelatedResultType = false;
@@ -2927,7 +2946,9 @@ Decl *Sema::ActOnMethodDeclaration(
if (InferRelatedResultType)
ObjCMethod->SetRelatedResultType();
}
-
+
+ ActOnDocumentableDecl(ObjCMethod);
+
return ObjCMethod;
}
@@ -2948,7 +2969,7 @@ bool Sema::CheckObjCDeclScope(Decl *D) {
return true;
}
-/// Called whenever @defs(ClassName) is encountered in the source. Inserts the
+/// Called whenever \@defs(ClassName) is encountered in the source. Inserts the
/// instance variables of ClassName into Decls.
void Sema::ActOnDefs(Scope *S, Decl *TagD, SourceLocation DeclStart,
IdentifierInfo *ClassName,
@@ -2959,7 +2980,7 @@ void Sema::ActOnDefs(Scope *S, Decl *TagD, SourceLocation DeclStart,
Diag(DeclStart, diag::err_undef_interface) << ClassName;
return;
}
- if (LangOpts.ObjCNonFragileABI) {
+ if (LangOpts.ObjCRuntime.isNonFragile()) {
Diag(DeclStart, diag::err_atdef_nonfragile_interface);
return;
}
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index 14b2434..63bfa9d 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -51,7 +51,8 @@ bool Sema::CheckSpecifiedExceptionType(QualType T, const SourceRange &Range) {
// C++ 15.4p2: A type denoted in an exception-specification shall not denote
// an incomplete type.
if (RequireCompleteType(Range.getBegin(), T,
- PDiag(diag::err_incomplete_in_exception_spec) << /*direct*/0 << Range))
+ diag::err_incomplete_in_exception_spec,
+ /*direct*/0, Range))
return true;
// C++ 15.4p2: A type denoted in an exception-specification shall not denote
@@ -71,8 +72,9 @@ bool Sema::CheckSpecifiedExceptionType(QualType T, const SourceRange &Range) {
if (T->isRecordType() && T->getAs<RecordType>()->isBeingDefined())
return false;
- if (!T->isVoidType() && RequireCompleteType(Range.getBegin(), T,
- PDiag(diag::err_incomplete_in_exception_spec) << kind << Range))
+ if (!T->isVoidType() &&
+ RequireCompleteType(Range.getBegin(), T,
+ diag::err_incomplete_in_exception_spec, kind, Range))
return true;
return false;
@@ -98,20 +100,22 @@ bool Sema::CheckDistantExceptionSpec(QualType T) {
const FunctionProtoType *
Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
- // FIXME: If FD is a special member, we should delay computing its exception
- // specification until this point.
- if (FPT->getExceptionSpecType() != EST_Uninstantiated)
+ if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
return FPT;
FunctionDecl *SourceDecl = FPT->getExceptionSpecDecl();
const FunctionProtoType *SourceFPT =
SourceDecl->getType()->castAs<FunctionProtoType>();
- if (SourceFPT->getExceptionSpecType() != EST_Uninstantiated)
+ // If the exception specification has already been resolved, just return it.
+ if (!isUnresolvedExceptionSpec(SourceFPT->getExceptionSpecType()))
return SourceFPT;
- // Instantiate the exception specification now.
- InstantiateExceptionSpec(Loc, SourceDecl);
+ // Compute or instantiate the exception specification now.
+ if (FPT->getExceptionSpecType() == EST_Unevaluated)
+ EvaluateImplicitExceptionSpec(Loc, cast<CXXMethodDecl>(SourceDecl));
+ else
+ InstantiateExceptionSpec(Loc, SourceDecl);
return SourceDecl->getType()->castAs<FunctionProtoType>();
}
@@ -344,8 +348,8 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
ExceptionSpecificationType NewEST = New->getExceptionSpecType();
- assert(OldEST != EST_Delayed && NewEST != EST_Delayed &&
- OldEST != EST_Uninstantiated && NewEST != EST_Uninstantiated &&
+ assert(!isUnresolvedExceptionSpec(OldEST) &&
+ !isUnresolvedExceptionSpec(NewEST) &&
"Shouldn't see unknown exception specifications here");
// Shortcut the case where both have no spec.
@@ -542,8 +546,8 @@ bool Sema::CheckExceptionSpecSubset(
ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
- assert(SuperEST != EST_Delayed && SubEST != EST_Delayed &&
- SuperEST != EST_Uninstantiated && SubEST != EST_Uninstantiated &&
+ assert(!isUnresolvedExceptionSpec(SuperEST) &&
+ !isUnresolvedExceptionSpec(SubEST) &&
"Shouldn't see unknown exception specifications here");
// It does not. If the subset contains everything, we've failed.
@@ -806,15 +810,6 @@ static CanThrowResult canCalleeThrow(Sema &S, const Expr *E,
if (!FT)
return CT_Can;
- if (FT->getExceptionSpecType() == EST_Delayed) {
- // FIXME: Try to resolve a delayed exception spec in ResolveExceptionSpec.
- assert(isa<CXXConstructorDecl>(D) &&
- "only constructor exception specs can be unknown");
- S.Diag(E->getLocStart(), diag::err_exception_spec_unknown)
- << E->getSourceRange();
- return CT_Can;
- }
-
return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can;
}
@@ -964,7 +959,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
// possibility.
case Expr::ObjCArrayLiteralClass:
case Expr::ObjCDictionaryLiteralClass:
- case Expr::ObjCNumericLiteralClass:
+ case Expr::ObjCBoxedExprClass:
return CT_Can;
// Many other things have subexpressions, so we have to test those.
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index d2e0e6b..6a503ee 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -130,6 +130,77 @@ void Sema::NoteDeletedFunction(FunctionDecl *Decl) {
<< 1 << Decl->isDeleted();
}
+/// \brief Determine whether a FunctionDecl was ever declared with an
+/// explicit storage class.
+static bool hasAnyExplicitStorageClass(const FunctionDecl *D) {
+ for (FunctionDecl::redecl_iterator I = D->redecls_begin(),
+ E = D->redecls_end();
+ I != E; ++I) {
+ if (I->getStorageClassAsWritten() != SC_None)
+ return true;
+ }
+ return false;
+}
+
+/// \brief Check whether we're in an extern inline function and referring to a
+/// variable or function with internal linkage (C11 6.7.4p3).
+///
+/// This is only a warning because we used to silently accept this code, but
+/// in many cases it will not behave correctly. This is not enabled in C++ mode
+/// because the restriction language is a bit weaker (C++11 [basic.def.odr]p6)
+/// and so while there may still be user mistakes, most of the time we can't
+/// prove that there are errors.
+static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S,
+ const NamedDecl *D,
+ SourceLocation Loc) {
+ // This is disabled under C++; there are too many ways for this to fire in
+ // contexts where the warning is a false positive, or where it is technically
+ // correct but benign.
+ if (S.getLangOpts().CPlusPlus)
+ return;
+
+ // Check if this is an inlined function or method.
+ FunctionDecl *Current = S.getCurFunctionDecl();
+ if (!Current)
+ return;
+ if (!Current->isInlined())
+ return;
+ if (Current->getLinkage() != ExternalLinkage)
+ return;
+
+ // Check if the decl has internal linkage.
+ if (D->getLinkage() != InternalLinkage)
+ return;
+
+ // Downgrade from ExtWarn to Extension if
+ // (1) the supposedly external inline function is in the main file,
+ // and probably won't be included anywhere else.
+ // (2) the thing we're referencing is a pure function.
+ // (3) the thing we're referencing is another inline function.
+ // This last can give us false negatives, but it's better than warning on
+ // wrappers for simple C library functions.
+ const FunctionDecl *UsedFn = dyn_cast<FunctionDecl>(D);
+ bool DowngradeWarning = S.getSourceManager().isFromMainFile(Loc);
+ if (!DowngradeWarning && UsedFn)
+ DowngradeWarning = UsedFn->isInlined() || UsedFn->hasAttr<ConstAttr>();
+
+ S.Diag(Loc, DowngradeWarning ? diag::ext_internal_in_extern_inline
+ : diag::warn_internal_in_extern_inline)
+ << /*IsVar=*/!UsedFn << D;
+
+ // Suggest "static" on the inline function, if possible.
+ if (!hasAnyExplicitStorageClass(Current)) {
+ const FunctionDecl *FirstDecl = Current->getCanonicalDecl();
+ SourceLocation DeclBegin = FirstDecl->getSourceRange().getBegin();
+ S.Diag(DeclBegin, diag::note_convert_inline_to_static)
+ << Current << FixItHint::CreateInsertion(DeclBegin, "static ");
+ }
+
+ S.Diag(D->getCanonicalDecl()->getLocation(),
+ diag::note_internal_decl_declared_here)
+ << D;
+}
+
/// \brief Determine whether the use of this declaration is valid, and
/// emit any corresponding diagnostics.
///
@@ -182,6 +253,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
// Warn if this is used but marked unused.
if (D->hasAttr<UnusedAttr>())
Diag(Loc, diag::warn_used_but_marked_unused) << D->getDeclName();
+
+ diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc);
+
return false;
}
@@ -510,8 +584,7 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) {
// is a prvalue for the temporary.
// FIXME: add some way to gate this entire thing for correctness in
// potentially potentially evaluated contexts.
- if (getLangOpts().CPlusPlus && E->isGLValue() &&
- ExprEvalContexts.back().Context != Unevaluated) {
+ if (getLangOpts().CPlusPlus && E->isGLValue() && !isUnevaluatedContext()) {
ExprResult Temp = PerformCopyInitialization(
InitializedEntity::InitializeTemporary(E->getType()),
E->getExprLoc(),
@@ -524,9 +597,66 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) {
return Owned(E);
}
+/// Determine the degree of POD-ness for an expression.
+/// Incomplete types are considered POD, since this check can be performed
+/// when we're in an unevaluated context.
+Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) {
+ if (Ty->isIncompleteType()) {
+ if (Ty->isObjCObjectType())
+ return VAK_Invalid;
+ return VAK_Valid;
+ }
+
+ if (Ty.isCXX98PODType(Context))
+ return VAK_Valid;
+
+ // C++0x [expr.call]p7:
+ // Passing a potentially-evaluated argument of class type (Clause 9)
+ // having a non-trivial copy constructor, a non-trivial move constructor,
+ // or a non-trivial destructor, with no corresponding parameter,
+ // is conditionally-supported with implementation-defined semantics.
+ if (getLangOpts().CPlusPlus0x && !Ty->isDependentType())
+ if (CXXRecordDecl *Record = Ty->getAsCXXRecordDecl())
+ if (Record->hasTrivialCopyConstructor() &&
+ Record->hasTrivialMoveConstructor() &&
+ Record->hasTrivialDestructor())
+ return VAK_ValidInCXX11;
+
+ if (getLangOpts().ObjCAutoRefCount && Ty->isObjCLifetimeType())
+ return VAK_Valid;
+ return VAK_Invalid;
+}
+
+bool Sema::variadicArgumentPODCheck(const Expr *E, VariadicCallType CT) {
+ // Don't allow one to pass an Objective-C interface to a vararg.
+ const QualType & Ty = E->getType();
+
+ // Complain about passing non-POD types through varargs.
+ switch (isValidVarArgType(Ty)) {
+ case VAK_Valid:
+ break;
+ case VAK_ValidInCXX11:
+ DiagRuntimeBehavior(E->getLocStart(), 0,
+ PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg)
+ << E->getType() << CT);
+ break;
+ case VAK_Invalid: {
+ if (Ty->isObjCObjectType())
+ return DiagRuntimeBehavior(E->getLocStart(), 0,
+ PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
+ << Ty << CT);
+
+ return DiagRuntimeBehavior(E->getLocStart(), 0,
+ PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg)
+ << getLangOpts().CPlusPlus0x << Ty << CT);
+ }
+ }
+ // c++ rules are enforced elsewhere.
+ return false;
+}
+
/// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but
-/// will warn if the resulting type is not a POD type, and rejects ObjC
-/// interfaces passed by value.
+/// will create a trap if the resulting type is not a POD type.
ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
FunctionDecl *FDecl) {
if (const BuiltinType *PlaceholderTy = E->getType()->getAsPlaceholderType()) {
@@ -550,76 +680,38 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
return ExprError();
E = ExprRes.take();
- // Don't allow one to pass an Objective-C interface to a vararg.
- if (E->getType()->isObjCObjectType() &&
- DiagRuntimeBehavior(E->getLocStart(), 0,
- PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
- << E->getType() << CT))
- return ExprError();
-
- // Complain about passing non-POD types through varargs. However, don't
- // perform this check for incomplete types, which we can get here when we're
- // in an unevaluated context.
- if (!E->getType()->isIncompleteType() && !E->getType().isPODType(Context)) {
- // C++0x [expr.call]p7:
- // Passing a potentially-evaluated argument of class type (Clause 9)
- // having a non-trivial copy constructor, a non-trivial move constructor,
- // or a non-trivial destructor, with no corresponding parameter,
- // is conditionally-supported with implementation-defined semantics.
- bool TrivialEnough = false;
- if (getLangOpts().CPlusPlus0x && !E->getType()->isDependentType()) {
- if (CXXRecordDecl *Record = E->getType()->getAsCXXRecordDecl()) {
- if (Record->hasTrivialCopyConstructor() &&
- Record->hasTrivialMoveConstructor() &&
- Record->hasTrivialDestructor()) {
- DiagRuntimeBehavior(E->getLocStart(), 0,
- PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg)
- << E->getType() << CT);
- TrivialEnough = true;
- }
- }
- }
+ // Diagnostics regarding non-POD argument types are
+ // emitted along with format string checking in Sema::CheckFunctionCall().
+ if (isValidVarArgType(E->getType()) == VAK_Invalid) {
+ // Turn this into a trap.
+ CXXScopeSpec SS;
+ SourceLocation TemplateKWLoc;
+ UnqualifiedId Name;
+ Name.setIdentifier(PP.getIdentifierInfo("__builtin_trap"),
+ E->getLocStart());
+ ExprResult TrapFn = ActOnIdExpression(TUScope, SS, TemplateKWLoc,
+ Name, true, false);
+ if (TrapFn.isInvalid())
+ return ExprError();
- if (!TrivialEnough &&
- getLangOpts().ObjCAutoRefCount &&
- E->getType()->isObjCLifetimeType())
- TrivialEnough = true;
-
- if (TrivialEnough) {
- // Nothing to diagnose. This is okay.
- } else if (DiagRuntimeBehavior(E->getLocStart(), 0,
- PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg)
- << getLangOpts().CPlusPlus0x << E->getType()
- << CT)) {
- // Turn this into a trap.
- CXXScopeSpec SS;
- SourceLocation TemplateKWLoc;
- UnqualifiedId Name;
- Name.setIdentifier(PP.getIdentifierInfo("__builtin_trap"),
- E->getLocStart());
- ExprResult TrapFn = ActOnIdExpression(TUScope, SS, TemplateKWLoc, Name,
- true, false);
- if (TrapFn.isInvalid())
- return ExprError();
+ ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(),
+ E->getLocStart(), MultiExprArg(),
+ E->getLocEnd());
+ if (Call.isInvalid())
+ return ExprError();
- ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(), E->getLocStart(),
- MultiExprArg(), E->getLocEnd());
- if (Call.isInvalid())
- return ExprError();
-
- ExprResult Comma = ActOnBinOp(TUScope, E->getLocStart(), tok::comma,
- Call.get(), E);
- if (Comma.isInvalid())
- return ExprError();
- E = Comma.get();
- }
+ ExprResult Comma = ActOnBinOp(TUScope, E->getLocStart(), tok::comma,
+ Call.get(), E);
+ if (Comma.isInvalid())
+ return ExprError();
+ return Comma.get();
}
- // c++ rules are enforced elsewhere.
+
if (!getLangOpts().CPlusPlus &&
RequireCompleteType(E->getExprLoc(), E->getType(),
diag::err_call_incomplete_argument))
return ExprError();
-
+
return Owned(E);
}
@@ -942,6 +1034,10 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
QualType RHSType =
Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType();
+ // For conversion purposes, we ignore any atomic qualifier on the LHS.
+ if (const AtomicType *AtomicLHS = LHSType->getAs<AtomicType>())
+ LHSType = AtomicLHS->getValueType();
+
// If both types are identical, no conversion is needed.
if (LHSType == RHSType)
return LHSType;
@@ -949,7 +1045,7 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
// If either side is a non-arithmetic type (e.g. a pointer), we are done.
// The caller can deal with this (e.g. pointer + int).
if (!LHSType->isArithmeticType() || !RHSType->isArithmeticType())
- return LHSType;
+ return QualType();
// Apply unary and bitfield promotions to the LHS's type.
QualType LHSUnpromotedType = LHSType;
@@ -1370,7 +1466,8 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
// unqualified lookup. This is useful when (for example) the
// original lookup would not have found something because it was a
// dependent name.
- DeclContext *DC = SS.isEmpty() ? CurContext : 0;
+ DeclContext *DC = (SS.isEmpty() && !CallsUndergoingInstantiation.empty())
+ ? CurContext : 0;
while (DC) {
if (isa<CXXRecordDecl>(DC)) {
LookupQualifiedName(R, DC);
@@ -1394,42 +1491,44 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
// Give a code modification hint to insert 'this->'.
// TODO: fixit for inserting 'Base<T>::' in the other cases.
// Actually quite difficult!
+ if (getLangOpts().MicrosoftMode)
+ diagnostic = diag::warn_found_via_dependent_bases_lookup;
if (isInstance) {
+ Diag(R.getNameLoc(), diagnostic) << Name
+ << FixItHint::CreateInsertion(R.getNameLoc(), "this->");
UnresolvedLookupExpr *ULE = cast<UnresolvedLookupExpr>(
CallsUndergoingInstantiation.back()->getCallee());
- CXXMethodDecl *DepMethod = cast_or_null<CXXMethodDecl>(
- CurMethod->getInstantiatedFromMemberFunction());
- if (DepMethod) {
- if (getLangOpts().MicrosoftMode)
- diagnostic = diag::warn_found_via_dependent_bases_lookup;
- Diag(R.getNameLoc(), diagnostic) << Name
- << FixItHint::CreateInsertion(R.getNameLoc(), "this->");
- QualType DepThisType = DepMethod->getThisType(Context);
- CheckCXXThisCapture(R.getNameLoc());
- CXXThisExpr *DepThis = new (Context) CXXThisExpr(
- R.getNameLoc(), DepThisType, false);
- TemplateArgumentListInfo TList;
- if (ULE->hasExplicitTemplateArgs())
- ULE->copyTemplateArgumentsInto(TList);
-
- CXXScopeSpec SS;
- SS.Adopt(ULE->getQualifierLoc());
- CXXDependentScopeMemberExpr *DepExpr =
- CXXDependentScopeMemberExpr::Create(
- Context, DepThis, DepThisType, true, SourceLocation(),
- SS.getWithLocInContext(Context),
- ULE->getTemplateKeywordLoc(), 0,
- R.getLookupNameInfo(),
- ULE->hasExplicitTemplateArgs() ? &TList : 0);
- CallsUndergoingInstantiation.back()->setCallee(DepExpr);
- } else {
- // FIXME: we should be able to handle this case too. It is correct
- // to add this-> here. This is a workaround for PR7947.
- Diag(R.getNameLoc(), diagnostic) << Name;
- }
+
+
+ CXXMethodDecl *DepMethod;
+ if (CurMethod->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization)
+ DepMethod = cast<CXXMethodDecl>(CurMethod->getPrimaryTemplate()->
+ getInstantiatedFromMemberTemplate()->getTemplatedDecl());
+ else
+ DepMethod = cast<CXXMethodDecl>(
+ CurMethod->getInstantiatedFromMemberFunction());
+ assert(DepMethod && "No template pattern found");
+
+ QualType DepThisType = DepMethod->getThisType(Context);
+ CheckCXXThisCapture(R.getNameLoc());
+ CXXThisExpr *DepThis = new (Context) CXXThisExpr(
+ R.getNameLoc(), DepThisType, false);
+ TemplateArgumentListInfo TList;
+ if (ULE->hasExplicitTemplateArgs())
+ ULE->copyTemplateArgumentsInto(TList);
+
+ CXXScopeSpec SS;
+ SS.Adopt(ULE->getQualifierLoc());
+ CXXDependentScopeMemberExpr *DepExpr =
+ CXXDependentScopeMemberExpr::Create(
+ Context, DepThis, DepThisType, true, SourceLocation(),
+ SS.getWithLocInContext(Context),
+ ULE->getTemplateKeywordLoc(), 0,
+ R.getLookupNameInfo(),
+ ULE->hasExplicitTemplateArgs() ? &TList : 0);
+ CallsUndergoingInstantiation.back()->setCallee(DepExpr);
} else {
- if (getLangOpts().MicrosoftMode)
- diagnostic = diag::warn_found_via_dependent_bases_lookup;
Diag(R.getNameLoc(), diagnostic) << Name;
}
@@ -1862,6 +1961,10 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
return ExprError();
MarkAnyDeclReferenced(Loc, IV);
+
+ ObjCMethodFamily MF = CurMethod->getMethodFamily();
+ if (MF != OMF_init && MF != OMF_dealloc && MF != OMF_finalize)
+ Diag(Loc, diag::warn_direct_ivar_access) << IV->getDeclName();
return Owned(new (Context)
ObjCIvarRefExpr(IV, IV->getType(), Loc,
SelfExpr.take(), true, true));
@@ -2303,7 +2406,7 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
// FIXME: Does the addition of const really only apply in
// potentially-evaluated contexts? Since the variable isn't actually
// captured in an unevaluated context, it seems that the answer is no.
- if (ExprEvalContexts.back().Context != Sema::Unevaluated) {
+ if (!isUnevaluatedContext()) {
QualType CapturedType = getCapturedDeclRefType(cast<VarDecl>(VD), Loc);
if (!CapturedType.isNull())
type = CapturedType;
@@ -2381,6 +2484,7 @@ ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
default: llvm_unreachable("Unknown simple primary expr!");
case tok::kw___func__: IT = PredefinedExpr::Func; break; // [C99 6.4.2.2]
case tok::kw___FUNCTION__: IT = PredefinedExpr::Function; break;
+ case tok::kw_L__FUNCTION__: IT = PredefinedExpr::LFunction; break;
case tok::kw___PRETTY_FUNCTION__: IT = PredefinedExpr::PrettyFunction; break;
}
@@ -2402,7 +2506,10 @@ ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
unsigned Length = PredefinedExpr::ComputeName(IT, currentDecl).length();
llvm::APInt LengthI(32, Length + 1);
- ResTy = Context.CharTy.withConst();
+ if (IT == PredefinedExpr::LFunction)
+ ResTy = Context.WCharTy.withConst();
+ else
+ ResTy = Context.CharTy.withConst();
ResTy = Context.getConstantArrayType(ResTy, LengthI, ArrayType::Normal, 0);
}
return Owned(new (Context) PredefinedExpr(Loc, ResTy, IT));
@@ -2603,7 +2710,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
llvm::APSInt Value(CharBits, CharIsUnsigned);
for (unsigned I = 0, N = Literal.getUDSuffixOffset(); I != N; ++I) {
Value = ThisTokBegin[I];
- TemplateArgument Arg(Value, Context.CharTy);
+ TemplateArgument Arg(Context, Value, Context.CharTy);
TemplateArgumentLocInfo ArgInfo;
ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo));
}
@@ -2647,7 +2754,12 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
diag::warn_cxx98_compat_longlong : diag::ext_longlong);
// Get the value in the widest-possible width.
- llvm::APInt ResultVal(Context.getTargetInfo().getIntMaxTWidth(), 0);
+ unsigned MaxWidth = Context.getTargetInfo().getIntMaxTWidth();
+ // The microsoft literal suffix extensions support 128-bit literals, which
+ // may be wider than [u]intmax_t.
+ if (Literal.isMicrosoftInteger && MaxWidth < 128)
+ MaxWidth = 128;
+ llvm::APInt ResultVal(MaxWidth, 0);
if (Literal.GetIntegerValue(ResultVal)) {
// If this value didn't fit into uintmax_t, warn and force to ull.
@@ -2695,7 +2807,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
}
}
- // Finally, check long long if needed.
+ // Check long long if needed.
if (Ty.isNull()) {
unsigned LongLongSize = Context.getTargetInfo().getLongLongWidth();
@@ -2712,6 +2824,16 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
Width = LongLongSize;
}
}
+
+ // If it doesn't fit in unsigned long long, and we're using Microsoft
+ // extensions, then its a 128-bit integer literal.
+ if (Ty.isNull() && Literal.isMicrosoftInteger) {
+ if (Literal.isUnsigned)
+ Ty = Context.UnsignedInt128Ty;
+ else
+ Ty = Context.Int128Ty;
+ Width = 128;
+ }
// If we still couldn't decide a type, we probably have something that
// does not fit in a signed long long, but has no U suffix.
@@ -2783,8 +2905,9 @@ static bool CheckObjCTraitOperandConstraints(Sema &S, QualType T,
SourceLocation Loc,
SourceRange ArgRange,
UnaryExprOrTypeTrait TraitKind) {
- // Reject sizeof(interface) and sizeof(interface<proto>) in 64-bit mode.
- if (S.LangOpts.ObjCNonFragileABI && T->isObjCObjectType()) {
+ // Reject sizeof(interface) and sizeof(interface<proto>) if the
+ // runtime doesn't allow it.
+ if (!S.LangOpts.ObjCRuntime.allowsSizeofAlignof() && T->isObjCObjectType()) {
S.Diag(Loc, diag::err_sizeof_nonfragile_interface)
<< T << (TraitKind == UETT_SizeOf)
<< ArgRange;
@@ -2822,9 +2945,8 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
return false;
if (RequireCompleteExprType(E,
- PDiag(diag::err_sizeof_alignof_incomplete_type)
- << ExprKind << E->getSourceRange(),
- std::make_pair(SourceLocation(), PDiag(0))))
+ diag::err_sizeof_alignof_incomplete_type,
+ ExprKind, E->getSourceRange()))
return true;
// Completeing the expression's type may have changed it.
@@ -2891,8 +3013,8 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
return false;
if (RequireCompleteType(OpLoc, ExprType,
- PDiag(diag::err_sizeof_alignof_incomplete_type)
- << ExprKind << ExprRange))
+ diag::err_sizeof_alignof_incomplete_type,
+ ExprKind, ExprRange))
return true;
if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange,
@@ -3075,6 +3197,22 @@ Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
return BuildUnaryOp(S, OpLoc, Opc, Input);
}
+/// \brief Diagnose if arithmetic on the given ObjC pointer is illegal.
+///
+/// \return true on error
+static bool checkArithmeticOnObjCPointer(Sema &S,
+ SourceLocation opLoc,
+ Expr *op) {
+ assert(op->getType()->isObjCObjectPointerType());
+ if (S.LangOpts.ObjCRuntime.allowsPointerArithmetic())
+ return false;
+
+ S.Diag(opLoc, diag::err_arithmetic_nonfragile_interface)
+ << op->getType()->castAs<ObjCObjectPointerType>()->getPointeeType()
+ << op->getSourceRange();
+ return true;
+}
+
ExprResult
Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc,
Expr *Idx, SourceLocation RLoc) {
@@ -3105,7 +3243,6 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc,
return CreateBuiltinArraySubscriptExpr(Base, LLoc, Idx, RLoc);
}
-
ExprResult
Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
Expr *Idx, SourceLocation RLoc) {
@@ -3143,13 +3280,21 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
IndexExpr = RHSExp;
ResultType = PTy->getPointeeType();
} else if (const ObjCObjectPointerType *PTy =
- LHSTy->getAs<ObjCObjectPointerType>()) {
+ LHSTy->getAs<ObjCObjectPointerType>()) {
BaseExpr = LHSExp;
IndexExpr = RHSExp;
- Result = BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0);
- if (!Result.isInvalid())
- return Owned(Result.take());
+
+ // Use custom logic if this should be the pseudo-object subscript
+ // expression.
+ if (!LangOpts.ObjCRuntime.isSubscriptPointerArithmetic())
+ return BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0);
+
ResultType = PTy->getPointeeType();
+ if (!LangOpts.ObjCRuntime.allowsPointerArithmetic()) {
+ Diag(LLoc, diag::err_subscript_nonfragile_interface)
+ << ResultType << BaseExpr->getSourceRange();
+ return ExprError();
+ }
} else if (const PointerType *PTy = RHSTy->getAs<PointerType>()) {
// Handle the uncommon case of "123[Ptr]".
BaseExpr = RHSExp;
@@ -3161,6 +3306,11 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
BaseExpr = RHSExp;
IndexExpr = LHSExp;
ResultType = PTy->getPointeeType();
+ if (!LangOpts.ObjCRuntime.allowsPointerArithmetic()) {
+ Diag(LLoc, diag::err_subscript_nonfragile_interface)
+ << ResultType << BaseExpr->getSourceRange();
+ return ExprError();
+ }
} else if (const VectorType *VTy = LHSTy->getAs<VectorType>()) {
BaseExpr = LHSExp; // vectors: V[123]
IndexExpr = RHSExp;
@@ -3230,16 +3380,8 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
if (!ResultType.hasQualifiers()) VK = VK_RValue;
} else if (!ResultType->isDependentType() &&
RequireCompleteType(LLoc, ResultType,
- PDiag(diag::err_subscript_incomplete_type)
- << BaseExpr->getSourceRange()))
- return ExprError();
-
- // Diagnose bad cases where we step over interface counts.
- if (ResultType->isObjCObjectType() && LangOpts.ObjCNonFragileABI) {
- Diag(LLoc, diag::err_subscript_nonfragile_interface)
- << ResultType << BaseExpr->getSourceRange();
+ diag::err_subscript_incomplete_type, BaseExpr))
return ExprError();
- }
assert(VK == VK_RValue || LangOpts.CPlusPlus ||
!ResultType.isCForbiddenLValueType());
@@ -3263,14 +3405,20 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
if (Param->hasUninstantiatedDefaultArg()) {
Expr *UninstExpr = Param->getUninstantiatedDefaultArg();
+ EnterExpressionEvaluationContext EvalContext(*this, PotentiallyEvaluated,
+ Param);
+
// Instantiate the expression.
MultiLevelTemplateArgumentList ArgList
= getTemplateInstantiationArgs(FD, 0, /*RelativeToPrimary=*/true);
std::pair<const TemplateArgument *, unsigned> Innermost
= ArgList.getInnermost();
- InstantiatingTemplate Inst(*this, CallLoc, Param, Innermost.first,
- Innermost.second);
+ InstantiatingTemplate Inst(*this, CallLoc, Param,
+ ArrayRef<TemplateArgument>(Innermost.first,
+ Innermost.second));
+ if (Inst)
+ return ExprError();
ExprResult Result;
{
@@ -3299,9 +3447,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
if (Result.isInvalid())
return ExprError();
+ Expr *Arg = Result.takeAs<Expr>();
+ CheckImplicitConversions(Arg, Param->getOuterLocStart());
// Build the default argument expression.
- return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param,
- Result.takeAs<Expr>()));
+ return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param, Arg));
}
// If the default expression creates temporaries, we need to
@@ -3331,6 +3480,25 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param));
}
+
+Sema::VariadicCallType
+Sema::getVariadicCallType(FunctionDecl *FDecl, const FunctionProtoType *Proto,
+ Expr *Fn) {
+ if (Proto && Proto->isVariadic()) {
+ if (dyn_cast_or_null<CXXConstructorDecl>(FDecl))
+ return VariadicConstructor;
+ else if (Fn && Fn->getType()->isBlockPointerType())
+ return VariadicBlock;
+ else if (FDecl) {
+ if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(FDecl))
+ if (Method->isInstance())
+ return VariadicMethod;
+ }
+ return VariadicFunction;
+ }
+ return VariadicDoesNotApply;
+}
+
/// ConvertArgumentsForCall - Converts the arguments specified in
/// Args/NumArgs to the parameter types of the function FDecl with
/// function prototype Proto. Call is the call expression itself, and
@@ -3365,11 +3533,18 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
// arguments for the remaining parameters), don't make the call.
if (NumArgs < NumArgsInProto) {
if (NumArgs < MinArgs) {
- Diag(RParenLoc, MinArgs == NumArgsInProto
- ? diag::err_typecheck_call_too_few_args
- : diag::err_typecheck_call_too_few_args_at_least)
- << FnKind
- << MinArgs << NumArgs << Fn->getSourceRange();
+ if (MinArgs == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName())
+ Diag(RParenLoc, MinArgs == NumArgsInProto && !Proto->isVariadic()
+ ? diag::err_typecheck_call_too_few_args_one
+ : diag::err_typecheck_call_too_few_args_at_least_one)
+ << FnKind
+ << FDecl->getParamDecl(0) << Fn->getSourceRange();
+ else
+ Diag(RParenLoc, MinArgs == NumArgsInProto && !Proto->isVariadic()
+ ? diag::err_typecheck_call_too_few_args
+ : diag::err_typecheck_call_too_few_args_at_least)
+ << FnKind
+ << MinArgs << NumArgs << Fn->getSourceRange();
// Emit the location of the prototype.
if (FDecl && !FDecl->getBuiltinID() && !IsExecConfig)
@@ -3385,14 +3560,24 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
// them.
if (NumArgs > NumArgsInProto) {
if (!Proto->isVariadic()) {
- Diag(Args[NumArgsInProto]->getLocStart(),
- MinArgs == NumArgsInProto
- ? diag::err_typecheck_call_too_many_args
- : diag::err_typecheck_call_too_many_args_at_most)
- << FnKind
- << NumArgsInProto << NumArgs << Fn->getSourceRange()
- << SourceRange(Args[NumArgsInProto]->getLocStart(),
- Args[NumArgs-1]->getLocEnd());
+ if (NumArgsInProto == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName())
+ Diag(Args[NumArgsInProto]->getLocStart(),
+ MinArgs == NumArgsInProto
+ ? diag::err_typecheck_call_too_many_args_one
+ : diag::err_typecheck_call_too_many_args_at_most_one)
+ << FnKind
+ << FDecl->getParamDecl(0) << NumArgs << Fn->getSourceRange()
+ << SourceRange(Args[NumArgsInProto]->getLocStart(),
+ Args[NumArgs-1]->getLocEnd());
+ else
+ Diag(Args[NumArgsInProto]->getLocStart(),
+ MinArgs == NumArgsInProto
+ ? diag::err_typecheck_call_too_many_args
+ : diag::err_typecheck_call_too_many_args_at_most)
+ << FnKind
+ << NumArgsInProto << NumArgs << Fn->getSourceRange()
+ << SourceRange(Args[NumArgsInProto]->getLocStart(),
+ Args[NumArgs-1]->getLocEnd());
// Emit the location of the prototype.
if (FDecl && !FDecl->getBuiltinID() && !IsExecConfig)
@@ -3405,12 +3590,8 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
}
}
SmallVector<Expr *, 8> AllArgs;
- VariadicCallType CallType =
- Proto->isVariadic() ? VariadicFunction : VariadicDoesNotApply;
- if (Fn->getType()->isBlockPointerType())
- CallType = VariadicBlock; // Block
- else if (isa<MemberExpr>(Fn))
- CallType = VariadicMethod;
+ VariadicCallType CallType = getVariadicCallType(FDecl, Proto, Fn);
+
Invalid = GatherArgumentsForCall(Call->getLocStart(), FDecl,
Proto, 0, Args, NumArgs, AllArgs, CallType);
if (Invalid)
@@ -3448,8 +3629,7 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc,
if (RequireCompleteType(Arg->getLocStart(),
ProtoArgType,
- PDiag(diag::err_call_incomplete_argument)
- << Arg->getSourceRange()))
+ diag::err_call_incomplete_argument, Arg))
return true;
// Pass the argument
@@ -3500,7 +3680,6 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc,
// If this is a variadic call, handle args passed through "...".
if (CallType != VariadicDoesNotApply) {
-
// Assume that extern "C" functions with variadic arguments that
// return __unknown_anytype aren't *really* variadic.
if (Proto->getResultType() == Context.UnknownAnyTy &&
@@ -3763,20 +3942,19 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
// Make the call expr early, before semantic checks. This guarantees cleanup
// of arguments and function on error.
CallExpr *TheCall;
- if (Config) {
+ if (Config)
TheCall = new (Context) CUDAKernelCallExpr(Context, Fn,
cast<CallExpr>(Config),
Args, NumArgs,
Context.BoolTy,
VK_RValue,
RParenLoc);
- } else {
+ else
TheCall = new (Context) CallExpr(Context, Fn,
Args, NumArgs,
Context.BoolTy,
VK_RValue,
RParenLoc);
- }
unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0);
@@ -3839,7 +4017,8 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
TheCall->setType(FuncT->getCallResultType(Context));
TheCall->setValueKind(Expr::getValueKindForType(FuncT->getResultType()));
- if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FuncT)) {
+ const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FuncT);
+ if (Proto) {
if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, NumArgs,
RParenLoc, IsExecConfig))
return ExprError();
@@ -3851,8 +4030,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
// on our knowledge of the function definition.
const FunctionDecl *Def = 0;
if (FDecl->hasBody(Def) && NumArgs != Def->param_size()) {
- const FunctionProtoType *Proto
- = Def->getType()->getAs<FunctionProtoType>();
+ Proto = Def->getType()->getAs<FunctionProtoType>();
if (!Proto || !(Proto->isVariadic() && NumArgs >= Def->param_size()))
Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments)
<< (NumArgs > Def->param_size()) << FDecl << Fn->getSourceRange();
@@ -3892,8 +4070,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
if (RequireCompleteType(Arg->getLocStart(),
Arg->getType(),
- PDiag(diag::err_call_incomplete_argument)
- << Arg->getSourceRange()))
+ diag::err_call_incomplete_argument, Arg))
return ExprError();
TheCall->setArg(i, Arg);
@@ -3911,13 +4088,13 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
// Do special checking on direct calls to functions.
if (FDecl) {
- if (CheckFunctionCall(FDecl, TheCall))
+ if (CheckFunctionCall(FDecl, TheCall, Proto))
return ExprError();
if (BuiltinID)
return CheckBuiltinFunctionCall(BuiltinID, TheCall);
} else if (NDecl) {
- if (CheckBlockCall(NDecl, TheCall))
+ if (CheckBlockCall(NDecl, TheCall, Proto))
return ExprError();
}
@@ -3946,18 +4123,17 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
if (literalType->isArrayType()) {
if (RequireCompleteType(LParenLoc, Context.getBaseElementType(literalType),
- PDiag(diag::err_illegal_decl_array_incomplete_type)
- << SourceRange(LParenLoc,
- LiteralExpr->getSourceRange().getEnd())))
+ diag::err_illegal_decl_array_incomplete_type,
+ SourceRange(LParenLoc,
+ LiteralExpr->getSourceRange().getEnd())))
return ExprError();
if (literalType->isVariableArrayType())
return ExprError(Diag(LParenLoc, diag::err_variable_object_no_init)
<< SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()));
} else if (!literalType->isDependentType() &&
RequireCompleteType(LParenLoc, literalType,
- PDiag(diag::err_typecheck_decl_incomplete_type)
- << SourceRange(LParenLoc,
- LiteralExpr->getSourceRange().getEnd())))
+ diag::err_typecheck_decl_incomplete_type,
+ SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
return ExprError();
InitializedEntity Entity
@@ -4054,11 +4230,6 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
// pointers. Everything else should be possible.
QualType SrcTy = Src.get()->getType();
- if (const AtomicType *SrcAtomicTy = SrcTy->getAs<AtomicType>())
- SrcTy = SrcAtomicTy->getValueType();
- if (const AtomicType *DestAtomicTy = DestTy->getAs<AtomicType>())
- DestTy = DestAtomicTy->getValueType();
-
if (Context.hasSameUnqualifiedType(SrcTy, DestTy))
return CK_NoOp;
@@ -4461,7 +4632,10 @@ bool Sema::DiagnoseConditionalForNull(Expr *LHSExpr, Expr *RHSExpr,
if (NullKind == Expr::NPCK_NotNull)
return false;
- if (NullKind == Expr::NPCK_ZeroInteger) {
+ if (NullKind == Expr::NPCK_ZeroExpression)
+ return false;
+
+ if (NullKind == Expr::NPCK_ZeroLiteral) {
// In this case, check to make sure that we got here from a "NULL"
// string in the source code.
NullExpr = NullExpr->IgnoreParenImpCasts();
@@ -5382,21 +5556,19 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
return Compatible;
}
+ // If we have an atomic type, try a non-atomic assignment, then just add an
+ // atomic qualification step.
if (const AtomicType *AtomicTy = dyn_cast<AtomicType>(LHSType)) {
- if (AtomicTy->getValueType() == RHSType) {
- Kind = CK_NonAtomicToAtomic;
- return Compatible;
- }
- }
-
- if (const AtomicType *AtomicTy = dyn_cast<AtomicType>(RHSType)) {
- if (AtomicTy->getValueType() == LHSType) {
- Kind = CK_AtomicToNonAtomic;
- return Compatible;
- }
+ Sema::AssignConvertType result =
+ CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind);
+ if (result != Compatible)
+ return result;
+ if (Kind != CK_NoOp)
+ RHS = ImpCastExprToType(RHS.take(), AtomicTy->getValueType(), Kind);
+ Kind = CK_NonAtomicToAtomic;
+ return Compatible;
}
-
// If the left-hand side is a reference type, then we are in a
// (rare!) case where we've allowed the use of references in C,
// e.g., as a parameter type in a built-in function. In this case,
@@ -5936,14 +6108,8 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
return QualType();
- if (!LHS.get()->getType()->isArithmeticType() ||
- !RHS.get()->getType()->isArithmeticType()) {
- if (IsCompAssign &&
- LHS.get()->getType()->isAtomicType() &&
- RHS.get()->getType()->isArithmeticType())
- return compType;
+ if (compType.isNull() || !compType->isArithmeticType())
return InvalidOperands(Loc, LHS, RHS);
- }
// Check for division by zero.
if (IsDiv &&
@@ -5971,8 +6137,7 @@ QualType Sema::CheckRemainderOperands(
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
- if (!LHS.get()->getType()->isIntegerType() ||
- !RHS.get()->getType()->isIntegerType())
+ if (compType.isNull() || !compType->isIntegerType())
return InvalidOperands(Loc, LHS, RHS);
// Check for remainder by zero.
@@ -6036,17 +6201,12 @@ static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc,
/// \returns True if pointer has incomplete type
static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc,
Expr *Operand) {
- if ((Operand->getType()->isPointerType() &&
- !Operand->getType()->isDependentType()) ||
- Operand->getType()->isObjCObjectPointerType()) {
- QualType PointeeTy = Operand->getType()->getPointeeType();
- if (S.RequireCompleteType(
- Loc, PointeeTy,
- S.PDiag(diag::err_typecheck_arithmetic_incomplete_type)
- << PointeeTy << Operand->getSourceRange()))
- return true;
- }
- return false;
+ assert(Operand->getType()->isAnyPointerType() &&
+ !Operand->getType()->isDependentType());
+ QualType PointeeTy = Operand->getType()->getPointeeType();
+ return S.RequireCompleteType(Loc, PointeeTy,
+ diag::err_typecheck_arithmetic_incomplete_type,
+ PointeeTy, Operand->getSourceRange());
}
/// \brief Check the validity of an arithmetic pointer operand.
@@ -6117,26 +6277,14 @@ static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc,
return !S.getLangOpts().CPlusPlus;
}
- if (checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) return false;
- if (checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) return false;
+ if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr))
+ return false;
+ if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr))
+ return false;
return true;
}
-/// \brief Check bad cases where we step over interface counts.
-static bool checkArithmethicPointerOnNonFragileABI(Sema &S,
- SourceLocation OpLoc,
- Expr *Op) {
- assert(Op->getType()->isAnyPointerType());
- QualType PointeeTy = Op->getType()->getPointeeType();
- if (!PointeeTy->isObjCObjectType() || !S.LangOpts.ObjCNonFragileABI)
- return true;
-
- S.Diag(OpLoc, diag::err_arithmetic_nonfragile_interface)
- << PointeeTy << Op->getSourceRange();
- return false;
-}
-
/// diagnoseStringPlusInt - Emit a warning when adding an integer to a string
/// literal.
static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc,
@@ -6208,25 +6356,31 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6
diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get());
// handle the common case first (both operands are arithmetic).
- if (LHS.get()->getType()->isArithmeticType() &&
- RHS.get()->getType()->isArithmeticType()) {
+ if (!compType.isNull() && compType->isArithmeticType()) {
if (CompLHSTy) *CompLHSTy = compType;
return compType;
}
- if (LHS.get()->getType()->isAtomicType() &&
- RHS.get()->getType()->isArithmeticType()) {
- *CompLHSTy = LHS.get()->getType();
- return compType;
- }
+ // Type-checking. Ultimately the pointer's going to be in PExp;
+ // note that we bias towards the LHS being the pointer.
+ Expr *PExp = LHS.get(), *IExp = RHS.get();
- // Put any potential pointer into PExp
- Expr* PExp = LHS.get(), *IExp = RHS.get();
- if (IExp->getType()->isAnyPointerType())
+ bool isObjCPointer;
+ if (PExp->getType()->isPointerType()) {
+ isObjCPointer = false;
+ } else if (PExp->getType()->isObjCObjectPointerType()) {
+ isObjCPointer = true;
+ } else {
std::swap(PExp, IExp);
-
- if (!PExp->getType()->isAnyPointerType())
- return InvalidOperands(Loc, LHS, RHS);
+ if (PExp->getType()->isPointerType()) {
+ isObjCPointer = false;
+ } else if (PExp->getType()->isObjCObjectPointerType()) {
+ isObjCPointer = true;
+ } else {
+ return InvalidOperands(Loc, LHS, RHS);
+ }
+ }
+ assert(PExp->getType()->isAnyPointerType());
if (!IExp->getType()->isIntegerType())
return InvalidOperands(Loc, LHS, RHS);
@@ -6234,8 +6388,7 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6
if (!checkArithmeticOpPointerOperand(*this, Loc, PExp))
return QualType();
- // Diagnose bad cases where we step over interface counts.
- if (!checkArithmethicPointerOnNonFragileABI(*this, Loc, PExp))
+ if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp))
return QualType();
// Check array bounds for pointer arithemtic
@@ -6274,24 +6427,18 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
// Enforce type constraints: C99 6.5.6p3.
// Handle the common case first (both operands are arithmetic).
- if (LHS.get()->getType()->isArithmeticType() &&
- RHS.get()->getType()->isArithmeticType()) {
+ if (!compType.isNull() && compType->isArithmeticType()) {
if (CompLHSTy) *CompLHSTy = compType;
return compType;
}
- if (LHS.get()->getType()->isAtomicType() &&
- RHS.get()->getType()->isArithmeticType()) {
- *CompLHSTy = LHS.get()->getType();
- return compType;
- }
-
// Either ptr - int or ptr - ptr.
if (LHS.get()->getType()->isAnyPointerType()) {
QualType lpointee = LHS.get()->getType()->getPointeeType();
// Diagnose bad cases where we step over interface counts.
- if (!checkArithmethicPointerOnNonFragileABI(*this, Loc, LHS.get()))
+ if (LHS.get()->getType()->isObjCObjectPointerType() &&
+ checkArithmeticOnObjCPointer(*this, Loc, LHS.get()))
return QualType();
// The result type of a pointer-int computation is the pointer type.
@@ -6560,6 +6707,163 @@ static void diagnoseFunctionPointerToVoidComparison(Sema &S, SourceLocation Loc,
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
}
+static bool isObjCObjectLiteral(ExprResult &E) {
+ switch (E.get()->getStmtClass()) {
+ case Stmt::ObjCArrayLiteralClass:
+ case Stmt::ObjCDictionaryLiteralClass:
+ case Stmt::ObjCStringLiteralClass:
+ case Stmt::ObjCBoxedExprClass:
+ return true;
+ default:
+ // Note that ObjCBoolLiteral is NOT an object literal!
+ return false;
+ }
+}
+
+static bool hasIsEqualMethod(Sema &S, const Expr *LHS, const Expr *RHS) {
+ // Get the LHS object's interface type.
+ QualType Type = LHS->getType();
+ QualType InterfaceType;
+ if (const ObjCObjectPointerType *PTy = Type->getAs<ObjCObjectPointerType>()) {
+ InterfaceType = PTy->getPointeeType();
+ if (const ObjCObjectType *iQFaceTy =
+ InterfaceType->getAsObjCQualifiedInterfaceType())
+ InterfaceType = iQFaceTy->getBaseType();
+ } else {
+ // If this is not actually an Objective-C object, bail out.
+ return false;
+ }
+
+ // If the RHS isn't an Objective-C object, bail out.
+ if (!RHS->getType()->isObjCObjectPointerType())
+ return false;
+
+ // Try to find the -isEqual: method.
+ Selector IsEqualSel = S.NSAPIObj->getIsEqualSelector();
+ ObjCMethodDecl *Method = S.LookupMethodInObjectType(IsEqualSel,
+ InterfaceType,
+ /*instance=*/true);
+ if (!Method) {
+ if (Type->isObjCIdType()) {
+ // For 'id', just check the global pool.
+ Method = S.LookupInstanceMethodInGlobalPool(IsEqualSel, SourceRange(),
+ /*receiverId=*/true,
+ /*warn=*/false);
+ } else {
+ // Check protocols.
+ Method = S.LookupMethodInQualifiedType(IsEqualSel,
+ cast<ObjCObjectPointerType>(Type),
+ /*instance=*/true);
+ }
+ }
+
+ if (!Method)
+ return false;
+
+ QualType T = Method->param_begin()[0]->getType();
+ if (!T->isObjCObjectPointerType())
+ return false;
+
+ QualType R = Method->getResultType();
+ if (!R->isScalarType())
+ return false;
+
+ return true;
+}
+
+static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc,
+ ExprResult &LHS, ExprResult &RHS,
+ BinaryOperator::Opcode Opc){
+ Expr *Literal;
+ Expr *Other;
+ if (isObjCObjectLiteral(LHS)) {
+ Literal = LHS.get();
+ Other = RHS.get();
+ } else {
+ Literal = RHS.get();
+ Other = LHS.get();
+ }
+
+ // Don't warn on comparisons against nil.
+ Other = Other->IgnoreParenCasts();
+ if (Other->isNullPointerConstant(S.getASTContext(),
+ Expr::NPC_ValueDependentIsNotNull))
+ return;
+
+ // This should be kept in sync with warn_objc_literal_comparison.
+ // LK_String should always be last, since it has its own warning flag.
+ enum {
+ LK_Array,
+ LK_Dictionary,
+ LK_Numeric,
+ LK_Boxed,
+ LK_String
+ } LiteralKind;
+
+ switch (Literal->getStmtClass()) {
+ case Stmt::ObjCStringLiteralClass:
+ // "string literal"
+ LiteralKind = LK_String;
+ break;
+ case Stmt::ObjCArrayLiteralClass:
+ // "array literal"
+ LiteralKind = LK_Array;
+ break;
+ case Stmt::ObjCDictionaryLiteralClass:
+ // "dictionary literal"
+ LiteralKind = LK_Dictionary;
+ break;
+ case Stmt::ObjCBoxedExprClass: {
+ Expr *Inner = cast<ObjCBoxedExpr>(Literal)->getSubExpr();
+ switch (Inner->getStmtClass()) {
+ case Stmt::IntegerLiteralClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::ObjCBoolLiteralExprClass:
+ case Stmt::CXXBoolLiteralExprClass:
+ // "numeric literal"
+ LiteralKind = LK_Numeric;
+ break;
+ case Stmt::ImplicitCastExprClass: {
+ CastKind CK = cast<CastExpr>(Inner)->getCastKind();
+ // Boolean literals can be represented by implicit casts.
+ if (CK == CK_IntegralToBoolean || CK == CK_IntegralCast) {
+ LiteralKind = LK_Numeric;
+ break;
+ }
+ // FALLTHROUGH
+ }
+ default:
+ // "boxed expression"
+ LiteralKind = LK_Boxed;
+ break;
+ }
+ break;
+ }
+ default:
+ llvm_unreachable("Unknown Objective-C object literal kind");
+ }
+
+ if (LiteralKind == LK_String)
+ S.Diag(Loc, diag::warn_objc_string_literal_comparison)
+ << Literal->getSourceRange();
+ else
+ S.Diag(Loc, diag::warn_objc_literal_comparison)
+ << LiteralKind << Literal->getSourceRange();
+
+ if (BinaryOperator::isEqualityOp(Opc) &&
+ hasIsEqualMethod(S, LHS.get(), RHS.get())) {
+ SourceLocation Start = LHS.get()->getLocStart();
+ SourceLocation End = S.PP.getLocForEndOfToken(RHS.get()->getLocEnd());
+ SourceRange OpRange(Loc, S.PP.getLocForEndOfToken(Loc));
+
+ S.Diag(Loc, diag::note_objc_literal_comparison_isequal)
+ << FixItHint::CreateInsertion(Start, Opc == BO_EQ ? "[" : "![")
+ << FixItHint::CreateReplacement(OpRange, "isEqual:")
+ << FixItHint::CreateInsertion(End, "]");
+ }
+}
+
// C99 6.5.8, C++ [expr.rel]
QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc, unsigned OpaqueOpc,
@@ -6884,6 +7188,9 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
if (!Context.areComparableObjCPointerTypes(LHSType, RHSType))
diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS,
/*isError*/false);
+ if (isObjCObjectLiteral(LHS) || isObjCObjectLiteral(RHS))
+ diagnoseObjCLiteralComparison(*this, Loc, LHS, RHS, Opc);
+
if (LHSIsNull && !RHSIsNull)
LHS = ImpCastExprToType(LHS.take(), RHSType, CK_BitCast);
else
@@ -7037,8 +7344,7 @@ inline QualType Sema::CheckBitwiseOperands(
LHS = LHSResult.take();
RHS = RHSResult.take();
- if (LHS.get()->getType()->isIntegralOrUnscopedEnumerationType() &&
- RHS.get()->getType()->isIntegralOrUnscopedEnumerationType())
+ if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType())
return compType;
return InvalidOperands(Loc, LHS, RHS);
}
@@ -7251,6 +7557,7 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
break;
case Expr::MLV_ArrayType:
+ case Expr::MLV_ArrayTemporary:
Diag = diag::err_typecheck_array_not_modifiable_lvalue;
NeedType = true;
break;
@@ -7271,8 +7578,7 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
case Expr::MLV_IncompleteType:
case Expr::MLV_IncompleteVoidType:
return S.RequireCompleteType(Loc, E->getType(),
- S.PDiag(diag::err_typecheck_incomplete_type_not_modifiable_lvalue)
- << E->getSourceRange());
+ diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E);
case Expr::MLV_DuplicateVectorComponents:
Diag = diag::err_typecheck_duplicate_vector_components_not_mlvalue;
break;
@@ -7297,7 +7603,27 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
return true;
}
+static void CheckIdentityFieldAssignment(Expr *LHSExpr, Expr *RHSExpr,
+ SourceLocation Loc,
+ Sema &Sema) {
+ // C / C++ fields
+ MemberExpr *ML = dyn_cast<MemberExpr>(LHSExpr);
+ MemberExpr *MR = dyn_cast<MemberExpr>(RHSExpr);
+ if (ML && MR && ML->getMemberDecl() == MR->getMemberDecl()) {
+ if (isa<CXXThisExpr>(ML->getBase()) && isa<CXXThisExpr>(MR->getBase()))
+ Sema.Diag(Loc, diag::warn_identity_field_assign) << 0;
+ }
+ // Objective-C instance variables
+ ObjCIvarRefExpr *OL = dyn_cast<ObjCIvarRefExpr>(LHSExpr);
+ ObjCIvarRefExpr *OR = dyn_cast<ObjCIvarRefExpr>(RHSExpr);
+ if (OL && OR && OL->getDecl() == OR->getDecl()) {
+ DeclRefExpr *RL = dyn_cast<DeclRefExpr>(OL->getBase()->IgnoreImpCasts());
+ DeclRefExpr *RR = dyn_cast<DeclRefExpr>(OR->getBase()->IgnoreImpCasts());
+ if (RL && RR && RL->getDecl() == RR->getDecl())
+ Sema.Diag(Loc, diag::warn_identity_field_assign) << 1;
+ }
+}
// C99 6.5.16.1
QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
@@ -7314,6 +7640,10 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
CompoundType;
AssignConvertType ConvTy;
if (CompoundType.isNull()) {
+ Expr *RHSCheck = RHS.get();
+
+ CheckIdentityFieldAssignment(LHSExpr, RHSCheck, Loc, *this);
+
QualType LHSTy(LHSType);
ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS);
if (RHS.isInvalid())
@@ -7334,7 +7664,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
// If the RHS is a unary plus or minus, check to see if they = and + are
// right next to each other. If so, the user may have typo'd "x =+ 4"
// instead of "x += 4".
- Expr *RHSCheck = RHS.get();
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(RHSCheck))
RHSCheck = ICE->getSubExpr();
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(RHSCheck)) {
@@ -7384,8 +7713,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
// C99 6.5.17
static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc) {
- S.DiagnoseUnusedExprResult(LHS.get());
-
LHS = S.CheckPlaceholderExpr(LHS.take());
RHS = S.CheckPlaceholderExpr(RHS.take());
if (LHS.isInvalid() || RHS.isInvalid())
@@ -7401,6 +7728,8 @@ static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS,
if (LHS.isInvalid())
return QualType();
+ S.DiagnoseUnusedExprResult(LHS.get());
+
if (!S.getLangOpts().CPlusPlus) {
RHS = S.DefaultFunctionArrayLvalueConversion(RHS.take());
if (RHS.isInvalid())
@@ -7441,14 +7770,16 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
S.Diag(OpLoc, diag::warn_increment_bool) << Op->getSourceRange();
} else if (ResType->isRealType()) {
// OK!
- } else if (ResType->isAnyPointerType()) {
+ } else if (ResType->isPointerType()) {
// C99 6.5.2.4p2, 6.5.6p2
if (!checkArithmeticOpPointerOperand(S, OpLoc, Op))
return QualType();
-
- // Diagnose bad cases where we step over interface counts.
- else if (!checkArithmethicPointerOnNonFragileABI(S, OpLoc, Op))
- return QualType();
+ } else if (ResType->isObjCObjectPointerType()) {
+ // On modern runtimes, ObjC pointer arithmetic is forbidden.
+ // Otherwise, we just need a complete type.
+ if (checkArithmeticIncompletePointerType(S, OpLoc, Op) ||
+ checkArithmeticOnObjCPointer(S, OpLoc, Op))
+ return QualType();
} else if (ResType->isAnyComplexType()) {
// C99 does not support ++/-- on complex types, we allow as an extension.
S.Diag(OpLoc, diag::ext_integer_increment_complex)
@@ -8064,7 +8395,7 @@ static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc,
<< DiagRange << BinOp::getOpcodeStr(Opc) << OpStr;
SuggestParentheses(Self, OpLoc,
Self.PDiag(diag::note_precedence_bitwise_silence) << OpStr,
- RHSExpr->getSourceRange());
+ (isLeftComp ? LHSExpr : RHSExpr)->getSourceRange());
SuggestParentheses(Self, OpLoc,
Self.PDiag(diag::note_precedence_bitwise_first) << BinOp::getOpcodeStr(Opc),
ParensRange);
@@ -8669,8 +9000,7 @@ ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc,
// with an incomplete type would be ill-formed.
if (!Dependent
&& RequireCompleteType(BuiltinLoc, ArgTy,
- PDiag(diag::err_offsetof_incomplete_type)
- << TypeRange))
+ diag::err_offsetof_incomplete_type, TypeRange))
return ExprError();
// offsetof with non-identifier designators (e.g. "offsetof(x, a.b[c])") are a
@@ -8743,10 +9073,18 @@ ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc,
// The macro offsetof accepts a restricted set of type arguments in this
// International Standard. type shall be a POD structure or a POD union
// (clause 9).
+ // C++11 [support.types]p4:
+ // If type is not a standard-layout class (Clause 9), the results are
+ // undefined.
if (CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) {
- if (!CRD->isPOD() && !DidWarnAboutNonPOD &&
+ bool IsSafe = LangOpts.CPlusPlus0x? CRD->isStandardLayout() : CRD->isPOD();
+ unsigned DiagID =
+ LangOpts.CPlusPlus0x? diag::warn_offsetof_non_standardlayout_type
+ : diag::warn_offsetof_non_pod_type;
+
+ if (!IsSafe && !DidWarnAboutNonPOD &&
DiagRuntimeBehavior(BuiltinLoc, 0,
- PDiag(diag::warn_offsetof_non_pod_type)
+ PDiag(DiagID)
<< SourceRange(CompPtr[0].LocStart, OC.LocEnd)
<< CurrentType))
DidWarnAboutNonPOD = true;
@@ -8850,8 +9188,9 @@ ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc,
} else {
// The conditional expression is required to be a constant expression.
llvm::APSInt condEval(32);
- ExprResult CondICE = VerifyIntegerConstantExpression(CondExpr, &condEval,
- PDiag(diag::err_typecheck_choose_expr_requires_constant), false);
+ ExprResult CondICE
+ = VerifyIntegerConstantExpression(CondExpr, &condEval,
+ diag::err_typecheck_choose_expr_requires_constant, false);
if (CondICE.isInvalid())
return ExprError();
CondExpr = CondICE.take();
@@ -8892,7 +9231,8 @@ void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) {
PushExpressionEvaluationContext(PotentiallyEvaluated);
}
-void Sema::ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) {
+void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo,
+ Scope *CurScope) {
assert(ParamInfo.getIdentifier()==0 && "block-id should have no identifier!");
assert(ParamInfo.getContext() == Declarator::BlockLiteralContext);
BlockScopeInfo *CurBlock = getCurBlock();
@@ -8900,6 +9240,18 @@ void Sema::ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) {
TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo, CurScope);
QualType T = Sig->getType();
+ // FIXME: We should allow unexpanded parameter packs here, but that would,
+ // in turn, make the block expression contain unexpanded parameter packs.
+ if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) {
+ // Drop the parameters.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.HasTrailingReturn = false;
+ EPI.TypeQuals |= DeclSpec::TQ_const;
+ T = Context.getFunctionType(Context.DependentTy, /*Args=*/0, /*NumArgs=*/0,
+ EPI);
+ Sig = Context.getTrivialTypeSourceInfo(T);
+ }
+
// GetTypeForDeclarator always produces a function type for a block
// literal signature. Furthermore, it is always a FunctionProtoType
// unless the function was written with a typedef.
@@ -9038,7 +9390,10 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
PopExpressionEvaluationContext();
BlockScopeInfo *BSI = cast<BlockScopeInfo>(FunctionScopes.back());
-
+
+ if (BSI->HasImplicitReturnType)
+ deduceClosureReturnType(*BSI);
+
PopDeclContext();
QualType RetTy = Context.VoidTy;
@@ -9111,7 +9466,12 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
BSI->TheDecl->setBody(cast<CompoundStmt>(Body));
- computeNRVO(Body, getCurBlock());
+ // Try to apply the named return value optimization. We have to check again
+ // if we can do this, though, because blocks keep return statements around
+ // to deduce an implicit return type.
+ if (getLangOpts().CPlusPlus && RetTy->isRecordType() &&
+ !BSI->TheDecl->isDependentContext())
+ computeNRVO(Body, getCurBlock());
BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy);
const AnalysisBasedWarnings::Policy &WP = AnalysisWarnings.getDefaultPolicy();
@@ -9182,14 +9542,14 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc,
if (!TInfo->getType()->isDependentType()) {
if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(),
- PDiag(diag::err_second_parameter_to_va_arg_incomplete)
- << TInfo->getTypeLoc().getSourceRange()))
+ diag::err_second_parameter_to_va_arg_incomplete,
+ TInfo->getTypeLoc()))
return ExprError();
if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(),
- TInfo->getType(),
- PDiag(diag::err_second_parameter_to_va_arg_abstract)
- << TInfo->getTypeLoc().getSourceRange()))
+ TInfo->getType(),
+ diag::err_second_parameter_to_va_arg_abstract,
+ TInfo->getTypeLoc()))
return ExprError();
if (!TInfo->getType().isPODType(Context)) {
@@ -9291,7 +9651,10 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
bool MayHaveFunctionDiff = false;
switch (ConvTy) {
- case Compatible: return false;
+ case Compatible:
+ DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr);
+ return false;
+
case PointerToInt:
DiagKind = diag::ext_typecheck_convert_pointer_int;
ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this);
@@ -9434,15 +9797,44 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
ExprResult Sema::VerifyIntegerConstantExpression(Expr *E,
llvm::APSInt *Result) {
- return VerifyIntegerConstantExpression(E, Result,
- PDiag(diag::err_expr_not_ice) << LangOpts.CPlusPlus);
+ class SimpleICEDiagnoser : public VerifyICEDiagnoser {
+ public:
+ virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) {
+ S.Diag(Loc, diag::err_expr_not_ice) << S.LangOpts.CPlusPlus << SR;
+ }
+ } Diagnoser;
+
+ return VerifyIntegerConstantExpression(E, Result, Diagnoser);
+}
+
+ExprResult Sema::VerifyIntegerConstantExpression(Expr *E,
+ llvm::APSInt *Result,
+ unsigned DiagID,
+ bool AllowFold) {
+ class IDDiagnoser : public VerifyICEDiagnoser {
+ unsigned DiagID;
+
+ public:
+ IDDiagnoser(unsigned DiagID)
+ : VerifyICEDiagnoser(DiagID == 0), DiagID(DiagID) { }
+
+ virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) {
+ S.Diag(Loc, DiagID) << SR;
+ }
+ } Diagnoser(DiagID);
+
+ return VerifyIntegerConstantExpression(E, Result, Diagnoser, AllowFold);
+}
+
+void Sema::VerifyICEDiagnoser::diagnoseFold(Sema &S, SourceLocation Loc,
+ SourceRange SR) {
+ S.Diag(Loc, diag::ext_expr_not_ice) << SR << S.LangOpts.CPlusPlus;
}
ExprResult
Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
- const PartialDiagnostic &NotIceDiag,
- bool AllowFold,
- const PartialDiagnostic &FoldDiag) {
+ VerifyICEDiagnoser &Diagnoser,
+ bool AllowFold) {
SourceLocation DiagLoc = E->getLocStart();
if (getLangOpts().CPlusPlus0x) {
@@ -9452,23 +9844,111 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
// have a single non-explicit conversion function to an integral or
// unscoped enumeration type
ExprResult Converted;
- if (NotIceDiag.getDiagID()) {
- Converted = ConvertToIntegralOrEnumerationType(
- DiagLoc, E,
- PDiag(diag::err_ice_not_integral),
- PDiag(diag::err_ice_incomplete_type),
- PDiag(diag::err_ice_explicit_conversion),
- PDiag(diag::note_ice_conversion_here),
- PDiag(diag::err_ice_ambiguous_conversion),
- PDiag(diag::note_ice_conversion_here),
- PDiag(0),
- /*AllowScopedEnumerations*/ false);
+ if (!Diagnoser.Suppress) {
+ class CXX11ConvertDiagnoser : public ICEConvertDiagnoser {
+ public:
+ CXX11ConvertDiagnoser() : ICEConvertDiagnoser(false, true) { }
+
+ virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_ice_not_integral) << T;
+ }
+
+ virtual DiagnosticBuilder diagnoseIncomplete(Sema &S,
+ SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_ice_incomplete_type) << T;
+ }
+
+ virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S,
+ SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return S.Diag(Loc, diag::err_ice_explicit_conversion) << T << ConvTy;
+ }
+
+ virtual DiagnosticBuilder noteExplicitConv(Sema &S,
+ CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+
+ virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_ice_ambiguous_conversion) << T;
+ }
+
+ virtual DiagnosticBuilder noteAmbiguous(Sema &S,
+ CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+
+ virtual DiagnosticBuilder diagnoseConversion(Sema &S,
+ SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return DiagnosticBuilder::getEmpty();
+ }
+ } ConvertDiagnoser;
+
+ Converted = ConvertToIntegralOrEnumerationType(DiagLoc, E,
+ ConvertDiagnoser,
+ /*AllowScopedEnumerations*/ false);
} else {
// The caller wants to silently enquire whether this is an ICE. Don't
// produce any diagnostics if it isn't.
- Converted = ConvertToIntegralOrEnumerationType(
- DiagLoc, E, PDiag(), PDiag(), PDiag(), PDiag(),
- PDiag(), PDiag(), PDiag(), false);
+ class SilentICEConvertDiagnoser : public ICEConvertDiagnoser {
+ public:
+ SilentICEConvertDiagnoser() : ICEConvertDiagnoser(true, true) { }
+
+ virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return DiagnosticBuilder::getEmpty();
+ }
+
+ virtual DiagnosticBuilder diagnoseIncomplete(Sema &S,
+ SourceLocation Loc,
+ QualType T) {
+ return DiagnosticBuilder::getEmpty();
+ }
+
+ virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S,
+ SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return DiagnosticBuilder::getEmpty();
+ }
+
+ virtual DiagnosticBuilder noteExplicitConv(Sema &S,
+ CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return DiagnosticBuilder::getEmpty();
+ }
+
+ virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return DiagnosticBuilder::getEmpty();
+ }
+
+ virtual DiagnosticBuilder noteAmbiguous(Sema &S,
+ CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return DiagnosticBuilder::getEmpty();
+ }
+
+ virtual DiagnosticBuilder diagnoseConversion(Sema &S,
+ SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return DiagnosticBuilder::getEmpty();
+ }
+ } ConvertDiagnoser;
+
+ Converted = ConvertToIntegralOrEnumerationType(DiagLoc, E,
+ ConvertDiagnoser, false);
}
if (Converted.isInvalid())
return Converted;
@@ -9477,8 +9957,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
return ExprError();
} else if (!E->getType()->isIntegralOrUnscopedEnumerationType()) {
// An ICE must be of integral or unscoped enumeration type.
- if (NotIceDiag.getDiagID())
- Diag(DiagLoc, NotIceDiag) << E->getSourceRange();
+ if (!Diagnoser.Suppress)
+ Diagnoser.diagnoseNotICE(*this, DiagLoc, E->getSourceRange());
return ExprError();
}
@@ -9518,8 +9998,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
}
if (!Folded || !AllowFold) {
- if (NotIceDiag.getDiagID()) {
- Diag(DiagLoc, NotIceDiag) << E->getSourceRange();
+ if (!Diagnoser.Suppress) {
+ Diagnoser.diagnoseNotICE(*this, DiagLoc, E->getSourceRange());
for (unsigned I = 0, N = Notes.size(); I != N; ++I)
Diag(Notes[I].first, Notes[I].second);
}
@@ -9527,11 +10007,7 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
return ExprError();
}
- if (FoldDiag.getDiagID())
- Diag(DiagLoc, FoldDiag) << E->getSourceRange();
- else
- Diag(DiagLoc, diag::ext_expr_not_ice)
- << E->getSourceRange() << LangOpts.CPlusPlus;
+ Diagnoser.diagnoseFold(*this, DiagLoc, E->getSourceRange());
for (unsigned I = 0, N = Notes.size(); I != N; ++I)
Diag(Notes[I].first, Notes[I].second);
@@ -9569,7 +10045,7 @@ namespace {
// Error on DeclRefExprs referring to FieldDecls.
ExprResult TransformDeclRefExpr(DeclRefExpr *E) {
if (isa<FieldDecl>(E->getDecl()) &&
- SemaRef.ExprEvalContexts.back().Context != Sema::Unevaluated)
+ !SemaRef.isUnevaluatedContext())
return SemaRef.Diag(E->getLocation(),
diag::err_invalid_non_static_member_use)
<< E->getDecl() << E->getSourceRange();
@@ -9774,11 +10250,11 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) {
// FIXME: Is this really right?
if (CurContext == Func) return;
- // Instantiate the exception specification for any function which is
+ // Resolve the exception specification for any function which is
// used: CodeGen will need it.
const FunctionProtoType *FPT = Func->getType()->getAs<FunctionProtoType>();
- if (FPT && FPT->getExceptionSpecType() == EST_Uninstantiated)
- InstantiateExceptionSpec(Loc, Func);
+ if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
+ ResolveExceptionSpec(Loc, FPT);
// Implicit instantiation of function templates and member functions of
// class templates.
@@ -9891,14 +10367,15 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
VarDecl *Var, QualType FieldType,
QualType DeclRefType,
- SourceLocation Loc) {
+ SourceLocation Loc,
+ bool RefersToEnclosingLocal) {
CXXRecordDecl *Lambda = LSI->Lambda;
// Build the non-static data member.
FieldDecl *Field
= FieldDecl::Create(S.Context, Lambda, Loc, Loc, 0, FieldType,
S.Context.getTrivialTypeSourceInfo(FieldType, Loc),
- 0, false, false);
+ 0, false, ICIS_NoInit);
Field->setImplicit(true);
Field->setAccess(AS_private);
Lambda->addDecl(Field);
@@ -9920,8 +10397,8 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
// C++ [expr.prim.labda]p12:
// An entity captured by a lambda-expression is odr-used (3.2) in
// the scope containing the lambda-expression.
- Expr *Ref = new (S.Context) DeclRefExpr(Var, false, DeclRefType,
- VK_LValue, Loc);
+ Expr *Ref = new (S.Context) DeclRefExpr(Var, RefersToEnclosingLocal,
+ DeclRefType, VK_LValue, Loc);
Var->setReferenced(true);
Var->setUsed(true);
@@ -10264,7 +10741,8 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
Expr *CopyExpr = 0;
if (BuildAndDiagnose) {
ExprResult Result = captureInLambda(*this, LSI, Var, CaptureType,
- DeclRefType, Loc);
+ DeclRefType, Loc,
+ I == N-1);
if (!Result.isInvalid())
CopyExpr = Result.take();
}
@@ -10439,6 +10917,23 @@ static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc,
}
SemaRef.MarkAnyDeclReferenced(Loc, D);
+
+ // If this is a call to a method via a cast, also mark the method in the
+ // derived class used in case codegen can devirtualize the call.
+ const MemberExpr *ME = dyn_cast<MemberExpr>(E);
+ if (!ME)
+ return;
+ CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ME->getMemberDecl());
+ if (!MD)
+ return;
+ const Expr *Base = ME->getBase();
+ const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType();
+ if (!MostDerivedClassDecl)
+ return;
+ CXXMethodDecl *DM = MD->getCorrespondingMethodInClass(MostDerivedClassDecl);
+ if (!DM)
+ return;
+ SemaRef.MarkAnyDeclReferenced(Loc, DM);
}
/// \brief Perform reference-marking and odr-use handling for a DeclRefExpr.
@@ -10645,18 +11140,30 @@ bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc,
return false;
}
- PartialDiagnostic Note =
- FD ? PDiag(diag::note_function_with_incomplete_return_type_declared_here)
- << FD->getDeclName() : PDiag();
- SourceLocation NoteLoc = FD ? FD->getLocation() : SourceLocation();
-
- if (RequireCompleteType(Loc, ReturnType,
- FD ?
- PDiag(diag::err_call_function_incomplete_return)
- << CE->getSourceRange() << FD->getDeclName() :
- PDiag(diag::err_call_incomplete_return)
- << CE->getSourceRange(),
- std::make_pair(NoteLoc, Note)))
+ class CallReturnIncompleteDiagnoser : public TypeDiagnoser {
+ FunctionDecl *FD;
+ CallExpr *CE;
+
+ public:
+ CallReturnIncompleteDiagnoser(FunctionDecl *FD, CallExpr *CE)
+ : FD(FD), CE(CE) { }
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ if (!FD) {
+ S.Diag(Loc, diag::err_call_incomplete_return)
+ << T << CE->getSourceRange();
+ return;
+ }
+
+ S.Diag(Loc, diag::err_call_function_incomplete_return)
+ << CE->getSourceRange() << FD->getDeclName() << T;
+ S.Diag(FD->getLocation(),
+ diag::note_function_with_incomplete_return_type_declared_here)
+ << FD->getDeclName();
+ }
+ } Diagnoser(FD, CE);
+
+ if (RequireCompleteType(Loc, ReturnType, Diagnoser))
return true;
return false;
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index af86cb2..2740259 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -6,9 +6,10 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file implements semantic analysis for C++ expressions.
-//
+///
+/// \file
+/// \brief Implements semantic analysis for C++ expressions.
+///
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaInternal.h"
@@ -332,7 +333,7 @@ ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType,
// When typeid is applied to an expression other than an glvalue of a
// polymorphic class type [...] [the] expression is an unevaluated
// operand. [...]
- if (RecordD->isPolymorphic() && E->Classify(Context).isGLValue()) {
+ if (RecordD->isPolymorphic() && E->isGLValue()) {
// The subexpression is potentially evaluated; switch the context
// and recheck the subexpression.
ExprResult Result = TranformToPotentiallyEvaluated(E);
@@ -375,10 +376,20 @@ Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc,
LookupResult R(*this, TypeInfoII, SourceLocation(), LookupTagName);
LookupQualifiedName(R, getStdNamespace());
CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
+ // Microsoft's typeinfo doesn't have type_info in std but in the global
+ // namespace if _HAS_EXCEPTIONS is defined to 0. See PR13153.
+ if (!CXXTypeInfoDecl && LangOpts.MicrosoftMode) {
+ LookupQualifiedName(R, Context.getTranslationUnitDecl());
+ CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
+ }
if (!CXXTypeInfoDecl)
return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
}
+ if (!getLangOpts().RTTI) {
+ return ExprError(Diag(OpLoc, diag::err_no_typeid_with_fno_rtti));
+ }
+
QualType TypeInfoType = Context.getTypeDeclType(CXXTypeInfoDecl);
if (isType) {
@@ -584,14 +595,13 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E,
}
if (!isPointer || !Ty->isVoidType()) {
if (RequireCompleteType(ThrowLoc, Ty,
- PDiag(isPointer ? diag::err_throw_incomplete_ptr
- : diag::err_throw_incomplete)
- << E->getSourceRange()))
+ isPointer? diag::err_throw_incomplete_ptr
+ : diag::err_throw_incomplete,
+ E->getSourceRange()))
return ExprError();
if (RequireNonAbstractType(ThrowLoc, E->getType(),
- PDiag(diag::err_throw_abstract_type)
- << E->getSourceRange()))
+ diag::err_throw_abstract_type, E))
return ExprError();
}
@@ -737,7 +747,7 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) {
FieldDecl *Field
= FieldDecl::Create(Context, Lambda, Loc, Loc, 0, ThisTy,
Context.getTrivialTypeSourceInfo(ThisTy, Loc),
- 0, false, false);
+ 0, false, ICIS_NoInit);
Field->setImplicit(true);
Field->setAccess(AS_private);
Lambda->addDecl(Field);
@@ -839,8 +849,7 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
if (!Ty->isVoidType() &&
RequireCompleteType(TyBeginLoc, ElemTy,
- PDiag(diag::err_invalid_incomplete_type_use)
- << FullRange))
+ diag::err_invalid_incomplete_type_use, FullRange))
return ExprError();
if (RequireNonAbstractType(TyBeginLoc, Ty,
@@ -932,7 +941,7 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
}
/// \brief Parsed a C++ 'new' expression (C++ 5.3.4).
-
+///
/// E.g.:
/// @code new (memory) int[size][4] @endcode
/// or
@@ -945,10 +954,8 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
/// \param PlacementRParen Closing paren of the placement arguments.
/// \param TypeIdParens If the type is in parens, the source range.
/// \param D The type to be allocated, as well as array dimensions.
-/// \param ConstructorLParen Opening paren of the constructor args, empty if
-/// initializer-list syntax is used.
-/// \param ConstructorArgs Constructor/initialization arguments.
-/// \param ConstructorRParen Closing paren of the constructor args.
+/// \param Initializer The initializing expression or initializer-list, or null
+/// if there is none.
ExprResult
Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
@@ -960,7 +967,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
// If the specified type is an array, unwrap it and save the expression.
if (D.getNumTypeObjects() > 0 &&
D.getTypeObject(0).Kind == DeclaratorChunk::Array) {
- DeclaratorChunk &Chunk = D.getTypeObject(0);
+ DeclaratorChunk &Chunk = D.getTypeObject(0);
if (TypeContainsAuto)
return ExprError(Diag(Chunk.Loc, diag::err_new_array_of_auto)
<< D.getSourceRange());
@@ -984,8 +991,10 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
DeclaratorChunk::ArrayTypeInfo &Array = D.getTypeObject(I).Arr;
if (Expr *NumElts = (Expr *)Array.NumElts) {
if (!NumElts->isTypeDependent() && !NumElts->isValueDependent()) {
- Array.NumElts = VerifyIntegerConstantExpression(NumElts, 0,
- PDiag(diag::err_new_array_nonconst)).take();
+ Array.NumElts
+ = VerifyIntegerConstantExpression(NumElts, 0,
+ diag::err_new_array_nonconst)
+ .take();
if (!Array.NumElts)
return ExprError();
}
@@ -1084,8 +1093,10 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
}
}
- // C++0x [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
- if (TypeMayContainAuto && AllocType->getContainedAutoType()) {
+ // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
+ AutoType *AT = 0;
+ if (TypeMayContainAuto &&
+ (AT = AllocType->getContainedAutoType()) && !AT->isDeduced()) {
if (initStyle == CXXNewExpr::NoInit || NumInits == 0)
return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg)
<< AllocType << TypeRange);
@@ -1101,8 +1112,7 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
}
Expr *Deduce = Inits[0];
TypeSourceInfo *DeducedType = 0;
- if (DeduceAutoType(AllocTypeInfo, Deduce, DeducedType) ==
- DAR_Failed)
+ if (DeduceAutoType(AllocTypeInfo, Deduce, DeducedType) == DAR_Failed)
return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure)
<< AllocType << Deduce->getType()
<< TypeRange << Deduce->getSourceRange());
@@ -1150,19 +1160,64 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal,
// enumeration type, or a class type for which a single non-explicit
// conversion function to integral or unscoped enumeration type exists.
if (ArraySize && !ArraySize->isTypeDependent()) {
- ExprResult ConvertedSize = ConvertToIntegralOrEnumerationType(
- StartLoc, ArraySize,
- PDiag(diag::err_array_size_not_integral) << getLangOpts().CPlusPlus0x,
- PDiag(diag::err_array_size_incomplete_type)
- << ArraySize->getSourceRange(),
- PDiag(diag::err_array_size_explicit_conversion),
- PDiag(diag::note_array_size_conversion),
- PDiag(diag::err_array_size_ambiguous_conversion),
- PDiag(diag::note_array_size_conversion),
- PDiag(getLangOpts().CPlusPlus0x ?
- diag::warn_cxx98_compat_array_size_conversion :
- diag::ext_array_size_conversion),
- /*AllowScopedEnumerations*/ false);
+ class SizeConvertDiagnoser : public ICEConvertDiagnoser {
+ Expr *ArraySize;
+
+ public:
+ SizeConvertDiagnoser(Expr *ArraySize)
+ : ICEConvertDiagnoser(false, false), ArraySize(ArraySize) { }
+
+ virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_array_size_not_integral)
+ << S.getLangOpts().CPlusPlus0x << T;
+ }
+
+ virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_array_size_incomplete_type)
+ << T << ArraySize->getSourceRange();
+ }
+
+ virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S,
+ SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return S.Diag(Loc, diag::err_array_size_explicit_conversion) << T << ConvTy;
+ }
+
+ virtual DiagnosticBuilder noteExplicitConv(Sema &S,
+ CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return S.Diag(Conv->getLocation(), diag::note_array_size_conversion)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+
+ virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_array_size_ambiguous_conversion) << T;
+ }
+
+ virtual DiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return S.Diag(Conv->getLocation(), diag::note_array_size_conversion)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+
+ virtual DiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return S.Diag(Loc,
+ S.getLangOpts().CPlusPlus0x
+ ? diag::warn_cxx98_compat_array_size_conversion
+ : diag::ext_array_size_conversion)
+ << T << ConvTy->isEnumeralType() << ConvTy;
+ }
+ } SizeDiagnoser(ArraySize);
+
+ ExprResult ConvertedSize
+ = ConvertToIntegralOrEnumerationType(StartLoc, ArraySize, SizeDiagnoser,
+ /*AllowScopedEnumerations*/ false);
if (ConvertedSize.isInvalid())
return ExprError();
@@ -1401,9 +1456,7 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
return Diag(Loc, diag::err_bad_new_type)
<< AllocType << 1 << R;
else if (!AllocType->isDependentType() &&
- RequireCompleteType(Loc, AllocType,
- PDiag(diag::err_new_incomplete_type)
- << R))
+ RequireCompleteType(Loc, AllocType, diag::err_new_incomplete_type,R))
return true;
else if (RequireNonAbstractType(Loc, AllocType,
diag::err_allocation_of_abstract_type))
@@ -2014,7 +2067,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
if (const RecordType *Record = Type->getAs<RecordType>()) {
if (RequireCompleteType(StartLoc, Type,
- PDiag(diag::err_delete_incomplete_class_type)))
+ diag::err_delete_incomplete_class_type))
return ExprError();
SmallVector<CXXConversionDecl*, 4> ObjectPtrConversions;
@@ -2084,8 +2137,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
<< Type << Ex.get()->getSourceRange());
} else if (!Pointee->isDependentType()) {
if (!RequireCompleteType(StartLoc, Pointee,
- PDiag(diag::warn_delete_incomplete)
- << Ex.get()->getSourceRange())) {
+ diag::warn_delete_incomplete, Ex.get())) {
if (const RecordType *RT = PointeeElem->getAs<RecordType>())
PointeeRD = cast<CXXRecordDecl>(RT->getDecl());
}
@@ -2096,9 +2148,6 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
// delete-expression; it is not necessary to cast away the constness
// (5.2.11) of the pointer expression before it is used as the operand
// of the delete-expression. ]
- if (!Context.hasSameType(Ex.get()->getType(), Context.VoidPtrTy))
- Ex = Owned(ImplicitCastExpr::Create(Context, Context.VoidPtrTy,
- CK_BitCast, Ex.take(), 0, VK_RValue));
if (Pointee->isArrayType() && !ArrayForm) {
Diag(StartLoc, diag::warn_delete_array_type)
@@ -2176,6 +2225,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
DeclareGlobalNewDelete();
DeclContext *TUDecl = Context.getTranslationUnitDecl();
Expr *Arg = Ex.get();
+ if (!Context.hasSameType(Arg->getType(), Context.VoidPtrTy))
+ Arg = ImplicitCastExpr::Create(Context, Context.VoidPtrTy,
+ CK_BitCast, Arg, 0, VK_RValue);
if (FindAllocationOverload(StartLoc, SourceRange(), DeleteName,
&Arg, 1, TUDecl, /*AllowMissing=*/false,
OperatorDelete))
@@ -3138,8 +3190,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT)
return false;
- if (CPT->getExceptionSpecType() == EST_Delayed)
- return false;
if (!CPT->isNothrow(Self.Context))
return false;
}
@@ -3180,8 +3230,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT)
return false;
- if (CPT->getExceptionSpecType() == EST_Delayed)
- return false;
// FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1)
@@ -3218,8 +3266,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT)
return false;
- if (CPT->getExceptionSpecType() == EST_Delayed)
- return false;
// TODO: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
return CPT->isNothrow(Self.Context) && CPT->getNumArgs() == 0;
@@ -3284,6 +3330,25 @@ ExprResult Sema::ActOnBinaryTypeTrait(BinaryTypeTrait BTT,
return BuildBinaryTypeTrait(BTT, KWLoc, LhsTSInfo, RhsTSInfo, RParen);
}
+/// \brief Determine whether T has a non-trivial Objective-C lifetime in
+/// ARC mode.
+static bool hasNontrivialObjCLifetime(QualType T) {
+ switch (T.getObjCLifetime()) {
+ case Qualifiers::OCL_ExplicitNone:
+ return false;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return true;
+
+ case Qualifiers::OCL_None:
+ return T->isObjCLifetimeType();
+ }
+
+ llvm_unreachable("Unknown ObjC lifetime qualifier");
+}
+
static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc) {
@@ -3357,8 +3422,14 @@ static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc,
ArgExprs.size()));
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;
-
- // The initialization succeeded; not make sure there are no non-trivial
+
+ // Under Objective-C ARC, if the destination has non-trivial Objective-C
+ // lifetime, this is a non-trivial construction.
+ if (S.getLangOpts().ObjCAutoRefCount &&
+ hasNontrivialObjCLifetime(Args[0]->getType().getNonReferenceType()))
+ return false;
+
+ // The initialization succeeded; now make sure there are no non-trivial
// calls.
return !Result.get()->hasNonTrivialCall(S.Context);
}
@@ -3471,9 +3542,25 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, BinaryTypeTrait BTT,
// We model the initialization as a copy-initialization of a temporary
// of the appropriate type, which for this expression is identical to the
// return statement (since NRVO doesn't apply).
+
+ // Functions aren't allowed to return function or array types.
+ if (RhsT->isFunctionType() || RhsT->isArrayType())
+ return false;
+
+ // A return statement in a void function must have void type.
+ if (RhsT->isVoidType())
+ return LhsT->isVoidType();
+
+ // A function definition requires a complete, non-abstract return type.
+ if (Self.RequireCompleteType(KeyLoc, RhsT, 0) ||
+ Self.RequireNonAbstractType(KeyLoc, RhsT, 0))
+ return false;
+
+ // Compute the result of add_rvalue_reference.
if (LhsT->isObjectType() || LhsT->isFunctionType())
LhsT = Self.Context.getRValueReferenceType(LhsT);
-
+
+ // Build a fake source and destination for initialization.
InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
Expr::getValueKindForType(LhsT));
@@ -3539,6 +3626,12 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, BinaryTypeTrait BTT,
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;
+ // Under Objective-C ARC, if the destination has non-trivial Objective-C
+ // lifetime, this is a non-trivial assignment.
+ if (Self.getLangOpts().ObjCAutoRefCount &&
+ hasNontrivialObjCLifetime(LhsT.getNonReferenceType()))
+ return false;
+
return !Result.get()->hasNonTrivialCall(Self.Context);
}
}
@@ -3615,7 +3708,7 @@ static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
llvm::APSInt Value;
uint64_t Dim;
if (Self.VerifyIntegerConstantExpression(DimExpr, &Value,
- Self.PDiag(diag::err_dimension_expr_not_constant_integer),
+ diag::err_dimension_expr_not_constant_integer,
false).isInvalid())
return 0;
if (Value.isSigned() && Value.isNegative()) {
@@ -3767,8 +3860,8 @@ QualType Sema::CheckPointerToMemberOperands(ExprResult &LHS, ExprResult &RHS,
if (!Context.hasSameUnqualifiedType(Class, LHSType)) {
// If we want to check the hierarchy, we need a complete type.
- if (RequireCompleteType(Loc, LHSType, PDiag(diag::err_bad_memptr_lhs)
- << OpSpelling << (int)isIndirect)) {
+ if (RequireCompleteType(Loc, LHSType, diag::err_bad_memptr_lhs,
+ OpSpelling, (int)isIndirect)) {
return QualType();
}
CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
@@ -4023,13 +4116,14 @@ static bool ConvertForConditional(Sema &Self, ExprResult &E, QualType T) {
///
/// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y
/// extension. In this case, LHS == Cond. (But they're not aliases.)
-QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, ExprResult &RHS,
- ExprValueKind &VK, ExprObjectKind &OK,
+QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
+ ExprResult &RHS, ExprValueKind &VK,
+ ExprObjectKind &OK,
SourceLocation QuestionLoc) {
// FIXME: Handle C99's complex types, vector types, block pointers and Obj-C++
// interface pointers.
- // C++0x 5.16p1
+ // C++11 [expr.cond]p1
// The first expression is contextually converted to bool.
if (!Cond.get()->isTypeDependent()) {
ExprResult CondRes = CheckCXXBooleanCondition(Cond.take());
@@ -4046,7 +4140,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
if (LHS.get()->isTypeDependent() || RHS.get()->isTypeDependent())
return Context.DependentTy;
- // C++0x 5.16p2
+ // C++11 [expr.cond]p2
// If either the second or the third operand has type (cv) void, ...
QualType LTy = LHS.get()->getType();
QualType RTy = RHS.get()->getType();
@@ -4059,12 +4153,26 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
RHS = DefaultFunctionArrayLvalueConversion(RHS.take());
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
+
+ // Finish off the lvalue-to-rvalue conversion by copy-initializing a
+ // temporary if necessary. DefaultFunctionArrayLvalueConversion doesn't
+ // do this part for us.
+ ExprResult &NonVoid = LVoid ? RHS : LHS;
+ if (NonVoid.get()->getType()->isRecordType() &&
+ NonVoid.get()->isGLValue()) {
+ InitializedEntity Entity =
+ InitializedEntity::InitializeTemporary(NonVoid.get()->getType());
+ NonVoid = PerformCopyInitialization(Entity, SourceLocation(), NonVoid);
+ if (NonVoid.isInvalid())
+ return QualType();
+ }
+
LTy = LHS.get()->getType();
RTy = RHS.get()->getType();
// ... and one of the following shall hold:
// -- The second or the third operand (but not both) is a throw-
- // expression; the result is of the type of the other and is an rvalue.
+ // expression; the result is of the type of the other and is a prvalue.
bool LThrow = isa<CXXThrowExpr>(LHS.get());
bool RThrow = isa<CXXThrowExpr>(RHS.get());
if (LThrow && !RThrow)
@@ -4073,7 +4181,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
return LTy;
// -- Both the second and third operands have type void; the result is of
- // type void and is an rvalue.
+ // type void and is a prvalue.
if (LVoid && RVoid)
return Context.VoidTy;
@@ -4086,10 +4194,10 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
// Neither is void.
- // C++0x 5.16p3
+ // C++11 [expr.cond]p3
// Otherwise, if the second and third operand have different types, and
- // either has (cv) class type, and attempt is made to convert each of those
- // operands to the other.
+ // either has (cv) class type [...] an attempt is made to convert each of
+ // those operands to the type of the other.
if (!Context.hasSameType(LTy, RTy) &&
(LTy->isRecordType() || RTy->isRecordType())) {
ImplicitConversionSequence ICSLeftToRight, ICSRightToLeft;
@@ -4122,7 +4230,31 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
}
}
- // C++0x 5.16p4
+ // C++11 [expr.cond]p3
+ // if both are glvalues of the same value category and the same type except
+ // for cv-qualification, an attempt is made to convert each of those
+ // operands to the type of the other.
+ ExprValueKind LVK = LHS.get()->getValueKind();
+ ExprValueKind RVK = RHS.get()->getValueKind();
+ if (!Context.hasSameType(LTy, RTy) &&
+ Context.hasSameUnqualifiedType(LTy, RTy) &&
+ LVK == RVK && LVK != VK_RValue) {
+ // Since the unqualified types are reference-related and we require the
+ // result to be as if a reference bound directly, the only conversion
+ // we can perform is to add cv-qualifiers.
+ Qualifiers LCVR = Qualifiers::fromCVRMask(LTy.getCVRQualifiers());
+ Qualifiers RCVR = Qualifiers::fromCVRMask(RTy.getCVRQualifiers());
+ if (RCVR.isStrictSupersetOf(LCVR)) {
+ LHS = ImpCastExprToType(LHS.take(), RTy, CK_NoOp, LVK);
+ LTy = LHS.get()->getType();
+ }
+ else if (LCVR.isStrictSupersetOf(RCVR)) {
+ RHS = ImpCastExprToType(RHS.take(), LTy, CK_NoOp, RVK);
+ RTy = RHS.get()->getType();
+ }
+ }
+
+ // C++11 [expr.cond]p4
// If the second and third operands are glvalues of the same value
// category and have the same type, the result is of that type and
// value category and it is a bit-field if the second or the third
@@ -4130,9 +4262,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
// We only extend this to bitfields, not to the crazy other kinds of
// l-values.
bool Same = Context.hasSameType(LTy, RTy);
- if (Same &&
- LHS.get()->isGLValue() &&
- LHS.get()->getValueKind() == RHS.get()->getValueKind() &&
+ if (Same && LVK == RVK && LVK != VK_RValue &&
LHS.get()->isOrdinaryOrBitFieldObject() &&
RHS.get()->isOrdinaryOrBitFieldObject()) {
VK = LHS.get()->getValueKind();
@@ -4142,8 +4272,8 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
return LTy;
}
- // C++0x 5.16p5
- // Otherwise, the result is an rvalue. If the second and third operands
+ // C++11 [expr.cond]p5
+ // Otherwise, the result is a prvalue. If the second and third operands
// do not have the same type, and either has (cv) class type, ...
if (!Same && (LTy->isRecordType() || RTy->isRecordType())) {
// ... overload resolution is used to determine the conversions (if any)
@@ -4153,8 +4283,8 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
return QualType();
}
- // C++0x 5.16p6
- // LValue-to-rvalue, array-to-pointer, and function-to-pointer standard
+ // C++11 [expr.cond]p6
+ // Lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard
// conversions are performed on the second and third operands.
LHS = DefaultFunctionArrayLvalueConversion(LHS.take());
RHS = DefaultFunctionArrayLvalueConversion(RHS.take());
@@ -4207,9 +4337,11 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
}
// -- The second and third operands have pointer type, or one has pointer
- // type and the other is a null pointer constant; pointer conversions
- // and qualification conversions are performed to bring them to their
- // composite pointer type. The result is of the composite pointer type.
+ // type and the other is a null pointer constant, or both are null
+ // pointer constants, at least one of which is non-integral; pointer
+ // conversions and qualification conversions are performed to bring them
+ // to their composite pointer type. The result is of the composite
+ // pointer type.
// -- The second and third operands have pointer to member type, or one has
// pointer to member type and the other is a null pointer constant;
// pointer to member conversions and qualification conversions are
@@ -4247,7 +4379,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex
/// \brief Find a merged pointer type and convert the two expressions to it.
///
/// This finds the composite pointer type (or member pointer type) for @p E1
-/// and @p E2 according to C++0x 5.9p2. It converts both expressions to this
+/// and @p E2 according to C++11 5.9p2. It converts both expressions to this
/// type and returns it.
/// It does not emit diagnostics.
///
@@ -4267,15 +4399,27 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
assert(getLangOpts().CPlusPlus && "This function assumes C++");
QualType T1 = E1->getType(), T2 = E2->getType();
- if (!T1->isAnyPointerType() && !T1->isMemberPointerType() &&
- !T2->isAnyPointerType() && !T2->isMemberPointerType())
- return QualType();
-
- // C++0x 5.9p2
+ // C++11 5.9p2
// Pointer conversions and qualification conversions are performed on
// pointer operands to bring them to their composite pointer type. If
// one operand is a null pointer constant, the composite pointer type is
- // the type of the other operand.
+ // std::nullptr_t if the other operand is also a null pointer constant or,
+ // if the other operand is a pointer, the type of the other operand.
+ if (!T1->isAnyPointerType() && !T1->isMemberPointerType() &&
+ !T2->isAnyPointerType() && !T2->isMemberPointerType()) {
+ if (T1->isNullPtrType() &&
+ E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
+ E2 = ImpCastExprToType(E2, T1, CK_NullToPointer).take();
+ return T1;
+ }
+ if (T2->isNullPtrType() &&
+ E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
+ E1 = ImpCastExprToType(E1, T2, CK_NullToPointer).take();
+ return T2;
+ }
+ return QualType();
+ }
+
if (E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
if (T2->isMemberPointerType())
E1 = ImpCastExprToType(E1, T2, CK_NullToMemberPointer).take();
@@ -4522,8 +4666,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
ObjCMethodDecl *D = 0;
if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
D = Send->getMethodDecl();
- } else if (ObjCNumericLiteral *NumLit = dyn_cast<ObjCNumericLiteral>(E)) {
- D = NumLit->getObjCNumericLiteralMethod();
+ } else if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(E)) {
+ D = BoxedExpr->getBoxingMethod();
} else if (ObjCArrayLiteral *ArrayLit = dyn_cast<ObjCArrayLiteral>(E)) {
D = ArrayLit->getArrayWithObjectsMethod();
} else if (ObjCDictionaryLiteral *DictLit
@@ -4706,6 +4850,11 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
// Disable the special decltype handling now.
Rec.IsDecltype = false;
+ // In MS mode, don't perform any extra checking of call return types within a
+ // decltype expression.
+ if (getLangOpts().MicrosoftMode)
+ return Owned(E);
+
// Perform the semantic checks we delayed until this point.
CallExpr *TopCall = dyn_cast<CallExpr>(E);
for (unsigned I = 0, N = Rec.DelayedDecltypeCalls.size(); I != N; ++I) {
@@ -4733,11 +4882,11 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
CXXDestructorDecl *Destructor = LookupDestructor(RD);
Temp->setDestructor(Destructor);
- MarkFunctionReferenced(E->getExprLoc(), Destructor);
- CheckDestructorAccess(E->getExprLoc(), Destructor,
+ MarkFunctionReferenced(Bind->getExprLoc(), Destructor);
+ CheckDestructorAccess(Bind->getExprLoc(), Destructor,
PDiag(diag::err_access_dtor_temp)
- << E->getType());
- DiagnoseUseOfDecl(Destructor, E->getExprLoc());
+ << Bind->getType());
+ DiagnoseUseOfDecl(Destructor, Bind->getExprLoc());
// We need a cleanup, but we don't need to remember the temporary.
ExprNeedsCleanups = true;
@@ -4833,8 +4982,7 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc,
// the member function body.
if (!BaseType->isDependentType() &&
!isThisOutsideMemberFunctionBody(BaseType) &&
- RequireCompleteType(OpLoc, BaseType,
- PDiag(diag::err_incomplete_member_access)))
+ RequireCompleteType(OpLoc, BaseType, diag::err_incomplete_member_access))
return ExprError();
// C++ [basic.lookup.classref]p2:
@@ -5222,6 +5370,61 @@ ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation,
return BuildCXXNoexceptExpr(KeyLoc, Operand, RParen);
}
+static bool IsSpecialDiscardedValue(Expr *E) {
+ // In C++11, discarded-value expressions of a certain form are special,
+ // according to [expr]p10:
+ // The lvalue-to-rvalue conversion (4.1) is applied only if the
+ // expression is an lvalue of volatile-qualified type and it has
+ // one of the following forms:
+ E = E->IgnoreParens();
+
+ // - id-expression (5.1.1),
+ if (isa<DeclRefExpr>(E))
+ return true;
+
+ // - subscripting (5.2.1),
+ if (isa<ArraySubscriptExpr>(E))
+ return true;
+
+ // - class member access (5.2.5),
+ if (isa<MemberExpr>(E))
+ return true;
+
+ // - indirection (5.3.1),
+ if (UnaryOperator *UO = dyn_cast<UnaryOperator>(E))
+ if (UO->getOpcode() == UO_Deref)
+ return true;
+
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+ // - pointer-to-member operation (5.5),
+ if (BO->isPtrMemOp())
+ return true;
+
+ // - comma expression (5.18) where the right operand is one of the above.
+ if (BO->getOpcode() == BO_Comma)
+ return IsSpecialDiscardedValue(BO->getRHS());
+ }
+
+ // - conditional expression (5.16) where both the second and the third
+ // operands are one of the above, or
+ if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E))
+ return IsSpecialDiscardedValue(CO->getTrueExpr()) &&
+ IsSpecialDiscardedValue(CO->getFalseExpr());
+ // The related edge case of "*x ?: *x".
+ if (BinaryConditionalOperator *BCO =
+ dyn_cast<BinaryConditionalOperator>(E)) {
+ if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(BCO->getTrueExpr()))
+ return IsSpecialDiscardedValue(OVE->getSourceExpr()) &&
+ IsSpecialDiscardedValue(BCO->getFalseExpr());
+ }
+
+ // Objective-C++ extensions to the rule.
+ if (isa<PseudoObjectExpr>(E) || isa<ObjCIvarRefExpr>(E))
+ return true;
+
+ return false;
+}
+
/// Perform the conversions required for an expression used in a
/// context that ignores the result.
ExprResult Sema::IgnoredValueConversions(Expr *E) {
@@ -5246,8 +5449,21 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
return Owned(E);
}
- // Otherwise, this rule does not apply in C++, at least not for the moment.
- if (getLangOpts().CPlusPlus) return Owned(E);
+ if (getLangOpts().CPlusPlus) {
+ // The C++11 standard defines the notion of a discarded-value expression;
+ // normally, we don't need to do anything to handle it, but if it is a
+ // volatile lvalue with a special form, we perform an lvalue-to-rvalue
+ // conversion.
+ if (getLangOpts().CPlusPlus0x && E->isGLValue() &&
+ E->getType().isVolatileQualified() &&
+ IsSpecialDiscardedValue(E)) {
+ ExprResult Res = DefaultLvalueConversion(E);
+ if (Res.isInvalid())
+ return Owned(E);
+ E = Res.take();
+ }
+ return Owned(E);
+ }
// GCC seems to also exclude expressions of incomplete enum type.
if (const EnumType *T = E->getType()->getAs<EnumType>()) {
@@ -5269,7 +5485,7 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
return Owned(E);
}
-ExprResult Sema::ActOnFinishFullExpr(Expr *FE) {
+ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC) {
ExprResult FullExpr = Owned(FE);
if (!FullExpr.get())
@@ -5295,7 +5511,7 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE) {
if (FullExpr.isInvalid())
return ExprError();
- CheckImplicitConversions(FullExpr.get(), FullExpr.get()->getExprLoc());
+ CheckImplicitConversions(FullExpr.get(), CC);
return MaybeCreateExprWithCleanups(FullExpr);
}
diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp
index 6c84caa..53f22f6 100644
--- a/lib/Sema/SemaExprMember.cpp
+++ b/lib/Sema/SemaExprMember.cpp
@@ -115,7 +115,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
NamedDecl *D = *I;
if (D->isCXXInstanceMember()) {
- if (dyn_cast<FieldDecl>(D))
+ if (dyn_cast<FieldDecl>(D) || dyn_cast<IndirectFieldDecl>(D))
isField = true;
CXXRecordDecl *R = cast<CXXRecordDecl>(D->getDeclContext());
@@ -436,8 +436,8 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType,
if (PT && (!getLangOpts().ObjC1 ||
PT->getPointeeType()->isRecordType())) {
assert(BaseExpr && "cannot happen with implicit member accesses");
- Diag(NameInfo.getLoc(), diag::err_typecheck_member_reference_struct_union)
- << BaseType << BaseExpr->getSourceRange();
+ Diag(OpLoc, diag::err_typecheck_member_reference_struct_union)
+ << BaseType << BaseExpr->getSourceRange() << NameInfo.getSourceRange();
return ExprError();
}
}
@@ -548,8 +548,8 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
RecordDecl *RDecl = RTy->getDecl();
if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) &&
SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0),
- SemaRef.PDiag(diag::err_typecheck_incomplete_tag)
- << BaseRange))
+ diag::err_typecheck_incomplete_tag,
+ BaseRange))
return true;
if (HasTemplateArgs) {
@@ -813,8 +813,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope,
LookupResult &R,
- const TemplateArgumentListInfo *TemplateArgs,
- bool SuppressQualifierCheck) {
+ const TemplateArgumentListInfo *TemplateArgs,
+ bool SuppressQualifierCheck,
+ ActOnMemberAccessExtraArgs *ExtraArgs) {
QualType BaseType = BaseExprType;
if (IsArrow) {
assert(BaseType->isPointerType());
@@ -835,6 +836,32 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
? computeDeclContext(SS, false)
: BaseType->getAs<RecordType>()->getDecl());
+ if (ExtraArgs) {
+ ExprResult RetryExpr;
+ if (!IsArrow && BaseExpr) {
+ SFINAETrap Trap(*this, true);
+ ParsedType ObjectType;
+ bool MayBePseudoDestructor = false;
+ RetryExpr = ActOnStartCXXMemberReference(getCurScope(), BaseExpr,
+ OpLoc, tok::arrow, ObjectType,
+ MayBePseudoDestructor);
+ if (RetryExpr.isUsable() && !Trap.hasErrorOccurred()) {
+ CXXScopeSpec TempSS(SS);
+ RetryExpr = ActOnMemberAccessExpr(
+ ExtraArgs->S, RetryExpr.get(), OpLoc, tok::arrow, TempSS,
+ TemplateKWLoc, ExtraArgs->Id, ExtraArgs->ObjCImpDecl,
+ ExtraArgs->HasTrailingLParen);
+ }
+ if (Trap.hasErrorOccurred())
+ RetryExpr = ExprError();
+ }
+ if (RetryExpr.isUsable()) {
+ Diag(OpLoc, diag::err_no_member_overloaded_arrow)
+ << MemberName << DC << FixItHint::CreateReplacement(OpLoc, "->");
+ return RetryExpr;
+ }
+ }
+
Diag(R.getNameLoc(), diag::err_no_member)
<< MemberName << DC
<< (BaseExpr ? BaseExpr->getSourceRange() : SourceRange());
@@ -1122,10 +1149,22 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr,
ObjCImpDecl, HasTemplateArgs);
goto fail;
}
-
- if (RequireCompleteType(OpLoc, BaseType,
- PDiag(diag::err_typecheck_incomplete_tag)
- << BaseExpr.get()->getSourceRange()))
+ else if (Member && Member->isStr("isa")) {
+ // If an ivar is (1) the first ivar in a root class and (2) named `isa`,
+ // then issue the same deprecated warning that id->isa gets.
+ ObjCInterfaceDecl *ClassDeclared = 0;
+ if (ObjCIvarDecl *IV =
+ IDecl->lookupInstanceVariable(Member, ClassDeclared)) {
+ if (!ClassDeclared->getSuperClass()
+ && (*ClassDeclared->ivar_begin()) == IV) {
+ Diag(MemberLoc, diag::warn_objc_isa_use);
+ Diag(IV->getLocation(), diag::note_ivar_decl);
+ }
+ }
+ }
+
+ if (RequireCompleteType(OpLoc, BaseType, diag::err_typecheck_incomplete_tag,
+ BaseExpr.get()))
return ExprError();
ObjCInterfaceDecl *ClassDeclared = 0;
@@ -1211,6 +1250,7 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr,
<< IV->getDeclName();
}
}
+ bool warn = true;
if (getLangOpts().ObjCAutoRefCount) {
Expr *BaseExp = BaseExpr.get()->IgnoreParenImpCasts();
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(BaseExp))
@@ -1218,10 +1258,20 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr,
BaseExp = UO->getSubExpr()->IgnoreParenCasts();
if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(BaseExp))
- if (DE->getType().getObjCLifetime() == Qualifiers::OCL_Weak)
+ if (DE->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
Diag(DE->getLocation(), diag::error_arc_weak_ivar_access);
+ warn = false;
+ }
+ }
+ if (warn) {
+ if (ObjCMethodDecl *MD = getCurMethodDecl()) {
+ ObjCMethodFamily MF = MD->getMethodFamily();
+ warn = (MF != OMF_init && MF != OMF_dealloc &&
+ MF != OMF_finalize);
+ }
+ if (warn)
+ Diag(MemberLoc, diag::warn_direct_ivar_access) << IV->getDeclName();
}
-
return Owned(new (Context) ObjCIvarRefExpr(IV, IV->getType(),
MemberLoc, BaseExpr.take(),
IsArrow));
@@ -1327,9 +1377,6 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr,
// methods.
Setter = IFace->lookupPrivateMethod(SetterSel, false);
}
- // Look through local category implementations associated with the class.
- if (!Setter)
- Setter = IFace->getCategoryClassMethod(SetterSel);
if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc))
return ExprError();
@@ -1418,8 +1465,8 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr,
ObjCImpDecl, HasTemplateArgs);
}
- Diag(MemberLoc, diag::err_typecheck_member_reference_struct_union)
- << BaseType << BaseExpr.get()->getSourceRange();
+ Diag(OpLoc, diag::err_typecheck_member_reference_struct_union)
+ << BaseType << BaseExpr.get()->getSourceRange() << MemberLoc;
return ExprError();
}
@@ -1434,9 +1481,9 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr,
/// \param HasTrailingLParen whether the next token is '(', which
/// is used to diagnose mis-uses of special members that can
/// only be called
-/// \param ObjCImpDecl the current ObjC @implementation decl;
-/// this is an ugly hack around the fact that ObjC @implementations
-/// aren't properly put in the context chain
+/// \param ObjCImpDecl the current Objective-C \@implementation
+/// decl; this is an ugly hack around the fact that Objective-C
+/// \@implementations aren't properly put in the context chain
ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
@@ -1506,9 +1553,11 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
return move(Result);
}
+ ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl, HasTrailingLParen};
Result = BuildMemberReferenceExpr(Base, Base->getType(),
OpLoc, IsArrow, SS, TemplateKWLoc,
- FirstQualifierInScope, R, TemplateArgs);
+ FirstQualifierInScope, R, TemplateArgs,
+ false, &ExtraArgs);
}
return move(Result);
@@ -1563,6 +1612,8 @@ BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow,
MemberType = S.Context.getQualifiedType(MemberType, Combined);
}
+ S.UnusedPrivateFields.remove(Field);
+
ExprResult Base =
S.PerformObjectMemberConversion(BaseExpr, SS.getScopeRep(),
FoundDecl, Field);
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index b62d56e..0aabf8b 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -111,7 +111,7 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){
Ty = Context.getObjCIdType();
}
} else {
- IdentifierInfo *NSIdent = &Context.Idents.get("NSString");
+ IdentifierInfo *NSIdent = NSAPIObj->getNSClassId(NSAPI::ClassId_NSString);
NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc,
LookupOrdinaryName);
if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) {
@@ -140,20 +140,47 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){
return new (Context) ObjCStringLiteral(S, Ty, AtLoc);
}
+/// \brief Emits an error if the given method does not exist, or if the return
+/// type is not an Objective-C object.
+static bool validateBoxingMethod(Sema &S, SourceLocation Loc,
+ const ObjCInterfaceDecl *Class,
+ Selector Sel, const ObjCMethodDecl *Method) {
+ if (!Method) {
+ // FIXME: Is there a better way to avoid quotes than using getName()?
+ S.Diag(Loc, diag::err_undeclared_boxing_method) << Sel << Class->getName();
+ return false;
+ }
+
+ // Make sure the return type is reasonable.
+ QualType ReturnType = Method->getResultType();
+ if (!ReturnType->isObjCObjectPointerType()) {
+ S.Diag(Loc, diag::err_objc_literal_method_sig)
+ << Sel;
+ S.Diag(Method->getLocation(), diag::note_objc_literal_method_return)
+ << ReturnType;
+ return false;
+ }
+
+ return true;
+}
+
/// \brief Retrieve the NSNumber factory method that should be used to create
/// an Objective-C literal for the given type.
static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc,
- QualType T, QualType ReturnType,
- SourceRange Range) {
+ QualType NumberType,
+ bool isLiteral = false,
+ SourceRange R = SourceRange()) {
llvm::Optional<NSAPI::NSNumberLiteralMethodKind> Kind
- = S.NSAPIObj->getNSNumberFactoryMethodKind(T);
+ = S.NSAPIObj->getNSNumberFactoryMethodKind(NumberType);
if (!Kind) {
- S.Diag(Loc, diag::err_invalid_nsnumber_type)
- << T << Range;
+ if (isLiteral) {
+ S.Diag(Loc, diag::err_invalid_nsnumber_type)
+ << NumberType << R;
+ }
return 0;
}
-
+
// If we already looked up this method, we're done.
if (S.NSNumberLiteralMethods[*Kind])
return S.NSNumberLiteralMethods[*Kind];
@@ -161,39 +188,62 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc,
Selector Sel = S.NSAPIObj->getNSNumberLiteralSelector(*Kind,
/*Instance=*/false);
+ ASTContext &CX = S.Context;
+
+ // Look up the NSNumber class, if we haven't done so already. It's cached
+ // in the Sema instance.
+ if (!S.NSNumberDecl) {
+ IdentifierInfo *NSNumberId =
+ S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber);
+ NamedDecl *IF = S.LookupSingleName(S.TUScope, NSNumberId,
+ Loc, Sema::LookupOrdinaryName);
+ S.NSNumberDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
+ if (!S.NSNumberDecl) {
+ if (S.getLangOpts().DebuggerObjCLiteral) {
+ // Create a stub definition of NSNumber.
+ S.NSNumberDecl = ObjCInterfaceDecl::Create(CX,
+ CX.getTranslationUnitDecl(),
+ SourceLocation(), NSNumberId,
+ 0, SourceLocation());
+ } else {
+ // Otherwise, require a declaration of NSNumber.
+ S.Diag(Loc, diag::err_undeclared_nsnumber);
+ return 0;
+ }
+ } else if (!S.NSNumberDecl->hasDefinition()) {
+ S.Diag(Loc, diag::err_undeclared_nsnumber);
+ return 0;
+ }
+
+ // generate the pointer to NSNumber type.
+ QualType NSNumberObject = CX.getObjCInterfaceType(S.NSNumberDecl);
+ S.NSNumberPointer = CX.getObjCObjectPointerType(NSNumberObject);
+ }
+
// Look for the appropriate method within NSNumber.
- ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel);;
+ ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel);
if (!Method && S.getLangOpts().DebuggerObjCLiteral) {
+ // create a stub definition this NSNumber factory method.
TypeSourceInfo *ResultTInfo = 0;
- Method = ObjCMethodDecl::Create(S.Context, SourceLocation(), SourceLocation(), Sel,
- ReturnType,
- ResultTInfo,
- S.Context.getTranslationUnitDecl(),
- false /*Instance*/, false/*isVariadic*/,
- /*isSynthesized=*/false,
- /*isImplicitlyDeclared=*/true, /*isDefined=*/false,
- ObjCMethodDecl::Required,
- false);
+ Method = ObjCMethodDecl::Create(CX, SourceLocation(), SourceLocation(), Sel,
+ S.NSNumberPointer, ResultTInfo,
+ S.NSNumberDecl,
+ /*isInstance=*/false, /*isVariadic=*/false,
+ /*isSynthesized=*/false,
+ /*isImplicitlyDeclared=*/true,
+ /*isDefined=*/false,
+ ObjCMethodDecl::Required,
+ /*HasRelatedResultType=*/false);
ParmVarDecl *value = ParmVarDecl::Create(S.Context, Method,
SourceLocation(), SourceLocation(),
- &S.Context.Idents.get("value"),
- T, /*TInfo=*/0, SC_None, SC_None, 0);
+ &CX.Idents.get("value"),
+ NumberType, /*TInfo=*/0, SC_None,
+ SC_None, 0);
Method->setMethodParams(S.Context, value, ArrayRef<SourceLocation>());
}
- if (!Method) {
- S.Diag(Loc, diag::err_undeclared_nsnumber_method) << Sel;
- return 0;
- }
-
- // Make sure the return type is reasonable.
- if (!Method->getResultType()->isObjCObjectPointerType()) {
- S.Diag(Loc, diag::err_objc_literal_method_sig)
- << Sel;
- S.Diag(Method->getLocation(), diag::note_objc_literal_method_return)
- << Method->getResultType();
+ if (!validateBoxingMethod(S, Loc, S.NSNumberDecl, Sel, Method))
return 0;
- }
// Note: if the parameter type is out-of-line, we'll catch it later in the
// implicit conversion.
@@ -202,29 +252,9 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc,
return Method;
}
-/// BuildObjCNumericLiteral - builds an ObjCNumericLiteral AST node for the
-/// numeric literal expression. Type of the expression will be "NSNumber *"
-/// or "id" if NSNumber is unavailable.
+/// BuildObjCNumericLiteral - builds an ObjCBoxedExpr AST node for the
+/// numeric literal expression. Type of the expression will be "NSNumber *".
ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) {
- // Look up the NSNumber class, if we haven't done so already.
- if (!NSNumberDecl) {
- NamedDecl *IF = LookupSingleName(TUScope,
- NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber),
- AtLoc, LookupOrdinaryName);
- NSNumberDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF);
-
- if (!NSNumberDecl && getLangOpts().DebuggerObjCLiteral)
- NSNumberDecl = ObjCInterfaceDecl::Create (Context,
- Context.getTranslationUnitDecl(),
- SourceLocation(),
- NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber),
- 0, SourceLocation());
- if (!NSNumberDecl) {
- Diag(AtLoc, diag::err_undeclared_nsnumber);
- return ExprError();
- }
- }
-
// Determine the type of the literal.
QualType NumberType = Number->getType();
if (CharacterLiteral *Char = dyn_cast<CharacterLiteral>(Number)) {
@@ -249,29 +279,29 @@ ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) {
}
}
- ObjCMethodDecl *Method = 0;
// Look for the appropriate method within NSNumber.
// Construct the literal.
- QualType Ty
- = Context.getObjCObjectPointerType(
- Context.getObjCInterfaceType(NSNumberDecl));
- Method = getNSNumberFactoryMethod(*this, AtLoc,
- NumberType, Ty,
- Number->getSourceRange());
-
+ SourceRange NR(Number->getSourceRange());
+ ObjCMethodDecl *Method = getNSNumberFactoryMethod(*this, AtLoc, NumberType,
+ true, NR);
if (!Method)
return ExprError();
// Convert the number to the type that the parameter expects.
- QualType ElementT = Method->param_begin()[0]->getType();
- ExprResult ConvertedNumber = PerformImplicitConversion(Number, ElementT,
- AA_Sending);
+ ParmVarDecl *ParamDecl = Method->param_begin()[0];
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(Context,
+ ParamDecl);
+ ExprResult ConvertedNumber = PerformCopyInitialization(Entity,
+ SourceLocation(),
+ Owned(Number));
if (ConvertedNumber.isInvalid())
return ExprError();
Number = ConvertedNumber.get();
+ // Use the effective source range of the literal, including the leading '@'.
return MaybeBindToTemporary(
- new (Context) ObjCNumericLiteral(Number, Ty, Method, AtLoc));
+ new (Context) ObjCBoxedExpr(Number, NSNumberPointer, Method,
+ SourceRange(AtLoc, NR.getEnd())));
}
ExprResult Sema::ActOnObjCBoolLiteral(SourceLocation AtLoc,
@@ -308,9 +338,11 @@ static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element,
// type.
if (S.getLangOpts().CPlusPlus && Element->getType()->isRecordType()) {
InitializedEntity Entity
- = InitializedEntity::InitializeParameter(S.Context, T, /*Consumed=*/false);
+ = InitializedEntity::InitializeParameter(S.Context, T,
+ /*Consumed=*/false);
InitializationKind Kind
- = InitializationKind::CreateCopy(Element->getLocStart(), SourceLocation());
+ = InitializationKind::CreateCopy(Element->getLocStart(),
+ SourceLocation());
InitializationSequence Seq(S, Entity, Kind, &Element, 1);
if (!Seq.Failed())
return Seq.Perform(S, Entity, Kind, MultiExprArg(S, &Element, 1));
@@ -385,26 +417,191 @@ static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element,
Element->getLocStart(), Element);
}
+ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) {
+ if (ValueExpr->isTypeDependent()) {
+ ObjCBoxedExpr *BoxedExpr =
+ new (Context) ObjCBoxedExpr(ValueExpr, Context.DependentTy, NULL, SR);
+ return Owned(BoxedExpr);
+ }
+ ObjCMethodDecl *BoxingMethod = NULL;
+ QualType BoxedType;
+ // Convert the expression to an RValue, so we can check for pointer types...
+ ExprResult RValue = DefaultFunctionArrayLvalueConversion(ValueExpr);
+ if (RValue.isInvalid()) {
+ return ExprError();
+ }
+ ValueExpr = RValue.get();
+ QualType ValueType(ValueExpr->getType());
+ if (const PointerType *PT = ValueType->getAs<PointerType>()) {
+ QualType PointeeType = PT->getPointeeType();
+ if (Context.hasSameUnqualifiedType(PointeeType, Context.CharTy)) {
+
+ if (!NSStringDecl) {
+ IdentifierInfo *NSStringId =
+ NSAPIObj->getNSClassId(NSAPI::ClassId_NSString);
+ NamedDecl *Decl = LookupSingleName(TUScope, NSStringId,
+ SR.getBegin(), LookupOrdinaryName);
+ NSStringDecl = dyn_cast_or_null<ObjCInterfaceDecl>(Decl);
+ if (!NSStringDecl) {
+ if (getLangOpts().DebuggerObjCLiteral) {
+ // Support boxed expressions in the debugger w/o NSString declaration.
+ DeclContext *TU = Context.getTranslationUnitDecl();
+ NSStringDecl = ObjCInterfaceDecl::Create(Context, TU,
+ SourceLocation(),
+ NSStringId,
+ 0, SourceLocation());
+ } else {
+ Diag(SR.getBegin(), diag::err_undeclared_nsstring);
+ return ExprError();
+ }
+ } else if (!NSStringDecl->hasDefinition()) {
+ Diag(SR.getBegin(), diag::err_undeclared_nsstring);
+ return ExprError();
+ }
+ assert(NSStringDecl && "NSStringDecl should not be NULL");
+ QualType NSStringObject = Context.getObjCInterfaceType(NSStringDecl);
+ NSStringPointer = Context.getObjCObjectPointerType(NSStringObject);
+ }
+
+ if (!StringWithUTF8StringMethod) {
+ IdentifierInfo *II = &Context.Idents.get("stringWithUTF8String");
+ Selector stringWithUTF8String = Context.Selectors.getUnarySelector(II);
+
+ // Look for the appropriate method within NSString.
+ BoxingMethod = NSStringDecl->lookupClassMethod(stringWithUTF8String);
+ if (!BoxingMethod && getLangOpts().DebuggerObjCLiteral) {
+ // Debugger needs to work even if NSString hasn't been defined.
+ TypeSourceInfo *ResultTInfo = 0;
+ ObjCMethodDecl *M =
+ ObjCMethodDecl::Create(Context, SourceLocation(), SourceLocation(),
+ stringWithUTF8String, NSStringPointer,
+ ResultTInfo, NSStringDecl,
+ /*isInstance=*/false, /*isVariadic=*/false,
+ /*isSynthesized=*/false,
+ /*isImplicitlyDeclared=*/true,
+ /*isDefined=*/false,
+ ObjCMethodDecl::Required,
+ /*HasRelatedResultType=*/false);
+ QualType ConstCharType = Context.CharTy.withConst();
+ ParmVarDecl *value =
+ ParmVarDecl::Create(Context, M,
+ SourceLocation(), SourceLocation(),
+ &Context.Idents.get("value"),
+ Context.getPointerType(ConstCharType),
+ /*TInfo=*/0,
+ SC_None, SC_None, 0);
+ M->setMethodParams(Context, value, ArrayRef<SourceLocation>());
+ BoxingMethod = M;
+ }
+
+ if (!validateBoxingMethod(*this, SR.getBegin(), NSStringDecl,
+ stringWithUTF8String, BoxingMethod))
+ return ExprError();
+
+ StringWithUTF8StringMethod = BoxingMethod;
+ }
+
+ BoxingMethod = StringWithUTF8StringMethod;
+ BoxedType = NSStringPointer;
+ }
+ } else if (ValueType->isBuiltinType()) {
+ // The other types we support are numeric, char and BOOL/bool. We could also
+ // provide limited support for structure types, such as NSRange, NSRect, and
+ // NSSize. See NSValue (NSValueGeometryExtensions) in <Foundation/NSGeometry.h>
+ // for more details.
+
+ // Check for a top-level character literal.
+ if (const CharacterLiteral *Char =
+ dyn_cast<CharacterLiteral>(ValueExpr->IgnoreParens())) {
+ // In C, character literals have type 'int'. That's not the type we want
+ // to use to determine the Objective-c literal kind.
+ switch (Char->getKind()) {
+ case CharacterLiteral::Ascii:
+ ValueType = Context.CharTy;
+ break;
+
+ case CharacterLiteral::Wide:
+ ValueType = Context.getWCharType();
+ break;
+
+ case CharacterLiteral::UTF16:
+ ValueType = Context.Char16Ty;
+ break;
+
+ case CharacterLiteral::UTF32:
+ ValueType = Context.Char32Ty;
+ break;
+ }
+ }
+
+ // FIXME: Do I need to do anything special with BoolTy expressions?
+
+ // Look for the appropriate method within NSNumber.
+ BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(), ValueType);
+ BoxedType = NSNumberPointer;
+
+ } else if (const EnumType *ET = ValueType->getAs<EnumType>()) {
+ if (!ET->getDecl()->isComplete()) {
+ Diag(SR.getBegin(), diag::err_objc_incomplete_boxed_expression_type)
+ << ValueType << ValueExpr->getSourceRange();
+ return ExprError();
+ }
+
+ BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(),
+ ET->getDecl()->getIntegerType());
+ BoxedType = NSNumberPointer;
+ }
+
+ if (!BoxingMethod) {
+ Diag(SR.getBegin(), diag::err_objc_illegal_boxed_expression_type)
+ << ValueType << ValueExpr->getSourceRange();
+ return ExprError();
+ }
+
+ // Convert the expression to the type that the parameter requires.
+ ParmVarDecl *ParamDecl = BoxingMethod->param_begin()[0];
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(Context,
+ ParamDecl);
+ ExprResult ConvertedValueExpr = PerformCopyInitialization(Entity,
+ SourceLocation(),
+ Owned(ValueExpr));
+ if (ConvertedValueExpr.isInvalid())
+ return ExprError();
+ ValueExpr = ConvertedValueExpr.get();
+
+ ObjCBoxedExpr *BoxedExpr =
+ new (Context) ObjCBoxedExpr(ValueExpr, BoxedType,
+ BoxingMethod, SR);
+ return MaybeBindToTemporary(BoxedExpr);
+}
+
+/// Build an ObjC subscript pseudo-object expression, given that
+/// that's supported by the runtime.
ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr,
Expr *IndexExpr,
ObjCMethodDecl *getterMethod,
ObjCMethodDecl *setterMethod) {
- // Feature support is for modern abi.
- if (!LangOpts.ObjCNonFragileABI)
- return ExprError();
- // If the expression is type-dependent, there's nothing for us to do.
- assert ((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) &&
- "base or index cannot have dependent type here");
+ assert(!LangOpts.ObjCRuntime.isSubscriptPointerArithmetic());
+
+ // We can't get dependent types here; our callers should have
+ // filtered them out.
+ assert((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) &&
+ "base or index cannot have dependent type here");
+
+ // Filter out placeholders in the index. In theory, overloads could
+ // be preserved here, although that might not actually work correctly.
ExprResult Result = CheckPlaceholderExpr(IndexExpr);
if (Result.isInvalid())
return ExprError();
IndexExpr = Result.get();
- // Perform lvalue-to-rvalue conversion.
+ // Perform lvalue-to-rvalue conversion on the base.
Result = DefaultLvalueConversion(BaseExpr);
if (Result.isInvalid())
return ExprError();
BaseExpr = Result.get();
+
+ // Build the pseudo-object expression.
return Owned(ObjCSubscriptRefExpr::Create(Context,
BaseExpr,
IndexExpr,
@@ -440,11 +637,10 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) {
if (!ArrayWithObjectsMethod) {
Selector
Sel = NSAPIObj->getNSArraySelector(NSAPI::NSArr_arrayWithObjectsCount);
- ArrayWithObjectsMethod = NSArrayDecl->lookupClassMethod(Sel);
- if (!ArrayWithObjectsMethod && getLangOpts().DebuggerObjCLiteral) {
+ ObjCMethodDecl *Method = NSArrayDecl->lookupClassMethod(Sel);
+ if (!Method && getLangOpts().DebuggerObjCLiteral) {
TypeSourceInfo *ResultTInfo = 0;
- ArrayWithObjectsMethod =
- ObjCMethodDecl::Create(Context,
+ Method = ObjCMethodDecl::Create(Context,
SourceLocation(), SourceLocation(), Sel,
IdT,
ResultTInfo,
@@ -455,80 +651,68 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) {
ObjCMethodDecl::Required,
false);
SmallVector<ParmVarDecl *, 2> Params;
- ParmVarDecl *objects = ParmVarDecl::Create(Context, ArrayWithObjectsMethod,
- SourceLocation(), SourceLocation(),
- &Context.Idents.get("objects"),
- Context.getPointerType(IdT),
- /*TInfo=*/0,
- SC_None,
- SC_None,
- 0);
+ ParmVarDecl *objects = ParmVarDecl::Create(Context, Method,
+ SourceLocation(),
+ SourceLocation(),
+ &Context.Idents.get("objects"),
+ Context.getPointerType(IdT),
+ /*TInfo=*/0, SC_None, SC_None,
+ 0);
Params.push_back(objects);
- ParmVarDecl *cnt = ParmVarDecl::Create(Context, ArrayWithObjectsMethod,
- SourceLocation(), SourceLocation(),
- &Context.Idents.get("cnt"),
- Context.UnsignedLongTy,
- /*TInfo=*/0,
- SC_None,
- SC_None,
- 0);
+ ParmVarDecl *cnt = ParmVarDecl::Create(Context, Method,
+ SourceLocation(),
+ SourceLocation(),
+ &Context.Idents.get("cnt"),
+ Context.UnsignedLongTy,
+ /*TInfo=*/0, SC_None, SC_None,
+ 0);
Params.push_back(cnt);
- ArrayWithObjectsMethod->setMethodParams(Context, Params,
- ArrayRef<SourceLocation>());
-
-
+ Method->setMethodParams(Context, Params, ArrayRef<SourceLocation>());
}
- if (!ArrayWithObjectsMethod) {
- Diag(SR.getBegin(), diag::err_undeclared_arraywithobjects) << Sel;
+ if (!validateBoxingMethod(*this, SR.getBegin(), NSArrayDecl, Sel, Method))
+ return ExprError();
+
+ // Dig out the type that all elements should be converted to.
+ QualType T = Method->param_begin()[0]->getType();
+ const PointerType *PtrT = T->getAs<PointerType>();
+ if (!PtrT ||
+ !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) {
+ Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
+ << Sel;
+ Diag(Method->param_begin()[0]->getLocation(),
+ diag::note_objc_literal_method_param)
+ << 0 << T
+ << Context.getPointerType(IdT.withConst());
return ExprError();
}
- }
- // Make sure the return type is reasonable.
- if (!ArrayWithObjectsMethod->getResultType()->isObjCObjectPointerType()) {
- Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << ArrayWithObjectsMethod->getSelector();
- Diag(ArrayWithObjectsMethod->getLocation(),
- diag::note_objc_literal_method_return)
- << ArrayWithObjectsMethod->getResultType();
- return ExprError();
- }
+ // Check that the 'count' parameter is integral.
+ if (!Method->param_begin()[1]->getType()->isIntegerType()) {
+ Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
+ << Sel;
+ Diag(Method->param_begin()[1]->getLocation(),
+ diag::note_objc_literal_method_param)
+ << 1
+ << Method->param_begin()[1]->getType()
+ << "integral";
+ return ExprError();
+ }
- // Dig out the type that all elements should be converted to.
- QualType T = ArrayWithObjectsMethod->param_begin()[0]->getType();
- const PointerType *PtrT = T->getAs<PointerType>();
- if (!PtrT ||
- !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) {
- Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << ArrayWithObjectsMethod->getSelector();
- Diag(ArrayWithObjectsMethod->param_begin()[0]->getLocation(),
- diag::note_objc_literal_method_param)
- << 0 << T
- << Context.getPointerType(IdT.withConst());
- return ExprError();
- }
- T = PtrT->getPointeeType();
-
- // Check that the 'count' parameter is integral.
- if (!ArrayWithObjectsMethod->param_begin()[1]->getType()->isIntegerType()) {
- Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << ArrayWithObjectsMethod->getSelector();
- Diag(ArrayWithObjectsMethod->param_begin()[1]->getLocation(),
- diag::note_objc_literal_method_param)
- << 1
- << ArrayWithObjectsMethod->param_begin()[1]->getType()
- << "integral";
- return ExprError();
+ // We've found a good +arrayWithObjects:count: method. Save it!
+ ArrayWithObjectsMethod = Method;
}
+ QualType ObjectsType = ArrayWithObjectsMethod->param_begin()[0]->getType();
+ QualType RequiredType = ObjectsType->castAs<PointerType>()->getPointeeType();
+
// Check that each of the elements provided is valid in a collection literal,
// performing conversions as necessary.
Expr **ElementsBuffer = Elements.get();
for (unsigned I = 0, N = Elements.size(); I != N; ++I) {
ExprResult Converted = CheckObjCCollectionLiteralElement(*this,
ElementsBuffer[I],
- T);
+ RequiredType);
if (Converted.isInvalid())
return ExprError();
@@ -573,11 +757,10 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR,
QualType IdT = Context.getObjCIdType();
if (!DictionaryWithObjectsMethod) {
Selector Sel = NSAPIObj->getNSDictionarySelector(
- NSAPI::NSDict_dictionaryWithObjectsForKeysCount);
- DictionaryWithObjectsMethod = NSDictionaryDecl->lookupClassMethod(Sel);
- if (!DictionaryWithObjectsMethod && getLangOpts().DebuggerObjCLiteral) {
- DictionaryWithObjectsMethod =
- ObjCMethodDecl::Create(Context,
+ NSAPI::NSDict_dictionaryWithObjectsForKeysCount);
+ ObjCMethodDecl *Method = NSDictionaryDecl->lookupClassMethod(Sel);
+ if (!Method && getLangOpts().DebuggerObjCLiteral) {
+ Method = ObjCMethodDecl::Create(Context,
SourceLocation(), SourceLocation(), Sel,
IdT,
0 /*TypeSourceInfo */,
@@ -588,117 +771,107 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR,
ObjCMethodDecl::Required,
false);
SmallVector<ParmVarDecl *, 3> Params;
- ParmVarDecl *objects = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod,
- SourceLocation(), SourceLocation(),
- &Context.Idents.get("objects"),
- Context.getPointerType(IdT),
- /*TInfo=*/0,
- SC_None,
- SC_None,
- 0);
+ ParmVarDecl *objects = ParmVarDecl::Create(Context, Method,
+ SourceLocation(),
+ SourceLocation(),
+ &Context.Idents.get("objects"),
+ Context.getPointerType(IdT),
+ /*TInfo=*/0, SC_None, SC_None,
+ 0);
Params.push_back(objects);
- ParmVarDecl *keys = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod,
- SourceLocation(), SourceLocation(),
- &Context.Idents.get("keys"),
- Context.getPointerType(IdT),
- /*TInfo=*/0,
- SC_None,
- SC_None,
- 0);
+ ParmVarDecl *keys = ParmVarDecl::Create(Context, Method,
+ SourceLocation(),
+ SourceLocation(),
+ &Context.Idents.get("keys"),
+ Context.getPointerType(IdT),
+ /*TInfo=*/0, SC_None, SC_None,
+ 0);
Params.push_back(keys);
- ParmVarDecl *cnt = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod,
- SourceLocation(), SourceLocation(),
- &Context.Idents.get("cnt"),
- Context.UnsignedLongTy,
- /*TInfo=*/0,
- SC_None,
- SC_None,
- 0);
+ ParmVarDecl *cnt = ParmVarDecl::Create(Context, Method,
+ SourceLocation(),
+ SourceLocation(),
+ &Context.Idents.get("cnt"),
+ Context.UnsignedLongTy,
+ /*TInfo=*/0, SC_None, SC_None,
+ 0);
Params.push_back(cnt);
- DictionaryWithObjectsMethod->setMethodParams(Context, Params,
- ArrayRef<SourceLocation>());
+ Method->setMethodParams(Context, Params, ArrayRef<SourceLocation>());
}
- if (!DictionaryWithObjectsMethod) {
- Diag(SR.getBegin(), diag::err_undeclared_dictwithobjects) << Sel;
- return ExprError();
+ if (!validateBoxingMethod(*this, SR.getBegin(), NSDictionaryDecl, Sel,
+ Method))
+ return ExprError();
+
+ // Dig out the type that all values should be converted to.
+ QualType ValueT = Method->param_begin()[0]->getType();
+ const PointerType *PtrValue = ValueT->getAs<PointerType>();
+ if (!PtrValue ||
+ !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) {
+ Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
+ << Sel;
+ Diag(Method->param_begin()[0]->getLocation(),
+ diag::note_objc_literal_method_param)
+ << 0 << ValueT
+ << Context.getPointerType(IdT.withConst());
+ return ExprError();
}
- }
-
- // Make sure the return type is reasonable.
- if (!DictionaryWithObjectsMethod->getResultType()->isObjCObjectPointerType()){
- Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << DictionaryWithObjectsMethod->getSelector();
- Diag(DictionaryWithObjectsMethod->getLocation(),
- diag::note_objc_literal_method_return)
- << DictionaryWithObjectsMethod->getResultType();
- return ExprError();
- }
- // Dig out the type that all values should be converted to.
- QualType ValueT = DictionaryWithObjectsMethod->param_begin()[0]->getType();
- const PointerType *PtrValue = ValueT->getAs<PointerType>();
- if (!PtrValue ||
- !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) {
- Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << DictionaryWithObjectsMethod->getSelector();
- Diag(DictionaryWithObjectsMethod->param_begin()[0]->getLocation(),
- diag::note_objc_literal_method_param)
- << 0 << ValueT
- << Context.getPointerType(IdT.withConst());
- return ExprError();
- }
- ValueT = PtrValue->getPointeeType();
-
- // Dig out the type that all keys should be converted to.
- QualType KeyT = DictionaryWithObjectsMethod->param_begin()[1]->getType();
- const PointerType *PtrKey = KeyT->getAs<PointerType>();
- if (!PtrKey ||
- !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(),
- IdT)) {
- bool err = true;
- if (PtrKey) {
- if (QIDNSCopying.isNull()) {
- // key argument of selector is id<NSCopying>?
- if (ObjCProtocolDecl *NSCopyingPDecl =
- LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) {
- ObjCProtocolDecl *PQ[] = {NSCopyingPDecl};
- QIDNSCopying =
- Context.getObjCObjectType(Context.ObjCBuiltinIdTy,
- (ObjCProtocolDecl**) PQ,1);
- QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying);
+ // Dig out the type that all keys should be converted to.
+ QualType KeyT = Method->param_begin()[1]->getType();
+ const PointerType *PtrKey = KeyT->getAs<PointerType>();
+ if (!PtrKey ||
+ !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(),
+ IdT)) {
+ bool err = true;
+ if (PtrKey) {
+ if (QIDNSCopying.isNull()) {
+ // key argument of selector is id<NSCopying>?
+ if (ObjCProtocolDecl *NSCopyingPDecl =
+ LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) {
+ ObjCProtocolDecl *PQ[] = {NSCopyingPDecl};
+ QIDNSCopying =
+ Context.getObjCObjectType(Context.ObjCBuiltinIdTy,
+ (ObjCProtocolDecl**) PQ,1);
+ QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying);
+ }
}
+ if (!QIDNSCopying.isNull())
+ err = !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(),
+ QIDNSCopying);
}
- if (!QIDNSCopying.isNull())
- err = !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(),
- QIDNSCopying);
- }
- if (err) {
+ if (err) {
+ Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
+ << Sel;
+ Diag(Method->param_begin()[1]->getLocation(),
+ diag::note_objc_literal_method_param)
+ << 1 << KeyT
+ << Context.getPointerType(IdT.withConst());
+ return ExprError();
+ }
+ }
+
+ // Check that the 'count' parameter is integral.
+ QualType CountType = Method->param_begin()[2]->getType();
+ if (!CountType->isIntegerType()) {
Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << DictionaryWithObjectsMethod->getSelector();
- Diag(DictionaryWithObjectsMethod->param_begin()[1]->getLocation(),
+ << Sel;
+ Diag(Method->param_begin()[2]->getLocation(),
diag::note_objc_literal_method_param)
- << 1 << KeyT
- << Context.getPointerType(IdT.withConst());
+ << 2 << CountType
+ << "integral";
return ExprError();
}
- }
- KeyT = PtrKey->getPointeeType();
- // Check that the 'count' parameter is integral.
- if (!DictionaryWithObjectsMethod->param_begin()[2]->getType()
- ->isIntegerType()) {
- Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
- << DictionaryWithObjectsMethod->getSelector();
- Diag(DictionaryWithObjectsMethod->param_begin()[2]->getLocation(),
- diag::note_objc_literal_method_param)
- << 2
- << DictionaryWithObjectsMethod->param_begin()[2]->getType()
- << "integral";
- return ExprError();
+ // We've found a good +dictionaryWithObjects:keys:count: method; save it!
+ DictionaryWithObjectsMethod = Method;
}
+ QualType ValuesT = DictionaryWithObjectsMethod->param_begin()[0]->getType();
+ QualType ValueT = ValuesT->castAs<PointerType>()->getPointeeType();
+ QualType KeysT = DictionaryWithObjectsMethod->param_begin()[1]->getType();
+ QualType KeyT = KeysT->castAs<PointerType>()->getPointeeType();
+
// Check that each of the keys and values provided is valid in a collection
// literal, performing conversions as necessary.
bool HasPackExpansions = false;
@@ -757,8 +930,8 @@ ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc,
if (!EncodedType->getAsArrayTypeUnsafe() && //// Incomplete array is handled.
!EncodedType->isVoidType()) // void is handled too.
if (RequireCompleteType(AtLoc, EncodedType,
- PDiag(diag::err_incomplete_type_objc_at_encode)
- << EncodedTypeInfo->getTypeLoc().getSourceRange()))
+ diag::err_incomplete_type_objc_at_encode,
+ EncodedTypeInfo->getTypeLoc()))
return ExprError();
std::string Str;
@@ -846,8 +1019,9 @@ ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId,
SourceLocation AtLoc,
SourceLocation ProtoLoc,
SourceLocation LParenLoc,
+ SourceLocation ProtoIdLoc,
SourceLocation RParenLoc) {
- ObjCProtocolDecl* PDecl = LookupProtocol(ProtocolId, ProtoLoc);
+ ObjCProtocolDecl* PDecl = LookupProtocol(ProtocolId, ProtoIdLoc);
if (!PDecl) {
Diag(ProtoLoc, diag::err_undeclared_protocol) << ProtocolId;
return true;
@@ -857,7 +1031,7 @@ ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId,
if (Ty.isNull())
return true;
Ty = Context.getObjCObjectPointerType(Ty);
- return new (Context) ObjCProtocolExpr(Ty, PDecl, AtLoc, RParenLoc);
+ return new (Context) ObjCProtocolExpr(Ty, PDecl, AtLoc, ProtoIdLoc, RParenLoc);
}
/// Try to capture an implicit reference to 'self'.
@@ -1023,8 +1197,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType,
if (RequireCompleteType(argExpr->getSourceRange().getBegin(),
param->getType(),
- PDiag(diag::err_call_incomplete_argument)
- << argExpr->getSourceRange()))
+ diag::err_call_incomplete_argument, argExpr))
return true;
InitializedEntity Entity = InitializedEntity::InitializeParameter(Context,
@@ -1042,7 +1215,8 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType,
if (Args[i]->isTypeDependent())
continue;
- ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, 0);
+ ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod,
+ 0);
IsError |= Arg.isInvalid();
Args[i] = Arg.take();
}
@@ -1079,57 +1253,6 @@ bool Sema::isSelfExpr(Expr *receiver) {
return false;
}
-// Helper method for ActOnClassMethod/ActOnInstanceMethod.
-// Will search "local" class/category implementations for a method decl.
-// If failed, then we search in class's root for an instance method.
-// Returns 0 if no method is found.
-ObjCMethodDecl *Sema::LookupPrivateClassMethod(Selector Sel,
- ObjCInterfaceDecl *ClassDecl) {
- ObjCMethodDecl *Method = 0;
- // lookup in class and all superclasses
- while (ClassDecl && !Method) {
- if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation())
- Method = ImpDecl->getClassMethod(Sel);
-
- // Look through local category implementations associated with the class.
- if (!Method)
- Method = ClassDecl->getCategoryClassMethod(Sel);
-
- // Before we give up, check if the selector is an instance method.
- // But only in the root. This matches gcc's behaviour and what the
- // runtime expects.
- if (!Method && !ClassDecl->getSuperClass()) {
- Method = ClassDecl->lookupInstanceMethod(Sel);
- // Look through local category implementations associated
- // with the root class.
- if (!Method)
- Method = LookupPrivateInstanceMethod(Sel, ClassDecl);
- }
-
- ClassDecl = ClassDecl->getSuperClass();
- }
- return Method;
-}
-
-ObjCMethodDecl *Sema::LookupPrivateInstanceMethod(Selector Sel,
- ObjCInterfaceDecl *ClassDecl) {
- if (!ClassDecl->hasDefinition())
- return 0;
-
- ObjCMethodDecl *Method = 0;
- while (ClassDecl && !Method) {
- // If we have implementations in scope, check "private" methods.
- if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation())
- Method = ImpDecl->getInstanceMethod(Sel);
-
- // Look through local category implementations associated with the class.
- if (!Method)
- Method = ClassDecl->getCategoryInstanceMethod(Sel);
- ClassDecl = ClassDecl->getSuperClass();
- }
- return Method;
-}
-
/// LookupMethodInType - Look up a method in an ObjCObjectType.
ObjCMethodDecl *Sema::LookupMethodInObjectType(Selector sel, QualType type,
bool isInstance) {
@@ -1141,13 +1264,8 @@ ObjCMethodDecl *Sema::LookupMethodInObjectType(Selector sel, QualType type,
// Okay, look for "private" methods declared in any
// @implementations we've seen.
- if (isInstance) {
- if (ObjCMethodDecl *method = LookupPrivateInstanceMethod(sel, iface))
- return method;
- } else {
- if (ObjCMethodDecl *method = LookupPrivateClassMethod(sel, iface))
- return method;
- }
+ if (ObjCMethodDecl *method = iface->lookupPrivateMethod(sel, isInstance))
+ return method;
}
// Check qualifiers.
@@ -1176,6 +1294,69 @@ ObjCMethodDecl *Sema::LookupMethodInQualifiedType(Selector Sel,
return 0;
}
+static void DiagnoseARCUseOfWeakReceiver(Sema &S, Expr *Receiver) {
+ if (!Receiver)
+ return;
+
+ if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Receiver))
+ Receiver = OVE->getSourceExpr();
+
+ Expr *RExpr = Receiver->IgnoreParenImpCasts();
+ SourceLocation Loc = RExpr->getLocStart();
+ QualType T = RExpr->getType();
+ ObjCPropertyDecl *PDecl = 0;
+ ObjCMethodDecl *GDecl = 0;
+ if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(RExpr)) {
+ RExpr = POE->getSyntacticForm();
+ if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(RExpr)) {
+ if (PRE->isImplicitProperty()) {
+ GDecl = PRE->getImplicitPropertyGetter();
+ if (GDecl) {
+ T = GDecl->getResultType();
+ }
+ }
+ else {
+ PDecl = PRE->getExplicitProperty();
+ if (PDecl) {
+ T = PDecl->getType();
+ }
+ }
+ }
+ }
+ else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RExpr)) {
+ // See if receiver is a method which envokes a synthesized getter
+ // backing a 'weak' property.
+ ObjCMethodDecl *Method = ME->getMethodDecl();
+ if (Method && Method->isSynthesized()) {
+ Selector Sel = Method->getSelector();
+ if (Sel.getNumArgs() == 0) {
+ const DeclContext *Container = Method->getDeclContext();
+ PDecl =
+ S.LookupPropertyDecl(cast<ObjCContainerDecl>(Container),
+ Sel.getIdentifierInfoForSlot(0));
+ }
+ if (PDecl)
+ T = PDecl->getType();
+ }
+ }
+
+ if (T.getObjCLifetime() == Qualifiers::OCL_Weak) {
+ S.Diag(Loc, diag::warn_receiver_is_weak)
+ << ((!PDecl && !GDecl) ? 0 : (PDecl ? 1 : 2));
+ if (PDecl)
+ S.Diag(PDecl->getLocation(), diag::note_property_declare);
+ else if (GDecl)
+ S.Diag(GDecl->getLocation(), diag::note_method_declared_at) << GDecl;
+ return;
+ }
+
+ if (PDecl &&
+ (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)) {
+ S.Diag(Loc, diag::warn_receiver_is_weak) << 1;
+ S.Diag(PDecl->getLocation(), diag::note_property_declare);
+ }
+}
+
/// HandleExprPropertyRefExpr - Handle foo.bar where foo is a pointer to an
/// objective C interface. This is a property reference expression.
ExprResult Sema::
@@ -1187,19 +1368,20 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
bool Super) {
const ObjCInterfaceType *IFaceT = OPT->getInterfaceType();
ObjCInterfaceDecl *IFace = IFaceT->getDecl();
-
- if (MemberName.getNameKind() != DeclarationName::Identifier) {
+
+ if (!MemberName.isIdentifier()) {
Diag(MemberLoc, diag::err_invalid_property_name)
<< MemberName << QualType(OPT, 0);
return ExprError();
}
-
+
IdentifierInfo *Member = MemberName.getAsIdentifierInfo();
+
SourceRange BaseRange = Super? SourceRange(SuperLoc)
: BaseExpr->getSourceRange();
if (RequireCompleteType(MemberLoc, OPT->getPointeeType(),
- PDiag(diag::err_property_not_found_forward_class)
- << MemberName << BaseRange))
+ diag::err_property_not_found_forward_class,
+ MemberName, BaseRange))
return ExprError();
// Search for a declared property first.
@@ -1207,7 +1389,6 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
// Check whether we can reference this property.
if (DiagnoseUseOfDecl(PD, MemberLoc))
return ExprError();
-
if (Super)
return Owned(new (Context) ObjCPropertyRefExpr(PD, Context.PseudoObjectTy,
VK_LValue, OK_ObjCProperty,
@@ -1225,7 +1406,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
// Check whether we can reference this property.
if (DiagnoseUseOfDecl(PD, MemberLoc))
return ExprError();
-
+
if (Super)
return Owned(new (Context) ObjCPropertyRefExpr(PD,
Context.PseudoObjectTy,
@@ -1258,9 +1439,6 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
if (!Getter)
Getter = IFace->lookupPrivateMethod(Sel);
- // Look through local category implementations associated with the class.
- if (!Getter)
- Getter = IFace->getCategoryInstanceMethod(Sel);
if (Getter) {
// Check if we can reference this property.
if (DiagnoseUseOfDecl(Getter, MemberLoc))
@@ -1272,7 +1450,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
SelectorTable::constructSetterName(PP.getIdentifierTable(),
PP.getSelectorTable(), Member);
ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel);
-
+
// May be founf in property's qualified list.
if (!Setter)
Setter = LookupMethodInQualifiedType(SetterSel, OPT, true);
@@ -1282,9 +1460,6 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
// methods.
Setter = IFace->lookupPrivateMethod(SetterSel);
}
- // Look through local category implementations associated with the class.
- if (!Setter)
- Setter = IFace->getCategoryInstanceMethod(SetterSel);
if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc))
return ExprError();
@@ -1328,8 +1503,8 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT,
if (const ObjCObjectPointerType * OBJPT =
T->getAsObjCInterfacePointerType()) {
if (RequireCompleteType(MemberLoc, OBJPT->getPointeeType(),
- PDiag(diag::err_property_not_as_forward_class)
- << MemberName << BaseExpr->getSourceRange()))
+ diag::err_property_not_as_forward_class,
+ MemberName, BaseExpr))
return ExprError();
}
Diag(MemberLoc,
@@ -1603,9 +1778,9 @@ ExprResult Sema::ActOnSuperMessage(Scope *S,
// is acting as a keyword.
if (Method->isInstanceMethod()) {
if (Sel.getMethodFamily() == OMF_dealloc)
- ObjCShouldCallSuperDealloc = false;
+ getCurFunction()->ObjCShouldCallSuperDealloc = false;
if (Sel.getMethodFamily() == OMF_finalize)
- ObjCShouldCallSuperFinalize = false;
+ getCurFunction()->ObjCShouldCallSuperFinalize = false;
// Since we are in an instance method, this is an instance
// message to the superclass instance.
@@ -1711,9 +1886,9 @@ static void checkCocoaAPI(Sema &S, const ObjCMessageExpr *Msg) {
///
/// \param LBracLoc The location of the opening square bracket ']'.
///
-/// \param RBrac The location of the closing square bracket ']'.
+/// \param RBracLoc The location of the closing square bracket ']'.
///
-/// \param Args The message arguments.
+/// \param ArgsIn The message arguments.
ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo,
QualType ReceiverType,
SourceLocation SuperLoc,
@@ -1762,11 +1937,11 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo,
SourceRange TypeRange
= SuperLoc.isValid()? SourceRange(SuperLoc)
: ReceiverTypeInfo->getTypeLoc().getSourceRange();
- if (RequireCompleteType(Loc, Context.getObjCInterfaceType(Class),
+ if (RequireCompleteType(Loc, Context.getObjCInterfaceType(Class),
(getLangOpts().ObjCAutoRefCount
- ? PDiag(diag::err_arc_receiver_forward_class)
- : PDiag(diag::warn_receiver_forward_class))
- << TypeRange)) {
+ ? diag::err_arc_receiver_forward_class
+ : diag::warn_receiver_forward_class),
+ TypeRange)) {
// A forward class used in messaging is treated as a 'Class'
Method = LookupFactoryMethodInGlobalPool(Sel,
SourceRange(LBracLoc, RBracLoc));
@@ -1779,7 +1954,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo,
// If we have an implementation in scope, check "private" methods.
if (!Method)
- Method = LookupPrivateClassMethod(Sel, Class);
+ Method = Class->lookupPrivateClassMethod(Sel);
if (Method && DiagnoseUseOfDecl(Method, Loc))
return ExprError();
@@ -1881,9 +2056,9 @@ ExprResult Sema::BuildInstanceMessageImplicit(Expr *Receiver,
///
/// \param LBracLoc The location of the opening square bracket ']'.
///
-/// \param RBrac The location of the closing square bracket ']'.
+/// \param RBracLoc The location of the closing square bracket ']'.
///
-/// \param Args The message arguments.
+/// \param ArgsIn The message arguments.
ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
QualType ReceiverType,
SourceLocation SuperLoc,
@@ -1948,7 +2123,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
receiverIsId);
if (!Method)
Method = LookupFactoryMethodInGlobalPool(Sel,
- SourceRange(LBracLoc, RBracLoc),
+ SourceRange(LBracLoc,RBracLoc),
receiverIsId);
} else if (ReceiverType->isObjCClassType() ||
ReceiverType->isObjCQualifiedClassType()) {
@@ -1976,7 +2151,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
Method = ClassDecl->lookupClassMethod(Sel);
if (!Method)
- Method = LookupPrivateClassMethod(Sel, ClassDecl);
+ Method = ClassDecl->lookupPrivateClassMethod(Sel);
}
if (Method && DiagnoseUseOfDecl(Method, Loc))
return ExprError();
@@ -2009,12 +2184,15 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
// We allow sending a message to a qualified ID ("id<foo>"), which is ok as
// long as one of the protocols implements the selector (if not, warn).
+ // And as long as message is not deprecated/unavailable (warn if it is).
if (const ObjCObjectPointerType *QIdTy
= ReceiverType->getAsObjCQualifiedIdType()) {
// Search protocols for instance methods.
Method = LookupMethodInQualifiedType(Sel, QIdTy, true);
if (!Method)
Method = LookupMethodInQualifiedType(Sel, QIdTy, false);
+ if (Method && DiagnoseUseOfDecl(Method, Loc))
+ return ExprError();
} else if (const ObjCObjectPointerType *OCIType
= ReceiverType->getAsObjCInterfacePointerType()) {
// We allow sending a message to a pointer to an interface (an object).
@@ -2025,12 +2203,10 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
const ObjCInterfaceDecl *forwardClass = 0;
if (RequireCompleteType(Loc, OCIType->getPointeeType(),
getLangOpts().ObjCAutoRefCount
- ? PDiag(diag::err_arc_receiver_forward_instance)
- << (Receiver ? Receiver->getSourceRange()
- : SourceRange(SuperLoc))
- : PDiag(diag::warn_receiver_forward_instance)
- << (Receiver ? Receiver->getSourceRange()
- : SourceRange(SuperLoc)))) {
+ ? diag::err_arc_receiver_forward_instance
+ : diag::warn_receiver_forward_instance,
+ Receiver? Receiver->getSourceRange()
+ : SourceRange(SuperLoc))) {
if (getLangOpts().ObjCAutoRefCount)
return ExprError();
@@ -2048,7 +2224,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
if (!Method) {
// If we have implementations in scope, check "private" methods.
- Method = LookupPrivateInstanceMethod(Sel, ClassDecl);
+ Method = ClassDecl->lookupPrivateMethod(Sel);
if (!Method && getLangOpts().ObjCAutoRefCount) {
Diag(Loc, diag::err_arc_may_not_respond)
@@ -2062,7 +2238,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
// compatibility. FIXME: should we deviate??
if (OCIType->qual_empty()) {
Method = LookupInstanceMethodInGlobalPool(Sel,
- SourceRange(LBracLoc, RBracLoc));
+ SourceRange(LBracLoc, RBracLoc));
if (Method && !forwardClass)
Diag(Loc, diag::warn_maynot_respond)
<< OCIType->getInterfaceDecl()->getIdentifier() << Sel;
@@ -2087,8 +2263,9 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
// TODO: specialized warning on null receivers?
bool IsNull = Receiver->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNull);
+ CastKind Kind = IsNull ? CK_NullToPointer : CK_IntegralToPointer;
Receiver = ImpCastExprToType(Receiver, Context.getObjCIdType(),
- IsNull ? CK_NullToPointer : CK_IntegralToPointer).take();
+ Kind).take();
}
ReceiverType = Receiver->getType();
} else {
@@ -2232,10 +2409,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
}
if (getLangOpts().ObjCAutoRefCount) {
- if (Receiver &&
- (Receiver->IgnoreParenImpCasts()->getType().getObjCLifetime()
- == Qualifiers::OCL_Weak))
- Diag(Receiver->getLocStart(), diag::warn_receiver_is_weak);
+ DiagnoseARCUseOfWeakReceiver(*this, Receiver);
// In ARC, annotate delegate init calls.
if (Result->getMethodFamily() == OMF_init &&
@@ -2373,6 +2547,7 @@ namespace {
ASTContext &Context;
ARCConversionTypeClass SourceClass;
ARCConversionTypeClass TargetClass;
+ bool Diagnose;
static bool isCFType(QualType type) {
// Someday this can use ns_bridged. For now, it has to do this.
@@ -2381,8 +2556,9 @@ namespace {
public:
ARCCastChecker(ASTContext &Context, ARCConversionTypeClass source,
- ARCConversionTypeClass target)
- : Context(Context), SourceClass(source), TargetClass(target) {}
+ ARCConversionTypeClass target, bool diagnose)
+ : Context(Context), SourceClass(source), TargetClass(target),
+ Diagnose(diagnose) {}
using super::Visit;
ACCResult Visit(Expr *e) {
@@ -2500,7 +2676,8 @@ namespace {
// now we're not going to permit implicit handling of +1 results,
// because it's a bit frightening.
if (fn->hasAttr<CFReturnsRetainedAttr>())
- return ACC_invalid; // ACC_plusOne if we start accepting this
+ return Diagnose ? ACC_plusOne
+ : ACC_invalid; // ACC_plusOne if we start accepting this
// Recognize this specific builtin function, which is used by CFSTR.
unsigned builtinID = fn->getBuiltinID();
@@ -2510,10 +2687,11 @@ namespace {
// Otherwise, don't do anything implicit with an unaudited function.
if (!fn->hasAttr<CFAuditedTransferAttr>())
return ACC_invalid;
-
+
// Otherwise, it's +0 unless it follows the create convention.
if (ento::coreFoundation::followsCreateRule(fn))
- return ACC_invalid; // ACC_plusOne if we start accepting this
+ return Diagnose ? ACC_plusOne
+ : ACC_invalid; // ACC_plusOne if we start accepting this
return ACC_plusZero;
}
@@ -2564,11 +2742,12 @@ namespace {
};
}
-static bool
-KnownName(Sema &S, const char *name) {
- LookupResult R(S, &S.Context.Idents.get(name), SourceLocation(),
+bool Sema::isKnownName(StringRef name) {
+ if (name.empty())
+ return false;
+ LookupResult R(*this, &Context.Idents.get(name), SourceLocation(),
Sema::LookupOrdinaryName);
- return S.LookupName(R, S.TUScope, false);
+ return LookupName(R, TUScope, false);
}
static void addFixitForObjCARCConversion(Sema &S,
@@ -2595,14 +2774,23 @@ static void addFixitForObjCARCConversion(Sema &S,
castedE = CCE->getSubExpr();
castedE = castedE->IgnoreImpCasts();
SourceRange range = castedE->getSourceRange();
+
+ SmallString<32> BridgeCall;
+
+ SourceManager &SM = S.getSourceManager();
+ char PrevChar = *SM.getCharacterData(range.getBegin().getLocWithOffset(-1));
+ if (Lexer::isIdentifierBodyChar(PrevChar, S.getLangOpts()))
+ BridgeCall += ' ';
+
+ BridgeCall += CFBridgeName;
+
if (isa<ParenExpr>(castedE)) {
DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(),
- CFBridgeName));
+ BridgeCall));
} else {
- std::string namePlusParen = CFBridgeName;
- namePlusParen += "(";
+ BridgeCall += '(';
DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(),
- namePlusParen));
+ BridgeCall));
DiagB.AddFixItHint(FixItHint::CreateInsertion(
S.PP.getLocForEndOfToken(range.getEnd()),
")"));
@@ -2677,14 +2865,20 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange,
<< castType
<< castRange
<< castExpr->getSourceRange();
- bool br = KnownName(S, "CFBridgingRelease");
+ bool br = S.isKnownName("CFBridgingRelease");
+ ACCResult CreateRule =
+ ARCCastChecker(S.Context, exprACTC, castACTC, true).Visit(castExpr);
+ assert(CreateRule != ACC_bottom && "This cast should already be accepted.");
+ if (CreateRule != ACC_plusOne)
{
DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge);
addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen,
castType, castExpr, "__bridge ", 0);
}
+ if (CreateRule != ACC_plusZero)
{
- DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge_transfer)
+ DiagnosticBuilder DiagB = S.Diag(br ? castExpr->getExprLoc() : noteLoc,
+ diag::note_arc_bridge_transfer)
<< castExprType << br;
addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen,
castType, castExpr, "__bridge_transfer ",
@@ -2696,7 +2890,7 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange,
// Bridge from a CF type to an ARC type.
if (exprACTC == ACTC_retainable && isAnyRetainable(castACTC)) {
- bool br = KnownName(S, "CFBridgingRetain");
+ bool br = S.isKnownName("CFBridgingRetain");
S.Diag(loc, diag::err_arc_cast_requires_bridge)
<< unsigned(CCK == Sema::CCK_ImplicitConversion) // cast|implicit
<< unsigned(castExprType->isBlockPointerType()) // of ObjC|block type
@@ -2705,14 +2899,19 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange,
<< castType
<< castRange
<< castExpr->getSourceRange();
-
+ ACCResult CreateRule =
+ ARCCastChecker(S.Context, exprACTC, castACTC, true).Visit(castExpr);
+ assert(CreateRule != ACC_bottom && "This cast should already be accepted.");
+ if (CreateRule != ACC_plusOne)
{
DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge);
addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen,
castType, castExpr, "__bridge ", 0);
}
+ if (CreateRule != ACC_plusZero)
{
- DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge_retained)
+ DiagnosticBuilder DiagB = S.Diag(br ? castExpr->getExprLoc() : noteLoc,
+ diag::note_arc_bridge_retained)
<< castType << br;
addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen,
castType, castExpr, "__bridge_retained ",
@@ -2785,7 +2984,7 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType,
CCK != CCK_ImplicitConversion)
return ACR_okay;
- switch (ARCCastChecker(Context, exprACTC, castACTC).Visit(castExpr)) {
+ switch (ARCCastChecker(Context, exprACTC, castACTC, false).Visit(castExpr)) {
// For invalid casts, fall through.
case ACC_invalid:
break;
@@ -2949,7 +3148,7 @@ ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc,
break;
case OBC_BridgeRetained: {
- bool br = KnownName(*this, "CFBridgingRelease");
+ bool br = isKnownName("CFBridgingRelease");
Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind)
<< 2
<< FromType
@@ -2992,7 +3191,7 @@ ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc,
break;
case OBC_BridgeTransfer: {
- bool br = KnownName(*this, "CFBridgingRetain");
+ bool br = isKnownName("CFBridgingRetain");
Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind)
<< (FromType->isBlockPointerType()? 1 : 0)
<< FromType
diff --git a/lib/Sema/SemaFixItUtils.cpp b/lib/Sema/SemaFixItUtils.cpp
index b78ea7d..b61b930 100644
--- a/lib/Sema/SemaFixItUtils.cpp
+++ b/lib/Sema/SemaFixItUtils.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Lex/Preprocessor.h"
@@ -163,42 +164,54 @@ static bool isMacroDefined(const Sema &S, StringRef Name) {
return S.PP.getMacroInfo(&S.getASTContext().Idents.get(Name));
}
-const char *Sema::getFixItZeroInitializerForType(QualType T) const {
+static std::string getScalarZeroExpressionForType(const Type& T, const Sema& S) {
+ assert(T.isScalarType() && "use scalar types only");
+ // Suggest "0" for non-enumeration scalar types, unless we can find a
+ // better initializer.
+ if (T.isEnumeralType())
+ return std::string();
+ if ((T.isObjCObjectPointerType() || T.isBlockPointerType()) &&
+ isMacroDefined(S, "nil"))
+ return "nil";
+ if (T.isRealFloatingType())
+ return "0.0";
+ if (T.isBooleanType() && S.LangOpts.CPlusPlus)
+ return "false";
+ if (T.isPointerType() || T.isMemberPointerType()) {
+ if (S.LangOpts.CPlusPlus0x)
+ return "nullptr";
+ if (isMacroDefined(S, "NULL"))
+ return "NULL";
+ }
+ if (T.isCharType())
+ return "'\\0'";
+ if (T.isWideCharType())
+ return "L'\\0'";
+ if (T.isChar16Type())
+ return "u'\\0'";
+ if (T.isChar32Type())
+ return "U'\\0'";
+ return "0";
+}
+
+std::string Sema::getFixItZeroInitializerForType(QualType T) const {
if (T->isScalarType()) {
- // Suggest " = 0" for non-enumeration scalar types, unless we can find a
- // better initializer.
- if (T->isEnumeralType())
- return 0;
- if ((T->isObjCObjectPointerType() || T->isBlockPointerType()) &&
- isMacroDefined(*this, "nil"))
- return " = nil";
- if (T->isRealFloatingType())
- return " = 0.0";
- if (T->isBooleanType() && LangOpts.CPlusPlus)
- return " = false";
- if (T->isPointerType() || T->isMemberPointerType()) {
- if (LangOpts.CPlusPlus0x)
- return " = nullptr";
- else if (isMacroDefined(*this, "NULL"))
- return " = NULL";
- }
- if (T->isCharType())
- return " = '\\0'";
- if (T->isWideCharType())
- return " = L'\\0'";
- if (T->isChar16Type())
- return " = u'\\0'";
- if (T->isChar32Type())
- return " = U'\\0'";
- return " = 0";
+ std::string s = getScalarZeroExpressionForType(*T, *this);
+ if (!s.empty())
+ s = " = " + s;
+ return s;
}
const CXXRecordDecl *RD = T->getAsCXXRecordDecl();
if (!RD || !RD->hasDefinition())
- return 0;
+ return std::string();
if (LangOpts.CPlusPlus0x && !RD->hasUserProvidedDefaultConstructor())
return "{}";
if (RD->isAggregate())
return " = {}";
- return 0;
+ return std::string();
+}
+
+std::string Sema::getFixItZeroLiteralForType(QualType T) const {
+ return getScalarZeroExpressionForType(*T, *this);
}
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index a65b41f..62ab1e6 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -92,8 +92,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) {
// C99 6.7.8p14. We have an array of character type with unknown size
// being initialized to a string literal.
- llvm::APSInt ConstVal(32);
- ConstVal = StrLength;
+ llvm::APInt ConstVal(32, StrLength);
// Return a new array type (C99 6.7.8p22).
DeclT = S.Context.getConstantArrayType(IAT->getElementType(),
ConstVal,
@@ -687,22 +686,21 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
} else if (DeclType->isVectorType()) {
CheckVectorType(Entity, IList, DeclType, Index,
StructuredList, StructuredIndex);
- } else if (DeclType->isAggregateType()) {
- if (DeclType->isRecordType()) {
- RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl();
- CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(),
- SubobjectIsDesignatorContext, Index,
- StructuredList, StructuredIndex,
- TopLevelObject);
- } else if (DeclType->isArrayType()) {
- llvm::APSInt Zero(
- SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()),
- false);
- CheckArrayType(Entity, IList, DeclType, Zero,
- SubobjectIsDesignatorContext, Index,
- StructuredList, StructuredIndex);
- } else
- llvm_unreachable("Aggregate that isn't a structure or array?!");
+ } else if (DeclType->isRecordType()) {
+ assert(DeclType->isAggregateType() &&
+ "non-aggregate records should be handed in CheckSubElementType");
+ RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl();
+ CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(),
+ SubobjectIsDesignatorContext, Index,
+ StructuredList, StructuredIndex,
+ TopLevelObject);
+ } else if (DeclType->isArrayType()) {
+ llvm::APSInt Zero(
+ SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()),
+ false);
+ CheckArrayType(Entity, IList, DeclType, Zero,
+ SubobjectIsDesignatorContext, Index,
+ StructuredList, StructuredIndex);
} else if (DeclType->isVoidType() || DeclType->isFunctionType()) {
// This type is invalid, issue a diagnostic.
++Index;
@@ -710,19 +708,6 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
SemaRef.Diag(IList->getLocStart(), diag::err_illegal_initializer_type)
<< DeclType;
hadError = true;
- } else if (DeclType->isRecordType()) {
- // C++ [dcl.init]p14:
- // [...] If the class is an aggregate (8.5.1), and the initializer
- // is a brace-enclosed list, see 8.5.1.
- //
- // Note: 8.5.1 is handled below; here, we diagnose the case where
- // we have an initializer list and a destination type that is not
- // an aggregate.
- // FIXME: In C++0x, this is yet another form of initialization.
- if (!VerifyOnly)
- SemaRef.Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list)
- << DeclType << IList->getSourceRange();
- hadError = true;
} else if (DeclType->isReferenceType()) {
CheckReferenceType(Entity, IList, DeclType, Index,
StructuredList, StructuredIndex);
@@ -747,18 +732,25 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
unsigned &StructuredIndex) {
Expr *expr = IList->getInit(Index);
if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) {
- unsigned newIndex = 0;
- unsigned newStructuredIndex = 0;
- InitListExpr *newStructuredList
- = getStructuredSubobjectInit(IList, Index, ElemType,
- StructuredList, StructuredIndex,
- SubInitList->getSourceRange());
- CheckExplicitInitList(Entity, SubInitList, ElemType, newIndex,
- newStructuredList, newStructuredIndex);
- ++StructuredIndex;
- ++Index;
- return;
- } else if (ElemType->isScalarType()) {
+ if (!ElemType->isRecordType() || ElemType->isAggregateType()) {
+ unsigned newIndex = 0;
+ unsigned newStructuredIndex = 0;
+ InitListExpr *newStructuredList
+ = getStructuredSubobjectInit(IList, Index, ElemType,
+ StructuredList, StructuredIndex,
+ SubInitList->getSourceRange());
+ CheckExplicitInitList(Entity, SubInitList, ElemType, newIndex,
+ newStructuredList, newStructuredIndex);
+ ++StructuredIndex;
+ ++Index;
+ return;
+ }
+ assert(SemaRef.getLangOpts().CPlusPlus &&
+ "non-aggregate records are only possible in C++");
+ // C++ initialization is handled later.
+ }
+
+ if (ElemType->isScalarType()) {
return CheckScalarType(Entity, IList, ElemType, Index,
StructuredList, StructuredIndex);
} else if (ElemType->isReferenceType()) {
@@ -1859,7 +1851,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
}
} else {
// Recurse to check later designated subobjects.
- QualType FieldType = (*Field)->getType();
+ QualType FieldType = Field->getType();
unsigned newStructuredIndex = FieldIndex;
InitializedEntity MemberEntity =
@@ -2708,84 +2700,39 @@ static void MaybeProduceObjCObject(Sema &S,
}
}
-/// \brief When initializing from init list via constructor, deal with the
-/// empty init list and std::initializer_list special cases.
+/// \brief When initializing from init list via constructor, handle
+/// initialization of an object of type std::initializer_list<T>.
///
-/// \return True if this was a special case, false otherwise.
-static bool TryListConstructionSpecialCases(Sema &S,
- InitListExpr *List,
- CXXRecordDecl *DestRecordDecl,
- QualType DestType,
- InitializationSequence &Sequence) {
- // C++11 [dcl.init.list]p3:
- // List-initialization of an object or reference of type T is defined as
- // follows:
- // - If T is an aggregate, aggregate initialization is performed.
- if (DestType->isAggregateType())
+/// \return true if we have handled initialization of an object of type
+/// std::initializer_list<T>, false otherwise.
+static bool TryInitializerListConstruction(Sema &S,
+ InitListExpr *List,
+ QualType DestType,
+ InitializationSequence &Sequence) {
+ QualType E;
+ if (!S.isStdInitializerList(DestType, &E))
return false;
- // - Otherwise, if the initializer list has no elements and T is a class
- // type with a default constructor, the object is value-initialized.
- if (List->getNumInits() == 0) {
- if (CXXConstructorDecl *DefaultConstructor =
- S.LookupDefaultConstructor(DestRecordDecl)) {
- if (DefaultConstructor->isDeleted() ||
- S.isFunctionConsideredUnavailable(DefaultConstructor)) {
- // Fake an overload resolution failure.
- OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet();
- DeclAccessPair FoundDecl = DeclAccessPair::make(DefaultConstructor,
- DefaultConstructor->getAccess());
- if (FunctionTemplateDecl *ConstructorTmpl =
- dyn_cast<FunctionTemplateDecl>(DefaultConstructor))
- S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl,
- /*ExplicitArgs*/ 0,
- ArrayRef<Expr*>(), CandidateSet,
- /*SuppressUserConversions*/ false);
- else
- S.AddOverloadCandidate(DefaultConstructor, FoundDecl,
- ArrayRef<Expr*>(), CandidateSet,
- /*SuppressUserConversions*/ false);
- Sequence.SetOverloadFailure(
- InitializationSequence::FK_ListConstructorOverloadFailed,
- OR_Deleted);
- } else
- Sequence.AddConstructorInitializationStep(DefaultConstructor,
- DefaultConstructor->getAccess(),
- DestType,
- /*MultipleCandidates=*/false,
- /*FromInitList=*/true,
- /*AsInitList=*/false);
+ // Check that each individual element can be copy-constructed. But since we
+ // have no place to store further information, we'll recalculate everything
+ // later.
+ InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary(
+ S.Context.getConstantArrayType(E,
+ llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()),
+ List->getNumInits()),
+ ArrayType::Normal, 0));
+ InitializedEntity Element = InitializedEntity::InitializeElement(S.Context,
+ 0, HiddenArray);
+ for (unsigned i = 0, n = List->getNumInits(); i < n; ++i) {
+ Element.setElementIndex(i);
+ if (!S.CanPerformCopyInitialization(Element, List->getInit(i))) {
+ Sequence.SetFailed(
+ InitializationSequence::FK_InitListElementCopyFailure);
return true;
}
}
-
- // - Otherwise, if T is a specialization of std::initializer_list, [...]
- QualType E;
- if (S.isStdInitializerList(DestType, &E)) {
- // Check that each individual element can be copy-constructed. But since we
- // have no place to store further information, we'll recalculate everything
- // later.
- InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary(
- S.Context.getConstantArrayType(E,
- llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()),
- List->getNumInits()),
- ArrayType::Normal, 0));
- InitializedEntity Element = InitializedEntity::InitializeElement(S.Context,
- 0, HiddenArray);
- for (unsigned i = 0, n = List->getNumInits(); i < n; ++i) {
- Element.setElementIndex(i);
- if (!S.CanPerformCopyInitialization(Element, List->getInit(i))) {
- Sequence.SetFailed(
- InitializationSequence::FK_InitListElementCopyFailure);
- return true;
- }
- }
- Sequence.AddStdInitializerListConstructionStep(DestType);
- return true;
- }
-
- // Not a special case.
- return false;
+ Sequence.AddStdInitializerListConstructionStep(DestType);
+ return true;
}
static OverloadingResult
@@ -2886,11 +2833,6 @@ static void TryConstructorInitialization(Sema &S,
CXXRecordDecl *DestRecordDecl
= cast<CXXRecordDecl>(DestRecordType->getDecl());
- if (InitListSyntax &&
- TryListConstructionSpecialCases(S, cast<InitListExpr>(Args[0]),
- DestRecordDecl, DestType, Sequence))
- return;
-
// Build the candidate set directly in the initialization sequence
// structure, so that it will persist if we fail.
OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet();
@@ -2917,15 +2859,21 @@ static void TryConstructorInitialization(Sema &S,
// constructors of the class T and the argument list consists of the
// initializer list as a single argument.
if (InitListSyntax) {
+ InitListExpr *ILE = cast<InitListExpr>(Args[0]);
AsInitializerList = true;
- Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs,
- CandidateSet, ConStart, ConEnd, Best,
- CopyInitialization, AllowExplicit,
- /*OnlyListConstructor=*/true,
- InitListSyntax);
+
+ // If the initializer list has no elements and T has a default constructor,
+ // the first phase is omitted.
+ if (ILE->getNumInits() != 0 ||
+ (!DestRecordDecl->hasDeclaredDefaultConstructor() &&
+ !DestRecordDecl->needsImplicitDefaultConstructor()))
+ Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs,
+ CandidateSet, ConStart, ConEnd, Best,
+ CopyInitialization, AllowExplicit,
+ /*OnlyListConstructor=*/true,
+ InitListSyntax);
// Time to unwrap the init list.
- InitListExpr *ILE = cast<InitListExpr>(Args[0]);
Args = ILE->getInits();
NumArgs = ILE->getNumInits();
}
@@ -2933,7 +2881,7 @@ static void TryConstructorInitialization(Sema &S,
// C++11 [over.match.list]p1:
// - If no viable initializer-list constructor is found, overload resolution
// is performed again, where the candidate functions are all the
- // constructors of the class T nad the argument list consists of the
+ // constructors of the class T and the argument list consists of the
// elements of the initializer list.
if (Result == OR_No_Viable_Function) {
AsInitializerList = false;
@@ -2951,13 +2899,13 @@ static void TryConstructorInitialization(Sema &S,
return;
}
- // C++0x [dcl.init]p6:
+ // C++11 [dcl.init]p6:
// If a program calls for the default initialization of an object
// of a const-qualified type T, T shall be a class type with a
// user-provided default constructor.
if (Kind.getKind() == InitializationKind::IK_Default &&
Entity.getType().isConstQualified() &&
- cast<CXXConstructorDecl>(Best->Function)->isImplicit()) {
+ !cast<CXXConstructorDecl>(Best->Function)->isUserProvided()) {
Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst);
return;
}
@@ -3018,6 +2966,12 @@ static void TryReferenceInitializationCore(Sema &S,
Qualifiers T2Quals,
InitializationSequence &Sequence);
+static void TryValueInitialization(Sema &S,
+ const InitializedEntity &Entity,
+ const InitializationKind &Kind,
+ InitializationSequence &Sequence,
+ InitListExpr *InitList = 0);
+
static void TryListInitialization(Sema &S,
const InitializedEntity &Entity,
const InitializationKind &Kind,
@@ -3108,19 +3062,36 @@ static void TryListInitialization(Sema &S,
return;
}
if (DestType->isRecordType()) {
- if (S.RequireCompleteType(InitList->getLocStart(), DestType, S.PDiag())) {
+ if (S.RequireCompleteType(InitList->getLocStart(), DestType, 0)) {
Sequence.setIncompleteTypeFailure(DestType);
return;
}
+ // C++11 [dcl.init.list]p3:
+ // - If T is an aggregate, aggregate initialization is performed.
if (!DestType->isAggregateType()) {
if (S.getLangOpts().CPlusPlus0x) {
+ // - Otherwise, if the initializer list has no elements and T is a
+ // class type with a default constructor, the object is
+ // value-initialized.
+ if (InitList->getNumInits() == 0) {
+ CXXRecordDecl *RD = DestType->getAsCXXRecordDecl();
+ if (RD->hasDeclaredDefaultConstructor() ||
+ RD->needsImplicitDefaultConstructor()) {
+ TryValueInitialization(S, Entity, Kind, Sequence, InitList);
+ return;
+ }
+ }
+
+ // - Otherwise, if T is a specialization of std::initializer_list<E>,
+ // an initializer_list object constructed [...]
+ if (TryInitializerListConstruction(S, InitList, DestType, Sequence))
+ return;
+
+ // - Otherwise, if T is a class type, constructors are considered.
Expr *Arg = InitList;
- // A direct-initializer is not list-syntax, i.e. there's no special
- // treatment of "A a({1, 2});".
- TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType,
- Sequence,
- Kind.getKind() != InitializationKind::IK_Direct);
+ TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType,
+ Sequence, /*InitListSyntax*/true);
} else
Sequence.SetFailed(
InitializationSequence::FK_InitListBadDestinationType);
@@ -3605,7 +3576,11 @@ static void TryStringLiteralInitialization(Sema &S,
static void TryValueInitialization(Sema &S,
const InitializedEntity &Entity,
const InitializationKind &Kind,
- InitializationSequence &Sequence) {
+ InitializationSequence &Sequence,
+ InitListExpr *InitList) {
+ assert((!InitList || InitList->getNumInits() == 0) &&
+ "Shouldn't use value-init for non-empty init lists");
+
// C++98 [dcl.init]p5, C++11 [dcl.init]p7:
//
// To value-initialize an object of type T means:
@@ -3616,17 +3591,15 @@ static void TryValueInitialization(Sema &S,
if (const RecordType *RT = T->getAs<RecordType>()) {
if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
- // C++98:
- // -- if T is a class type (clause 9) with a user-declared
- // constructor (12.1), then the default constructor for T is
- // called (and the initialization is ill-formed if T has no
- // accessible default constructor);
+ bool NeedZeroInitialization = true;
if (!S.getLangOpts().CPlusPlus0x) {
+ // C++98:
+ // -- if T is a class type (clause 9) with a user-declared constructor
+ // (12.1), then the default constructor for T is called (and the
+ // initialization is ill-formed if T has no accessible default
+ // constructor);
if (ClassDecl->hasUserDeclaredConstructor())
- // FIXME: we really want to refer to a single subobject of the array,
- // but Entity doesn't have a way to capture that (yet).
- return TryConstructorInitialization(S, Entity, Kind, 0, 0,
- T, Sequence);
+ NeedZeroInitialization = false;
} else {
// C++11:
// -- if T is a class type (clause 9) with either no default constructor
@@ -3634,19 +3607,28 @@ static void TryValueInitialization(Sema &S,
// or deleted, then the object is default-initialized;
CXXConstructorDecl *CD = S.LookupDefaultConstructor(ClassDecl);
if (!CD || !CD->getCanonicalDecl()->isDefaulted() || CD->isDeleted())
- return TryConstructorInitialization(S, Entity, Kind, 0, 0,
- T, Sequence);
+ NeedZeroInitialization = false;
}
// -- if T is a (possibly cv-qualified) non-union class type without a
// user-provided or deleted default constructor, then the object is
// zero-initialized and, if T has a non-trivial default constructor,
// default-initialized;
- if ((ClassDecl->getTagKind() == TTK_Class ||
- ClassDecl->getTagKind() == TTK_Struct)) {
+ // FIXME: The 'non-union' here is a defect (not yet assigned an issue
+ // number). Update the quotation when the defect is resolved.
+ if (NeedZeroInitialization)
Sequence.AddZeroInitializationStep(Entity.getType());
- return TryConstructorInitialization(S, Entity, Kind, 0, 0, T, Sequence);
- }
+
+ // If this is list-value-initialization, pass the empty init list on when
+ // building the constructor call. This affects the semantics of a few
+ // things (such as whether an explicit default constructor can be called).
+ Expr *InitListAsExpr = InitList;
+ Expr **Args = InitList ? &InitListAsExpr : 0;
+ unsigned NumArgs = InitList ? 1 : 0;
+ bool InitListSyntax = InitList;
+
+ return TryConstructorInitialization(S, Entity, Kind, Args, NumArgs, T,
+ Sequence, InitListSyntax);
}
}
@@ -4101,8 +4083,8 @@ InitializationSequence::InitializationSequence(Sema &S,
AddArrayInitStep(DestType);
}
}
- // Note: as a GNU C++ extension, we allow initialization of a
- // class member from a parenthesized initializer list.
+ // Note: as a GNU C++ extension, we allow list-initialization of a
+ // class member of array type from a parenthesized initializer list.
else if (S.getLangOpts().CPlusPlus &&
Entity.getKind() == InitializedEntity::EK_Member &&
Initializer && isa<InitListExpr>(Initializer)) {
@@ -4409,7 +4391,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity,
/// \param T The type of the temporary object, which must either be
/// the type of the initializer expression or a superclass thereof.
///
-/// \param Enter The entity being initialized.
+/// \param Entity The entity being initialized.
///
/// \param CurInit The initializer expression.
///
@@ -4452,7 +4434,7 @@ static ExprResult CopyObject(Sema &S,
SourceLocation Loc = getInitializationLoc(Entity, CurInit.get());
// Make sure that the type we are copying is complete.
- if (S.RequireCompleteType(Loc, T, S.PDiag(diag::err_temp_copy_incomplete)))
+ if (S.RequireCompleteType(Loc, T, diag::err_temp_copy_incomplete))
return move(CurInit);
// Perform overload resolution using the class's copy/move constructors.
@@ -4516,7 +4498,7 @@ static ExprResult CopyObject(Sema &S,
for (unsigned I = 1, N = Constructor->getNumParams(); I != N; ++I) {
ParmVarDecl *Parm = Constructor->getParamDecl(I);
if (S.RequireCompleteType(Loc, Parm->getType(),
- S.PDiag(diag::err_call_incomplete_argument)))
+ diag::err_call_incomplete_argument))
break;
// Build the default argument expression; we don't actually care
@@ -4748,6 +4730,43 @@ PerformConstructorInitialization(Sema &S,
return move(CurInit);
}
+/// Determine whether the specified InitializedEntity definitely has a lifetime
+/// longer than the current full-expression. Conservatively returns false if
+/// it's unclear.
+static bool
+InitializedEntityOutlivesFullExpression(const InitializedEntity &Entity) {
+ const InitializedEntity *Top = &Entity;
+ while (Top->getParent())
+ Top = Top->getParent();
+
+ switch (Top->getKind()) {
+ case InitializedEntity::EK_Variable:
+ case InitializedEntity::EK_Result:
+ case InitializedEntity::EK_Exception:
+ case InitializedEntity::EK_Member:
+ case InitializedEntity::EK_New:
+ case InitializedEntity::EK_Base:
+ case InitializedEntity::EK_Delegating:
+ return true;
+
+ case InitializedEntity::EK_ArrayElement:
+ case InitializedEntity::EK_VectorElement:
+ case InitializedEntity::EK_BlockElement:
+ case InitializedEntity::EK_ComplexElement:
+ // Could not determine what the full initialization is. Assume it might not
+ // outlive the full-expression.
+ return false;
+
+ case InitializedEntity::EK_Parameter:
+ case InitializedEntity::EK_Temporary:
+ case InitializedEntity::EK_LambdaCapture:
+ // The entity being initialized might not outlive the full-expression.
+ return false;
+ }
+
+ llvm_unreachable("unknown entity kind");
+}
+
ExprResult
InitializationSequence::Perform(Sema &S,
const InitializedEntity &Entity,
@@ -4816,6 +4835,29 @@ InitializationSequence::Perform(Sema &S,
if (Steps.empty())
return S.Owned((Expr *)0);
+ if (S.getLangOpts().CPlusPlus0x && Entity.getType()->isReferenceType() &&
+ Args.size() == 1 && isa<InitListExpr>(Args.get()[0]) &&
+ Entity.getKind() != InitializedEntity::EK_Parameter) {
+ // Produce a C++98 compatibility warning if we are initializing a reference
+ // from an initializer list. For parameters, we produce a better warning
+ // elsewhere.
+ Expr *Init = Args.get()[0];
+ S.Diag(Init->getLocStart(), diag::warn_cxx98_compat_reference_list_init)
+ << Init->getSourceRange();
+ }
+
+ // Diagnose cases where we initialize a pointer to an array temporary, and the
+ // pointer obviously outlives the temporary.
+ if (Args.size() == 1 && Args.get()[0]->getType()->isArrayType() &&
+ Entity.getType()->isPointerType() &&
+ InitializedEntityOutlivesFullExpression(Entity)) {
+ Expr *Init = Args.get()[0];
+ Expr::LValueClassification Kind = Init->ClassifyLValue(S.Context);
+ if (Kind == Expr::LV_ClassTemporary || Kind == Expr::LV_ArrayTemporary)
+ S.Diag(Init->getLocStart(), diag::warn_temporary_array_to_pointer_decay)
+ << Init->getSourceRange();
+ }
+
QualType DestType = Entity.getType().getNonReferenceType();
// FIXME: Ugly hack around the fact that Entity.getType() is not
// the same as Entity.getDecl()->getType() in cases involving type merging,
@@ -4842,7 +4884,6 @@ InitializationSequence::Perform(Sema &S,
case SK_QualificationConversionXValue:
case SK_QualificationConversionRValue:
case SK_ConversionSequence:
- case SK_ListConstructorCall:
case SK_ListInitialization:
case SK_UnwrapInitList:
case SK_RewrapInitList:
@@ -4862,6 +4903,7 @@ InitializationSequence::Perform(Sema &S,
}
case SK_ConstructorInitialization:
+ case SK_ListConstructorCall:
case SK_ZeroInitialization:
break;
}
@@ -5152,7 +5194,10 @@ InitializationSequence::Perform(Sema &S,
InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(
Entity.getType().getNonReferenceType());
bool UseTemporary = Entity.getType()->isReferenceType();
- InitListExpr *InitList = cast<InitListExpr>(CurInit.get());
+ assert(Args.size() == 1 && "expected a single argument for list init");
+ InitListExpr *InitList = cast<InitListExpr>(Args.get()[0]);
+ S.Diag(InitList->getExprLoc(), diag::warn_cxx98_compat_ctor_list_init)
+ << InitList->getSourceRange();
MultiExprArg Arg(InitList->getInits(), InitList->getNumInits());
CurInit = PerformConstructorInitialization(S, UseTemporary ? TempEntity :
Entity,
@@ -5198,7 +5243,8 @@ InitializationSequence::Perform(Sema &S,
step_iterator NextStep = Step;
++NextStep;
if (NextStep != StepEnd &&
- NextStep->Kind == SK_ConstructorInitialization) {
+ (NextStep->Kind == SK_ConstructorInitialization ||
+ NextStep->Kind == SK_ListConstructorCall)) {
// The need for zero-initialization is recorded directly into
// the call to the object's constructor within the next step.
ConstructorInitRequiresZeroInit = true;
@@ -5330,6 +5376,8 @@ InitializationSequence::Perform(Sema &S,
}
InitListExpr *ILE = cast<InitListExpr>(CurInit.take());
+ S.Diag(ILE->getExprLoc(), diag::warn_cxx98_compat_initializer_list_init)
+ << ILE->getSourceRange();
unsigned NumInits = ILE->getNumInits();
SmallVector<Expr*, 16> Converted(NumInits);
InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary(
@@ -6130,8 +6178,8 @@ Sema::CanPerformCopyInitialization(const InitializedEntity &Entity,
Expr *InitE = Init.get();
assert(InitE && "No initialization expression");
- InitializationKind Kind = InitializationKind::CreateCopy(SourceLocation(),
- SourceLocation());
+ InitializationKind Kind
+ = InitializationKind::CreateCopy(InitE->getLocStart(), SourceLocation());
InitializationSequence Seq(*this, Entity, Kind, &InitE, 1);
return !Seq.Failed();
}
diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp
index 6ef8d88..6414c6f 100644
--- a/lib/Sema/SemaLambda.cpp
+++ b/lib/Sema/SemaLambda.cpp
@@ -54,9 +54,7 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
SourceRange IntroducerRange,
TypeSourceInfo *MethodType,
SourceLocation EndLoc,
- llvm::ArrayRef<ParmVarDecl *> Params,
- llvm::Optional<unsigned> ManglingNumber,
- Decl *ContextDecl) {
+ llvm::ArrayRef<ParmVarDecl *> Params) {
// C++11 [expr.prim.lambda]p5:
// The closure type for a lambda-expression has a public inline function
// call operator (13.5.4) whose parameters and return type are described by
@@ -98,64 +96,76 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
P != PEnd; ++P)
(*P)->setOwningFunction(Method);
}
-
- // If we don't already have a mangling number for this lambda expression,
- // allocate one now.
- if (!ManglingNumber) {
- ContextDecl = ExprEvalContexts.back().LambdaContextDecl;
-
- enum ContextKind {
- Normal,
- DefaultArgument,
- DataMember,
- StaticDataMember
- } Kind = Normal;
-
- // Default arguments of member function parameters that appear in a class
- // definition, as well as the initializers of data members, receive special
- // treatment. Identify them.
- if (ContextDecl) {
- if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(ContextDecl)) {
- if (const DeclContext *LexicalDC
- = Param->getDeclContext()->getLexicalParent())
- if (LexicalDC->isRecord())
- Kind = DefaultArgument;
- } else if (VarDecl *Var = dyn_cast<VarDecl>(ContextDecl)) {
- if (Var->getDeclContext()->isRecord())
- Kind = StaticDataMember;
- } else if (isa<FieldDecl>(ContextDecl)) {
- Kind = DataMember;
- }
- }
-
- switch (Kind) {
- case Normal:
- if (CurContext->isDependentContext() || isInInlineFunction(CurContext))
- ManglingNumber = Context.getLambdaManglingNumber(Method);
- else
- ManglingNumber = 0;
-
- // There is no special context for this lambda.
- ContextDecl = 0;
- break;
-
- case StaticDataMember:
- if (!CurContext->isDependentContext()) {
- ManglingNumber = 0;
- ContextDecl = 0;
- break;
- }
- // Fall through to assign a mangling number.
-
- case DataMember:
- case DefaultArgument:
- ManglingNumber = ExprEvalContexts.back().getLambdaMangleContext()
- .getManglingNumber(Method);
- break;
+
+ // Allocate a mangling number for this lambda expression, if the ABI
+ // requires one.
+ Decl *ContextDecl = ExprEvalContexts.back().LambdaContextDecl;
+
+ enum ContextKind {
+ Normal,
+ DefaultArgument,
+ DataMember,
+ StaticDataMember
+ } Kind = Normal;
+
+ // Default arguments of member function parameters that appear in a class
+ // definition, as well as the initializers of data members, receive special
+ // treatment. Identify them.
+ if (ContextDecl) {
+ if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(ContextDecl)) {
+ if (const DeclContext *LexicalDC
+ = Param->getDeclContext()->getLexicalParent())
+ if (LexicalDC->isRecord())
+ Kind = DefaultArgument;
+ } else if (VarDecl *Var = dyn_cast<VarDecl>(ContextDecl)) {
+ if (Var->getDeclContext()->isRecord())
+ Kind = StaticDataMember;
+ } else if (isa<FieldDecl>(ContextDecl)) {
+ Kind = DataMember;
}
}
- Class->setLambdaMangling(*ManglingNumber, ContextDecl);
+ // Itanium ABI [5.1.7]:
+ // In the following contexts [...] the one-definition rule requires closure
+ // types in different translation units to "correspond":
+ bool IsInNonspecializedTemplate =
+ !ActiveTemplateInstantiations.empty() || CurContext->isDependentContext();
+ unsigned ManglingNumber;
+ switch (Kind) {
+ case Normal:
+ // -- the bodies of non-exported nonspecialized template functions
+ // -- the bodies of inline functions
+ if ((IsInNonspecializedTemplate &&
+ !(ContextDecl && isa<ParmVarDecl>(ContextDecl))) ||
+ isInInlineFunction(CurContext))
+ ManglingNumber = Context.getLambdaManglingNumber(Method);
+ else
+ ManglingNumber = 0;
+
+ // There is no special context for this lambda.
+ ContextDecl = 0;
+ break;
+
+ case StaticDataMember:
+ // -- the initializers of nonspecialized static members of template classes
+ if (!IsInNonspecializedTemplate) {
+ ManglingNumber = 0;
+ ContextDecl = 0;
+ break;
+ }
+ // Fall through to assign a mangling number.
+
+ case DataMember:
+ // -- the in-class initializers of class members
+ case DefaultArgument:
+ // -- default arguments appearing in class definitions
+ ManglingNumber = ExprEvalContexts.back().getLambdaMangleContext()
+ .getManglingNumber(Method);
+ break;
+ }
+
+ Class->setLambdaMangling(ManglingNumber, ContextDecl);
+
return Method;
}
@@ -214,6 +224,141 @@ void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) {
}
}
+static bool checkReturnValueType(const ASTContext &Ctx, const Expr *E,
+ QualType &DeducedType,
+ QualType &AlternateType) {
+ // Handle ReturnStmts with no expressions.
+ if (!E) {
+ if (AlternateType.isNull())
+ AlternateType = Ctx.VoidTy;
+
+ return Ctx.hasSameType(DeducedType, Ctx.VoidTy);
+ }
+
+ QualType StrictType = E->getType();
+ QualType LooseType = StrictType;
+
+ // In C, enum constants have the type of their underlying integer type,
+ // not the enum. When inferring block return types, we should allow
+ // the enum type if an enum constant is used, unless the enum is
+ // anonymous (in which case there can be no variables of its type).
+ if (!Ctx.getLangOpts().CPlusPlus) {
+ const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
+ if (DRE) {
+ const Decl *D = DRE->getDecl();
+ if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) {
+ const EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext());
+ if (Enum->getDeclName() || Enum->getTypedefNameForAnonDecl())
+ LooseType = Ctx.getTypeDeclType(Enum);
+ }
+ }
+ }
+
+ // Special case for the first return statement we find.
+ // The return type has already been tentatively set, but we might still
+ // have an alternate type we should prefer.
+ if (AlternateType.isNull())
+ AlternateType = LooseType;
+
+ if (Ctx.hasSameType(DeducedType, StrictType)) {
+ // FIXME: The loose type is different when there are constants from two
+ // different enums. We could consider warning here.
+ if (AlternateType != Ctx.DependentTy)
+ if (!Ctx.hasSameType(AlternateType, LooseType))
+ AlternateType = Ctx.VoidTy;
+ return true;
+ }
+
+ if (Ctx.hasSameType(DeducedType, LooseType)) {
+ // Use DependentTy to signal that we're using an alternate type and may
+ // need to add casts somewhere.
+ AlternateType = Ctx.DependentTy;
+ return true;
+ }
+
+ if (Ctx.hasSameType(AlternateType, StrictType) ||
+ Ctx.hasSameType(AlternateType, LooseType)) {
+ DeducedType = AlternateType;
+ // Use DependentTy to signal that we're using an alternate type and may
+ // need to add casts somewhere.
+ AlternateType = Ctx.DependentTy;
+ return true;
+ }
+
+ return false;
+}
+
+void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) {
+ assert(CSI.HasImplicitReturnType);
+
+ // First case: no return statements, implicit void return type.
+ ASTContext &Ctx = getASTContext();
+ if (CSI.Returns.empty()) {
+ // It's possible there were simply no /valid/ return statements.
+ // In this case, the first one we found may have at least given us a type.
+ if (CSI.ReturnType.isNull())
+ CSI.ReturnType = Ctx.VoidTy;
+ return;
+ }
+
+ // Second case: at least one return statement has dependent type.
+ // Delay type checking until instantiation.
+ assert(!CSI.ReturnType.isNull() && "We should have a tentative return type.");
+ if (CSI.ReturnType->isDependentType())
+ return;
+
+ // Third case: only one return statement. Don't bother doing extra work!
+ SmallVectorImpl<ReturnStmt*>::iterator I = CSI.Returns.begin(),
+ E = CSI.Returns.end();
+ if (I+1 == E)
+ return;
+
+ // General case: many return statements.
+ // Check that they all have compatible return types.
+ // For now, that means "identical", with an exception for enum constants.
+ // (In C, enum constants have the type of their underlying integer type,
+ // not the type of the enum. C++ uses the type of the enum.)
+ QualType AlternateType;
+
+ // We require the return types to strictly match here.
+ for (; I != E; ++I) {
+ const ReturnStmt *RS = *I;
+ const Expr *RetE = RS->getRetValue();
+ if (!checkReturnValueType(Ctx, RetE, CSI.ReturnType, AlternateType)) {
+ // FIXME: This is a poor diagnostic for ReturnStmts without expressions.
+ Diag(RS->getLocStart(),
+ diag::err_typecheck_missing_return_type_incompatible)
+ << (RetE ? RetE->getType() : Ctx.VoidTy) << CSI.ReturnType
+ << isa<LambdaScopeInfo>(CSI);
+ // Don't bother fixing up the return statements in the block if some of
+ // them are unfixable anyway.
+ AlternateType = Ctx.VoidTy;
+ // Continue iterating so that we keep emitting diagnostics.
+ }
+ }
+
+ // If our return statements turned out to be compatible, but we needed to
+ // pick a different return type, go through and fix the ones that need it.
+ if (AlternateType == Ctx.DependentTy) {
+ for (SmallVectorImpl<ReturnStmt*>::iterator I = CSI.Returns.begin(),
+ E = CSI.Returns.end();
+ I != E; ++I) {
+ ReturnStmt *RS = *I;
+ Expr *RetE = RS->getRetValue();
+ if (RetE->getType() == CSI.ReturnType)
+ continue;
+
+ // Right now we only support integral fixup casts.
+ assert(CSI.ReturnType->isIntegralOrUnscopedEnumerationType());
+ assert(RetE->getType()->isIntegralOrUnscopedEnumerationType());
+ ExprResult Casted = ImpCastExprToType(RetE, CSI.ReturnType,
+ CK_IntegralCast);
+ assert(Casted.isUsable());
+ RS->setRetValue(Casted.take());
+ }
+ }
+}
+
void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
Declarator &ParamInfo,
Scope *CurScope) {
@@ -230,6 +375,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
TypeSourceInfo *MethodTyInfo;
bool ExplicitParams = true;
bool ExplicitResultType = true;
+ bool ContainsUnexpandedParameterPack = false;
SourceLocation EndLoc;
llvm::ArrayRef<ParmVarDecl *> Params;
if (ParamInfo.getNumTypeObjects() == 0) {
@@ -269,9 +415,13 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
FunctionProtoTypeLoc Proto = cast<FunctionProtoTypeLoc>(TL);
Params = llvm::ArrayRef<ParmVarDecl *>(Proto.getParmArray(),
Proto.getNumArgs());
+
+ // Check for unexpanded parameter packs in the method type.
+ if (MethodTyInfo->getType()->containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
}
- CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range,
+ CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range,
MethodTyInfo, EndLoc, Params);
if (ExplicitParams)
@@ -287,7 +437,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
LambdaScopeInfo *LSI
= enterLambdaScope(Method, Intro.Range, Intro.Default, ExplicitParams,
ExplicitResultType,
- (Method->getTypeQualifiers() & Qualifiers::Const) == 0);
+ !Method->isConst());
// Handle explicit captures.
SourceLocation PrevCaptureLoc
@@ -409,8 +559,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
// Just ignore the ellipsis.
}
} else if (Var->isParameterPack()) {
- Diag(C->Loc, diag::err_lambda_unexpanded_pack);
- continue;
+ ContainsUnexpandedParameterPack = true;
}
TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
@@ -419,6 +568,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
}
finishLambdaExplicitCaptures(LSI);
+ LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack;
+
// Add lambda parameters into scope.
addLambdaParameters(Method, CurScope);
@@ -441,7 +592,10 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope,
LambdaScopeInfo *LSI = getCurLambda();
CXXRecordDecl *Class = LSI->Lambda;
Class->setInvalidDecl();
- SmallVector<Decl*, 4> Fields(Class->field_begin(), Class->field_end());
+ SmallVector<Decl*, 4> Fields;
+ for (RecordDecl::field_iterator i = Class->field_begin(),
+ e = Class->field_end(); i != e; ++i)
+ Fields.push_back(*i);
ActOnFields(0, Class->getLocation(), Class, Fields,
SourceLocation(), SourceLocation(), 0);
CheckCompletedCXXClass(Class);
@@ -578,6 +732,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
bool ExplicitParams;
bool ExplicitResultType;
bool LambdaExprNeedsCleanups;
+ bool ContainsUnexpandedParameterPack;
llvm::SmallVector<VarDecl *, 4> ArrayIndexVars;
llvm::SmallVector<unsigned, 4> ArrayIndexStarts;
{
@@ -588,6 +743,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
ExplicitParams = LSI->ExplicitParams;
ExplicitResultType = !LSI->HasImplicitReturnType;
LambdaExprNeedsCleanups = LSI->ExprNeedsCleanups;
+ ContainsUnexpandedParameterPack = LSI->ContainsUnexpandedParameterPack;
ArrayIndexVars.swap(LSI->ArrayIndexVars);
ArrayIndexStarts.swap(LSI->ArrayIndexStarts);
@@ -639,32 +795,14 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
// denotes the following type:
// FIXME: Assumes current resolution to core issue 975.
if (LSI->HasImplicitReturnType) {
+ deduceClosureReturnType(*LSI);
+
// - if there are no return statements in the
// compound-statement, or all return statements return
// either an expression of type void or no expression or
// braced-init-list, the type void;
if (LSI->ReturnType.isNull()) {
LSI->ReturnType = Context.VoidTy;
- } else {
- // C++11 [expr.prim.lambda]p4:
- // - if the compound-statement is of the form
- //
- // { attribute-specifier-seq[opt] return expression ; }
- //
- // the type of the returned expression after
- // lvalue-to-rvalue conversion (4.1), array-to-pointer
- // conver- sion (4.2), and function-to-pointer conversion
- // (4.3);
- //
- // Since we're accepting the resolution to a post-C++11 core
- // issue with a non-trivial extension, provide a warning (by
- // default).
- CompoundStmt *CompoundBody = cast<CompoundStmt>(Body);
- if (!(CompoundBody->size() == 1 &&
- isa<ReturnStmt>(*CompoundBody->body_begin())) &&
- !Context.hasSameType(LSI->ReturnType, Context.VoidTy))
- Diag(IntroducerRange.getBegin(),
- diag::ext_lambda_implies_void_return);
}
// Create a function type with the inferred return type.
@@ -704,7 +842,10 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
addBlockPointerConversion(*this, IntroducerRange, Class, CallOperator);
// Finalize the lambda class.
- SmallVector<Decl*, 4> Fields(Class->field_begin(), Class->field_end());
+ SmallVector<Decl*, 4> Fields;
+ for (RecordDecl::field_iterator i = Class->field_begin(),
+ e = Class->field_end(); i != e; ++i)
+ Fields.push_back(*i);
ActOnFields(0, Class->getLocation(), Class, Fields,
SourceLocation(), SourceLocation(), 0);
CheckCompletedCXXClass(Class);
@@ -717,7 +858,8 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
CaptureDefault, Captures,
ExplicitParams, ExplicitResultType,
CaptureInits, ArrayIndexVars,
- ArrayIndexStarts, Body->getLocEnd());
+ ArrayIndexStarts, Body->getLocEnd(),
+ ContainsUnexpandedParameterPack);
// C++11 [expr.prim.lambda]p2:
// A lambda-expression shall not appear in an unevaluated operand
@@ -807,9 +949,7 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
// Add a fake function body to the block. IR generation is responsible
// for filling in the actual body, which cannot be expressed as an AST.
- Block->setBody(new (Context) CompoundStmt(Context, 0, 0,
- ConvLocation,
- ConvLocation));
+ Block->setBody(new (Context) CompoundStmt(ConvLocation));
// Create the block literal expression.
Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index 9f5138b..dad196b 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -899,7 +899,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
S->getParent() && !S->getParent()->isTemplateParamScope()) {
// We've just searched the last template parameter scope and
- // found nothing, so look into the the contexts between the
+ // found nothing, so look into the contexts between the
// lexical and semantic declaration contexts returned by
// findOuterContext(). This implements the name lookup behavior
// of C++ [temp.local]p8.
@@ -1004,7 +1004,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
S->getParent() && !S->getParent()->isTemplateParamScope()) {
// We've just searched the last template parameter scope and
- // found nothing, so look into the the contexts between the
+ // found nothing, so look into the contexts between the
// lexical and semantic declaration contexts returned by
// findOuterContext(). This implements the name lookup behavior
// of C++ [temp.local]p8.
@@ -1100,15 +1100,12 @@ static NamedDecl *getVisibleDecl(NamedDecl *D) {
/// begin. If the lookup criteria permits, name lookup may also search
/// in the parent scopes.
///
-/// @param Name The name of the entity that we are searching for.
+/// @param [in,out] R Specifies the lookup to perform (e.g., the name to
+/// look up and the lookup kind), and is updated with the results of lookup
+/// including zero or more declarations and possibly additional information
+/// used to diagnose ambiguities.
///
-/// @param Loc If provided, the source location where we're performing
-/// name lookup. At present, this is only used to produce diagnostics when
-/// C library functions (like "malloc") are implicitly declared.
-///
-/// @returns The result of name lookup, which includes zero or more
-/// declarations and possibly additional information used to diagnose
-/// ambiguities.
+/// @returns \c true if lookup succeeded and false otherwise.
bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
DeclarationName Name = R.getLookupName();
if (!Name) return false;
@@ -1231,7 +1228,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
/// using directives by the given context.
///
/// C++98 [namespace.qual]p2:
-/// Given X::m (where X is a user-declared namespace), or given ::m
+/// Given X::m (where X is a user-declared namespace), or given \::m
/// (where X is the global namespace), let S be the set of all
/// declarations of m in X and in the transitive closure of all
/// namespaces nominated by using-directives in X and its used
@@ -1244,6 +1241,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
/// (namespace.udecl), S is the required set of declarations of
/// m. Otherwise if the use of m is not one that allows a unique
/// declaration to be chosen from S, the program is ill-formed.
+///
/// C++98 [namespace.qual]p5:
/// During the lookup of a qualified namespace member name, if the
/// lookup finds more than one declaration of the member, and if one
@@ -1636,22 +1634,12 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS,
}
-/// @brief Produce a diagnostic describing the ambiguity that resulted
+/// \brief Produce a diagnostic describing the ambiguity that resulted
/// from name lookup.
///
-/// @param Result The ambiguous name lookup result.
-///
-/// @param Name The name of the entity that name lookup was
-/// searching for.
-///
-/// @param NameLoc The location of the name within the source code.
+/// \param Result The result of the ambiguous lookup to be diagnosed.
///
-/// @param LookupRange A source range that provides more
-/// source-location information concerning the lookup itself. For
-/// example, this range might highlight a nested-name-specifier that
-/// precedes the name.
-///
-/// @returns true
+/// \returns true
bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
assert(Result.isAmbiguous() && "Lookup result must be ambiguous");
@@ -2444,10 +2432,11 @@ CXXConstructorDecl *Sema::LookupCopyingConstructor(CXXRecordDecl *Class,
}
/// \brief Look up the moving constructor for the given class.
-CXXConstructorDecl *Sema::LookupMovingConstructor(CXXRecordDecl *Class) {
+CXXConstructorDecl *Sema::LookupMovingConstructor(CXXRecordDecl *Class,
+ unsigned Quals) {
SpecialMemberOverloadResult *Result =
- LookupSpecialMember(Class, CXXMoveConstructor, false,
- false, false, false, false);
+ LookupSpecialMember(Class, CXXMoveConstructor, Quals & Qualifiers::Const,
+ Quals & Qualifiers::Volatile, false, false, false);
return cast_or_null<CXXConstructorDecl>(Result->getMethod());
}
@@ -2488,12 +2477,14 @@ CXXMethodDecl *Sema::LookupCopyingAssignment(CXXRecordDecl *Class,
/// \brief Look up the moving assignment operator for the given class.
CXXMethodDecl *Sema::LookupMovingAssignment(CXXRecordDecl *Class,
+ unsigned Quals,
bool RValueThis,
unsigned ThisQuals) {
assert(!(ThisQuals & ~(Qualifiers::Const | Qualifiers::Volatile)) &&
"non-const, non-volatile qualifiers for copy assignment this");
SpecialMemberOverloadResult *Result =
- LookupSpecialMember(Class, CXXMoveAssignment, false, false, RValueThis,
+ LookupSpecialMember(Class, CXXMoveAssignment, Quals & Qualifiers::Const,
+ Quals & Qualifiers::Volatile, RValueThis,
ThisQuals & Qualifiers::Const,
ThisQuals & Qualifiers::Volatile);
@@ -3147,7 +3138,8 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
namespace {
-typedef llvm::StringMap<TypoCorrection, llvm::BumpPtrAllocator> TypoResultsMap;
+typedef llvm::SmallVector<TypoCorrection, 1> TypoResultList;
+typedef llvm::StringMap<TypoResultList, llvm::BumpPtrAllocator> TypoResultsMap;
typedef std::map<unsigned, TypoResultsMap> TypoEditDistanceMap;
static const unsigned MaxTypoDistanceResultSets = 5;
@@ -3161,7 +3153,7 @@ class TypoCorrectionConsumer : public VisibleDeclConsumer {
///
/// The pointer value being set to the current DeclContext indicates
/// whether there is a keyword with this name.
- TypoEditDistanceMap BestResults;
+ TypoEditDistanceMap CorrectionResults;
Sema &SemaRef;
@@ -3180,23 +3172,28 @@ public:
typedef TypoResultsMap::iterator result_iterator;
typedef TypoEditDistanceMap::iterator distance_iterator;
- distance_iterator begin() { return BestResults.begin(); }
- distance_iterator end() { return BestResults.end(); }
- void erase(distance_iterator I) { BestResults.erase(I); }
- unsigned size() const { return BestResults.size(); }
- bool empty() const { return BestResults.empty(); }
-
- TypoCorrection &operator[](StringRef Name) {
- return BestResults.begin()->second[Name];
+ distance_iterator begin() { return CorrectionResults.begin(); }
+ distance_iterator end() { return CorrectionResults.end(); }
+ void erase(distance_iterator I) { CorrectionResults.erase(I); }
+ unsigned size() const { return CorrectionResults.size(); }
+ bool empty() const { return CorrectionResults.empty(); }
+
+ TypoResultList &operator[](StringRef Name) {
+ return CorrectionResults.begin()->second[Name];
}
unsigned getBestEditDistance(bool Normalized) {
- if (BestResults.empty())
+ if (CorrectionResults.empty())
return (std::numeric_limits<unsigned>::max)();
- unsigned BestED = BestResults.begin()->first;
+ unsigned BestED = CorrectionResults.begin()->first;
return Normalized ? TypoCorrection::NormalizeEditDistance(BestED) : BestED;
}
+
+ TypoResultsMap &getBestResults() {
+ return CorrectionResults.begin()->second;
+ }
+
};
}
@@ -3251,19 +3248,31 @@ void TypoCorrectionConsumer::addName(StringRef Name,
void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) {
StringRef Name = Correction.getCorrectionAsIdentifierInfo()->getName();
- TypoResultsMap &Map = BestResults[Correction.getEditDistance(false)];
-
- TypoCorrection &CurrentCorrection = Map[Name];
- if (!CurrentCorrection ||
- // FIXME: The following should be rolled up into an operator< on
- // TypoCorrection with a more principled definition.
- CurrentCorrection.isKeyword() < Correction.isKeyword() ||
- Correction.getAsString(SemaRef.getLangOpts()) <
- CurrentCorrection.getAsString(SemaRef.getLangOpts()))
- CurrentCorrection = Correction;
+ TypoResultList &CList =
+ CorrectionResults[Correction.getEditDistance(false)][Name];
+
+ if (!CList.empty() && !CList.back().isResolved())
+ CList.pop_back();
+ if (NamedDecl *NewND = Correction.getCorrectionDecl()) {
+ std::string CorrectionStr = Correction.getAsString(SemaRef.getLangOpts());
+ for (TypoResultList::iterator RI = CList.begin(), RIEnd = CList.end();
+ RI != RIEnd; ++RI) {
+ // If the Correction refers to a decl already in the result list,
+ // replace the existing result if the string representation of Correction
+ // comes before the current result alphabetically, then stop as there is
+ // nothing more to be done to add Correction to the candidate set.
+ if (RI->getCorrectionDecl() == NewND) {
+ if (CorrectionStr < RI->getAsString(SemaRef.getLangOpts()))
+ *RI = Correction;
+ return;
+ }
+ }
+ }
+ if (CList.empty() || Correction.isResolved())
+ CList.push_back(Correction);
- while (BestResults.size() > MaxTypoDistanceResultSets)
- erase(llvm::prior(BestResults.end()));
+ while (CorrectionResults.size() > MaxTypoDistanceResultSets)
+ erase(llvm::prior(CorrectionResults.end()));
}
// Fill the supplied vector with the IdentifierInfo pointers for each piece of
@@ -3348,7 +3357,7 @@ class NamespaceSpecifierSet {
getNestedNameSpecifierIdentifiers(CurScopeSpec->getScopeRep(),
CurNameSpecifierIdentifiers);
// Build the list of identifiers that would be used for an absolute
- // (from the global context) NestedNameSpecifier refering to the current
+ // (from the global context) NestedNameSpecifier referring to the current
// context.
for (DeclContextList::reverse_iterator C = CurContextChain.rbegin(),
CEnd = CurContextChain.rend();
@@ -3515,7 +3524,16 @@ static void LookupPotentialTypoResult(Sema &SemaRef,
/// \brief Add keywords to the consumer as possible typo corrections.
static void AddKeywordsToConsumer(Sema &SemaRef,
TypoCorrectionConsumer &Consumer,
- Scope *S, CorrectionCandidateCallback &CCC) {
+ Scope *S, CorrectionCandidateCallback &CCC,
+ bool AfterNestedNameSpecifier) {
+ if (AfterNestedNameSpecifier) {
+ // For 'X::', we know exactly which keywords can appear next.
+ Consumer.addKeywordResult("template");
+ if (CCC.WantExpressionKeywords)
+ Consumer.addKeywordResult("operator");
+ return;
+ }
+
if (CCC.WantObjCSuper)
Consumer.addKeywordResult("super");
@@ -3589,6 +3607,12 @@ static void AddKeywordsToConsumer(Sema &SemaRef,
Consumer.addKeywordResult("nullptr");
}
}
+
+ if (SemaRef.getLangOpts().C11) {
+ // FIXME: We should not suggest _Alignof if the alignof macro
+ // is present.
+ Consumer.addKeywordResult("_Alignof");
+ }
}
if (CCC.WantRemainingKeywords) {
@@ -3777,6 +3801,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
bool SearchNamespaces
= getLangOpts().CPlusPlus &&
(IsUnqualifiedLookup || (QualifiedDC && QualifiedDC->isNamespace()));
+ // In a few cases we *only* want to search for corrections bases on just
+ // adding or changing the nested name specifier.
+ bool AllowOnlyNNSChanges = Typo->getName().size() < 3;
if (IsUnqualifiedLookup || SearchNamespaces) {
// For unqualified lookup, look through all of the names that we have
@@ -3802,7 +3829,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
}
}
- AddKeywordsToConsumer(*this, Consumer, S, CCC);
+ AddKeywordsToConsumer(*this, Consumer, S, CCC, SS && SS->isNotEmpty());
// If we haven't found anything, we're done.
if (Consumer.empty()) {
@@ -3813,8 +3840,8 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
return TypoCorrection();
}
- // Make sure that the user typed at least 3 characters for each correction
- // made. Otherwise, we don't even both looking at the results.
+ // Make sure the best edit distance (prior to adding any namespace qualifiers)
+ // is not more that about a third of the length of the typo's identifier.
unsigned ED = Consumer.getBestEditDistance(true);
if (ED > 0 && Typo->getName().size() / ED < 3) {
// If this was an unqualified lookup, note that no correction was found.
@@ -3854,19 +3881,43 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
for (TypoCorrectionConsumer::result_iterator I = DI->second.begin(),
IEnd = DI->second.end();
I != IEnd; /* Increment in loop. */) {
+ // If we only want nested name specifier corrections, ignore potential
+ // corrections that have a different base identifier from the typo.
+ if (AllowOnlyNNSChanges &&
+ I->second.front().getCorrectionAsIdentifierInfo() != Typo) {
+ TypoCorrectionConsumer::result_iterator Prev = I;
+ ++I;
+ DI->second.erase(Prev);
+ continue;
+ }
+
// If the item already has been looked up or is a keyword, keep it.
// If a validator callback object was given, drop the correction
// unless it passes validation.
- if (I->second.isResolved()) {
+ bool Viable = false;
+ for (TypoResultList::iterator RI = I->second.begin();
+ RI != I->second.end(); /* Increment in loop. */) {
+ TypoResultList::iterator Prev = RI;
+ ++RI;
+ if (Prev->isResolved()) {
+ if (!isCandidateViable(CCC, *Prev))
+ RI = I->second.erase(Prev);
+ else
+ Viable = true;
+ }
+ }
+ if (Viable || I->second.empty()) {
TypoCorrectionConsumer::result_iterator Prev = I;
++I;
- if (!isCandidateViable(CCC, Prev->second))
+ if (!Viable)
DI->second.erase(Prev);
continue;
}
+ assert(I->second.size() == 1 && "Expected a single unresolved candidate");
// Perform name lookup on this name.
- IdentifierInfo *Name = I->second.getCorrectionAsIdentifierInfo();
+ TypoCorrection &Candidate = I->second.front();
+ IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo();
LookupPotentialTypoResult(*this, TmpRes, Name, S, SS, MemberContext,
EnteringContext, CCC.IsObjCIvarLookup);
@@ -3874,7 +3925,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
case LookupResult::NotFound:
case LookupResult::NotFoundInCurrentInstantiation:
case LookupResult::FoundUnresolvedValue:
- QualifiedResults.push_back(I->second);
+ QualifiedResults.push_back(Candidate);
// We didn't find this name in our scope, or didn't like what we found;
// ignore it.
{
@@ -3895,18 +3946,18 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
for (LookupResult::iterator TRD = TmpRes.begin(),
TRDEnd = TmpRes.end();
TRD != TRDEnd; ++TRD)
- I->second.addCorrectionDecl(*TRD);
+ Candidate.addCorrectionDecl(*TRD);
++I;
- if (!isCandidateViable(CCC, Prev->second))
+ if (!isCandidateViable(CCC, Candidate))
DI->second.erase(Prev);
break;
}
case LookupResult::Found: {
TypoCorrectionConsumer::result_iterator Prev = I;
- I->second.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>());
+ Candidate.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>());
++I;
- if (!isCandidateViable(CCC, Prev->second))
+ if (!isCandidateViable(CCC, Candidate))
DI->second.erase(Prev);
break;
}
@@ -3978,10 +4029,10 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// No corrections remain...
if (Consumer.empty()) return TypoCorrection();
- TypoResultsMap &BestResults = Consumer.begin()->second;
- ED = TypoCorrection::NormalizeEditDistance(Consumer.begin()->first);
+ TypoResultsMap &BestResults = Consumer.getBestResults();
+ ED = Consumer.getBestEditDistance(true);
- if (ED > 0 && Typo->getName().size() / ED < 3) {
+ if (!AllowOnlyNNSChanges && ED > 0 && Typo->getName().size() / ED < 3) {
// If this was an unqualified lookup and we believe the callback
// object wouldn't have filtered out possible corrections, note
// that no correction was found.
@@ -3993,8 +4044,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// If only a single name remains, return that result.
if (BestResults.size() == 1) {
- const llvm::StringMapEntry<TypoCorrection> &Correction = *(BestResults.begin());
- const TypoCorrection &Result = Correction.second;
+ const TypoResultList &CorrectionList = BestResults.begin()->second;
+ const TypoCorrection &Result = CorrectionList.front();
+ if (CorrectionList.size() != 1) return TypoCorrection();
// Don't correct to a keyword that's the same as the typo; the keyword
// wasn't actually in scope.
@@ -4012,7 +4064,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// some instances of CTC_Unknown, while WantRemainingKeywords is true
// for CTC_Unknown but not for CTC_ObjCMessageReceiver.
&& CCC.WantObjCSuper && !CCC.WantRemainingKeywords
- && BestResults["super"].isKeyword()) {
+ && BestResults["super"].front().isKeyword()) {
// Prefer 'super' when we're completing in a message-receiver
// context.
@@ -4022,9 +4074,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// Record the correction for unqualified lookup.
if (IsUnqualifiedLookup)
- UnqualifiedTyposCorrected[Typo] = BestResults["super"];
+ UnqualifiedTyposCorrected[Typo] = BestResults["super"].front();
- return BestResults["super"];
+ return BestResults["super"].front();
}
// If this was an unqualified lookup and we believe the callback object did
diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp
index 5ece8f1..27deab2 100644
--- a/lib/Sema/SemaObjCProperty.cpp
+++ b/lib/Sema/SemaObjCProperty.cpp
@@ -18,6 +18,8 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ASTMutationListener.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
@@ -133,7 +135,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
// Proceed with constructing the ObjCPropertDecls.
ObjCContainerDecl *ClassDecl = cast<ObjCContainerDecl>(CurContext);
-
if (ObjCCategoryDecl *CDecl = dyn_cast<ObjCCategoryDecl>(ClassDecl))
if (CDecl->IsClassExtension()) {
Decl *Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc,
@@ -144,10 +145,11 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
isOverridingProperty, TSI,
MethodImplKind);
if (Res) {
- CheckObjCPropertyAttributes(Res, AtLoc, Attributes);
+ CheckObjCPropertyAttributes(Res, AtLoc, Attributes, false);
if (getLangOpts().ObjCAutoRefCount)
checkARCPropertyDecl(*this, cast<ObjCPropertyDecl>(Res));
}
+ ActOnDocumentableDecl(Res);
return Res;
}
@@ -161,11 +163,14 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
Res->setLexicalDeclContext(lexicalDC);
// Validate the attributes on the @property.
- CheckObjCPropertyAttributes(Res, AtLoc, Attributes);
+ CheckObjCPropertyAttributes(Res, AtLoc, Attributes,
+ (isa<ObjCInterfaceDecl>(ClassDecl) ||
+ isa<ObjCProtocolDecl>(ClassDecl)));
if (getLangOpts().ObjCAutoRefCount)
checkARCPropertyDecl(*this, Res);
+ ActOnDocumentableDecl(Res);
return Res;
}
@@ -200,6 +205,37 @@ makePropertyAttributesAsWritten(unsigned Attributes) {
return (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten;
}
+static bool LocPropertyAttribute( ASTContext &Context, const char *attrName,
+ SourceLocation LParenLoc, SourceLocation &Loc) {
+ if (LParenLoc.isMacroID())
+ return false;
+
+ SourceManager &SM = Context.getSourceManager();
+ std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(LParenLoc);
+ // Try to load the file buffer.
+ bool invalidTemp = false;
+ StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
+ if (invalidTemp)
+ return false;
+ const char *tokenBegin = file.data() + locInfo.second;
+
+ // Lex from the start of the given location.
+ Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
+ Context.getLangOpts(),
+ file.begin(), tokenBegin, file.end());
+ Token Tok;
+ do {
+ lexer.LexFromRawLexer(Tok);
+ if (Tok.is(tok::raw_identifier) &&
+ StringRef(Tok.getRawIdentifierData(), Tok.getLength()) == attrName) {
+ Loc = Tok.getLocation();
+ return true;
+ }
+ } while (Tok.isNot(tok::r_paren));
+ return false;
+
+}
+
Decl *
Sema::HandlePropertyInClassExtension(Scope *S,
SourceLocation AtLoc,
@@ -568,9 +604,70 @@ static void setImpliedPropertyAttributeForReadOnlyProperty(
return;
}
+/// DiagnoseClassAndClassExtPropertyMismatch - diagnose inconsistant property
+/// attribute declared in primary class and attributes overridden in any of its
+/// class extensions.
+static void
+DiagnoseClassAndClassExtPropertyMismatch(Sema &S, ObjCInterfaceDecl *ClassDecl,
+ ObjCPropertyDecl *property) {
+ unsigned Attributes = property->getPropertyAttributesAsWritten();
+ bool warn = (Attributes & ObjCDeclSpec::DQ_PR_readonly);
+ for (const ObjCCategoryDecl *CDecl = ClassDecl->getFirstClassExtension();
+ CDecl; CDecl = CDecl->getNextClassExtension()) {
+ ObjCPropertyDecl *ClassExtProperty = 0;
+ for (ObjCContainerDecl::prop_iterator P = CDecl->prop_begin(),
+ E = CDecl->prop_end(); P != E; ++P) {
+ if ((*P)->getIdentifier() == property->getIdentifier()) {
+ ClassExtProperty = *P;
+ break;
+ }
+ }
+ if (ClassExtProperty) {
+ warn = false;
+ unsigned classExtPropertyAttr =
+ ClassExtProperty->getPropertyAttributesAsWritten();
+ // We are issuing the warning that we postponed because class extensions
+ // can override readonly->readwrite and 'setter' attributes originally
+ // placed on class's property declaration now make sense in the overridden
+ // property.
+ if (Attributes & ObjCDeclSpec::DQ_PR_readonly) {
+ if (!classExtPropertyAttr ||
+ (classExtPropertyAttr & ObjCDeclSpec::DQ_PR_readwrite))
+ continue;
+ warn = true;
+ break;
+ }
+ }
+ }
+ if (warn) {
+ unsigned setterAttrs = (ObjCDeclSpec::DQ_PR_assign |
+ ObjCDeclSpec::DQ_PR_unsafe_unretained |
+ ObjCDeclSpec::DQ_PR_copy |
+ ObjCDeclSpec::DQ_PR_retain |
+ ObjCDeclSpec::DQ_PR_strong);
+ if (Attributes & setterAttrs) {
+ const char * which =
+ (Attributes & ObjCDeclSpec::DQ_PR_assign) ?
+ "assign" :
+ (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) ?
+ "unsafe_unretained" :
+ (Attributes & ObjCDeclSpec::DQ_PR_copy) ?
+ "copy" :
+ (Attributes & ObjCDeclSpec::DQ_PR_retain) ?
+ "retain" : "strong";
+
+ S.Diag(property->getLocation(),
+ diag::warn_objc_property_attr_mutually_exclusive)
+ << "readonly" << which;
+ }
+ }
+
+
+}
+
/// ActOnPropertyImplDecl - This routine performs semantic checks and
/// builds the AST node for a property implementation declaration; declared
-/// as @synthesize or @dynamic.
+/// as \@synthesize or \@dynamic.
///
Decl *Sema::ActOnPropertyImplDecl(Scope *S,
SourceLocation AtLoc,
@@ -588,6 +685,9 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
}
if (PropertyIvarLoc.isInvalid())
PropertyIvarLoc = PropertyLoc;
+ SourceLocation PropertyDiagLoc = PropertyLoc;
+ if (PropertyDiagLoc.isInvalid())
+ PropertyDiagLoc = ClassImpDecl->getLocStart();
ObjCPropertyDecl *property = 0;
ObjCInterfaceDecl* IDecl = 0;
// Find the class or category class where this property must have
@@ -625,6 +725,27 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
return 0;
}
}
+
+ if (Synthesize&&
+ (PIkind & ObjCPropertyDecl::OBJC_PR_readonly) &&
+ property->hasAttr<IBOutletAttr>() &&
+ !AtLoc.isValid()) {
+ Diag(IC->getLocation(), diag::warn_auto_readonly_iboutlet_property);
+ Diag(property->getLocation(), diag::note_property_declare);
+ SourceLocation readonlyLoc;
+ if (LocPropertyAttribute(Context, "readonly",
+ property->getLParenLoc(), readonlyLoc)) {
+ SourceLocation endLoc =
+ readonlyLoc.getLocWithOffset(strlen("readonly")-1);
+ SourceRange ReadonlySourceRange(readonlyLoc, endLoc);
+ Diag(property->getLocation(),
+ diag::note_auto_readonly_iboutlet_fixup_suggest) <<
+ FixItHint::CreateReplacement(ReadonlySourceRange, "readwrite");
+ }
+ }
+
+ DiagnoseClassAndClassExtPropertyMismatch(*this, IDecl, property);
+
} else if ((CatImplClass = dyn_cast<ObjCCategoryImplDecl>(ClassImpDecl))) {
if (Synthesize) {
Diag(AtLoc, diag::error_synthesize_category_decl);
@@ -654,6 +775,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
return 0;
}
ObjCIvarDecl *Ivar = 0;
+ bool CompleteTypeErr = false;
+ bool compat = true;
// Check that we have a valid, previously declared ivar for @synthesize
if (Synthesize) {
// @synthesize
@@ -664,7 +787,14 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
Ivar = IDecl->lookupInstanceVariable(PropertyIvar, ClassDeclared);
QualType PropType = property->getType();
QualType PropertyIvarType = PropType.getNonReferenceType();
-
+
+ if (RequireCompleteType(PropertyDiagLoc, PropertyIvarType,
+ diag::err_incomplete_synthesized_property,
+ property->getDeclName())) {
+ Diag(property->getLocation(), diag::note_property_declare);
+ CompleteTypeErr = true;
+ }
+
if (getLangOpts().ObjCAutoRefCount &&
(property->getPropertyAttributesAsWritten() &
ObjCPropertyDecl::OBJC_PR_readonly) &&
@@ -680,14 +810,32 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
getLangOpts().getGC() != LangOptions::NonGC) {
assert(!getLangOpts().ObjCAutoRefCount);
if (PropertyIvarType.isObjCGCStrong()) {
- Diag(PropertyLoc, diag::err_gc_weak_property_strong_type);
+ Diag(PropertyDiagLoc, diag::err_gc_weak_property_strong_type);
Diag(property->getLocation(), diag::note_property_declare);
} else {
PropertyIvarType =
Context.getObjCGCQualType(PropertyIvarType, Qualifiers::Weak);
}
}
-
+ if (AtLoc.isInvalid()) {
+ // Check when default synthesizing a property that there is
+ // an ivar matching property name and issue warning; since this
+ // is the most common case of not using an ivar used for backing
+ // property in non-default synthesis case.
+ ObjCInterfaceDecl *ClassDeclared=0;
+ ObjCIvarDecl *originalIvar =
+ IDecl->lookupInstanceVariable(property->getIdentifier(),
+ ClassDeclared);
+ if (originalIvar) {
+ Diag(PropertyDiagLoc,
+ diag::warn_autosynthesis_property_ivar_match)
+ << PropertyId << (Ivar == 0) << PropertyIvar
+ << originalIvar->getIdentifier();
+ Diag(property->getLocation(), diag::note_property_declare);
+ Diag(originalIvar->getLocation(), diag::note_ivar_decl);
+ }
+ }
+
if (!Ivar) {
// In ARC, give the ivar a lifetime qualifier based on the
// property attributes.
@@ -699,7 +847,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
// explicitly write an ownership attribute on the property.
if (!property->hasWrittenStorageAttribute() &&
!(kind & ObjCPropertyDecl::OBJC_PR_strong)) {
- Diag(PropertyLoc,
+ Diag(PropertyDiagLoc,
diag::err_arc_objc_property_default_assign_on_object);
Diag(property->getLocation(), diag::note_property_declare);
} else {
@@ -711,12 +859,12 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
if (const ObjCObjectPointerType *ObjT =
PropertyIvarType->getAs<ObjCObjectPointerType>())
if (ObjT->getInterfaceDecl()->isArcWeakrefUnavailable()) {
- Diag(PropertyLoc, diag::err_arc_weak_unavailable_property);
+ Diag(PropertyDiagLoc, diag::err_arc_weak_unavailable_property);
Diag(property->getLocation(), diag::note_property_declare);
err = true;
}
if (!err && !getLangOpts().ObjCRuntimeHasWeak) {
- Diag(PropertyLoc, diag::err_arc_weak_no_runtime);
+ Diag(PropertyDiagLoc, diag::err_arc_weak_no_runtime);
Diag(property->getLocation(), diag::note_property_declare);
}
}
@@ -730,7 +878,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
if (kind & ObjCPropertyDecl::OBJC_PR_weak &&
!getLangOpts().ObjCAutoRefCount &&
getLangOpts().getGC() == LangOptions::NonGC) {
- Diag(PropertyLoc, diag::error_synthesize_weak_non_arc_or_gc);
+ Diag(PropertyDiagLoc, diag::error_synthesize_weak_non_arc_or_gc);
Diag(property->getLocation(), diag::note_property_declare);
}
@@ -739,17 +887,20 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
PropertyIvarType, /*Dinfo=*/0,
ObjCIvarDecl::Private,
(Expr *)0, true);
+ if (CompleteTypeErr)
+ Ivar->setInvalidDecl();
ClassImpDecl->addDecl(Ivar);
IDecl->makeDeclVisibleInContext(Ivar);
property->setPropertyIvarDecl(Ivar);
- if (!getLangOpts().ObjCNonFragileABI)
- Diag(PropertyLoc, diag::error_missing_property_ivar_decl) << PropertyId;
+ if (getLangOpts().ObjCRuntime.isFragile())
+ Diag(PropertyDiagLoc, diag::error_missing_property_ivar_decl)
+ << PropertyId;
// Note! I deliberately want it to fall thru so, we have a
// a property implementation and to avoid future warnings.
- } else if (getLangOpts().ObjCNonFragileABI &&
+ } else if (getLangOpts().ObjCRuntime.isNonFragile() &&
!declaresSameEntity(ClassDeclared, IDecl)) {
- Diag(PropertyLoc, diag::error_ivar_in_superclass_use)
+ Diag(PropertyDiagLoc, diag::error_ivar_in_superclass_use)
<< property->getDeclName() << Ivar->getDeclName()
<< ClassDeclared->getDeclName();
Diag(Ivar->getLocation(), diag::note_previous_access_declaration)
@@ -759,8 +910,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
QualType IvarType = Context.getCanonicalType(Ivar->getType());
// Check that type of property and its ivar are type compatible.
- if (Context.getCanonicalType(PropertyIvarType) != IvarType) {
- bool compat = false;
+ if (!Context.hasSameType(PropertyIvarType, IvarType)) {
+ compat = false;
if (isa<ObjCObjectPointerType>(PropertyIvarType)
&& isa<ObjCObjectPointerType>(IvarType))
compat =
@@ -773,31 +924,32 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
== Compatible);
}
if (!compat) {
- Diag(PropertyLoc, diag::error_property_ivar_type)
+ Diag(PropertyDiagLoc, diag::error_property_ivar_type)
<< property->getDeclName() << PropType
<< Ivar->getDeclName() << IvarType;
Diag(Ivar->getLocation(), diag::note_ivar_decl);
// Note! I deliberately want it to fall thru so, we have a
// a property implementation and to avoid future warnings.
}
-
- // FIXME! Rules for properties are somewhat different that those
- // for assignments. Use a new routine to consolidate all cases;
- // specifically for property redeclarations as well as for ivars.
- QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType();
- QualType rhsType =Context.getCanonicalType(IvarType).getUnqualifiedType();
- if (lhsType != rhsType &&
- lhsType->isArithmeticType()) {
- Diag(PropertyLoc, diag::error_property_ivar_type)
- << property->getDeclName() << PropType
- << Ivar->getDeclName() << IvarType;
- Diag(Ivar->getLocation(), diag::note_ivar_decl);
- // Fall thru - see previous comment
+ else {
+ // FIXME! Rules for properties are somewhat different that those
+ // for assignments. Use a new routine to consolidate all cases;
+ // specifically for property redeclarations as well as for ivars.
+ QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType();
+ QualType rhsType =Context.getCanonicalType(IvarType).getUnqualifiedType();
+ if (lhsType != rhsType &&
+ lhsType->isArithmeticType()) {
+ Diag(PropertyDiagLoc, diag::error_property_ivar_type)
+ << property->getDeclName() << PropType
+ << Ivar->getDeclName() << IvarType;
+ Diag(Ivar->getLocation(), diag::note_ivar_decl);
+ // Fall thru - see previous comment
+ }
}
// __weak is explicit. So it works on Canonical type.
if ((PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() &&
getLangOpts().getGC() != LangOptions::NonGC)) {
- Diag(PropertyLoc, diag::error_weak_property)
+ Diag(PropertyDiagLoc, diag::error_weak_property)
<< property->getDeclName() << Ivar->getDeclName();
Diag(Ivar->getLocation(), diag::note_ivar_decl);
// Fall thru - see previous comment
@@ -806,7 +958,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
if ((property->getType()->isObjCObjectPointerType() ||
PropType.isObjCGCStrong()) && IvarType.isObjCGCWeak() &&
getLangOpts().getGC() != LangOptions::NonGC) {
- Diag(PropertyLoc, diag::error_strong_property)
+ Diag(PropertyDiagLoc, diag::error_strong_property)
<< property->getDeclName() << Ivar->getDeclName();
// Fall thru - see previous comment
}
@@ -815,7 +967,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
checkARCPropertyImpl(*this, PropertyLoc, property, Ivar);
} else if (PropertyIvar)
// @dynamic
- Diag(PropertyLoc, diag::error_dynamic_property_ivar_decl);
+ Diag(PropertyDiagLoc, diag::error_dynamic_property_ivar_decl);
assert (property && "ActOnPropertyImplDecl - property declaration missing");
ObjCPropertyImplDecl *PIDecl =
@@ -825,9 +977,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
ObjCPropertyImplDecl::Synthesize
: ObjCPropertyImplDecl::Dynamic),
Ivar, PropertyIvarLoc);
+
+ if (CompleteTypeErr || !compat)
+ PIDecl->setInvalidDecl();
+
if (ObjCMethodDecl *getterMethod = property->getGetterMethodDecl()) {
getterMethod->createImplicitParams(Context, IDecl);
- if (getLangOpts().CPlusPlus && Synthesize &&
+ if (getLangOpts().CPlusPlus && Synthesize && !CompleteTypeErr &&
Ivar->getType()->isRecordType()) {
// For Objective-C++, need to synthesize the AST for the IVAR object to be
// returned by the getter as it must conform to C++'s copy-return rules.
@@ -862,8 +1018,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
}
if (ObjCMethodDecl *setterMethod = property->getSetterMethodDecl()) {
setterMethod->createImplicitParams(Context, IDecl);
- if (getLangOpts().CPlusPlus && Synthesize
- && Ivar->getType()->isRecordType()) {
+ if (getLangOpts().CPlusPlus && Synthesize && !CompleteTypeErr &&
+ Ivar->getType()->isRecordType()) {
// FIXME. Eventually we want to do this for Objective-C as well.
ImplicitParamDecl *SelfDecl = setterMethod->getSelfDecl();
DeclRefExpr *SelfExpr =
@@ -916,7 +1072,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
}
IC->addPropertyImplementation(PIDecl);
if (getLangOpts().ObjCDefaultSynthProperties &&
- getLangOpts().ObjCNonFragileABI2 &&
+ getLangOpts().ObjCRuntime.isNonFragile() &&
!IDecl->isObjCRequiresPropertyDefs()) {
// Diagnose if an ivar was lazily synthesdized due to a previous
// use and if 1) property is @dynamic or 2) property is synthesized
@@ -941,7 +1097,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
if (Synthesize)
if (ObjCPropertyImplDecl *PPIDecl =
CatImplClass->FindPropertyImplIvarDecl(PropertyIvar)) {
- Diag(PropertyLoc, diag::error_duplicate_ivar_use)
+ Diag(PropertyDiagLoc, diag::error_duplicate_ivar_use)
<< PropertyId << PPIDecl->getPropertyDecl()->getIdentifier()
<< PropertyIvar;
Diag(PPIDecl->getLocation(), diag::note_previous_use);
@@ -949,7 +1105,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
if (ObjCPropertyImplDecl *PPIDecl =
CatImplClass->FindPropertyImplDecl(PropertyId)) {
- Diag(PropertyLoc, diag::error_property_implemented) << PropertyId;
+ Diag(PropertyDiagLoc, diag::error_property_implemented) << PropertyId;
Diag(PPIDecl->getLocation(), diag::note_previous_declaration);
return 0;
}
@@ -1030,21 +1186,42 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property,
bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property,
ObjCMethodDecl *GetterMethod,
SourceLocation Loc) {
- if (GetterMethod &&
- !Context.hasSameType(GetterMethod->getResultType().getNonReferenceType(),
- property->getType().getNonReferenceType())) {
- AssignConvertType result = Incompatible;
- if (property->getType()->isObjCObjectPointerType())
- result = CheckAssignmentConstraints(Loc, GetterMethod->getResultType(),
- property->getType());
- if (result != Compatible) {
- Diag(Loc, diag::warn_accessor_property_type_mismatch)
- << property->getDeclName()
- << GetterMethod->getSelector();
- Diag(GetterMethod->getLocation(), diag::note_declared_at);
- return true;
+ if (!GetterMethod)
+ return false;
+ QualType GetterType = GetterMethod->getResultType().getNonReferenceType();
+ QualType PropertyIvarType = property->getType().getNonReferenceType();
+ bool compat = Context.hasSameType(PropertyIvarType, GetterType);
+ if (!compat) {
+ if (isa<ObjCObjectPointerType>(PropertyIvarType) &&
+ isa<ObjCObjectPointerType>(GetterType))
+ compat =
+ Context.canAssignObjCInterfaces(
+ GetterType->getAs<ObjCObjectPointerType>(),
+ PropertyIvarType->getAs<ObjCObjectPointerType>());
+ else if (CheckAssignmentConstraints(Loc, GetterType, PropertyIvarType)
+ != Compatible) {
+ Diag(Loc, diag::error_property_accessor_type)
+ << property->getDeclName() << PropertyIvarType
+ << GetterMethod->getSelector() << GetterType;
+ Diag(GetterMethod->getLocation(), diag::note_declared_at);
+ return true;
+ } else {
+ compat = true;
+ QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType();
+ QualType rhsType =Context.getCanonicalType(GetterType).getUnqualifiedType();
+ if (lhsType != rhsType && lhsType->isArithmeticType())
+ compat = false;
}
}
+
+ if (!compat) {
+ Diag(Loc, diag::warn_accessor_property_type_mismatch)
+ << property->getDeclName()
+ << GetterMethod->getSelector();
+ Diag(GetterMethod->getLocation(), diag::note_declared_at);
+ return true;
+ }
+
return false;
}
@@ -1059,11 +1236,11 @@ void Sema::ComparePropertiesInBaseAndSuper(ObjCInterfaceDecl *IDecl) {
// FIXME: O(N^2)
for (ObjCInterfaceDecl::prop_iterator S = SDecl->prop_begin(),
E = SDecl->prop_end(); S != E; ++S) {
- ObjCPropertyDecl *SuperPDecl = (*S);
+ ObjCPropertyDecl *SuperPDecl = *S;
// Does property in super class has declaration in current class?
for (ObjCInterfaceDecl::prop_iterator I = IDecl->prop_begin(),
E = IDecl->prop_end(); I != E; ++I) {
- ObjCPropertyDecl *PDecl = (*I);
+ ObjCPropertyDecl *PDecl = *I;
if (SuperPDecl->getIdentifier() == PDecl->getIdentifier())
DiagnosePropertyMismatch(PDecl, SuperPDecl,
SDecl->getIdentifier());
@@ -1085,29 +1262,29 @@ Sema::MatchOneProtocolPropertiesInClass(Decl *CDecl,
if (!CatDecl->IsClassExtension())
for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
E = PDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Pr = (*P);
+ ObjCPropertyDecl *Pr = *P;
ObjCCategoryDecl::prop_iterator CP, CE;
// Is this property already in category's list of properties?
for (CP = CatDecl->prop_begin(), CE = CatDecl->prop_end(); CP!=CE; ++CP)
- if ((*CP)->getIdentifier() == Pr->getIdentifier())
+ if (CP->getIdentifier() == Pr->getIdentifier())
break;
if (CP != CE)
// Property protocol already exist in class. Diagnose any mismatch.
- DiagnosePropertyMismatch((*CP), Pr, PDecl->getIdentifier());
+ DiagnosePropertyMismatch(*CP, Pr, PDecl->getIdentifier());
}
return;
}
for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
E = PDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Pr = (*P);
+ ObjCPropertyDecl *Pr = *P;
ObjCInterfaceDecl::prop_iterator CP, CE;
// Is this property already in class's list of properties?
for (CP = IDecl->prop_begin(), CE = IDecl->prop_end(); CP != CE; ++CP)
- if ((*CP)->getIdentifier() == Pr->getIdentifier())
+ if (CP->getIdentifier() == Pr->getIdentifier())
break;
if (CP != CE)
// Property protocol already exist in class. Diagnose any mismatch.
- DiagnosePropertyMismatch((*CP), Pr, PDecl->getIdentifier());
+ DiagnosePropertyMismatch(*CP, Pr, PDecl->getIdentifier());
}
}
@@ -1223,7 +1400,7 @@ void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl,
if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) {
for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(),
E = IDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
PropMap[Prop->getIdentifier()] = Prop;
}
// scan through class's protocols.
@@ -1236,7 +1413,7 @@ void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl,
if (!CATDecl->IsClassExtension())
for (ObjCContainerDecl::prop_iterator P = CATDecl->prop_begin(),
E = CATDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
PropMap[Prop->getIdentifier()] = Prop;
}
// scan through class's protocols.
@@ -1247,7 +1424,7 @@ void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl,
else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(CDecl)) {
for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
E = PDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
ObjCPropertyDecl *PropertyFromSuper = SuperPropMap[Prop->getIdentifier()];
// Exclude property for protocols which conform to class's super-class,
// as super-class has to implement the property.
@@ -1273,7 +1450,7 @@ static void CollectClassPropertyImplementations(ObjCContainerDecl *CDecl,
if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) {
for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(),
E = IDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
PropMap[Prop->getIdentifier()] = Prop;
}
for (ObjCInterfaceDecl::all_protocol_iterator
@@ -1284,7 +1461,7 @@ static void CollectClassPropertyImplementations(ObjCContainerDecl *CDecl,
else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(CDecl)) {
for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
E = PDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
if (!PropMap.count(Prop->getIdentifier()))
PropMap[Prop->getIdentifier()] = Prop;
}
@@ -1316,7 +1493,7 @@ ObjCPropertyDecl *Sema::LookupPropertyDecl(const ObjCContainerDecl *CDecl,
dyn_cast<ObjCInterfaceDecl>(CDecl)) {
for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(),
E = IDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
if (Prop->getIdentifier() == II)
return Prop;
}
@@ -1333,7 +1510,7 @@ ObjCPropertyDecl *Sema::LookupPropertyDecl(const ObjCContainerDecl *CDecl,
dyn_cast<ObjCProtocolDecl>(CDecl)) {
for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
E = PDecl->prop_end(); P != E; ++P) {
- ObjCPropertyDecl *Prop = (*P);
+ ObjCPropertyDecl *Prop = *P;
if (Prop->getIdentifier() == II)
return Prop;
}
@@ -1358,8 +1535,8 @@ static IdentifierInfo * getDefaultSynthIvarName(ObjCPropertyDecl *Prop,
return &Ctx.Idents.get(ivarName.str());
}
-/// DefaultSynthesizeProperties - This routine default synthesizes all
-/// properties which must be synthesized in class's @implementation.
+/// \brief Default synthesizes all properties which must be synthesized
+/// in class's \@implementation.
void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl,
ObjCInterfaceDecl *IDecl) {
@@ -1402,16 +1579,21 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl,
// aren't really synthesized at a particular location; they just exist.
// Saying that they are located at the @implementation isn't really going
// to help users.
- ActOnPropertyImplDecl(S, SourceLocation(), SourceLocation(),
- true,
- /* property = */ Prop->getIdentifier(),
- /* ivar = */ getDefaultSynthIvarName(Prop, Context),
- SourceLocation());
+ ObjCPropertyImplDecl *PIDecl = dyn_cast_or_null<ObjCPropertyImplDecl>(
+ ActOnPropertyImplDecl(S, SourceLocation(), SourceLocation(),
+ true,
+ /* property = */ Prop->getIdentifier(),
+ /* ivar = */ getDefaultSynthIvarName(Prop, Context),
+ Prop->getLocation()));
+ if (PIDecl) {
+ Diag(Prop->getLocation(), diag::warn_missing_explicit_synthesis);
+ Diag(IMPDecl->getLocation(), diag::note_while_in_implementation);
+ }
}
}
void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) {
- if (!LangOpts.ObjCDefaultSynthProperties || !LangOpts.ObjCNonFragileABI2)
+ if (!LangOpts.ObjCDefaultSynthProperties || LangOpts.ObjCRuntime.isFragile())
return;
ObjCImplementationDecl *IC=dyn_cast_or_null<ObjCImplementationDecl>(D);
if (!IC)
@@ -1423,7 +1605,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) {
void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl,
ObjCContainerDecl *CDecl,
- const llvm::DenseSet<Selector>& InsMap) {
+ const SelectorSet &InsMap) {
llvm::DenseMap<IdentifierInfo *, ObjCPropertyDecl*> SuperPropMap;
if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl))
CollectSuperClassPropertyImplementations(IDecl, SuperPropMap);
@@ -1437,7 +1619,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl,
for (ObjCImplDecl::propimpl_iterator
I = IMPDecl->propimpl_begin(),
EI = IMPDecl->propimpl_end(); I != EI; ++I)
- PropImplMap.insert((*I)->getPropertyDecl());
+ PropImplMap.insert(I->getPropertyDecl());
for (llvm::DenseMap<IdentifierInfo *, ObjCPropertyDecl*>::iterator
P = PropMap.begin(), E = PropMap.end(); P != E; ++P) {
@@ -1455,7 +1637,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl,
<< Prop->getDeclName() << Prop->getGetterName();
Diag(Prop->getLocation(),
diag::note_property_declare);
- if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCNonFragileABI2)
+ if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCRuntime.isNonFragile())
if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CDecl))
if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs())
Diag(RID->getLocation(), diag::note_suppressed_class_declare);
@@ -1470,7 +1652,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl,
<< Prop->getDeclName() << Prop->getSetterName();
Diag(Prop->getLocation(),
diag::note_property_declare);
- if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCNonFragileABI2)
+ if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCRuntime.isNonFragile())
if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CDecl))
if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs())
Diag(RID->getLocation(), diag::note_suppressed_class_declare);
@@ -1487,7 +1669,7 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl,
for (ObjCContainerDecl::prop_iterator I = IDecl->prop_begin(),
E = IDecl->prop_end();
I != E; ++I) {
- ObjCPropertyDecl *Property = (*I);
+ ObjCPropertyDecl *Property = *I;
ObjCMethodDecl *GetterMethod = 0;
ObjCMethodDecl *SetterMethod = 0;
bool LookedUpGetterSetter = false;
@@ -1753,11 +1935,24 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property,
AddInstanceMethodToGlobalPool(GetterMethod);
if (SetterMethod)
AddInstanceMethodToGlobalPool(SetterMethod);
+
+ ObjCInterfaceDecl *CurrentClass = dyn_cast<ObjCInterfaceDecl>(CD);
+ if (!CurrentClass) {
+ if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(CD))
+ CurrentClass = Cat->getClassInterface();
+ else if (ObjCImplDecl *Impl = dyn_cast<ObjCImplDecl>(CD))
+ CurrentClass = Impl->getClassInterface();
+ }
+ if (GetterMethod)
+ CheckObjCMethodOverrides(GetterMethod, CurrentClass, Sema::RTC_Unknown);
+ if (SetterMethod)
+ CheckObjCMethodOverrides(SetterMethod, CurrentClass, Sema::RTC_Unknown);
}
void Sema::CheckObjCPropertyAttributes(Decl *PDecl,
SourceLocation Loc,
- unsigned &Attributes) {
+ unsigned &Attributes,
+ bool propertyInPrimaryClass) {
// FIXME: Improve the reported location.
if (!PDecl || PDecl->isInvalidDecl())
return;
@@ -1780,9 +1975,18 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl,
return;
}
+ if (propertyInPrimaryClass) {
+ // we postpone most property diagnosis until class's implementation
+ // because, its readonly attribute may be overridden in its class
+ // extensions making other attributes, which make no sense, to make sense.
+ if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
+ (Attributes & ObjCDeclSpec::DQ_PR_readwrite))
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "readonly" << "readwrite";
+ }
// readonly and readwrite/assign/retain/copy conflict.
- if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
- (Attributes & (ObjCDeclSpec::DQ_PR_readwrite |
+ else if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
+ (Attributes & (ObjCDeclSpec::DQ_PR_readwrite |
ObjCDeclSpec::DQ_PR_assign |
ObjCDeclSpec::DQ_PR_unsafe_unretained |
ObjCDeclSpec::DQ_PR_copy |
@@ -1939,8 +2143,7 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl,
&& getLangOpts().getGC() == LangOptions::GCOnly
&& PropertyTy->isBlockPointerType())
Diag(Loc, diag::warn_objc_property_copy_missing_on_block);
- else if (getLangOpts().ObjCAutoRefCount &&
- (Attributes & ObjCDeclSpec::DQ_PR_retain) &&
+ else if ((Attributes & ObjCDeclSpec::DQ_PR_retain) &&
!(Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
!(Attributes & ObjCDeclSpec::DQ_PR_strong) &&
PropertyTy->isBlockPointerType())
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index 50230f0..a874489 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -28,6 +28,7 @@
#include "clang/Basic/PartialDiagnostic.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include <algorithm>
@@ -388,13 +389,22 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx,
const unsigned ToWidth = Ctx.getIntWidth(ToType);
if (FromWidth > ToWidth ||
- (FromWidth == ToWidth && FromSigned != ToSigned)) {
+ (FromWidth == ToWidth && FromSigned != ToSigned) ||
+ (FromSigned && !ToSigned)) {
// Not all values of FromType can be represented in ToType.
llvm::APSInt InitializerValue;
const Expr *Initializer = IgnoreNarrowingConversion(Converted);
- if (Initializer->isIntegerConstantExpr(InitializerValue, Ctx)) {
- ConstantValue = APValue(InitializerValue);
-
+ if (!Initializer->isIntegerConstantExpr(InitializerValue, Ctx)) {
+ // Such conversions on variables are always narrowing.
+ return NK_Variable_Narrowing;
+ }
+ bool Narrowing = false;
+ if (FromWidth < ToWidth) {
+ // Negative -> unsigned is narrowing. Otherwise, more bits is never
+ // narrowing.
+ if (InitializerValue.isSigned() && InitializerValue.isNegative())
+ Narrowing = true;
+ } else {
// Add a bit to the InitializerValue so we don't have to worry about
// signed vs. unsigned comparisons.
InitializerValue = InitializerValue.extend(
@@ -406,13 +416,13 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx,
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
ConvertedValue.setIsSigned(InitializerValue.isSigned());
// If the result is different, this was a narrowing conversion.
- if (ConvertedValue != InitializerValue) {
- ConstantType = Initializer->getType();
- return NK_Constant_Narrowing;
- }
- } else {
- // Variables are always narrowings.
- return NK_Variable_Narrowing;
+ if (ConvertedValue != InitializerValue)
+ Narrowing = true;
+ }
+ if (Narrowing) {
+ ConstantType = Initializer->getType();
+ ConstantValue = APValue(InitializerValue);
+ return NK_Constant_Narrowing;
}
}
return NK_Not_Narrowing;
@@ -541,6 +551,7 @@ static MakeDeductionFailureInfo(ASTContext &Context,
TemplateDeductionInfo &Info) {
OverloadCandidate::DeductionFailureInfo Result;
Result.Result = static_cast<unsigned>(TDK);
+ Result.HasDiagnostic = false;
Result.Data = 0;
switch (TDK) {
case Sema::TDK_Success:
@@ -567,6 +578,12 @@ static MakeDeductionFailureInfo(ASTContext &Context,
case Sema::TDK_SubstitutionFailure:
Result.Data = Info.take();
+ if (Info.hasSFINAEDiagnostic()) {
+ PartialDiagnosticAt *Diag = new (Result.Diagnostic) PartialDiagnosticAt(
+ SourceLocation(), PartialDiagnostic::NullDiagnostic());
+ Info.takeSFINAEDiagnostic(*Diag);
+ Result.HasDiagnostic = true;
+ }
break;
case Sema::TDK_NonDeducedMismatch:
@@ -594,8 +611,12 @@ void OverloadCandidate::DeductionFailureInfo::Destroy() {
break;
case Sema::TDK_SubstitutionFailure:
- // FIXME: Destroy the template arugment list?
+ // FIXME: Destroy the template argument list?
Data = 0;
+ if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {
+ Diag->~PartialDiagnosticAt();
+ HasDiagnostic = false;
+ }
break;
// Unhandled
@@ -605,6 +626,13 @@ void OverloadCandidate::DeductionFailureInfo::Destroy() {
}
}
+PartialDiagnosticAt *
+OverloadCandidate::DeductionFailureInfo::getSFINAEDiagnostic() {
+ if (HasDiagnostic)
+ return static_cast<PartialDiagnosticAt*>(static_cast<void*>(Diagnostic));
+ return 0;
+}
+
TemplateParameter
OverloadCandidate::DeductionFailureInfo::getTemplateParameter() {
switch (static_cast<Sema::TemplateDeductionResult>(Result)) {
@@ -707,9 +735,12 @@ OverloadCandidate::DeductionFailureInfo::getSecondArg() {
}
void OverloadCandidateSet::clear() {
- for (iterator i = begin(), e = end(); i != e; ++i)
+ for (iterator i = begin(), e = end(); i != e; ++i) {
for (unsigned ii = 0, ie = i->NumConversions; ii != ie; ++ii)
i->Conversions[ii].~ImplicitConversionSequence();
+ if (!i->Viable && i->FailureKind == ovl_fail_bad_deduction)
+ i->DeductionFailure.Destroy();
+ }
NumInlineSequences = 0;
Candidates.clear();
Functions.clear();
@@ -1657,7 +1688,7 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) {
// We have already pre-calculated the promotion type, so this is trivial.
if (ToType->isIntegerType() &&
- !RequireCompleteType(From->getLocStart(), FromType, PDiag()))
+ !RequireCompleteType(From->getLocStart(), FromType, 0))
return Context.hasSameUnqualifiedType(ToType,
FromEnumType->getDecl()->getPromotionType());
}
@@ -1987,7 +2018,7 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
if (getLangOpts().CPlusPlus &&
FromPointeeType->isRecordType() && ToPointeeType->isRecordType() &&
!Context.hasSameUnqualifiedType(FromPointeeType, ToPointeeType) &&
- !RequireCompleteType(From->getLocStart(), FromPointeeType, PDiag()) &&
+ !RequireCompleteType(From->getLocStart(), FromPointeeType, 0) &&
IsDerivedFrom(FromPointeeType, ToPointeeType)) {
ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr,
ToPointeeType,
@@ -2469,7 +2500,7 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
/// for equality of their argument types. Caller has already checked that
/// they have same number of arguments. This routine assumes that Objective-C
/// pointer types which only differ in their protocol qualifiers are equal.
-/// If the parameters are different, ArgPos will have the the parameter index
+/// If the parameters are different, ArgPos will have the parameter index
/// of the first different parameter.
bool Sema::FunctionArgTypesAreEqual(const FunctionProtoType *OldType,
const FunctionProtoType *NewType,
@@ -2531,13 +2562,17 @@ bool Sema::CheckPointerConversion(Expr *From, QualType ToType,
Kind = CK_BitCast;
- if (!IsCStyleOrFunctionalCast &&
- Context.hasSameUnqualifiedType(From->getType(), Context.BoolTy) &&
- From->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull))
- DiagRuntimeBehavior(From->getExprLoc(), From,
- PDiag(diag::warn_impcast_bool_to_null_pointer)
- << ToType << From->getSourceRange());
-
+ if (!IsCStyleOrFunctionalCast && !FromType->isAnyPointerType() &&
+ From->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull) ==
+ Expr::NPCK_ZeroExpression) {
+ if (Context.hasSameUnqualifiedType(From->getType(), Context.BoolTy))
+ DiagRuntimeBehavior(From->getExprLoc(), From,
+ PDiag(diag::warn_impcast_bool_to_null_pointer)
+ << ToType << From->getSourceRange());
+ else if (!isUnevaluatedContext())
+ Diag(From->getExprLoc(), diag::warn_non_literal_null_pointer)
+ << ToType << From->getSourceRange();
+ }
if (const PointerType *ToPtrType = ToType->getAs<PointerType>()) {
if (const PointerType *FromPtrType = FromType->getAs<PointerType>()) {
QualType FromPointeeType = FromPtrType->getPointeeType(),
@@ -2616,7 +2651,7 @@ bool Sema::IsMemberPointerConversion(Expr *From, QualType FromType,
QualType ToClass(ToTypePtr->getClass(), 0);
if (!Context.hasSameUnqualifiedType(FromClass, ToClass) &&
- !RequireCompleteType(From->getLocStart(), ToClass, PDiag()) &&
+ !RequireCompleteType(From->getLocStart(), ToClass, 0) &&
IsDerivedFrom(ToClass, FromClass)) {
ConvertedType = Context.getMemberPointerType(FromTypePtr->getPointeeType(),
ToClass.getTypePtr());
@@ -2923,7 +2958,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
S.IsDerivedFrom(From->getType(), ToType)))
ConstructorsOnly = true;
- S.RequireCompleteType(From->getLocStart(), ToType, S.PDiag());
+ S.RequireCompleteType(From->getLocStart(), ToType, 0);
// RequireCompleteType may have returned true due to some invalid decl
// during template instantiation, but ToType may be complete enough now
// to try to recover.
@@ -3001,8 +3036,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
// Enumerate conversion functions, if we're allowed to.
if (ConstructorsOnly || isa<InitListExpr>(From)) {
- } else if (S.RequireCompleteType(From->getLocStart(), From->getType(),
- S.PDiag(0) << From->getSourceRange())) {
+ } else if (S.RequireCompleteType(From->getLocStart(), From->getType(), 0)) {
// No conversion functions from incomplete types.
} else if (const RecordType *FromRecordType
= From->getType()->getAs<RecordType>()) {
@@ -3848,7 +3882,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
ObjCLifetimeConversion = false;
if (UnqualT1 == UnqualT2) {
// Nothing to do.
- } else if (!RequireCompleteType(Loc, OrigT2, PDiag()) &&
+ } else if (!RequireCompleteType(Loc, OrigT2, 0) &&
IsDerivedFrom(UnqualT2, UnqualT1))
DerivedToBase = true;
else if (UnqualT1->isObjCObjectOrInterfaceType() &&
@@ -4135,7 +4169,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
// qualifier.
// This is also the point where rvalue references and lvalue inits no longer
// go together.
- if (!isRValRef && !T1.isConstQualified())
+ if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified()))
return ICS;
// -- If the initializer expression
@@ -4313,7 +4347,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// We need a complete type for what follows. Incomplete types can never be
// initialized from init lists.
- if (S.RequireCompleteType(From->getLocStart(), ToType, S.PDiag()))
+ if (S.RequireCompleteType(From->getLocStart(), ToType, 0))
return Result;
// C++11 [over.ics.list]p2:
@@ -4995,29 +5029,9 @@ static bool isIntegralOrEnumerationType(QualType T, bool AllowScopedEnum) {
/// \param Loc The source location of the construct that requires the
/// conversion.
///
-/// \param FromE The expression we're converting from.
-///
-/// \param NotIntDiag The diagnostic to be emitted if the expression does not
-/// have integral or enumeration type.
-///
-/// \param IncompleteDiag The diagnostic to be emitted if the expression has
-/// incomplete class type.
+/// \param From The expression we're converting from.
///
-/// \param ExplicitConvDiag The diagnostic to be emitted if we're calling an
-/// explicit conversion function (because no implicit conversion functions
-/// were available). This is a recovery mode.
-///
-/// \param ExplicitConvNote The note to be emitted with \p ExplicitConvDiag,
-/// showing which conversion was picked.
-///
-/// \param AmbigDiag The diagnostic to be emitted if there is more than one
-/// conversion function that could convert to integral or enumeration type.
-///
-/// \param AmbigNote The note to be emitted with \p AmbigDiag for each
-/// usable conversion function.
-///
-/// \param ConvDiag The diagnostic to be emitted if we are calling a conversion
-/// function, which may be an extension in this case.
+/// \param Diagnoser Used to output any diagnostics.
///
/// \param AllowScopedEnumerations Specifies whether conversions to scoped
/// enumerations should be considered.
@@ -5026,13 +5040,7 @@ static bool isIntegralOrEnumerationType(QualType T, bool AllowScopedEnum) {
/// successful.
ExprResult
Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
- const PartialDiagnostic &NotIntDiag,
- const PartialDiagnostic &IncompleteDiag,
- const PartialDiagnostic &ExplicitConvDiag,
- const PartialDiagnostic &ExplicitConvNote,
- const PartialDiagnostic &AmbigDiag,
- const PartialDiagnostic &AmbigNote,
- const PartialDiagnostic &ConvDiag,
+ ICEConvertDiagnoser &Diagnoser,
bool AllowScopedEnumerations) {
// We can't perform any more checking for type-dependent expressions.
if (From->isTypeDependent())
@@ -5056,13 +5064,25 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
// expression of integral or enumeration type.
const RecordType *RecordTy = T->getAs<RecordType>();
if (!RecordTy || !getLangOpts().CPlusPlus) {
- if (NotIntDiag.getDiagID())
- Diag(Loc, NotIntDiag) << T << From->getSourceRange();
+ if (!Diagnoser.Suppress)
+ Diagnoser.diagnoseNotInt(*this, Loc, T) << From->getSourceRange();
return Owned(From);
}
// We must have a complete class type.
- if (RequireCompleteType(Loc, T, IncompleteDiag))
+ struct TypeDiagnoserPartialDiag : TypeDiagnoser {
+ ICEConvertDiagnoser &Diagnoser;
+ Expr *From;
+
+ TypeDiagnoserPartialDiag(ICEConvertDiagnoser &Diagnoser, Expr *From)
+ : TypeDiagnoser(Diagnoser.Suppress), Diagnoser(Diagnoser), From(From) {}
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ Diagnoser.diagnoseIncomplete(S, Loc, T) << From->getSourceRange();
+ }
+ } IncompleteDiagnoser(Diagnoser, From);
+
+ if (RequireCompleteType(Loc, T, IncompleteDiagnoser))
return Owned(From);
// Look for a conversion to an integral or enumeration type.
@@ -5092,7 +5112,7 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
switch (ViableConversions.size()) {
case 0:
- if (ExplicitConversions.size() == 1 && ExplicitConvDiag.getDiagID()) {
+ if (ExplicitConversions.size() == 1 && !Diagnoser.Suppress) {
DeclAccessPair Found = ExplicitConversions[0];
CXXConversionDecl *Conversion
= cast<CXXConversionDecl>(Found->getUnderlyingDecl());
@@ -5104,14 +5124,12 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
std::string TypeStr;
ConvTy.getAsStringInternal(TypeStr, getPrintingPolicy());
- Diag(Loc, ExplicitConvDiag)
- << T << ConvTy
+ Diagnoser.diagnoseExplicitConv(*this, Loc, T, ConvTy)
<< FixItHint::CreateInsertion(From->getLocStart(),
"static_cast<" + TypeStr + ">(")
<< FixItHint::CreateInsertion(PP.getLocForEndOfToken(From->getLocEnd()),
")");
- Diag(Conversion->getLocation(), ExplicitConvNote)
- << ConvTy->isEnumeralType() << ConvTy;
+ Diagnoser.noteExplicitConv(*this, Conversion, ConvTy);
// If we aren't in a SFINAE context, build a call to the
// explicit conversion function.
@@ -5142,12 +5160,12 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
= cast<CXXConversionDecl>(Found->getUnderlyingDecl());
QualType ConvTy
= Conversion->getConversionType().getNonReferenceType();
- if (ConvDiag.getDiagID()) {
+ if (!Diagnoser.SuppressConversion) {
if (isSFINAEContext())
return ExprError();
- Diag(Loc, ConvDiag)
- << T << ConvTy->isEnumeralType() << ConvTy << From->getSourceRange();
+ Diagnoser.diagnoseConversion(*this, Loc, T, ConvTy)
+ << From->getSourceRange();
}
ExprResult Result = BuildCXXMemberCallExpr(From, Found, Conversion,
@@ -5163,24 +5181,24 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
}
default:
- if (!AmbigDiag.getDiagID())
- return Owned(From);
+ if (Diagnoser.Suppress)
+ return ExprError();
- Diag(Loc, AmbigDiag)
- << T << From->getSourceRange();
+ Diagnoser.diagnoseAmbiguous(*this, Loc, T) << From->getSourceRange();
for (unsigned I = 0, N = ViableConversions.size(); I != N; ++I) {
CXXConversionDecl *Conv
= cast<CXXConversionDecl>(ViableConversions[I]->getUnderlyingDecl());
QualType ConvTy = Conv->getConversionType().getNonReferenceType();
- Diag(Conv->getLocation(), AmbigNote)
- << ConvTy->isEnumeralType() << ConvTy;
+ Diagnoser.noteAmbiguous(*this, Conv, ConvTy);
}
return Owned(From);
}
if (!isIntegralOrEnumerationType(From->getType(), AllowScopedEnumerations) &&
- NotIntDiag.getDiagID())
- Diag(Loc, NotIntDiag) << From->getType() << From->getSourceRange();
+ !Diagnoser.Suppress) {
+ Diagnoser.diagnoseNotInt(*this, Loc, From->getType())
+ << From->getSourceRange();
+ }
return DefaultLvalueConversion(From);
}
@@ -5190,7 +5208,7 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From,
/// @p SuppressUserConversions, then don't allow user-defined
/// conversions via constructors or conversion operators.
///
-/// \para PartialOverloading true if we are performing "partial" overloading
+/// \param PartialOverloading true if we are performing "partial" overloading
/// based on an incomplete set of function arguments. This feature is used by
/// code completion.
void
@@ -5906,7 +5924,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
// empty.
if (const RecordType *T1Rec = T1->getAs<RecordType>()) {
// Complete the type if it can be completed. Otherwise, we're done.
- if (RequireCompleteType(OpLoc, T1, PDiag()))
+ if (RequireCompleteType(OpLoc, T1, 0))
return;
LookupResult Operators(*this, OpName, OpLoc, LookupOrdinaryName);
@@ -6098,40 +6116,49 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty,
const PointerType *PointerTy = Ty->getAs<PointerType>();
bool buildObjCPtr = false;
if (!PointerTy) {
- if (const ObjCObjectPointerType *PTy = Ty->getAs<ObjCObjectPointerType>()) {
- PointeeTy = PTy->getPointeeType();
- buildObjCPtr = true;
- }
- else
- llvm_unreachable("type was not a pointer type!");
- }
- else
+ const ObjCObjectPointerType *PTy = Ty->castAs<ObjCObjectPointerType>();
+ PointeeTy = PTy->getPointeeType();
+ buildObjCPtr = true;
+ } else {
PointeeTy = PointerTy->getPointeeType();
-
+ }
+
// Don't add qualified variants of arrays. For one, they're not allowed
// (the qualifier would sink to the element type), and for another, the
// only overload situation where it matters is subscript or pointer +- int,
// and those shouldn't have qualifier variants anyway.
if (PointeeTy->isArrayType())
return true;
+
unsigned BaseCVR = PointeeTy.getCVRQualifiers();
- if (const ConstantArrayType *Array =Context.getAsConstantArrayType(PointeeTy))
- BaseCVR = Array->getElementType().getCVRQualifiers();
bool hasVolatile = VisibleQuals.hasVolatile();
bool hasRestrict = VisibleQuals.hasRestrict();
// Iterate through all strict supersets of BaseCVR.
for (unsigned CVR = BaseCVR+1; CVR <= Qualifiers::CVRMask; ++CVR) {
if ((CVR | BaseCVR) != CVR) continue;
- // Skip over Volatile/Restrict if no Volatile/Restrict found anywhere
- // in the types.
+ // Skip over volatile if no volatile found anywhere in the types.
if ((CVR & Qualifiers::Volatile) && !hasVolatile) continue;
- if ((CVR & Qualifiers::Restrict) && !hasRestrict) continue;
+
+ // Skip over restrict if no restrict found anywhere in the types, or if
+ // the type cannot be restrict-qualified.
+ if ((CVR & Qualifiers::Restrict) &&
+ (!hasRestrict ||
+ (!(PointeeTy->isAnyPointerType() || PointeeTy->isReferenceType()))))
+ continue;
+
+ // Build qualified pointee type.
QualType QPointeeTy = Context.getCVRQualifiedType(PointeeTy, CVR);
+
+ // Build qualified pointer type.
+ QualType QPointerTy;
if (!buildObjCPtr)
- PointerTypes.insert(Context.getPointerType(QPointeeTy));
+ QPointerTy = Context.getPointerType(QPointeeTy);
else
- PointerTypes.insert(Context.getObjCObjectPointerType(QPointeeTy));
+ QPointerTy = Context.getObjCObjectPointerType(QPointeeTy);
+
+ // Insert qualified pointer type.
+ PointerTypes.insert(QPointerTy);
}
return true;
@@ -6328,6 +6355,8 @@ static Qualifiers CollectVRQualifiers(ASTContext &Context, Expr* ArgExpr) {
// as see them.
bool done = false;
while (!done) {
+ if (CanTy.isRestrictQualified())
+ VRQuals.addRestrict();
if (const PointerType *ResTypePtr = CanTy->getAs<PointerType>())
CanTy = ResTypePtr->getPointeeType();
else if (const MemberPointerType *ResTypeMPtr =
@@ -6337,8 +6366,6 @@ static Qualifiers CollectVRQualifiers(ASTContext &Context, Expr* ArgExpr) {
done = true;
if (CanTy.isVolatileQualified())
VRQuals.addVolatile();
- if (CanTy.isRestrictQualified())
- VRQuals.addRestrict();
if (VRQuals.hasRestrict() && VRQuals.hasVolatile())
return VRQuals;
}
@@ -6368,12 +6395,12 @@ class BuiltinOperatorOverloadBuilder {
// The "promoted arithmetic types" are the arithmetic
// types are that preserved by promotion (C++ [over.built]p2).
static const unsigned FirstIntegralType = 3;
- static const unsigned LastIntegralType = 18;
+ static const unsigned LastIntegralType = 20;
static const unsigned FirstPromotedIntegralType = 3,
- LastPromotedIntegralType = 9;
+ LastPromotedIntegralType = 11;
static const unsigned FirstPromotedArithmeticType = 0,
- LastPromotedArithmeticType = 9;
- static const unsigned NumArithmeticTypes = 18;
+ LastPromotedArithmeticType = 11;
+ static const unsigned NumArithmeticTypes = 20;
/// \brief Get the canonical type for a given arithmetic type index.
CanQualType getArithmeticType(unsigned index) {
@@ -6389,9 +6416,11 @@ class BuiltinOperatorOverloadBuilder {
&ASTContext::IntTy,
&ASTContext::LongTy,
&ASTContext::LongLongTy,
+ &ASTContext::Int128Ty,
&ASTContext::UnsignedIntTy,
&ASTContext::UnsignedLongTy,
&ASTContext::UnsignedLongLongTy,
+ &ASTContext::UnsignedInt128Ty,
// End of promoted types.
&ASTContext::BoolTy,
@@ -6404,7 +6433,7 @@ class BuiltinOperatorOverloadBuilder {
&ASTContext::UnsignedCharTy,
&ASTContext::UnsignedShortTy,
// End of integral types.
- // FIXME: What about complex?
+ // FIXME: What about complex? What about half?
};
return S.Context.*ArithmeticTypes[index];
}
@@ -6423,20 +6452,24 @@ class BuiltinOperatorOverloadBuilder {
// *except* when dealing with signed types of higher rank.
// (we could precompute SLL x UI for all known platforms, but it's
// better not to make any assumptions).
+ // We assume that int128 has a higher rank than long long on all platforms.
enum PromotedType {
- Flt, Dbl, LDbl, SI, SL, SLL, UI, UL, ULL, Dep=-1
+ Dep=-1,
+ Flt, Dbl, LDbl, SI, SL, SLL, S128, UI, UL, ULL, U128
};
- static PromotedType ConversionsTable[LastPromotedArithmeticType]
+ static const PromotedType ConversionsTable[LastPromotedArithmeticType]
[LastPromotedArithmeticType] = {
- /* Flt*/ { Flt, Dbl, LDbl, Flt, Flt, Flt, Flt, Flt, Flt },
- /* Dbl*/ { Dbl, Dbl, LDbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl },
- /*LDbl*/ { LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl },
- /* SI*/ { Flt, Dbl, LDbl, SI, SL, SLL, UI, UL, ULL },
- /* SL*/ { Flt, Dbl, LDbl, SL, SL, SLL, Dep, UL, ULL },
- /* SLL*/ { Flt, Dbl, LDbl, SLL, SLL, SLL, Dep, Dep, ULL },
- /* UI*/ { Flt, Dbl, LDbl, UI, Dep, Dep, UI, UL, ULL },
- /* UL*/ { Flt, Dbl, LDbl, UL, UL, Dep, UL, UL, ULL },
- /* ULL*/ { Flt, Dbl, LDbl, ULL, ULL, ULL, ULL, ULL, ULL },
+/* Flt*/ { Flt, Dbl, LDbl, Flt, Flt, Flt, Flt, Flt, Flt, Flt, Flt },
+/* Dbl*/ { Dbl, Dbl, LDbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl },
+/*LDbl*/ { LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl },
+/* SI*/ { Flt, Dbl, LDbl, SI, SL, SLL, S128, UI, UL, ULL, U128 },
+/* SL*/ { Flt, Dbl, LDbl, SL, SL, SLL, S128, Dep, UL, ULL, U128 },
+/* SLL*/ { Flt, Dbl, LDbl, SLL, SLL, SLL, S128, Dep, Dep, ULL, U128 },
+/*S128*/ { Flt, Dbl, LDbl, S128, S128, S128, S128, S128, S128, S128, U128 },
+/* UI*/ { Flt, Dbl, LDbl, UI, Dep, Dep, S128, UI, UL, ULL, U128 },
+/* UL*/ { Flt, Dbl, LDbl, UL, UL, Dep, S128, UL, UL, ULL, U128 },
+/* ULL*/ { Flt, Dbl, LDbl, ULL, ULL, ULL, S128, ULL, ULL, ULL, U128 },
+/*U128*/ { Flt, Dbl, LDbl, U128, U128, U128, U128, U128, U128, U128, U128 },
};
assert(L < LastPromotedArithmeticType);
@@ -6466,7 +6499,8 @@ class BuiltinOperatorOverloadBuilder {
/// \brief Helper method to factor out the common pattern of adding overloads
/// for '++' and '--' builtin operators.
void addPlusPlusMinusMinusStyleOverloads(QualType CandidateTy,
- bool HasVolatile) {
+ bool HasVolatile,
+ bool HasRestrict) {
QualType ParamTypes[2] = {
S.Context.getLValueReferenceType(CandidateTy),
S.Context.IntTy
@@ -6489,6 +6523,33 @@ class BuiltinOperatorOverloadBuilder {
else
S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet);
}
+
+ // Add restrict version only if there are conversions to a restrict type
+ // and our candidate type is a non-restrict-qualified pointer.
+ if (HasRestrict && CandidateTy->isAnyPointerType() &&
+ !CandidateTy.isRestrictQualified()) {
+ ParamTypes[0]
+ = S.Context.getLValueReferenceType(
+ S.Context.getCVRQualifiedType(CandidateTy, Qualifiers::Restrict));
+ if (NumArgs == 1)
+ S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet);
+ else
+ S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet);
+
+ if (HasVolatile) {
+ ParamTypes[0]
+ = S.Context.getLValueReferenceType(
+ S.Context.getCVRQualifiedType(CandidateTy,
+ (Qualifiers::Volatile |
+ Qualifiers::Restrict)));
+ if (NumArgs == 1)
+ S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1,
+ CandidateSet);
+ else
+ S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet);
+ }
+ }
+
}
public:
@@ -6508,13 +6569,13 @@ public:
assert(getArithmeticType(FirstPromotedIntegralType) == S.Context.IntTy &&
"Invalid first promoted integral type");
assert(getArithmeticType(LastPromotedIntegralType - 1)
- == S.Context.UnsignedLongLongTy &&
+ == S.Context.UnsignedInt128Ty &&
"Invalid last promoted integral type");
assert(getArithmeticType(FirstPromotedArithmeticType)
== S.Context.FloatTy &&
"Invalid first promoted arithmetic type");
assert(getArithmeticType(LastPromotedArithmeticType - 1)
- == S.Context.UnsignedLongLongTy &&
+ == S.Context.UnsignedInt128Ty &&
"Invalid last promoted arithmetic type");
}
@@ -6543,7 +6604,8 @@ public:
Arith < NumArithmeticTypes; ++Arith) {
addPlusPlusMinusMinusStyleOverloads(
getArithmeticType(Arith),
- VisibleTypeConversionsQuals.hasVolatile());
+ VisibleTypeConversionsQuals.hasVolatile(),
+ VisibleTypeConversionsQuals.hasRestrict());
}
}
@@ -6567,8 +6629,10 @@ public:
continue;
addPlusPlusMinusMinusStyleOverloads(*Ptr,
- (!S.Context.getCanonicalType(*Ptr).isVolatileQualified() &&
- VisibleTypeConversionsQuals.hasVolatile()));
+ (!(*Ptr).isVolatileQualified() &&
+ VisibleTypeConversionsQuals.hasVolatile()),
+ (!(*Ptr).isRestrictQualified() &&
+ VisibleTypeConversionsQuals.hasRestrict()));
}
}
@@ -7026,14 +7090,36 @@ public:
S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet,
/*IsAssigmentOperator=*/ isEqualOp);
- if (!S.Context.getCanonicalType(*Ptr).isVolatileQualified() &&
- VisibleTypeConversionsQuals.hasVolatile()) {
+ bool NeedVolatile = !(*Ptr).isVolatileQualified() &&
+ VisibleTypeConversionsQuals.hasVolatile();
+ if (NeedVolatile) {
// volatile version
ParamTypes[0] =
S.Context.getLValueReferenceType(S.Context.getVolatileType(*Ptr));
S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet,
/*IsAssigmentOperator=*/isEqualOp);
}
+
+ if (!(*Ptr).isRestrictQualified() &&
+ VisibleTypeConversionsQuals.hasRestrict()) {
+ // restrict version
+ ParamTypes[0]
+ = S.Context.getLValueReferenceType(S.Context.getRestrictType(*Ptr));
+ S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet,
+ /*IsAssigmentOperator=*/isEqualOp);
+
+ if (NeedVolatile) {
+ // volatile restrict version
+ ParamTypes[0]
+ = S.Context.getLValueReferenceType(
+ S.Context.getCVRQualifiedType(*Ptr,
+ (Qualifiers::Volatile |
+ Qualifiers::Restrict)));
+ S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,
+ CandidateSet,
+ /*IsAssigmentOperator=*/isEqualOp);
+ }
+ }
}
if (isEqualOp) {
@@ -7054,14 +7140,36 @@ public:
S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet,
/*IsAssigmentOperator=*/true);
- if (!S.Context.getCanonicalType(*Ptr).isVolatileQualified() &&
- VisibleTypeConversionsQuals.hasVolatile()) {
+ bool NeedVolatile = !(*Ptr).isVolatileQualified() &&
+ VisibleTypeConversionsQuals.hasVolatile();
+ if (NeedVolatile) {
// volatile version
ParamTypes[0] =
S.Context.getLValueReferenceType(S.Context.getVolatileType(*Ptr));
S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,
CandidateSet, /*IsAssigmentOperator=*/true);
}
+
+ if (!(*Ptr).isRestrictQualified() &&
+ VisibleTypeConversionsQuals.hasRestrict()) {
+ // restrict version
+ ParamTypes[0]
+ = S.Context.getLValueReferenceType(S.Context.getRestrictType(*Ptr));
+ S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,
+ CandidateSet, /*IsAssigmentOperator=*/true);
+
+ if (NeedVolatile) {
+ // volatile restrict version
+ ParamTypes[0]
+ = S.Context.getLValueReferenceType(
+ S.Context.getCVRQualifiedType(*Ptr,
+ (Qualifiers::Volatile |
+ Qualifiers::Restrict)));
+ S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,
+ CandidateSet, /*IsAssigmentOperator=*/true);
+
+ }
+ }
}
}
}
@@ -7705,13 +7813,11 @@ isBetterOverloadCandidate(Sema &S,
/// \brief Computes the best viable function (C++ 13.3.3)
/// within an overload candidate set.
///
-/// \param CandidateSet the set of candidate functions.
-///
-/// \param Loc the location of the function name (or operator symbol) for
+/// \param Loc The location of the function name (or operator symbol) for
/// which overload resolution occurs.
///
-/// \param Best f overload resolution was successful or found a deleted
-/// function, Best points to the candidate function found.
+/// \param Best If overload resolution was successful or found a deleted
+/// function, \p Best points to the candidate function found.
///
/// \returns The result of overload resolution.
OverloadingResult
@@ -8035,12 +8141,22 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) {
FromIface->isSuperClassOf(ToIface))
BaseToDerivedConversion = 2;
} else if (const ReferenceType *ToRefTy = ToTy->getAs<ReferenceType>()) {
- if (ToRefTy->getPointeeType().isAtLeastAsQualifiedAs(FromTy) &&
- !FromTy->isIncompleteType() &&
- !ToRefTy->getPointeeType()->isIncompleteType() &&
- S.IsDerivedFrom(ToRefTy->getPointeeType(), FromTy))
- BaseToDerivedConversion = 3;
+ if (ToRefTy->getPointeeType().isAtLeastAsQualifiedAs(FromTy) &&
+ !FromTy->isIncompleteType() &&
+ !ToRefTy->getPointeeType()->isIncompleteType() &&
+ S.IsDerivedFrom(ToRefTy->getPointeeType(), FromTy)) {
+ BaseToDerivedConversion = 3;
+ } else if (ToTy->isLValueReferenceType() && !FromExpr->isLValue() &&
+ ToTy.getNonReferenceType().getCanonicalType() ==
+ FromTy.getNonReferenceType().getCanonicalType()) {
+ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_lvalue)
+ << (unsigned) FnKind << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
+ << (unsigned) isObjectArgument << I + 1;
+ MaybeEmitInheritedConstructorNote(S, Fn);
+ return;
}
+ }
if (BaseToDerivedConversion) {
S.Diag(Fn->getLocation(),
@@ -8127,9 +8243,14 @@ void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand,
std::string Description;
OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, Description);
- S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity)
- << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode
- << modeCount << NumFormalArgs;
+ if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName())
+ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
+ << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode
+ << Fn->getParamDecl(0) << NumFormalArgs;
+ else
+ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity)
+ << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode
+ << modeCount << NumFormalArgs;
MaybeEmitInheritedConstructorNote(S, Fn);
}
@@ -8232,14 +8353,39 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand,
return;
case Sema::TDK_SubstitutionFailure: {
- std::string ArgString;
- if (TemplateArgumentList *Args
- = Cand->DeductionFailure.getTemplateArgumentList())
- ArgString = S.getTemplateArgumentBindingsText(
- Fn->getDescribedFunctionTemplate()->getTemplateParameters(),
- *Args);
+ // Format the template argument list into the argument string.
+ llvm::SmallString<128> TemplateArgString;
+ if (TemplateArgumentList *Args =
+ Cand->DeductionFailure.getTemplateArgumentList()) {
+ TemplateArgString = " ";
+ TemplateArgString += S.getTemplateArgumentBindingsText(
+ Fn->getDescribedFunctionTemplate()->getTemplateParameters(), *Args);
+ }
+
+ // If this candidate was disabled by enable_if, say so.
+ PartialDiagnosticAt *PDiag = Cand->DeductionFailure.getSFINAEDiagnostic();
+ if (PDiag && PDiag->second.getDiagID() ==
+ diag::err_typename_nested_not_found_enable_if) {
+ // FIXME: Use the source range of the condition, and the fully-qualified
+ // name of the enable_if template. These are both present in PDiag.
+ S.Diag(PDiag->first, diag::note_ovl_candidate_disabled_by_enable_if)
+ << "'enable_if'" << TemplateArgString;
+ return;
+ }
+
+ // Format the SFINAE diagnostic into the argument string.
+ // FIXME: Add a general mechanism to include a PartialDiagnostic *'s
+ // formatted message in another diagnostic.
+ llvm::SmallString<128> SFINAEArgString;
+ SourceRange R;
+ if (PDiag) {
+ SFINAEArgString = ": ";
+ R = SourceRange(PDiag->first, PDiag->first);
+ PDiag->second.EmitToString(S.getDiagnostics(), SFINAEArgString);
+ }
+
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_substitution_failure)
- << ArgString;
+ << TemplateArgString << SFINAEArgString << R;
MaybeEmitInheritedConstructorNote(S, Fn);
return;
}
@@ -9190,7 +9336,7 @@ bool Sema::ResolveAndFixSingleFunctionTemplateSpecialization(
return true;
}
- // Fix the expresion to refer to 'fn'.
+ // Fix the expression to refer to 'fn'.
SingleFunctionExpression =
Owned(FixOverloadedFunctionReference(SrcExpr.take(), found, fn));
@@ -9692,14 +9838,14 @@ static bool IsOverloaded(const UnresolvedSetImpl &Functions) {
/// \param OpcIn The UnaryOperator::Opcode that describes this
/// operator.
///
-/// \param Functions The set of non-member functions that will be
+/// \param Fns The set of non-member functions that will be
/// considered by overload resolution. The caller needs to build this
/// set based on the context using, e.g.,
/// LookupOverloadedOperatorName() and ArgumentDependentLookup(). This
/// set should not contain any member functions; those will be added
/// by CreateOverloadedUnaryOp().
///
-/// \param input The input argument.
+/// \param Input The input argument.
ExprResult
Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn,
const UnresolvedSetImpl &Fns,
@@ -9892,7 +10038,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn,
/// \param OpcIn The BinaryOperator::Opcode that describes this
/// operator.
///
-/// \param Functions The set of non-member functions that will be
+/// \param Fns The set of non-member functions that will be
/// considered by overload resolution. The caller needs to build this
/// set based on the context using, e.g.,
/// LookupOverloadedOperatorName() and ArgumentDependentLookup(). This
@@ -10559,7 +10705,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
DiagnoseSentinelCalls(Method, LParenLoc, Args, NumArgs);
- if (CheckFunctionCall(Method, TheCall))
+ if (CheckFunctionCall(Method, TheCall, Proto))
return ExprError();
if ((isa<CXXConstructorDecl>(CurContext) ||
@@ -10610,8 +10756,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(OO_Call);
if (RequireCompleteType(LParenLoc, Object.get()->getType(),
- PDiag(diag::err_incomplete_object_call)
- << Object.get()->getSourceRange()))
+ diag::err_incomplete_object_call, Object.get()))
return true;
LookupResult R(*this, OpName, LParenLoc, LookupOrdinaryName);
@@ -10857,7 +11002,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
// If this is a variadic call, handle args passed through "...".
if (Proto->isVariadic()) {
// Promote the arguments (C99 6.5.2.2p7).
- for (unsigned i = NumArgsInProto; i != NumArgs; i++) {
+ for (unsigned i = NumArgsInProto; i < NumArgs; i++) {
ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, 0);
IsError |= Arg.isInvalid();
TheCall->setArg(i + 1, Arg.take());
@@ -10868,7 +11013,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
DiagnoseSentinelCalls(Method, LParenLoc, Args, NumArgs);
- if (CheckFunctionCall(Method, TheCall))
+ if (CheckFunctionCall(Method, TheCall, Proto))
return true;
return MaybeBindToTemporary(TheCall);
@@ -10899,8 +11044,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) {
const RecordType *BaseRecord = Base->getType()->getAs<RecordType>();
if (RequireCompleteType(Loc, Base->getType(),
- PDiag(diag::err_typecheck_incomplete_tag)
- << Base->getSourceRange()))
+ diag::err_typecheck_incomplete_tag, Base))
return ExprError();
LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName);
@@ -11049,7 +11193,7 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R,
if (CheckCallReturnType(FD->getResultType(), UDSuffixLoc, UDL, FD))
return ExprError();
- if (CheckFunctionCall(FD, UDL))
+ if (CheckFunctionCall(FD, UDL, NULL))
return ExprError();
return MaybeBindToTemporary(UDL);
diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp
index 0e66329..722ac19 100644
--- a/lib/Sema/SemaPseudoObject.cpp
+++ b/lib/Sema/SemaPseudoObject.cpp
@@ -34,6 +34,7 @@
#include "clang/Sema/Initialization.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/SmallString.h"
using namespace clang;
using namespace sema;
@@ -232,7 +233,7 @@ namespace {
Expr *op);
bool tryBuildGetOfReference(Expr *op, ExprResult &result);
- bool findSetter();
+ bool findSetter(bool warn=true);
bool findGetter();
Expr *rebuildAndCaptureObject(Expr *syntacticBase);
@@ -505,7 +506,7 @@ bool ObjCPropertyOpBuilder::findGetter() {
/// reference.
///
/// \return true if a setter was found, in which case Setter
-bool ObjCPropertyOpBuilder::findSetter() {
+bool ObjCPropertyOpBuilder::findSetter(bool warn) {
// For implicit properties, just trust the lookup we already did.
if (RefExpr->isImplicitProperty()) {
if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
@@ -531,6 +532,23 @@ bool ObjCPropertyOpBuilder::findSetter() {
// Do a normal method lookup first.
if (ObjCMethodDecl *setter =
LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
+ if (setter->isSynthesized() && warn)
+ if (const ObjCInterfaceDecl *IFace =
+ dyn_cast<ObjCInterfaceDecl>(setter->getDeclContext())) {
+ const StringRef thisPropertyName(prop->getName());
+ char front = thisPropertyName.front();
+ front = islower(front) ? toupper(front) : tolower(front);
+ SmallString<100> PropertyName = thisPropertyName;
+ PropertyName[0] = front;
+ IdentifierInfo *AltMember = &S.PP.getIdentifierTable().get(PropertyName);
+ if (ObjCPropertyDecl *prop1 = IFace->FindPropertyDeclaration(AltMember))
+ if (prop != prop1 && (prop1->getSetterMethodDecl() == setter)) {
+ S.Diag(RefExpr->getExprLoc(), diag::error_property_setter_ambiguous_use)
+ << prop->getName() << prop1->getName() << setter->getSelector();
+ S.Diag(prop->getLocation(), diag::note_property_declare);
+ S.Diag(prop1->getLocation(), diag::note_property_declare);
+ }
+ }
Setter = setter;
return true;
}
@@ -603,7 +621,7 @@ ExprResult ObjCPropertyOpBuilder::buildGet() {
/// value being set as the value of the property operation.
ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc,
bool captureSetValueAsResult) {
- bool hasSetter = findSetter();
+ bool hasSetter = findSetter(false);
assert(hasSetter); (void) hasSetter;
if (SyntacticRefExpr)
@@ -889,8 +907,7 @@ Sema::ObjCSubscriptKind
// We must have a complete class type.
if (RequireCompleteType(FromE->getExprLoc(), T,
- PDiag(diag::err_objc_index_incomplete_class_type)
- << FromE->getSourceRange()))
+ diag::err_objc_index_incomplete_class_type, FromE))
return OS_Error;
// Look for a conversion to an integral, enumeration type, or
@@ -938,6 +955,27 @@ Sema::ObjCSubscriptKind
return OS_Error;
}
+/// CheckKeyForObjCARCConversion - This routine suggests bridge casting of CF
+/// objects used as dictionary subscript key objects.
+static void CheckKeyForObjCARCConversion(Sema &S, QualType ContainerT,
+ Expr *Key) {
+ if (ContainerT.isNull())
+ return;
+ // dictionary subscripting.
+ // - (id)objectForKeyedSubscript:(id)key;
+ IdentifierInfo *KeyIdents[] = {
+ &S.Context.Idents.get("objectForKeyedSubscript")
+ };
+ Selector GetterSelector = S.Context.Selectors.getSelector(1, KeyIdents);
+ ObjCMethodDecl *Getter = S.LookupMethodInObjectType(GetterSelector, ContainerT,
+ true /*instance*/);
+ if (!Getter)
+ return;
+ QualType T = Getter->param_begin()[0]->getType();
+ S.CheckObjCARCConversion(Key->getSourceRange(),
+ T, Key, Sema::CCK_ImplicitConversion);
+}
+
bool ObjCSubscriptOpBuilder::findAtIndexGetter() {
if (AtIndexGetter)
return true;
@@ -955,8 +993,12 @@ bool ObjCSubscriptOpBuilder::findAtIndexGetter() {
}
Sema::ObjCSubscriptKind Res =
S.CheckSubscriptingKind(RefExpr->getKeyExpr());
- if (Res == Sema::OS_Error)
+ if (Res == Sema::OS_Error) {
+ if (S.getLangOpts().ObjCAutoRefCount)
+ CheckKeyForObjCARCConversion(S, ResultType,
+ RefExpr->getKeyExpr());
return false;
+ }
bool arrayRef = (Res == Sema::OS_Array);
if (ResultType.isNull()) {
@@ -1063,8 +1105,12 @@ bool ObjCSubscriptOpBuilder::findAtIndexSetter() {
Sema::ObjCSubscriptKind Res =
S.CheckSubscriptingKind(RefExpr->getKeyExpr());
- if (Res == Sema::OS_Error)
+ if (Res == Sema::OS_Error) {
+ if (S.getLangOpts().ObjCAutoRefCount)
+ CheckKeyForObjCARCConversion(S, ResultType,
+ RefExpr->getKeyExpr());
return false;
+ }
bool arrayRef = (Res == Sema::OS_Array);
if (ResultType.isNull()) {
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 9052278..d22deb2 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -19,6 +19,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/StmtObjC.h"
@@ -28,7 +29,21 @@
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetAsmParser.h"
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
using namespace clang;
using namespace sema;
@@ -150,10 +165,11 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
if (!E)
return;
+ const Expr *WarnExpr;
SourceLocation Loc;
SourceRange R1, R2;
if (SourceMgr.isInSystemMacro(E->getExprLoc()) ||
- !E->isUnusedResultAWarning(Loc, R1, R2, Context))
+ !E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, Context))
return;
// Okay, we have an unused result. Depending on what the base expression is,
@@ -168,7 +184,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
if (DiagnoseUnusedComparison(*this, E))
return;
- E = E->IgnoreParenImpCasts();
+ E = WarnExpr;
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
if (E->getType()->isVoidType())
return;
@@ -226,6 +242,11 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
}
}
+ if (E->isGLValue() && E->getType().isVolatileQualified()) {
+ Diag(Loc, diag::warn_unused_volatile) << R1 << R2;
+ return;
+ }
+
DiagRuntimeBehavior(Loc, 0, PDiag(DiagID) << R1 << R2);
}
@@ -361,12 +382,10 @@ Sema::ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl,
}
StmtResult Sema::ActOnAttributedStmt(SourceLocation AttrLoc,
- const AttrVec &Attrs,
+ ArrayRef<const Attr*> Attrs,
Stmt *SubStmt) {
- // Fill in the declaration and return it. Variable length will require to
- // change this to AttributedStmt::Create(Context, ....);
- // and probably using ArrayRef
- AttributedStmt *LS = new (Context) AttributedStmt(AttrLoc, Attrs, SubStmt);
+ // Fill in the declaration and return it.
+ AttributedStmt *LS = AttributedStmt::Create(Context, AttrLoc, Attrs, SubStmt);
return Owned(LS);
}
@@ -519,16 +538,56 @@ Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, Expr *Cond,
if (!Cond)
return StmtError();
+ class SwitchConvertDiagnoser : public ICEConvertDiagnoser {
+ Expr *Cond;
+
+ public:
+ SwitchConvertDiagnoser(Expr *Cond)
+ : ICEConvertDiagnoser(false, true), Cond(Cond) { }
+
+ virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_typecheck_statement_requires_integer) << T;
+ }
+
+ virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_switch_incomplete_class_type)
+ << T << Cond->getSourceRange();
+ }
+
+ virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return S.Diag(Loc, diag::err_switch_explicit_conversion) << T << ConvTy;
+ }
+
+ virtual DiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return S.Diag(Conv->getLocation(), diag::note_switch_conversion)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+
+ virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_switch_multiple_conversions) << T;
+ }
+
+ virtual DiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) {
+ return S.Diag(Conv->getLocation(), diag::note_switch_conversion)
+ << ConvTy->isEnumeralType() << ConvTy;
+ }
+
+ virtual DiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) {
+ return DiagnosticBuilder::getEmpty();
+ }
+ } SwitchDiagnoser(Cond);
+
CondResult
- = ConvertToIntegralOrEnumerationType(SwitchLoc, Cond,
- PDiag(diag::err_typecheck_statement_requires_integer),
- PDiag(diag::err_switch_incomplete_class_type)
- << Cond->getSourceRange(),
- PDiag(diag::err_switch_explicit_conversion),
- PDiag(diag::note_switch_conversion),
- PDiag(diag::err_switch_multiple_conversions),
- PDiag(diag::note_switch_conversion),
- PDiag(0),
+ = ConvertToIntegralOrEnumerationType(SwitchLoc, Cond, SwitchDiagnoser,
/*AllowScopedEnumerations*/ true);
if (CondResult.isInvalid()) return StmtError();
Cond = CondResult.take();
@@ -609,7 +668,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
= CondExpr->isTypeDependent() || CondExpr->isValueDependent();
unsigned CondWidth
= HasDependentValue ? 0 : Context.getIntWidth(CondTypeBeforePromotion);
- bool CondIsSigned
+ bool CondIsSigned
= CondTypeBeforePromotion->isSignedIntegerOrEnumerationType();
// Accumulate all of the case values in a vector so that we can sort them
@@ -726,8 +785,30 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
if (i != 0 && CaseVals[i].first == CaseVals[i-1].first) {
// If we have a duplicate, report it.
- Diag(CaseVals[i].second->getLHS()->getLocStart(),
- diag::err_duplicate_case) << CaseVals[i].first.toString(10);
+ // First, determine if either case value has a name
+ StringRef PrevString, CurrString;
+ Expr *PrevCase = CaseVals[i-1].second->getLHS()->IgnoreParenCasts();
+ Expr *CurrCase = CaseVals[i].second->getLHS()->IgnoreParenCasts();
+ if (DeclRefExpr *DeclRef = dyn_cast<DeclRefExpr>(PrevCase)) {
+ PrevString = DeclRef->getDecl()->getName();
+ }
+ if (DeclRefExpr *DeclRef = dyn_cast<DeclRefExpr>(CurrCase)) {
+ CurrString = DeclRef->getDecl()->getName();
+ }
+ llvm::SmallString<16> CaseValStr;
+ CaseVals[i-1].first.toString(CaseValStr);
+
+ if (PrevString == CurrString)
+ Diag(CaseVals[i].second->getLHS()->getLocStart(),
+ diag::err_duplicate_case) <<
+ (PrevString.empty() ? CaseValStr.str() : PrevString);
+ else
+ Diag(CaseVals[i].second->getLHS()->getLocStart(),
+ diag::err_duplicate_case_differing_expr) <<
+ (PrevString.empty() ? CaseValStr.str() : PrevString) <<
+ (CurrString.empty() ? CaseValStr.str() : CurrString) <<
+ CaseValStr;
+
Diag(CaseVals[i-1].second->getLHS()->getLocStart(),
diag::note_duplicate_case_prev);
// FIXME: We really want to remove the bogus case stmt from the
@@ -904,7 +985,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
<< CondTypeBeforePromotion;
}
- llvm::APSInt Hi =
+ llvm::APSInt Hi =
RI->second->getRHS()->EvaluateKnownConstInt(Context);
AdjustAPSInt(Hi, CondWidth, CondIsSigned);
while (EI != EIend && EI->first < Hi)
@@ -952,12 +1033,12 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
switch (UnhandledNames.size()) {
case 0: break;
case 1:
- Diag(CondExpr->getExprLoc(), TheDefaultStmt
+ Diag(CondExpr->getExprLoc(), TheDefaultStmt
? diag::warn_def_missing_case1 : diag::warn_missing_case1)
<< UnhandledNames[0];
break;
case 2:
- Diag(CondExpr->getExprLoc(), TheDefaultStmt
+ Diag(CondExpr->getExprLoc(), TheDefaultStmt
? diag::warn_def_missing_case2 : diag::warn_missing_case2)
<< UnhandledNames[0] << UnhandledNames[1];
break;
@@ -990,6 +1071,55 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
return Owned(SS);
}
+void
+Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType,
+ Expr *SrcExpr) {
+ unsigned DIAG = diag::warn_not_in_enum_assignement;
+ if (Diags.getDiagnosticLevel(DIAG, SrcExpr->getExprLoc())
+ == DiagnosticsEngine::Ignored)
+ return;
+
+ if (const EnumType *ET = DstType->getAs<EnumType>())
+ if (!Context.hasSameType(SrcType, DstType) &&
+ SrcType->isIntegerType()) {
+ if (!SrcExpr->isTypeDependent() && !SrcExpr->isValueDependent() &&
+ SrcExpr->isIntegerConstantExpr(Context)) {
+ // Get the bitwidth of the enum value before promotions.
+ unsigned DstWith = Context.getIntWidth(DstType);
+ bool DstIsSigned = DstType->isSignedIntegerOrEnumerationType();
+
+ llvm::APSInt RhsVal = SrcExpr->EvaluateKnownConstInt(Context);
+ const EnumDecl *ED = ET->getDecl();
+ typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64>
+ EnumValsTy;
+ EnumValsTy EnumVals;
+
+ // Gather all enum values, set their type and sort them,
+ // allowing easier comparison with rhs constant.
+ for (EnumDecl::enumerator_iterator EDI = ED->enumerator_begin();
+ EDI != ED->enumerator_end(); ++EDI) {
+ llvm::APSInt Val = EDI->getInitVal();
+ AdjustAPSInt(Val, DstWith, DstIsSigned);
+ EnumVals.push_back(std::make_pair(Val, *EDI));
+ }
+ if (EnumVals.empty())
+ return;
+ std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals);
+ EnumValsTy::iterator EIend =
+ std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals);
+
+ // See which case values aren't in enum.
+ EnumValsTy::const_iterator EI = EnumVals.begin();
+ while (EI != EIend && EI->first < RhsVal)
+ EI++;
+ if (EI == EIend || EI->first != RhsVal) {
+ Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignement)
+ << DstType;
+ }
+ }
+ }
+}
+
StmtResult
Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond,
Decl *CondVar, Stmt *Body) {
@@ -1037,6 +1167,215 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body,
return Owned(new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen));
}
+namespace {
+ // This visitor will traverse a conditional statement and store all
+ // the evaluated decls into a vector. Simple is set to true if none
+ // of the excluded constructs are used.
+ class DeclExtractor : public EvaluatedExprVisitor<DeclExtractor> {
+ llvm::SmallPtrSet<VarDecl*, 8> &Decls;
+ llvm::SmallVector<SourceRange, 10> &Ranges;
+ bool Simple;
+public:
+ typedef EvaluatedExprVisitor<DeclExtractor> Inherited;
+
+ DeclExtractor(Sema &S, llvm::SmallPtrSet<VarDecl*, 8> &Decls,
+ llvm::SmallVector<SourceRange, 10> &Ranges) :
+ Inherited(S.Context),
+ Decls(Decls),
+ Ranges(Ranges),
+ Simple(true) {}
+
+ bool isSimple() { return Simple; }
+
+ // Replaces the method in EvaluatedExprVisitor.
+ void VisitMemberExpr(MemberExpr* E) {
+ Simple = false;
+ }
+
+ // Any Stmt not whitelisted will cause the condition to be marked complex.
+ void VisitStmt(Stmt *S) {
+ Simple = false;
+ }
+
+ void VisitBinaryOperator(BinaryOperator *E) {
+ Visit(E->getLHS());
+ Visit(E->getRHS());
+ }
+
+ void VisitCastExpr(CastExpr *E) {
+ Visit(E->getSubExpr());
+ }
+
+ void VisitUnaryOperator(UnaryOperator *E) {
+ // Skip checking conditionals with derefernces.
+ if (E->getOpcode() == UO_Deref)
+ Simple = false;
+ else
+ Visit(E->getSubExpr());
+ }
+
+ void VisitConditionalOperator(ConditionalOperator *E) {
+ Visit(E->getCond());
+ Visit(E->getTrueExpr());
+ Visit(E->getFalseExpr());
+ }
+
+ void VisitParenExpr(ParenExpr *E) {
+ Visit(E->getSubExpr());
+ }
+
+ void VisitBinaryConditionalOperator(BinaryConditionalOperator *E) {
+ Visit(E->getOpaqueValue()->getSourceExpr());
+ Visit(E->getFalseExpr());
+ }
+
+ void VisitIntegerLiteral(IntegerLiteral *E) { }
+ void VisitFloatingLiteral(FloatingLiteral *E) { }
+ void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { }
+ void VisitCharacterLiteral(CharacterLiteral *E) { }
+ void VisitGNUNullExpr(GNUNullExpr *E) { }
+ void VisitImaginaryLiteral(ImaginaryLiteral *E) { }
+
+ void VisitDeclRefExpr(DeclRefExpr *E) {
+ VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (!VD) return;
+
+ Ranges.push_back(E->getSourceRange());
+
+ Decls.insert(VD);
+ }
+
+ }; // end class DeclExtractor
+
+ // DeclMatcher checks to see if the decls are used in a non-evauluated
+ // context.
+ class DeclMatcher : public EvaluatedExprVisitor<DeclMatcher> {
+ llvm::SmallPtrSet<VarDecl*, 8> &Decls;
+ bool FoundDecl;
+
+public:
+ typedef EvaluatedExprVisitor<DeclMatcher> Inherited;
+
+ DeclMatcher(Sema &S, llvm::SmallPtrSet<VarDecl*, 8> &Decls, Stmt *Statement) :
+ Inherited(S.Context), Decls(Decls), FoundDecl(false) {
+ if (!Statement) return;
+
+ Visit(Statement);
+ }
+
+ void VisitReturnStmt(ReturnStmt *S) {
+ FoundDecl = true;
+ }
+
+ void VisitBreakStmt(BreakStmt *S) {
+ FoundDecl = true;
+ }
+
+ void VisitGotoStmt(GotoStmt *S) {
+ FoundDecl = true;
+ }
+
+ void VisitCastExpr(CastExpr *E) {
+ if (E->getCastKind() == CK_LValueToRValue)
+ CheckLValueToRValueCast(E->getSubExpr());
+ else
+ Visit(E->getSubExpr());
+ }
+
+ void CheckLValueToRValueCast(Expr *E) {
+ E = E->IgnoreParenImpCasts();
+
+ if (isa<DeclRefExpr>(E)) {
+ return;
+ }
+
+ if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ Visit(CO->getCond());
+ CheckLValueToRValueCast(CO->getTrueExpr());
+ CheckLValueToRValueCast(CO->getFalseExpr());
+ return;
+ }
+
+ if (BinaryConditionalOperator *BCO =
+ dyn_cast<BinaryConditionalOperator>(E)) {
+ CheckLValueToRValueCast(BCO->getOpaqueValue()->getSourceExpr());
+ CheckLValueToRValueCast(BCO->getFalseExpr());
+ return;
+ }
+
+ Visit(E);
+ }
+
+ void VisitDeclRefExpr(DeclRefExpr *E) {
+ if (VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
+ if (Decls.count(VD))
+ FoundDecl = true;
+ }
+
+ bool FoundDeclInUse() { return FoundDecl; }
+
+ }; // end class DeclMatcher
+
+ void CheckForLoopConditionalStatement(Sema &S, Expr *Second,
+ Expr *Third, Stmt *Body) {
+ // Condition is empty
+ if (!Second) return;
+
+ if (S.Diags.getDiagnosticLevel(diag::warn_variables_not_in_loop_body,
+ Second->getLocStart())
+ == DiagnosticsEngine::Ignored)
+ return;
+
+ PartialDiagnostic PDiag = S.PDiag(diag::warn_variables_not_in_loop_body);
+ llvm::SmallPtrSet<VarDecl*, 8> Decls;
+ llvm::SmallVector<SourceRange, 10> Ranges;
+ DeclExtractor DE(S, Decls, Ranges);
+ DE.Visit(Second);
+
+ // Don't analyze complex conditionals.
+ if (!DE.isSimple()) return;
+
+ // No decls found.
+ if (Decls.size() == 0) return;
+
+ // Don't warn on volatile, static, or global variables.
+ for (llvm::SmallPtrSet<VarDecl*, 8>::iterator I = Decls.begin(),
+ E = Decls.end();
+ I != E; ++I)
+ if ((*I)->getType().isVolatileQualified() ||
+ (*I)->hasGlobalStorage()) return;
+
+ if (DeclMatcher(S, Decls, Second).FoundDeclInUse() ||
+ DeclMatcher(S, Decls, Third).FoundDeclInUse() ||
+ DeclMatcher(S, Decls, Body).FoundDeclInUse())
+ return;
+
+ // Load decl names into diagnostic.
+ if (Decls.size() > 4)
+ PDiag << 0;
+ else {
+ PDiag << Decls.size();
+ for (llvm::SmallPtrSet<VarDecl*, 8>::iterator I = Decls.begin(),
+ E = Decls.end();
+ I != E; ++I)
+ PDiag << (*I)->getDeclName();
+ }
+
+ // Load SourceRanges into diagnostic if there is room.
+ // Otherwise, load the SourceRange of the conditional expression.
+ if (Ranges.size() <= PartialDiagnostic::MaxArguments)
+ for (llvm::SmallVector<SourceRange, 10>::iterator I = Ranges.begin(),
+ E = Ranges.end();
+ I != E; ++I)
+ PDiag << *I;
+ else
+ PDiag << Second->getSourceRange();
+
+ S.Diag(Ranges.begin()->getBegin(), PDiag);
+ }
+
+} // end namespace
+
StmtResult
Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
Stmt *First, FullExprArg second, Decl *secondVar,
@@ -1059,6 +1398,8 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
}
}
+ CheckForLoopConditionalStatement(*this, second.get(), third.get(), Body);
+
ExprResult SecondResult(second.release());
VarDecl *ConditionVar = 0;
if (secondVar) {
@@ -1103,8 +1444,9 @@ StmtResult Sema::ActOnForEachLValueExpr(Expr *E) {
}
ExprResult
-Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
- assert(collection);
+Sema::CheckObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
+ if (!collection)
+ return ExprError();
// Bail out early if we've got a type-dependent expression.
if (collection->isTypeDependent()) return Owned(collection);
@@ -1130,12 +1472,12 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
// If we have a forward-declared type, we can't do this check.
// Under ARC, it is an error not to have a forward-declared class.
- if (iface &&
+ if (iface &&
RequireCompleteType(forLoc, QualType(objectType, 0),
getLangOpts().ObjCAutoRefCount
- ? PDiag(diag::err_arc_collection_forward)
- << collection->getSourceRange()
- : PDiag(0))) {
+ ? diag::err_arc_collection_forward
+ : 0,
+ collection)) {
// Otherwise, if we have any useful type information, check that
// the type declares the appropriate method.
} else if (iface || !objectType->qual_empty()) {
@@ -1151,7 +1493,7 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
// If there's an interface, look in both the public and private APIs.
if (iface) {
method = iface->lookupInstanceMethod(selector);
- if (!method) method = LookupPrivateInstanceMethod(selector, iface);
+ if (!method) method = iface->lookupPrivateMethod(selector);
}
// Also check protocol qualifiers.
@@ -1175,8 +1517,12 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
StmtResult
Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
SourceLocation LParenLoc,
- Stmt *First, Expr *Second,
- SourceLocation RParenLoc, Stmt *Body) {
+ Stmt *First, Expr *collection,
+ SourceLocation RParenLoc) {
+
+ ExprResult CollectionExprResult =
+ CheckObjCForCollectionOperand(ForLoc, collection);
+
if (First) {
QualType FirstType;
if (DeclStmt *DS = dyn_cast<DeclStmt>(First)) {
@@ -1204,11 +1550,15 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
if (!FirstType->isDependentType() &&
!FirstType->isObjCObjectPointerType() &&
!FirstType->isBlockPointerType())
- Diag(ForLoc, diag::err_selector_element_type)
- << FirstType << First->getSourceRange();
+ return StmtError(Diag(ForLoc, diag::err_selector_element_type)
+ << FirstType << First->getSourceRange());
}
- return Owned(new (Context) ObjCForCollectionStmt(First, Second, Body,
+ if (CollectionExprResult.isInvalid())
+ return StmtError();
+
+ return Owned(new (Context) ObjCForCollectionStmt(First,
+ CollectionExprResult.take(), 0,
ForLoc, RParenLoc));
}
@@ -1252,7 +1602,7 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init,
// In ARC, infer lifetime.
// FIXME: ARC may want to turn this into 'const __unsafe_unretained' if
// we're doing the equivalent of fast iteration.
- if (SemaRef.getLangOpts().ObjCAutoRefCount &&
+ if (SemaRef.getLangOpts().ObjCAutoRefCount &&
SemaRef.inferObjCARCLifetime(Decl))
Decl->setInvalidDecl();
@@ -1343,6 +1693,11 @@ static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S,
}
+static bool ObjCEnumerationCollection(Expr *Collection) {
+ return !Collection->isTypeDependent()
+ && Collection->getType()->getAs<ObjCObjectPointerType>() != 0;
+}
+
/// ActOnCXXForRangeStmt - Check and build a C++0x for-range statement.
///
/// C++0x [stmt.ranged]:
@@ -1368,6 +1723,10 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
if (!First || !Range)
return StmtError();
+ if (ObjCEnumerationCollection(Range))
+ return ActOnObjCForCollectionStmt(ForLoc, LParenLoc, First, Range,
+ RParenLoc);
+
DeclStmt *DS = dyn_cast<DeclStmt>(First);
assert(DS && "first part of for range not a decl stmt");
@@ -1442,7 +1801,7 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
QualType RangeType = Range->getType();
if (RequireCompleteType(RangeLoc, RangeType,
- PDiag(diag::err_for_range_incomplete_type)))
+ diag::err_for_range_incomplete_type))
return StmtError();
// Build auto __begin = begin-expr, __end = end-expr.
@@ -1618,6 +1977,17 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc,
ColonLoc, RParenLoc));
}
+/// FinishObjCForCollectionStmt - Attach the body to a objective-C foreach
+/// statement.
+StmtResult Sema::FinishObjCForCollectionStmt(Stmt *S, Stmt *B) {
+ if (!S || !B)
+ return StmtError();
+ ObjCForCollectionStmt * ForStmt = cast<ObjCForCollectionStmt>(S);
+
+ ForStmt->setBody(B);
+ return S;
+}
+
/// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement.
/// This is a separate step from ActOnCXXForRangeStmt because analysis of the
/// body cannot be performed until after the type of the range variable is
@@ -1626,6 +1996,9 @@ StmtResult Sema::FinishCXXForRangeStmt(Stmt *S, Stmt *B) {
if (!S || !B)
return StmtError();
+ if (isa<ObjCForCollectionStmt>(S))
+ return FinishObjCForCollectionStmt(S, B);
+
CXXForRangeStmt *ForStmt = cast<CXXForRangeStmt>(S);
ForStmt->setBody(B);
@@ -1723,7 +2096,7 @@ const VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType,
// ... the expression is the name of a non-volatile automatic object
// (other than a function or catch-clause parameter)) ...
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
- if (!DR)
+ if (!DR || DR->refersToEnclosingLocal())
return 0;
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
if (!VD)
@@ -1776,8 +2149,7 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
if (AllowNRVO &&
(NRVOCandidate || getCopyElisionCandidate(ResultType, Value, true))) {
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack,
- Value->getType(), CK_LValueToRValue,
- Value, VK_XValue);
+ Value->getType(), CK_NoOp, Value, VK_XValue);
Expr *InitExpr = &AsRvalue;
InitializationKind Kind
@@ -1812,8 +2184,7 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
// Promote "AsRvalue" to the heap, since we now need this
// expression node to persist.
Value = ImplicitCastExpr::Create(Context, Value->getType(),
- CK_LValueToRValue, Value, 0,
- VK_XValue);
+ CK_NoOp, Value, 0, VK_XValue);
// Complete type-checking the initialization of the return type
// using the constructor we found.
@@ -1840,8 +2211,12 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// [expr.prim.lambda]p4 in C++11; block literals follow a superset of those
// rules which allows multiple return statements.
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
+ QualType FnRetType = CurCap->ReturnType;
+
+ // For blocks/lambdas with implicit return types, we check each return
+ // statement individually, and deduce the common return type when the block
+ // or lambda is completed.
if (CurCap->HasImplicitReturnType) {
- QualType ReturnT;
if (RetValExp && !isa<InitListExpr>(RetValExp)) {
ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp);
if (Result.isInvalid())
@@ -1849,10 +2224,10 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
RetValExp = Result.take();
if (!RetValExp->isTypeDependent())
- ReturnT = RetValExp->getType();
+ FnRetType = RetValExp->getType();
else
- ReturnT = Context.DependentTy;
- } else {
+ FnRetType = CurCap->ReturnType = Context.DependentTy;
+ } else {
if (RetValExp) {
// C++11 [expr.lambda.prim]p4 bans inferring the result from an
// initializer list, because it is not an expression (even
@@ -1861,21 +2236,14 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
<< RetValExp->getSourceRange();
}
- ReturnT = Context.VoidTy;
- }
- // We require the return types to strictly match here.
- if (!CurCap->ReturnType.isNull() &&
- !CurCap->ReturnType->isDependentType() &&
- !ReturnT->isDependentType() &&
- !Context.hasSameType(ReturnT, CurCap->ReturnType)) {
- Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible)
- << ReturnT << CurCap->ReturnType
- << (getCurLambda() != 0);
- return StmtError();
+ FnRetType = Context.VoidTy;
}
- CurCap->ReturnType = ReturnT;
+
+ // Although we'll properly infer the type of the block once it's completed,
+ // make sure we provide a return type now for better error recovery.
+ if (CurCap->ReturnType.isNull())
+ CurCap->ReturnType = FnRetType;
}
- QualType FnRetType = CurCap->ReturnType;
assert(!FnRetType.isNull());
if (BlockScopeInfo *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
@@ -1943,10 +2311,12 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
ReturnStmt *Result = new (Context) ReturnStmt(ReturnLoc, RetValExp,
NRVOCandidate);
- // If we need to check for the named return value optimization, save the
- // return statement in our scope for later processing.
- if (getLangOpts().CPlusPlus && FnRetType->isRecordType() &&
- !CurContext->isDependentContext())
+ // If we need to check for the named return value optimization,
+ // or if we need to infer the return type,
+ // save the return statement in our scope for later processing.
+ if (CurCap->HasImplicitReturnType ||
+ (getLangOpts().CPlusPlus && FnRetType->isRecordType() &&
+ !CurContext->isDependentContext()))
FunctionScopes.back()->Returns.push_back(Result);
return Owned(Result);
@@ -1957,7 +2327,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// Check for unexpanded parameter packs.
if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
return StmtError();
-
+
if (isa<CapturingScopeInfo>(getCurFunction()))
return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
@@ -1973,7 +2343,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
FnRetType = MD->getResultType();
if (MD->hasRelatedResultType() && MD->getClassInterface()) {
// In the implementation of a method with a related return type, the
- // type used to type-check the validity of return statements within the
+ // type used to type-check the validity of return statements within the
// method body is a pointer to the type of the class being implemented.
RelatedRetType = Context.getObjCInterfaceType(MD->getClassInterface());
RelatedRetType = Context.getObjCObjectPointerType(RelatedRetType);
@@ -2064,7 +2434,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// FIXME: The diagnostics here don't really describe what is happening.
InitializedEntity Entity =
InitializedEntity::InitializeTemporary(RelatedRetType);
-
+
ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(),
RetValExp);
if (Res.isInvalid()) {
@@ -2108,7 +2478,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
if (getLangOpts().CPlusPlus && FnRetType->isRecordType() &&
!CurContext->isDependentContext())
FunctionScopes.back()->Returns.push_back(Result);
-
+
return Owned(Result);
}
@@ -2147,18 +2517,17 @@ static bool CheckAsmLValue(const Expr *E, Sema &S) {
/// isOperandMentioned - Return true if the specified operand # is mentioned
/// anywhere in the decomposed asm string.
-static bool isOperandMentioned(unsigned OpNo,
+static bool isOperandMentioned(unsigned OpNo,
ArrayRef<AsmStmt::AsmStringPiece> AsmStrPieces) {
for (unsigned p = 0, e = AsmStrPieces.size(); p != e; ++p) {
const AsmStmt::AsmStringPiece &Piece = AsmStrPieces[p];
if (!Piece.isOperand()) continue;
-
+
// If this is a reference to the input and if the input was the smaller
// one, then we have to reject this asm.
if (Piece.getOperandNo() == OpNo)
return true;
}
-
return false;
}
@@ -2343,7 +2712,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple,
// then we can promote the smaller one to a larger input and the asm string
// won't notice.
bool SmallerValueMentioned = false;
-
+
// If this is a reference to the input and if the input was the smaller
// one, then we have to reject this asm.
if (isOperandMentioned(InputOpNo, Pieces)) {
@@ -2364,7 +2733,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple,
if (!SmallerValueMentioned && InputDomain != AD_Other &&
OutputConstraintInfos[TiedTo].allowsRegister())
continue;
-
+
// Either both of the operands were mentioned or the smaller one was
// mentioned. One more special case that we'll allow: if the tied input is
// integer, unmentioned, and is a constant, then we'll allow truncating it
@@ -2379,7 +2748,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple,
NS->setInputExpr(i, InputExpr);
continue;
}
-
+
Diag(InputExpr->getLocStart(),
diag::err_asm_tying_incompatible_types)
<< InTy << OutTy << OutputExpr->getSourceRange()
@@ -2390,6 +2759,213 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple,
return Owned(NS);
}
+// needSpaceAsmToken - This function handles whitespace around asm punctuation.
+// Returns true if a space should be emitted.
+static inline bool needSpaceAsmToken(Token currTok) {
+ static Token prevTok;
+
+ // No need for space after prevToken.
+ switch(prevTok.getKind()) {
+ default:
+ break;
+ case tok::l_square:
+ case tok::r_square:
+ case tok::l_brace:
+ case tok::r_brace:
+ case tok::colon:
+ prevTok = currTok;
+ return false;
+ }
+
+ // No need for a space before currToken.
+ switch(currTok.getKind()) {
+ default:
+ break;
+ case tok::l_square:
+ case tok::r_square:
+ case tok::l_brace:
+ case tok::r_brace:
+ case tok::comma:
+ case tok::colon:
+ prevTok = currTok;
+ return false;
+ }
+ prevTok = currTok;
+ return true;
+}
+
+static void patchMSAsmStrings(Sema &SemaRef, bool &IsSimple,
+ SourceLocation AsmLoc,
+ ArrayRef<Token> AsmToks,
+ ArrayRef<unsigned> LineEnds,
+ const TargetInfo &TI,
+ std::vector<std::string> &AsmStrings) {
+ assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!");
+
+ // Assume simple asm stmt until we parse a non-register identifer.
+ IsSimple = true;
+
+ for (unsigned i = 0, e = LineEnds.size(); i != e; ++i) {
+ SmallString<512> Asm;
+
+ // Check the operands.
+ for (unsigned j = (i == 0) ? 0 : LineEnds[i-1], e = LineEnds[i]; j != e; ++j) {
+
+ IdentifierInfo *II;
+ if (j == 0 || (i > 0 && j == LineEnds[i-1])) {
+ II = AsmToks[j].getIdentifierInfo();
+ Asm = II->getName().str();
+ continue;
+ }
+
+ if (needSpaceAsmToken(AsmToks[j]))
+ Asm += " ";
+
+ switch (AsmToks[j].getKind()) {
+ default:
+ //llvm_unreachable("Unknown token.");
+ break;
+ case tok::comma: Asm += ","; break;
+ case tok::colon: Asm += ":"; break;
+ case tok::l_square: Asm += "["; break;
+ case tok::r_square: Asm += "]"; break;
+ case tok::l_brace: Asm += "{"; break;
+ case tok::r_brace: Asm += "}"; break;
+ case tok::numeric_constant: {
+ SmallString<32> TokenBuf;
+ TokenBuf.resize(32);
+ bool StringInvalid = false;
+ Asm += SemaRef.PP.getSpelling(AsmToks[j], TokenBuf, &StringInvalid);
+ assert (!StringInvalid && "Expected valid string!");
+ break;
+ }
+ case tok::identifier: {
+ II = AsmToks[j].getIdentifierInfo();
+ StringRef Name = II->getName();
+
+ // Valid registers don't need modification.
+ if (TI.isValidGCCRegisterName(Name)) {
+ Asm += Name;
+ break;
+ }
+
+ // TODO: Lookup the identifier.
+ IsSimple = false;
+ }
+ } // AsmToks[i].getKind()
+ }
+ AsmStrings[i] = Asm.c_str();
+ }
+}
+
+// Build the unmodified MSAsmString.
+static std::string buildMSAsmString(Sema &SemaRef,
+ ArrayRef<Token> AsmToks,
+ ArrayRef<unsigned> LineEnds) {
+ assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!");
+ SmallString<512> Asm;
+ SmallString<512> TokenBuf;
+ TokenBuf.resize(512);
+ unsigned AsmLineNum = 0;
+ for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) {
+ const char *ThisTokBuf = &TokenBuf[0];
+ bool StringInvalid = false;
+ unsigned ThisTokLen =
+ Lexer::getSpelling(AsmToks[i], ThisTokBuf, SemaRef.getSourceManager(),
+ SemaRef.getLangOpts(), &StringInvalid);
+ if (i && (!AsmLineNum || i != LineEnds[AsmLineNum-1]) &&
+ needSpaceAsmToken(AsmToks[i]))
+ Asm += ' ';
+ Asm += StringRef(ThisTokBuf, ThisTokLen);
+ if (i + 1 == LineEnds[AsmLineNum] && i + 1 != AsmToks.size()) {
+ Asm += '\n';
+ ++AsmLineNum;
+ }
+ }
+ return Asm.c_str();
+}
+
+StmtResult Sema::ActOnMSAsmStmt(SourceLocation AsmLoc,
+ ArrayRef<Token> AsmToks,
+ ArrayRef<unsigned> LineEnds,
+ SourceLocation EndLoc) {
+ // MS-style inline assembly is not fully supported, so emit a warning.
+ Diag(AsmLoc, diag::warn_unsupported_msasm);
+ SmallVector<StringRef,4> Clobbers;
+
+ // Empty asm statements don't need to instantiate the AsmParser, etc.
+ if (AsmToks.empty()) {
+ StringRef AsmString;
+ MSAsmStmt *NS =
+ new (Context) MSAsmStmt(Context, AsmLoc, /* IsSimple */ true,
+ /* IsVolatile */ true, AsmToks, LineEnds,
+ AsmString, Clobbers, EndLoc);
+ return Owned(NS);
+ }
+
+ std::string AsmString = buildMSAsmString(*this, AsmToks, LineEnds);
+
+ bool IsSimple;
+ std::vector<std::string> PatchedAsmStrings;
+ PatchedAsmStrings.resize(LineEnds.size());
+
+ // Rewrite operands to appease the AsmParser.
+ patchMSAsmStrings(*this, IsSimple, AsmLoc, AsmToks, LineEnds,
+ Context.getTargetInfo(), PatchedAsmStrings);
+
+ // patchMSAsmStrings doesn't correctly patch non-simple asm statements.
+ if (!IsSimple) {
+ MSAsmStmt *NS =
+ new (Context) MSAsmStmt(Context, AsmLoc, /* IsSimple */ true,
+ /* IsVolatile */ true, AsmToks, LineEnds,
+ AsmString, Clobbers, EndLoc);
+ return Owned(NS);
+ }
+
+ // Initialize targets and assembly printers/parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+
+ // Get the target specific parser.
+ std::string Error;
+ const std::string &TT = Context.getTargetInfo().getTriple().getTriple();
+ const llvm::Target *TheTarget(llvm::TargetRegistry::lookupTarget(TT, Error));
+
+ OwningPtr<llvm::MCAsmInfo> MAI(TheTarget->createMCAsmInfo(TT));
+ OwningPtr<llvm::MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT));
+ OwningPtr<llvm::MCObjectFileInfo> MOFI(new llvm::MCObjectFileInfo());
+ OwningPtr<llvm::MCSubtargetInfo>
+ STI(TheTarget->createMCSubtargetInfo(TT, "", ""));
+
+ for (unsigned i = 0, e = PatchedAsmStrings.size(); i != e; ++i) {
+ llvm::SourceMgr SrcMgr;
+ llvm::MCContext Ctx(*MAI, *MRI, MOFI.get(), &SrcMgr);
+ llvm::MemoryBuffer *Buffer =
+ llvm::MemoryBuffer::getMemBuffer(PatchedAsmStrings[i], "<inline asm>");
+
+ // Tell SrcMgr about this buffer, which is what the parser will pick up.
+ SrcMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc());
+
+ OwningPtr<llvm::MCStreamer> Str;
+ OwningPtr<llvm::MCAsmParser>
+ Parser(createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI));
+ OwningPtr<llvm::MCTargetAsmParser>
+ TargetParser(TheTarget->createMCAsmParser(*STI, *Parser));
+ // Change to the Intel dialect.
+ Parser->setAssemblerDialect(1);
+ Parser->setTargetParser(*TargetParser.get());
+
+ // TODO: Start parsing.
+ }
+
+ MSAsmStmt *NS =
+ new (Context) MSAsmStmt(Context, AsmLoc, IsSimple, /* IsVolatile */ true,
+ AsmToks, LineEnds, AsmString, Clobbers, EndLoc);
+
+ return Owned(NS);
+}
+
StmtResult
Sema::ActOnObjCAtCatchStmt(SourceLocation AtLoc,
SourceLocation RParen, Decl *Parm,
@@ -2420,15 +2996,13 @@ Sema::ActOnObjCAtTryStmt(SourceLocation AtLoc, Stmt *Try,
Finally));
}
-StmtResult Sema::BuildObjCAtThrowStmt(SourceLocation AtLoc,
- Expr *Throw) {
+StmtResult Sema::BuildObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw) {
if (Throw) {
- Throw = MaybeCreateExprWithCleanups(Throw);
ExprResult Result = DefaultLvalueConversion(Throw);
if (Result.isInvalid())
return StmtError();
- Throw = Result.take();
+ Throw = MaybeCreateExprWithCleanups(Result.take());
QualType ThrowType = Throw->getType();
// Make sure the expression type is an ObjC pointer or "void *".
if (!ThrowType->isDependentType() &&
@@ -2458,7 +3032,6 @@ Sema::ActOnObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw,
if (!AtCatchParent)
return StmtError(Diag(AtLoc, diag::error_rethrow_used_outside_catch));
}
-
return BuildObjCAtThrowStmt(AtLoc, Throw);
}
@@ -2646,17 +3219,17 @@ StmtResult Sema::BuildMSDependentExistsStmt(SourceLocation KeywordLoc,
Stmt *Nested)
{
return new (Context) MSDependentExistsStmt(KeywordLoc, IsIfExists,
- QualifierLoc, NameInfo,
+ QualifierLoc, NameInfo,
cast<CompoundStmt>(Nested));
}
-StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc,
+StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
- CXXScopeSpec &SS,
+ CXXScopeSpec &SS,
UnqualifiedId &Name,
Stmt *Nested) {
- return BuildMSDependentExistsStmt(KeywordLoc, IsIfExists,
+ return BuildMSDependentExistsStmt(KeywordLoc, IsIfExists,
SS.getWithLocInContext(Context),
GetNameFromUnqualifiedId(Name),
Nested);
diff --git a/lib/Sema/SemaStmtAttr.cpp b/lib/Sema/SemaStmtAttr.cpp
index 21c3297..3c15b7a 100644
--- a/lib/Sema/SemaStmtAttr.cpp
+++ b/lib/Sema/SemaStmtAttr.cpp
@@ -15,29 +15,55 @@
#include "TargetAttributesSema.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/Lookup.h"
+#include "clang/Sema/ScopeInfo.h"
#include "llvm/ADT/StringExtras.h"
+
using namespace clang;
using namespace sema;
+static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A,
+ SourceRange Range) {
+ if (!isa<NullStmt>(St)) {
+ S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
+ << St->getLocStart();
+ if (isa<SwitchCase>(St)) {
+ SourceLocation L = Lexer::getLocForEndOfToken(Range.getEnd(), 0,
+ S.getSourceManager(), S.getLangOpts());
+ S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
+ << FixItHint::CreateInsertion(L, ";");
+ }
+ return 0;
+ }
+ if (S.getCurFunction()->SwitchStack.empty()) {
+ S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
+ return 0;
+ }
+ return ::new (S.Context) FallThroughAttr(A.getRange(), S.Context);
+}
+
-static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A) {
+static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
+ SourceRange Range) {
switch (A.getKind()) {
+ case AttributeList::AT_FallThrough:
+ return handleFallThroughAttr(S, St, A, Range);
default:
// if we're here, then we parsed an attribute, but didn't recognize it as a
// statement attribute => it is declaration attribute
- S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt) <<
- A.getName()->getName();
+ S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt)
+ << A.getName()->getName() << St->getLocStart();
return 0;
}
}
StmtResult Sema::ProcessStmtAttributes(Stmt *S, AttributeList *AttrList,
SourceRange Range) {
- AttrVec Attrs;
+ SmallVector<const Attr*, 8> Attrs;
for (const AttributeList* l = AttrList; l; l = l->getNext()) {
- if (Attr *a = ProcessStmtAttribute(*this, S, *l))
+ if (Attr *a = ProcessStmtAttribute(*this, S, *l, Range))
Attrs.push_back(a);
}
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 51ce2a1..c8e4501 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -354,12 +354,14 @@ void Sema::LookupTemplateName(LookupResult &Found,
return;
}
- if (S && !ObjectType.isNull() && !ObjectTypeSearchedInScope) {
- // C++ [basic.lookup.classref]p1:
+ if (S && !ObjectType.isNull() && !ObjectTypeSearchedInScope &&
+ !(getLangOpts().CPlusPlus0x && !Found.empty())) {
+ // C++03 [basic.lookup.classref]p1:
// [...] If the lookup in the class of the object expression finds a
// template, the name is also looked up in the context of the entire
// postfix-expression and [...]
//
+ // Note: C++11 does not perform this second lookup.
LookupResult FoundOuter(*this, Found.getLookupName(), Found.getNameLoc(),
LookupOrdinaryName);
LookupName(FoundOuter, S);
@@ -743,7 +745,7 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
}
/// ActOnTemplateTemplateParameter - Called when a C++ template template
-/// parameter (e.g. T in template <template <typename> class T> class array)
+/// parameter (e.g. T in template <template \<typename> class T> class array)
/// has been parsed. S is the current scope.
Decl *Sema::ActOnTemplateTemplateParameter(Scope* S,
SourceLocation TmpLoc,
@@ -865,9 +867,13 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
return true;
}
- // Find any previous declaration with this name.
+ // Find any previous declaration with this name. For a friend with no
+ // scope explicitly specified, we only look for tag declarations (per
+ // C++11 [basic.lookup.elab]p2).
DeclContext *SemanticContext;
- LookupResult Previous(*this, Name, NameLoc, LookupOrdinaryName,
+ LookupResult Previous(*this, Name, NameLoc,
+ (SS.isEmpty() && TUK == TUK_Friend)
+ ? LookupTagName : LookupOrdinaryName,
ForRedeclaration);
if (SS.isNotEmpty() && !SS.isInvalid()) {
SemanticContext = computeDeclContext(SS, true);
@@ -893,7 +899,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
Invalid = true;
} else if (TUK != TUK_Friend && TUK != TUK_Reference)
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc);
-
+
LookupQualifiedName(Previous, SemanticContext);
} else {
SemanticContext = CurContext;
@@ -948,22 +954,32 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
// declaration.
PrevDecl = PrevClassTemplate = 0;
SemanticContext = OutermostContext;
- }
- }
- if (CurContext->isDependentContext()) {
- // If this is a dependent context, we don't want to link the friend
- // class template to the template in scope, because that would perform
- // checking of the template parameter lists that can't be performed
- // until the outer context is instantiated.
- PrevDecl = PrevClassTemplate = 0;
+ // Check that the chosen semantic context doesn't already contain a
+ // declaration of this name as a non-tag type.
+ LookupResult Previous(*this, Name, NameLoc, LookupOrdinaryName,
+ ForRedeclaration);
+ DeclContext *LookupContext = SemanticContext;
+ while (LookupContext->isTransparentContext())
+ LookupContext = LookupContext->getLookupParent();
+ LookupQualifiedName(Previous, LookupContext);
+
+ if (Previous.isAmbiguous())
+ return true;
+
+ if (Previous.begin() != Previous.end())
+ PrevDecl = (*Previous.begin())->getUnderlyingDecl();
+ }
}
} else if (PrevDecl && !isDeclInScope(PrevDecl, SemanticContext, S))
PrevDecl = PrevClassTemplate = 0;
if (PrevClassTemplate) {
- // Ensure that the template parameter lists are compatible.
- if (!TemplateParameterListsAreEqual(TemplateParams,
+ // Ensure that the template parameter lists are compatible. Skip this check
+ // for a friend in a dependent context: the template parameter list itself
+ // could be dependent.
+ if (!(TUK == TUK_Friend && CurContext->isDependentContext()) &&
+ !TemplateParameterListsAreEqual(TemplateParams,
PrevClassTemplate->getTemplateParameters(),
/*Complain=*/true,
TPL_TemplateMatch))
@@ -1012,8 +1028,10 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
// Check the template parameter list of this declaration, possibly
// merging in the template parameter list from the previous class
- // template declaration.
- if (CheckTemplateParameterList(TemplateParams,
+ // template declaration. Skip this check for a friend in a dependent
+ // context, because the template parameter list might be dependent.
+ if (!(TUK == TUK_Friend && CurContext->isDependentContext()) &&
+ CheckTemplateParameterList(TemplateParams,
PrevClassTemplate? PrevClassTemplate->getTemplateParameters() : 0,
(SS.isSet() && SemanticContext &&
SemanticContext->isRecord() &&
@@ -1025,9 +1043,9 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (SS.isSet()) {
// If the name of the template was qualified, we must be defining the
// template out-of-line.
- if (!SS.isInvalid() && !Invalid && !PrevClassTemplate &&
- !(TUK == TUK_Friend && CurContext->isDependentContext())) {
- Diag(NameLoc, diag::err_member_def_does_not_match)
+ if (!SS.isInvalid() && !Invalid && !PrevClassTemplate) {
+ Diag(NameLoc, TUK == TUK_Friend ? diag::err_friend_decl_does_not_match
+ : diag::err_member_def_does_not_match)
<< Name << SemanticContext << SS.getRange();
Invalid = true;
}
@@ -1046,8 +1064,10 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
// Add alignment attributes if necessary; these attributes are checked when
// the ASTContext lays out the structure.
- AddAlignmentAttributesForRecord(NewClass);
- AddMsStructLayoutForRecord(NewClass);
+ if (TUK == TUK_Definition) {
+ AddAlignmentAttributesForRecord(NewClass);
+ AddMsStructLayoutForRecord(NewClass);
+ }
ClassTemplateDecl *NewTemplate
= ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
@@ -1084,6 +1104,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (Attr)
ProcessDeclAttributeList(S, NewClass, Attr);
+ AddPushedVisibilityAttribute(NewClass);
+
if (TUK != TUK_Friend)
PushOnScopeChains(NewTemplate, S);
else {
@@ -1116,6 +1138,11 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
NewTemplate->setInvalidDecl();
NewClass->setInvalidDecl();
}
+ if (PrevClassTemplate)
+ mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl());
+
+ ActOnDocumentableDecl(NewTemplate);
+
return NewTemplate;
}
@@ -1972,6 +1999,8 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
TemplateArgLists.addOuterTemplateArguments(0, 0);
InstantiatingTemplate Inst(*this, TemplateLoc, Template);
+ if (Inst)
+ return QualType();
CanonType = SubstType(Pattern->getUnderlyingType(),
TemplateArgLists, AliasTemplate->getLocation(),
AliasTemplate->getDeclName());
@@ -2420,6 +2449,43 @@ bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
return true;
}
+ case TemplateArgument::Expression: {
+ // We have a template type parameter but the template argument is an
+ // expression; see if maybe it is missing the "typename" keyword.
+ CXXScopeSpec SS;
+ DeclarationNameInfo NameInfo;
+
+ if (DeclRefExpr *ArgExpr = dyn_cast<DeclRefExpr>(Arg.getAsExpr())) {
+ SS.Adopt(ArgExpr->getQualifierLoc());
+ NameInfo = ArgExpr->getNameInfo();
+ } else if (DependentScopeDeclRefExpr *ArgExpr =
+ dyn_cast<DependentScopeDeclRefExpr>(Arg.getAsExpr())) {
+ SS.Adopt(ArgExpr->getQualifierLoc());
+ NameInfo = ArgExpr->getNameInfo();
+ } else if (CXXDependentScopeMemberExpr *ArgExpr =
+ dyn_cast<CXXDependentScopeMemberExpr>(Arg.getAsExpr())) {
+ if (ArgExpr->isImplicitAccess()) {
+ SS.Adopt(ArgExpr->getQualifierLoc());
+ NameInfo = ArgExpr->getMemberNameInfo();
+ }
+ }
+
+ if (NameInfo.getName().isIdentifier()) {
+ LookupResult Result(*this, NameInfo, LookupOrdinaryName);
+ LookupParsedName(Result, CurScope, &SS);
+
+ if (Result.getAsSingle<TypeDecl>() ||
+ Result.getResultKind() ==
+ LookupResult::NotFoundInCurrentInstantiation) {
+ // FIXME: Add a FixIt and fix up the template argument for recovery.
+ SourceLocation Loc = AL.getSourceRange().getBegin();
+ Diag(Loc, diag::err_template_arg_must_be_type_suggest);
+ Diag(Param->getLocation(), diag::note_template_param_here);
+ return true;
+ }
+ }
+ // fallthrough
+ }
default: {
// We have a template type parameter but the template argument
// is not a type.
@@ -2492,9 +2558,10 @@ SubstDefaultTemplateArgument(Sema &SemaRef,
= SemaRef.getTemplateInstantiationArgs(Template, &TemplateArgs);
Sema::InstantiatingTemplate Inst(SemaRef, TemplateLoc,
- Template, Converted.data(),
- Converted.size(),
+ Template, Converted,
SourceRange(TemplateLoc, RAngleLoc));
+ if (Inst)
+ return 0;
Sema::ContextRAII SavedContext(SemaRef, Template->getDeclContext());
ArgType = SemaRef.SubstType(ArgType, AllTemplateArgs,
@@ -2541,9 +2608,10 @@ SubstDefaultTemplateArgument(Sema &SemaRef,
= SemaRef.getTemplateInstantiationArgs(Template, &TemplateArgs);
Sema::InstantiatingTemplate Inst(SemaRef, TemplateLoc,
- Template, Converted.data(),
- Converted.size(),
+ Template, Converted,
SourceRange(TemplateLoc, RAngleLoc));
+ if (Inst)
+ return ExprError();
Sema::ContextRAII SavedContext(SemaRef, Template->getDeclContext());
EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated);
@@ -2590,9 +2658,10 @@ SubstDefaultTemplateArgument(Sema &SemaRef,
= SemaRef.getTemplateInstantiationArgs(Template, &TemplateArgs);
Sema::InstantiatingTemplate Inst(SemaRef, TemplateLoc,
- Template, Converted.data(),
- Converted.size(),
+ Template, Converted,
SourceRange(TemplateLoc, RAngleLoc));
+ if (Inst)
+ return TemplateName();
Sema::ContextRAII SavedContext(SemaRef, Template->getDeclContext());
// Substitute into the nested-name-specifier first,
@@ -2724,8 +2793,10 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
!Template->getDeclContext()->isDependentContext()) {
// Do substitution on the type of the non-type template parameter.
InstantiatingTemplate Inst(*this, TemplateLoc, Template,
- NTTP, Converted.data(), Converted.size(),
+ NTTP, Converted,
SourceRange(TemplateLoc, RAngleLoc));
+ if (Inst)
+ return true;
TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack,
Converted.data(), Converted.size());
@@ -2856,8 +2927,10 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
// Set up a template instantiation context.
LocalInstantiationScope Scope(*this);
InstantiatingTemplate Inst(*this, TemplateLoc, Template,
- TempParm, Converted.data(), Converted.size(),
+ TempParm, Converted,
SourceRange(TemplateLoc, RAngleLoc));
+ if (Inst)
+ return true;
TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack,
Converted.data(), Converted.size());
@@ -3086,9 +3159,11 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
// Introduce an instantiation record that describes where we are using
// the default template argument.
- InstantiatingTemplate Instantiating(*this, RAngleLoc, Template, *Param,
- Converted.data(), Converted.size(),
+ InstantiatingTemplate Instantiating(*this, RAngleLoc, Template,
+ *Param, Converted,
SourceRange(TemplateLoc, RAngleLoc));
+ if (Instantiating)
+ return true;
// Check the default template argument.
if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc,
@@ -3574,6 +3649,7 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S,
if (ParamType->isPointerType() || ParamType->isNullPtrType()) {
switch (isNullPointerValueTemplateArgument(S, Param, ParamType, Arg)) {
case NPV_NullPointer:
+ S.Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null);
Converted = TemplateArgument((Decl *)0);
return false;
@@ -3870,6 +3946,7 @@ static bool CheckTemplateArgumentPointerToMember(Sema &S,
case NPV_Error:
return true;
case NPV_NullPointer:
+ S.Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null);
Converted = TemplateArgument((Decl *)0);
return false;
case NPV_NotNullPointer:
@@ -4066,7 +4143,8 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
IntegerType = Enum->getDecl()->getIntegerType();
Value = Value.extOrTrunc(Context.getTypeSize(IntegerType));
- Converted = TemplateArgument(Value, Context.getCanonicalType(ParamType));
+ Converted = TemplateArgument(Context, Value,
+ Context.getCanonicalType(ParamType));
return ArgResult;
}
@@ -4093,8 +4171,20 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
Diag(Param->getLocation(), diag::note_template_param_here);
return ExprError();
} else if (!Arg->isValueDependent()) {
- Arg = VerifyIntegerConstantExpression(Arg, &Value,
- PDiag(diag::err_template_arg_not_ice) << ArgType, false).take();
+ class TmplArgICEDiagnoser : public VerifyICEDiagnoser {
+ QualType T;
+
+ public:
+ TmplArgICEDiagnoser(QualType T) : T(T) { }
+
+ virtual void diagnoseNotICE(Sema &S, SourceLocation Loc,
+ SourceRange SR) {
+ S.Diag(Loc, diag::err_template_arg_not_ice) << T << SR;
+ }
+ } Diagnoser(ArgType);
+
+ Arg = VerifyIntegerConstantExpression(Arg, &Value, Diagnoser,
+ false).take();
if (!Arg)
return ExprError();
}
@@ -4180,7 +4270,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
}
}
- Converted = TemplateArgument(Value,
+ Converted = TemplateArgument(Context, Value,
ParamType->isEnumeralType()
? Context.getCanonicalType(ParamType)
: IntegerType);
@@ -4305,6 +4395,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ExprError();
case NPV_NullPointer:
+ Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null);
Converted = TemplateArgument((Decl *)0);
return Owned(Arg);;
}
@@ -4497,13 +4588,13 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
Kind = CharacterLiteral::Ascii;
return Owned(new (Context) CharacterLiteral(
- Arg.getAsIntegral()->getZExtValue(),
+ Arg.getAsIntegral().getZExtValue(),
Kind, T, Loc));
}
if (T->isBooleanType())
return Owned(new (Context) CXXBoolLiteralExpr(
- Arg.getAsIntegral()->getBoolValue(),
+ Arg.getAsIntegral().getBoolValue(),
T, Loc));
if (T->isNullPtrType())
@@ -4518,7 +4609,7 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
else
BT = T;
- Expr *E = IntegerLiteral::Create(Context, *Arg.getAsIntegral(), BT, Loc);
+ Expr *E = IntegerLiteral::Create(Context, Arg.getAsIntegral(), BT, Loc);
if (T->isEnumeralType()) {
// FIXME: This is a hack. We need a better way to handle substituted
// non-type template parameters.
@@ -5041,7 +5132,7 @@ static bool CheckNonTypeClassTemplatePartialSpecializationArgs(Sema &S,
/// \param TemplateParams the template parameters of the primary class
/// template.
///
-/// \param TemplateArg the template arguments of the class template
+/// \param TemplateArgs the template arguments of the class template
/// partial specialization.
///
/// \returns true if there was an error, false otherwise.
@@ -5481,7 +5572,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
Decl *Sema::ActOnTemplateDeclarator(Scope *S,
MultiTemplateParamsArg TemplateParameterLists,
Declarator &D) {
- return HandleDeclarator(S, D, move(TemplateParameterLists));
+ Decl *NewDecl = HandleDeclarator(S, D, move(TemplateParameterLists));
+ ActOnDocumentableDecl(NewDecl);
+ return NewDecl;
}
Decl *Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope,
@@ -5719,14 +5812,17 @@ Sema::CheckSpecializationInstantiationRedecl(SourceLocation NewLoc,
}
/// \brief Perform semantic analysis for the given dependent function
-/// template specialization. The only possible way to get a dependent
-/// function template specialization is with a friend declaration,
-/// like so:
+/// template specialization.
+///
+/// The only possible way to get a dependent function template specialization
+/// is with a friend declaration, like so:
///
-/// template <class T> void foo(T);
-/// template <class T> class A {
+/// \code
+/// template \<class T> void foo(T);
+/// template \<class T> class A {
/// friend void foo<>(T);
/// };
+/// \endcode
///
/// There really isn't any useful analysis we can do here, so we
/// just store the information.
@@ -6890,6 +6986,42 @@ Sema::ActOnTypenameType(Scope *S,
}
+/// Determine whether this failed name lookup should be treated as being
+/// disabled by a usage of std::enable_if.
+static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II,
+ SourceRange &CondRange) {
+ // We must be looking for a ::type...
+ if (!II.isStr("type"))
+ return false;
+
+ // ... within an explicitly-written template specialization...
+ if (!NNS || !NNS.getNestedNameSpecifier()->getAsType())
+ return false;
+ TypeLoc EnableIfTy = NNS.getTypeLoc();
+ TemplateSpecializationTypeLoc *EnableIfTSTLoc =
+ dyn_cast<TemplateSpecializationTypeLoc>(&EnableIfTy);
+ if (!EnableIfTSTLoc || EnableIfTSTLoc->getNumArgs() == 0)
+ return false;
+ const TemplateSpecializationType *EnableIfTST =
+ cast<TemplateSpecializationType>(EnableIfTSTLoc->getTypePtr());
+
+ // ... which names a complete class template declaration...
+ const TemplateDecl *EnableIfDecl =
+ EnableIfTST->getTemplateName().getAsTemplateDecl();
+ if (!EnableIfDecl || EnableIfTST->isIncompleteType())
+ return false;
+
+ // ... called "enable_if".
+ const IdentifierInfo *EnableIfII =
+ EnableIfDecl->getDeclName().getAsIdentifierInfo();
+ if (!EnableIfII || !EnableIfII->isStr("enable_if"))
+ return false;
+
+ // Assume the first template argument is the condition.
+ CondRange = EnableIfTSTLoc->getArgLoc(0).getSourceRange();
+ return true;
+}
+
/// \brief Build the type that describes a C++ typename specifier,
/// e.g., "typename T::type".
QualType
@@ -6926,9 +7058,19 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
unsigned DiagID = 0;
Decl *Referenced = 0;
switch (Result.getResultKind()) {
- case LookupResult::NotFound:
+ case LookupResult::NotFound: {
+ // If we're looking up 'type' within a template named 'enable_if', produce
+ // a more specific diagnostic.
+ SourceRange CondRange;
+ if (isEnableIf(QualifierLoc, II, CondRange)) {
+ Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if)
+ << Ctx << CondRange;
+ return QualType();
+ }
+
DiagID = diag::err_typename_nested_not_found;
break;
+ }
case LookupResult::FoundUnresolvedValue: {
// We found a using declaration that is a value. Most likely, the using
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index d68e464..9500ec3 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -137,8 +137,17 @@ DeduceTemplateArguments(Sema &S,
/// of a non-type template parameter, return the declaration of that
/// non-type template parameter.
static NonTypeTemplateParmDecl *getDeducedParameterFromExpr(Expr *E) {
- if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E))
- E = IC->getSubExpr();
+ // If we are within an alias template, the expression may have undergone
+ // any number of parameter substitutions already.
+ while (1) {
+ if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E))
+ E = IC->getSubExpr();
+ else if (SubstNonTypeTemplateParmExpr *Subst =
+ dyn_cast<SubstNonTypeTemplateParmExpr>(E))
+ E = Subst->getReplacement();
+ else
+ break;
+ }
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl());
@@ -193,7 +202,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.getKind() == TemplateArgument::Expression ||
Y.getKind() == TemplateArgument::Declaration ||
(Y.getKind() == TemplateArgument::Integral &&
- hasSameExtendedValue(*X.getAsIntegral(), *Y.getAsIntegral())))
+ hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
return DeducedTemplateArgument(X,
X.wasDeducedFromArrayBound() &&
Y.wasDeducedFromArrayBound());
@@ -293,7 +302,8 @@ DeduceNonTypeTemplateArgument(Sema &S,
assert(NTTP->getDepth() == 0 &&
"Cannot deduce non-type template argument with depth > 0");
- DeducedTemplateArgument NewDeduced(Value, ValueType, DeducedFromArrayBound);
+ DeducedTemplateArgument NewDeduced(S.Context, Value, ValueType,
+ DeducedFromArrayBound);
DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
Deduced[NTTP->getIndex()],
NewDeduced);
@@ -1595,7 +1605,7 @@ DeduceTemplateArguments(Sema &S,
case TemplateArgument::Integral:
if (Arg.getKind() == TemplateArgument::Integral) {
- if (hasSameExtendedValue(*Param.getAsIntegral(), *Arg.getAsIntegral()))
+ if (hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral()))
return Sema::TDK_Success;
Info.FirstArg = Param;
@@ -1618,7 +1628,7 @@ DeduceTemplateArguments(Sema &S,
= getDeducedParameterFromExpr(Param.getAsExpr())) {
if (Arg.getKind() == TemplateArgument::Integral)
return DeduceNonTypeTemplateArgument(S, NTTP,
- *Arg.getAsIntegral(),
+ Arg.getAsIntegral(),
Arg.getIntegralType(),
/*ArrayBound=*/false,
Info, Deduced);
@@ -1867,7 +1877,7 @@ static bool isSameTemplateArg(ASTContext &Context,
Y.getAsTemplateOrTemplatePattern()).getAsVoidPointer();
case TemplateArgument::Integral:
- return *X.getAsIntegral() == *Y.getAsIntegral();
+ return X.getAsIntegral() == Y.getAsIntegral();
case TemplateArgument::Expression: {
llvm::FoldingSetNodeID XID, YID;
@@ -1898,7 +1908,7 @@ static bool isSameTemplateArg(ASTContext &Context,
///
/// \param S The semantic analysis object.
///
-/// \param The template argument we are producing template argument
+/// \param Arg The template argument we are producing template argument
/// location information for.
///
/// \param NTTPType For a declaration template argument, the type of
@@ -2171,8 +2181,9 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
TemplateArgs, Info, Deduced))
return Result;
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
InstantiatingTemplate Inst(*this, Partial->getLocation(), Partial,
- Deduced.data(), Deduced.size(), Info);
+ DeducedArgs, Info);
if (Inst)
return TDK_InstantiationDepth;
@@ -2198,7 +2209,7 @@ static bool isSimpleTemplateIdType(QualType T) {
/// \param FunctionTemplate the function template into which the explicit
/// template arguments will be substituted.
///
-/// \param ExplicitTemplateArguments the explicitly-specified template
+/// \param ExplicitTemplateArgs the explicitly-specified template
/// arguments.
///
/// \param Deduced the deduced template arguments, which will be populated
@@ -2256,8 +2267,9 @@ Sema::SubstituteExplicitTemplateArguments(
// Enter a new template instantiation context where we check the
// explicitly-specified template arguments against this function template,
// and then substitute them into the function parameter types.
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(),
- FunctionTemplate, Deduced.data(), Deduced.size(),
+ FunctionTemplate, DeducedArgs,
ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution,
Info);
if (Inst)
@@ -2424,6 +2436,16 @@ CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg,
Qualifiers AQuals = A.getQualifiers();
Qualifiers DeducedAQuals = DeducedA.getQualifiers();
+
+ // Under Objective-C++ ARC, the deduced type may have implicitly been
+ // given strong lifetime. If so, update the original qualifiers to
+ // include this strong lifetime.
+ if (S.getLangOpts().ObjCAutoRefCount &&
+ DeducedAQuals.getObjCLifetime() == Qualifiers::OCL_Strong &&
+ AQuals.getObjCLifetime() == Qualifiers::OCL_None) {
+ AQuals.setObjCLifetime(Qualifiers::OCL_Strong);
+ }
+
if (AQuals == DeducedAQuals) {
// Qualifiers match; there's nothing to do.
} else if (!DeducedAQuals.compatiblyIncludes(AQuals)) {
@@ -2502,8 +2524,9 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
// Enter a new template instantiation context while we instantiate the
// actual function declaration.
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(),
- FunctionTemplate, Deduced.data(), Deduced.size(),
+ FunctionTemplate, DeducedArgs,
ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution,
Info);
if (Inst)
@@ -2825,9 +2848,7 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S,
QualType PointeeType = ParamRefType->getPointeeType();
// If the argument has incomplete array type, try to complete it's type.
- if (ArgType->isIncompleteArrayType() &&
- !S.RequireCompleteExprType(Arg, S.PDiag(),
- std::make_pair(SourceLocation(), S.PDiag())))
+ if (ArgType->isIncompleteArrayType() && !S.RequireCompleteExprType(Arg, 0))
ArgType = Arg->getType();
// [C++0x] If P is an rvalue reference to a cv-unqualified
@@ -2966,7 +2987,7 @@ DeduceTemplateArgumentByListElement(Sema &S,
/// \param FunctionTemplate the function template for which we are performing
/// template argument deduction.
///
-/// \param ExplicitTemplateArguments the explicit template arguments provided
+/// \param ExplicitTemplateArgs the explicit template arguments provided
/// for this call.
///
/// \param Args the function call arguments
@@ -3226,7 +3247,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
/// \param FunctionTemplate the function template for which we are performing
/// template argument deduction.
///
-/// \param ExplicitTemplateArguments the explicitly-specified template
+/// \param ExplicitTemplateArgs the explicitly-specified template
/// arguments.
///
/// \param ArgFunctionType the function type that will be used as the
@@ -3409,7 +3430,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
/// \param FunctionTemplate the function template for which we are performing
/// template argument deduction.
///
-/// \param ExplicitTemplateArguments the explicitly-specified template
+/// \param ExplicitTemplateArgs the explicitly-specified template
/// arguments.
///
/// \param Specialization if template argument deduction was successful,
@@ -3465,6 +3486,41 @@ namespace {
return E;
}
};
+
+ /// Determine whether the specified type (which contains an 'auto' type
+ /// specifier) is dependent. This is not trivial, because the 'auto' specifier
+ /// itself claims to be type-dependent.
+ bool isDependentAutoType(QualType Ty) {
+ while (1) {
+ QualType Pointee = Ty->getPointeeType();
+ if (!Pointee.isNull()) {
+ Ty = Pointee;
+ } else if (const MemberPointerType *MPT = Ty->getAs<MemberPointerType>()){
+ if (MPT->getClass()->isDependentType())
+ return true;
+ Ty = MPT->getPointeeType();
+ } else if (const FunctionProtoType *FPT = Ty->getAs<FunctionProtoType>()){
+ for (FunctionProtoType::arg_type_iterator I = FPT->arg_type_begin(),
+ E = FPT->arg_type_end();
+ I != E; ++I)
+ if ((*I)->isDependentType())
+ return true;
+ Ty = FPT->getResultType();
+ } else if (Ty->isDependentSizedArrayType()) {
+ return true;
+ } else if (const ArrayType *AT = Ty->getAsArrayTypeUnsafe()) {
+ Ty = AT->getElementType();
+ } else if (Ty->getAs<DependentSizedExtVectorType>()) {
+ return true;
+ } else if (const VectorType *VT = Ty->getAs<VectorType>()) {
+ Ty = VT->getElementType();
+ } else {
+ break;
+ }
+ }
+ assert(Ty->getAs<AutoType>() && "didn't find 'auto' in auto type");
+ return false;
+ }
}
/// \brief Deduce the type for an auto type-specifier (C++0x [dcl.spec.auto]p6)
@@ -3487,7 +3543,7 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init,
Init = result.take();
}
- if (Init->isTypeDependent()) {
+ if (Init->isTypeDependent() || isDependentAutoType(Type->getType())) {
Result = Type;
return DAR_Succeeded;
}
@@ -3518,10 +3574,10 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init,
TemplateDeductionInfo Info(Context, Loc);
- InitListExpr * InitList = dyn_cast<InitListExpr>(Init);
+ InitListExpr *InitList = dyn_cast<InitListExpr>(Init);
if (InitList) {
for (unsigned i = 0, e = InitList->getNumInits(); i < e; ++i) {
- if (DeduceTemplateArgumentByListElement(*this, &TemplateParams,
+ if (DeduceTemplateArgumentByListElement(*this, &TemplateParams,
TemplArg,
InitList->getInit(i),
Info, Deduced, TDF))
@@ -3532,7 +3588,7 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init,
FuncParam, InitType, Init,
TDF))
return DAR_Failed;
-
+
if (DeduceTemplateArgumentsByTypeMatch(*this, &TemplateParams, FuncParam,
InitType, Info, Deduced, TDF))
return DAR_Failed;
@@ -4075,8 +4131,9 @@ Sema::getMoreSpecializedPartialSpecialization(
/*PartialOrdering=*/true,
/*RefParamComparisons=*/0);
if (Better1) {
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),Deduced.end());
InstantiatingTemplate Inst(*this, PS2->getLocation(), PS2,
- Deduced.data(), Deduced.size(), Info);
+ DeducedArgs, Info);
Better1 = !::FinishTemplateArgumentDeduction(*this, PS2,
PS1->getTemplateArgs(),
Deduced, Info);
@@ -4091,8 +4148,9 @@ Sema::getMoreSpecializedPartialSpecialization(
/*PartialOrdering=*/true,
/*RefParamComparisons=*/0);
if (Better2) {
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),Deduced.end());
InstantiatingTemplate Inst(*this, PS1->getLocation(), PS1,
- Deduced.data(), Deduced.size(), Info);
+ DeducedArgs, Info);
Better2 = !::FinishTemplateArgumentDeduction(*this, PS1,
PS2->getTemplateArgs(),
Deduced, Info);
@@ -4123,9 +4181,17 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
if (const PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(E))
E = Expansion->getPattern();
- // Skip through any implicit casts we added while type-checking.
- while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
- E = ICE->getSubExpr();
+ // Skip through any implicit casts we added while type-checking, and any
+ // substitutions performed by template alias expansion.
+ while (1) {
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
+ E = ICE->getSubExpr();
+ else if (const SubstNonTypeTemplateParmExpr *Subst =
+ dyn_cast<SubstNonTypeTemplateParmExpr>(E))
+ E = Subst->getReplacement();
+ else
+ break;
+ }
// FIXME: if !OnlyDeduced, we have to walk the whole subexpression to
// find other occurrences of template parameters.
@@ -4456,13 +4522,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
}
}
-/// \brief Mark the template parameters can be deduced by the given
+/// \brief Mark which template parameters can be deduced from a given
/// template argument list.
///
/// \param TemplateArgs the template argument list from which template
/// parameters will be deduced.
///
-/// \param Deduced a bit vector whose elements will be set to \c true
+/// \param Used a bit vector whose elements will be set to \c true
/// to indicate when the corresponding template parameter will be
/// deduced.
void
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 128dc2f..c7cbc41 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -156,11 +156,11 @@ bool Sema::ActiveTemplateInstantiation::isInstantiationRecord() const {
case ExceptionSpecInstantiation:
case DefaultTemplateArgumentInstantiation:
case DefaultFunctionArgumentInstantiation:
- return true;
-
case ExplicitTemplateArgumentSubstitution:
case DeducedTemplateArgumentSubstitution:
case PriorTemplateArgumentSubstitution:
+ return true;
+
case DefaultTemplateArgumentChecking:
return false;
}
@@ -214,12 +214,11 @@ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
}
}
-Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
- SourceLocation PointOfInstantiation,
- TemplateDecl *Template,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- SourceRange InstantiationRange)
+Sema::InstantiatingTemplate::
+InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ TemplateDecl *Template,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
@@ -232,35 +231,33 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
= ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation;
Inst.PointOfInstantiation = PointOfInstantiation;
Inst.Entity = reinterpret_cast<uintptr_t>(Template);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
Inst.InstantiationRange = InstantiationRange;
SemaRef.InNonInstantiationSFINAEContext = false;
SemaRef.ActiveTemplateInstantiations.push_back(Inst);
}
}
-Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
- SourceLocation PointOfInstantiation,
- FunctionTemplateDecl *FunctionTemplate,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- ActiveTemplateInstantiation::InstantiationKind Kind,
- sema::TemplateDeductionInfo &DeductionInfo,
- SourceRange InstantiationRange)
+Sema::InstantiatingTemplate::
+InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ FunctionTemplateDecl *FunctionTemplate,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ ActiveTemplateInstantiation::InstantiationKind Kind,
+ sema::TemplateDeductionInfo &DeductionInfo,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
{
- Invalid = CheckInstantiationDepth(PointOfInstantiation,
- InstantiationRange);
+ Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange);
if (!Invalid) {
ActiveTemplateInstantiation Inst;
Inst.Kind = Kind;
Inst.PointOfInstantiation = PointOfInstantiation;
Inst.Entity = reinterpret_cast<uintptr_t>(FunctionTemplate);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
Inst.DeductionInfo = &DeductionInfo;
Inst.InstantiationRange = InstantiationRange;
SemaRef.InNonInstantiationSFINAEContext = false;
@@ -271,54 +268,49 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
}
}
-Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
- SourceLocation PointOfInstantiation,
- ClassTemplatePartialSpecializationDecl *PartialSpec,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- sema::TemplateDeductionInfo &DeductionInfo,
- SourceRange InstantiationRange)
+Sema::InstantiatingTemplate::
+InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ ClassTemplatePartialSpecializationDecl *PartialSpec,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ sema::TemplateDeductionInfo &DeductionInfo,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
{
- Invalid = false;
-
- ActiveTemplateInstantiation Inst;
- Inst.Kind = ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution;
- Inst.PointOfInstantiation = PointOfInstantiation;
- Inst.Entity = reinterpret_cast<uintptr_t>(PartialSpec);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
- Inst.DeductionInfo = &DeductionInfo;
- Inst.InstantiationRange = InstantiationRange;
- SemaRef.InNonInstantiationSFINAEContext = false;
- SemaRef.ActiveTemplateInstantiations.push_back(Inst);
-
- assert(!Inst.isInstantiationRecord());
- ++SemaRef.NonInstantiationEntries;
+ Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange);
+ if (!Invalid) {
+ ActiveTemplateInstantiation Inst;
+ Inst.Kind = ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution;
+ Inst.PointOfInstantiation = PointOfInstantiation;
+ Inst.Entity = reinterpret_cast<uintptr_t>(PartialSpec);
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
+ Inst.DeductionInfo = &DeductionInfo;
+ Inst.InstantiationRange = InstantiationRange;
+ SemaRef.InNonInstantiationSFINAEContext = false;
+ SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+ }
}
-Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
- SourceLocation PointOfInstantiation,
- ParmVarDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- SourceRange InstantiationRange)
+Sema::InstantiatingTemplate::
+InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ ParmVarDecl *Param,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
{
Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange);
-
if (!Invalid) {
ActiveTemplateInstantiation Inst;
Inst.Kind
= ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation;
Inst.PointOfInstantiation = PointOfInstantiation;
Inst.Entity = reinterpret_cast<uintptr_t>(Param);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
Inst.InstantiationRange = InstantiationRange;
SemaRef.InNonInstantiationSFINAEContext = false;
SemaRef.ActiveTemplateInstantiations.push_back(Inst);
@@ -327,66 +319,57 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef,
Sema::InstantiatingTemplate::
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- NamedDecl *Template,
- NonTypeTemplateParmDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- SourceRange InstantiationRange)
+ NamedDecl *Template, NonTypeTemplateParmDecl *Param,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
{
- Invalid = false;
-
- ActiveTemplateInstantiation Inst;
- Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution;
- Inst.PointOfInstantiation = PointOfInstantiation;
- Inst.Template = Template;
- Inst.Entity = reinterpret_cast<uintptr_t>(Param);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
- Inst.InstantiationRange = InstantiationRange;
- SemaRef.InNonInstantiationSFINAEContext = false;
- SemaRef.ActiveTemplateInstantiations.push_back(Inst);
-
- assert(!Inst.isInstantiationRecord());
- ++SemaRef.NonInstantiationEntries;
+ Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange);
+ if (!Invalid) {
+ ActiveTemplateInstantiation Inst;
+ Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution;
+ Inst.PointOfInstantiation = PointOfInstantiation;
+ Inst.Template = Template;
+ Inst.Entity = reinterpret_cast<uintptr_t>(Param);
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
+ Inst.InstantiationRange = InstantiationRange;
+ SemaRef.InNonInstantiationSFINAEContext = false;
+ SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+ }
}
Sema::InstantiatingTemplate::
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- NamedDecl *Template,
- TemplateTemplateParmDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- SourceRange InstantiationRange)
+ NamedDecl *Template, TemplateTemplateParmDecl *Param,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
{
- Invalid = false;
- ActiveTemplateInstantiation Inst;
- Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution;
- Inst.PointOfInstantiation = PointOfInstantiation;
- Inst.Template = Template;
- Inst.Entity = reinterpret_cast<uintptr_t>(Param);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
- Inst.InstantiationRange = InstantiationRange;
- SemaRef.InNonInstantiationSFINAEContext = false;
- SemaRef.ActiveTemplateInstantiations.push_back(Inst);
-
- assert(!Inst.isInstantiationRecord());
- ++SemaRef.NonInstantiationEntries;
+ Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange);
+ if (!Invalid) {
+ ActiveTemplateInstantiation Inst;
+ Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution;
+ Inst.PointOfInstantiation = PointOfInstantiation;
+ Inst.Template = Template;
+ Inst.Entity = reinterpret_cast<uintptr_t>(Param);
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
+ Inst.InstantiationRange = InstantiationRange;
+ SemaRef.InNonInstantiationSFINAEContext = false;
+ SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+ }
}
Sema::InstantiatingTemplate::
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- TemplateDecl *Template,
- NamedDecl *Param,
- const TemplateArgument *TemplateArgs,
- unsigned NumTemplateArgs,
- SourceRange InstantiationRange)
+ TemplateDecl *Template, NamedDecl *Param,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange InstantiationRange)
: SemaRef(SemaRef),
SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext)
@@ -398,8 +381,8 @@ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
Inst.PointOfInstantiation = PointOfInstantiation;
Inst.Template = Template;
Inst.Entity = reinterpret_cast<uintptr_t>(Param);
- Inst.TemplateArgs = TemplateArgs;
- Inst.NumTemplateArgs = NumTemplateArgs;
+ Inst.TemplateArgs = TemplateArgs.data();
+ Inst.NumTemplateArgs = TemplateArgs.size();
Inst.InstantiationRange = InstantiationRange;
SemaRef.InNonInstantiationSFINAEContext = false;
SemaRef.ActiveTemplateInstantiations.push_back(Inst);
@@ -638,8 +621,13 @@ llvm::Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
++Active)
{
switch(Active->Kind) {
- case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation:
case ActiveTemplateInstantiation::TemplateInstantiation:
+ // An instantiation of an alias template may or may not be a SFINAE
+ // context, depending on what else is on the stack.
+ if (isa<TypeAliasTemplateDecl>(reinterpret_cast<Decl *>(Active->Entity)))
+ break;
+ // Fall through.
+ case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation:
case ActiveTemplateInstantiation::ExceptionSpecInstantiation:
// This is a template instantiation, so there is no SFINAE.
return llvm::Optional<TemplateDeductionInfo *>();
@@ -850,10 +838,23 @@ namespace {
return move(Result);
}
+ ExprResult TransformLambdaExpr(LambdaExpr *E) {
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E);
+ }
+
+ ExprResult TransformLambdaScope(LambdaExpr *E,
+ CXXMethodDecl *CallOperator) {
+ CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(),
+ TSK_ImplicitInstantiation);
+ return TreeTransform<TemplateInstantiator>::
+ TransformLambdaScope(E, CallOperator);
+ }
+
private:
ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm,
SourceLocation loc,
- const TemplateArgument &arg);
+ TemplateArgument arg);
};
}
@@ -1088,7 +1089,11 @@ TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) {
unsigned Length = PredefinedExpr::ComputeName(IT, currentDecl).length();
llvm::APInt LengthI(32, Length + 1);
- QualType ResTy = getSema().Context.CharTy.withConst();
+ QualType ResTy;
+ if (IT == PredefinedExpr::LFunction)
+ ResTy = getSema().Context.WCharTy.withConst();
+ else
+ ResTy = getSema().Context.CharTy.withConst();
ResTy = getSema().Context.getConstantArrayType(ResTy, LengthI,
ArrayType::Normal, 0);
PredefinedExpr *PE =
@@ -1138,10 +1143,18 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
NonTypeTemplateParmDecl *parm,
SourceLocation loc,
- const TemplateArgument &arg) {
+ TemplateArgument arg) {
ExprResult result;
QualType type;
+ // If the argument is a pack expansion, the parameter must actually be a
+ // parameter pack, and we should substitute the pattern itself, producing
+ // an expression which contains an unexpanded parameter pack.
+ if (arg.isPackExpansion()) {
+ assert(parm->isParameterPack() && "pack expansion for non-pack");
+ arg = arg.getPackExpansionPattern();
+ }
+
// The template argument itself might be an expression, in which
// case we just return that expression.
if (arg.getKind() == TemplateArgument::Expression) {
@@ -1378,7 +1391,7 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
/// substituted. If this type is not dependent, it will be returned
/// immediately.
///
-/// \param TemplateArgs the template arguments that will be
+/// \param Args the template arguments that will be
/// substituted for the top-level template parameters within T.
///
/// \param Loc the location in the source code where this substitution
@@ -1590,6 +1603,9 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
NewParm->setUnparsedDefaultArg();
UnparsedDefaultArgInstantiations[OldParm].push_back(NewParm);
} else if (Expr *Arg = OldParm->getDefaultArg())
+ // FIXME: if we non-lazily instantiated non-dependent default args for
+ // non-dependent parameter types we could remove a bunch of duplicate
+ // conversion warnings for such arguments.
NewParm->setUninstantiatedDefaultArg(Arg);
NewParm->setHasInheritedDefaultArg(OldParm->hasInheritedDefaultArg());
@@ -1820,8 +1836,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK,
bool Complain) {
- bool Invalid = false;
-
CXXRecordDecl *PatternDef
= cast_or_null<CXXRecordDecl>(Pattern->getDefinition());
if (DiagnoseUninstantiableTemplate(*this, PointOfInstantiation, Instantiation,
@@ -1867,7 +1881,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
// Do substitution on the base class specifiers.
if (SubstBaseSpecifiers(Instantiation, Pattern, TemplateArgs))
- Invalid = true;
+ Instantiation->setInvalidDecl();
TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs);
SmallVector<Decl*, 4> Fields;
@@ -1893,7 +1907,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
continue;
if ((*Member)->isInvalidDecl()) {
- Invalid = true;
+ Instantiation->setInvalidDecl();
continue;
}
@@ -1917,14 +1931,22 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
MSInfo->setTemplateSpecializationKind(TSK_ImplicitInstantiation);
MSInfo->setPointOfInstantiation(PointOfInstantiation);
}
+ } else if (StaticAssertDecl *SA = dyn_cast<StaticAssertDecl>(NewMember)) {
+ if (SA->isFailed()) {
+ // A static_assert failed. Bail out; instantiating this
+ // class is probably not meaningful.
+ Instantiation->setInvalidDecl();
+ break;
+ }
}
if (NewMember->isInvalidDecl())
- Invalid = true;
+ Instantiation->setInvalidDecl();
} else {
// FIXME: Eventually, a NULL return will mean that one of the
- // instantiations was a semantic disaster, and we'll want to set Invalid =
- // true. For now, we expect to skip some members that we can't yet handle.
+ // instantiations was a semantic disaster, and we'll want to mark the
+ // declaration invalid.
+ // For now, we expect to skip some members that we can't yet handle.
}
}
@@ -1934,7 +1956,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
CheckCompletedCXXClass(Instantiation);
// Attach any in-class member initializers now the class is complete.
- {
+ if (!FieldsWithMemberInitializers.empty()) {
// C++11 [expr.prim.general]p4:
// Otherwise, if a member-declarator declares a non-static data member
// (9.2) of a class X, the expression this is a prvalue of type "pointer
@@ -1955,9 +1977,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Expr *Init = NewInit.take();
assert(Init && "no-argument initializer in class");
assert(!isa<ParenListExpr>(Init) && "call-style init in class");
- ActOnCXXInClassMemberInitializer(NewField,
- Init->getSourceRange().getBegin(),
- Init);
+ ActOnCXXInClassMemberInitializer(NewField, Init->getLocStart(), Init);
}
}
}
@@ -1976,8 +1996,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Instantiator.disableLateAttributeInstantiation();
LateAttrs.clear();
- if (!FieldsWithMemberInitializers.empty())
- ActOnFinishDelayedMemberInitializers(Instantiation);
+ ActOnFinishDelayedMemberInitializers(Instantiation);
if (TSK == TSK_ImplicitInstantiation) {
Instantiation->setLocation(Pattern->getLocation());
@@ -1985,9 +2004,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Instantiation->setRBraceLoc(Pattern->getRBraceLoc());
}
- if (Instantiation->isInvalidDecl())
- Invalid = true;
- else {
+ if (!Instantiation->isInvalidDecl()) {
+ // Perform any dependent diagnostics from the pattern.
+ PerformDependentDiagnostics(Pattern, TemplateArgs);
+
// Instantiate any out-of-line class template partial
// specializations now.
for (TemplateDeclInstantiator::delayed_partial_spec_iterator
@@ -1997,7 +2017,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
if (!Instantiator.InstantiateClassTemplatePartialSpecialization(
P->first,
P->second)) {
- Invalid = true;
+ Instantiation->setInvalidDecl();
break;
}
}
@@ -2006,7 +2026,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
// Exit the scope of this instantiation.
SavedContext.pop();
- if (!Invalid) {
+ if (!Instantiation->isInvalidDecl()) {
Consumer.HandleTagDeclDefinition(Instantiation);
// Always emit the vtable for an explicit instantiation definition
@@ -2015,7 +2035,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
MarkVTableUsed(PointOfInstantiation, Instantiation, true);
}
- return Invalid;
+ return Instantiation->isInvalidDecl();
}
/// \brief Instantiate the definition of an enum from a given pattern.
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c7bd99c..bdbe71d 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -79,14 +79,16 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
ExprResult Result = SubstExpr(Aligned->getAlignmentExpr(),
TemplateArgs);
if (!Result.isInvalid())
- AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs<Expr>());
+ AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs<Expr>(),
+ Aligned->getIsMSDeclSpec());
} else {
TypeSourceInfo *Result = SubstType(Aligned->getAlignmentType(),
TemplateArgs,
Aligned->getLocation(),
DeclarationName());
if (Result)
- AddAlignedAttr(Aligned->getLocation(), New, Result);
+ AddAlignedAttr(Aligned->getLocation(), New, Result,
+ Aligned->getIsMSDeclSpec());
}
continue;
}
@@ -102,7 +104,8 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
} else {
Attr *NewAttr = sema::instantiateTemplateAttribute(TmplAttr, Context,
*this, TemplateArgs);
- New->addAttr(NewAttr);
+ if (NewAttr)
+ New->addAttr(NewAttr);
}
}
}
@@ -338,9 +341,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
// We already have an initializer in the class.
} else if (D->getInit()) {
if (Var->isStaticDataMember() && !D->isOutOfLine())
- SemaRef.PushExpressionEvaluationContext(Sema::ConstantEvaluated);
+ SemaRef.PushExpressionEvaluationContext(Sema::ConstantEvaluated, D);
else
- SemaRef.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated);
+ SemaRef.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated, D);
// Instantiate the initializer.
ExprResult Init = SemaRef.SubstInitializer(D->getInit(), TemplateArgs,
@@ -429,8 +432,8 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) {
D->getLocation(),
D->isMutable(),
BitWidth,
- D->hasInClassInitializer(),
- D->getTypeSpecStartLoc(),
+ D->getInClassInitStyle(),
+ D->getInnerLocStart(),
D->getAccess(),
0);
if (!Field) {
@@ -549,12 +552,11 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
if (InstantiatedAssertExpr.isInvalid())
return 0;
- ExprResult Message(D->getMessage());
- D->getMessage();
- return SemaRef.ActOnStaticAssertDeclaration(D->getLocation(),
+ return SemaRef.BuildStaticAssertDeclaration(D->getLocation(),
InstantiatedAssertExpr.get(),
- Message.get(),
- D->getRParenLoc());
+ D->getMessage(),
+ D->getRParenLoc(),
+ D->isFailed());
}
Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
@@ -933,8 +935,6 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
if (!Instantiated)
return 0;
- Instantiated->setAccess(D->getAccess());
-
// Link the instantiated function template declaration to the function
// template from which it was instantiated.
FunctionTemplateDecl *InstTemplate
@@ -952,8 +952,12 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
InstTemplate->setInstantiatedFromMemberTemplate(D);
// Make declarations visible in the appropriate context.
- if (!isFriend)
+ if (!isFriend) {
Owner->addDecl(InstTemplate);
+ } else if (InstTemplate->getDeclContext()->isRecord() &&
+ !D->getPreviousDecl()) {
+ SemaRef.CheckFriendAccess(InstTemplate);
+ }
return InstTemplate;
}
@@ -1524,7 +1528,15 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
if (D->isPure())
SemaRef.CheckPureMethod(Method, SourceRange());
- Method->setAccess(D->getAccess());
+ // Propagate access. For a non-friend declaration, the access is
+ // whatever we're propagating from. For a friend, it should be the
+ // previous declaration we just found.
+ if (isFriend && Method->getPreviousDecl())
+ Method->setAccess(Method->getPreviousDecl()->getAccess());
+ else
+ Method->setAccess(D->getAccess());
+ if (FunctionTemplate)
+ FunctionTemplate->setAccess(Method->getAccess());
SemaRef.CheckOverrideControl(Method);
@@ -1534,18 +1546,28 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
if (D->isDeletedAsWritten())
Method->setDeletedAsWritten();
+ // If there's a function template, let our caller handle it.
if (FunctionTemplate) {
- // If there's a function template, let our caller handle it.
+ // do nothing
+
+ // Don't hide a (potentially) valid declaration with an invalid one.
} else if (Method->isInvalidDecl() && !Previous.empty()) {
- // Don't hide a (potentially) valid declaration with an invalid one.
- } else {
- NamedDecl *DeclToAdd = (TemplateParams
- ? cast<NamedDecl>(FunctionTemplate)
- : Method);
- if (isFriend)
- Record->makeDeclVisibleInContext(DeclToAdd);
- else if (!IsClassScopeSpecialization)
- Owner->addDecl(DeclToAdd);
+ // do nothing
+
+ // Otherwise, check access to friends and make them visible.
+ } else if (isFriend) {
+ // We only need to re-check access for methods which we didn't
+ // manage to match during parsing.
+ if (!D->getPreviousDecl())
+ SemaRef.CheckFriendAccess(Method);
+
+ Record->makeDeclVisibleInContext(Method);
+
+ // Otherwise, add the declaration. We don't need to do this for
+ // class-scope specializations because we'll have matched them with
+ // the appropriate template.
+ } else if (!IsClassScopeSpecialization) {
+ Owner->addDecl(Method);
}
if (D->isExplicitlyDefaulted()) {
@@ -1949,13 +1971,22 @@ Decl * TemplateDeclInstantiator
Decl *TemplateDeclInstantiator::VisitClassScopeFunctionSpecializationDecl(
ClassScopeFunctionSpecializationDecl *Decl) {
CXXMethodDecl *OldFD = Decl->getSpecialization();
- CXXMethodDecl *NewFD = cast<CXXMethodDecl>(VisitCXXMethodDecl(OldFD, 0, true));
+ CXXMethodDecl *NewFD = cast<CXXMethodDecl>(VisitCXXMethodDecl(OldFD,
+ 0, true));
LookupResult Previous(SemaRef, NewFD->getNameInfo(), Sema::LookupOrdinaryName,
Sema::ForRedeclaration);
+ TemplateArgumentListInfo TemplateArgs;
+ TemplateArgumentListInfo* TemplateArgsPtr = 0;
+ if (Decl->hasExplicitTemplateArgs()) {
+ TemplateArgs = Decl->templateArgs();
+ TemplateArgsPtr = &TemplateArgs;
+ }
+
SemaRef.LookupQualifiedName(Previous, SemaRef.CurContext);
- if (SemaRef.CheckFunctionTemplateSpecialization(NewFD, 0, Previous)) {
+ if (SemaRef.CheckFunctionTemplateSpecialization(NewFD, TemplateArgsPtr,
+ Previous)) {
NewFD->setInvalidDecl();
return NewFD;
}
@@ -2165,35 +2196,31 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
TypeLoc NewTL = NewTInfo->getTypeLoc().IgnoreParens();
FunctionProtoTypeLoc *NewProtoLoc = cast<FunctionProtoTypeLoc>(&NewTL);
assert(NewProtoLoc && "Missing prototype?");
- unsigned NewIdx = 0, NumNewParams = NewProtoLoc->getNumArgs();
+ unsigned NewIdx = 0;
for (unsigned OldIdx = 0, NumOldParams = OldProtoLoc->getNumArgs();
OldIdx != NumOldParams; ++OldIdx) {
ParmVarDecl *OldParam = OldProtoLoc->getArg(OldIdx);
- if (!OldParam->isParameterPack() ||
- // FIXME: Is this right? OldParam could expand to an empty parameter
- // pack and the next parameter could be an unexpanded parameter pack
- (NewIdx < NumNewParams &&
- NewProtoLoc->getArg(NewIdx)->isParameterPack())) {
+ LocalInstantiationScope *Scope = SemaRef.CurrentInstantiationScope;
+
+ llvm::Optional<unsigned> NumArgumentsInExpansion;
+ if (OldParam->isParameterPack())
+ NumArgumentsInExpansion =
+ SemaRef.getNumArgumentsInExpansion(OldParam->getType(),
+ TemplateArgs);
+ if (!NumArgumentsInExpansion) {
// Simple case: normal parameter, or a parameter pack that's
// instantiated to a (still-dependent) parameter pack.
ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++);
Params.push_back(NewParam);
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam,
- NewParam);
- continue;
- }
-
- // Parameter pack: make the instantiation an argument pack.
- SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(
- OldParam);
- unsigned NumArgumentsInExpansion
- = SemaRef.getNumArgumentsInExpansion(OldParam->getType(),
- TemplateArgs);
- while (NumArgumentsInExpansion--) {
- ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++);
- Params.push_back(NewParam);
- SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(OldParam,
- NewParam);
+ Scope->InstantiatedLocal(OldParam, NewParam);
+ } else {
+ // Parameter pack expansion: make the instantiation an argument pack.
+ Scope->MakeInstantiatedLocalArgPack(OldParam);
+ for (unsigned I = 0; I != *NumArgumentsInExpansion; ++I) {
+ ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++);
+ Params.push_back(NewParam);
+ Scope->InstantiatedLocalPackArg(OldParam, NewParam);
+ }
}
}
}
@@ -2237,9 +2264,11 @@ static void addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function,
// Expand the parameter pack.
Scope.MakeInstantiatedLocalArgPack(PatternParam);
- unsigned NumArgumentsInExpansion
+ llvm::Optional<unsigned> NumArgumentsInExpansion
= S.getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs);
- for (unsigned Arg = 0; Arg < NumArgumentsInExpansion; ++Arg) {
+ assert(NumArgumentsInExpansion &&
+ "should only be called when all template arguments are known");
+ for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) {
ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx);
FunctionParam->setDeclName(PatternParam->getDeclName());
Scope.InstantiatedLocalPackArg(PatternParam, FunctionParam);
@@ -2354,9 +2383,10 @@ static void InstantiateExceptionSpec(Sema &SemaRef, FunctionDecl *New,
NoexceptExpr = E.take();
if (!NoexceptExpr->isTypeDependent() &&
!NoexceptExpr->isValueDependent())
- NoexceptExpr = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr,
- 0, SemaRef.PDiag(diag::err_noexcept_needs_constant_expression),
- /*AllowFold*/ false).take();
+ NoexceptExpr
+ = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr,
+ 0, diag::err_noexcept_needs_constant_expression,
+ /*AllowFold*/ false).take();
}
}
@@ -2385,8 +2415,17 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
InstantiatingTemplate Inst(*this, PointOfInstantiation, Decl,
InstantiatingTemplate::ExceptionSpecification());
- if (Inst)
+ if (Inst) {
+ // We hit the instantiation depth limit. Clear the exception specification
+ // so that our callers don't have to cope with EST_Uninstantiated.
+ FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();
+ EPI.ExceptionSpecType = EST_None;
+ Decl->setType(Context.getFunctionType(Proto->getResultType(),
+ Proto->arg_type_begin(),
+ Proto->getNumArgs(),
+ EPI));
return;
+ }
// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
@@ -2411,7 +2450,7 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
bool
TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
FunctionDecl *Tmpl) {
- if (Tmpl->isDeletedAsWritten())
+ if (Tmpl->isDeleted())
New->setDeletedAsWritten();
// If we are performing substituting explicitly-specified template arguments
@@ -2433,7 +2472,6 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
(void) FunTmpl;
ActiveInst.Kind = ActiveInstType::TemplateInstantiation;
ActiveInst.Entity = reinterpret_cast<uintptr_t>(New);
- --SemaRef.NonInstantiationEntries;
}
}
@@ -2452,6 +2490,8 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
FunctionDecl *ExceptionSpecTemplate = Tmpl;
if (EPI.ExceptionSpecType == EST_Uninstantiated)
ExceptionSpecTemplate = EPI.ExceptionSpecTemplate;
+ assert(EPI.ExceptionSpecType != EST_Unevaluated &&
+ "instantiating implicitly-declared special member");
// Mark the function has having an uninstantiated exception specification.
const FunctionProtoType *NewProto
@@ -3238,8 +3278,8 @@ DeclContext *Sema::FindInstantiatedContext(SourceLocation Loc, DeclContext* DC,
///
/// In the instantiation of X<int>::getKind(), we need to map the
/// EnumConstantDecl for KnownValue (which refers to
-/// X<T>::<Kind>::KnownValue) to its instantiation
-/// (X<int>::<Kind>::KnownValue). InstantiateCurrentDeclRef() performs
+/// X<T>::\<Kind>\::KnownValue) to its instantiation
+/// (X<int>::\<Kind>\::KnownValue). InstantiateCurrentDeclRef() performs
/// this mapping from within the instantiation of X<int>.
NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs) {
@@ -3422,7 +3462,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
void Sema::PerformPendingInstantiations(bool LocalOnly) {
// Load pending instantiations from the external source.
if (!LocalOnly && ExternalSource) {
- SmallVector<std::pair<ValueDecl *, SourceLocation>, 4> Pending;
+ SmallVector<PendingImplicitInstantiation, 4> Pending;
ExternalSource->ReadPendingInstantiations(Pending);
PendingInstantiations.insert(PendingInstantiations.begin(),
Pending.begin(), Pending.end());
diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp
index a40100c..aece90b 100644
--- a/lib/Sema/SemaTemplateVariadic.cpp
+++ b/lib/Sema/SemaTemplateVariadic.cpp
@@ -12,6 +12,7 @@
#include "clang/Sema/Sema.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedTemplate.h"
+#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
#include "clang/AST/Expr.h"
@@ -34,10 +35,12 @@ namespace {
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded;
+ bool InLambda;
+
public:
explicit CollectUnexpandedParameterPacksVisitor(
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
- : Unexpanded(Unexpanded) { }
+ : Unexpanded(Unexpanded), InLambda(false) { }
bool shouldWalkTypesOfTypeLocs() const { return false; }
@@ -107,17 +110,17 @@ namespace {
/// \brief Suppress traversal into statements and expressions that
/// do not contain unexpanded parameter packs.
bool TraverseStmt(Stmt *S) {
- if (Expr *E = dyn_cast_or_null<Expr>(S))
- if (E->containsUnexpandedParameterPack())
- return inherited::TraverseStmt(E);
+ Expr *E = dyn_cast_or_null<Expr>(S);
+ if ((E && E->containsUnexpandedParameterPack()) || InLambda)
+ return inherited::TraverseStmt(S);
- return true;
+ return true;
}
/// \brief Suppress traversal into types that do not contain
/// unexpanded parameter packs.
bool TraverseType(QualType T) {
- if (!T.isNull() && T->containsUnexpandedParameterPack())
+ if ((!T.isNull() && T->containsUnexpandedParameterPack()) || InLambda)
return inherited::TraverseType(T);
return true;
@@ -126,8 +129,9 @@ namespace {
/// \brief Suppress traversel into types with location information
/// that do not contain unexpanded parameter packs.
bool TraverseTypeLoc(TypeLoc TL) {
- if (!TL.getType().isNull() &&
- TL.getType()->containsUnexpandedParameterPack())
+ if ((!TL.getType().isNull() &&
+ TL.getType()->containsUnexpandedParameterPack()) ||
+ InLambda)
return inherited::TraverseTypeLoc(TL);
return true;
@@ -136,10 +140,10 @@ namespace {
/// \brief Suppress traversal of non-parameter declarations, since
/// they cannot contain unexpanded parameter packs.
bool TraverseDecl(Decl *D) {
- if (D && isa<ParmVarDecl>(D))
+ if ((D && isa<ParmVarDecl>(D)) || InLambda)
return inherited::TraverseDecl(D);
- return true;
+ return true;
}
/// \brief Suppress traversal of template argument pack expansions.
@@ -157,17 +161,57 @@ namespace {
return inherited::TraverseTemplateArgumentLoc(ArgLoc);
}
+
+ /// \brief Note whether we're traversing a lambda containing an unexpanded
+ /// parameter pack. In this case, the unexpanded pack can occur anywhere,
+ /// including all the places where we normally wouldn't look. Within a
+ /// lambda, we don't propagate the 'contains unexpanded parameter pack' bit
+ /// outside an expression.
+ bool TraverseLambdaExpr(LambdaExpr *Lambda) {
+ // The ContainsUnexpandedParameterPack bit on a lambda is always correct,
+ // even if it's contained within another lambda.
+ if (!Lambda->containsUnexpandedParameterPack())
+ return true;
+
+ bool WasInLambda = InLambda;
+ InLambda = true;
+
+ // If any capture names a function parameter pack, that pack is expanded
+ // when the lambda is expanded.
+ for (LambdaExpr::capture_iterator I = Lambda->capture_begin(),
+ E = Lambda->capture_end(); I != E; ++I)
+ if (VarDecl *VD = I->getCapturedVar())
+ if (VD->isParameterPack())
+ Unexpanded.push_back(std::make_pair(VD, I->getLocation()));
+
+ inherited::TraverseLambdaExpr(Lambda);
+
+ InLambda = WasInLambda;
+ return true;
+ }
};
}
/// \brief Diagnose all of the unexpanded parameter packs in the given
/// vector.
-void
+bool
Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
UnexpandedParameterPackContext UPPC,
ArrayRef<UnexpandedParameterPack> Unexpanded) {
if (Unexpanded.empty())
- return;
+ return false;
+
+ // If we are within a lambda expression, that lambda contains an unexpanded
+ // parameter pack, and we are done.
+ // FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it
+ // later.
+ for (unsigned N = FunctionScopes.size(); N; --N) {
+ if (sema::LambdaScopeInfo *LSI =
+ dyn_cast<sema::LambdaScopeInfo>(FunctionScopes[N-1])) {
+ LSI->ContainsUnexpandedParameterPack = true;
+ return false;
+ }
+ }
SmallVector<SourceLocation, 4> Locations;
SmallVector<IdentifierInfo *, 4> Names;
@@ -200,6 +244,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
for (unsigned I = 0, N = Locations.size(); I != N; ++I)
DB << SourceRange(Locations[I]);
+ return true;
}
bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
@@ -215,8 +260,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc(
T->getTypeLoc());
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
- DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
- return true;
+ return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
@@ -230,8 +274,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(E);
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
- DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded);
- return true;
+ return DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS,
@@ -247,9 +290,8 @@ bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseNestedNameSpecifier(SS.getScopeRep());
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
- DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(),
- UPPC, Unexpanded);
- return true;
+ return DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(),
+ UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo,
@@ -284,8 +326,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseType(NameInfo.getName().getCXXNameType());
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
- DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded);
- return true;
+ return DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
@@ -299,8 +340,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseTemplateName(Template);
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
- DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
- return true;
+ return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg,
@@ -313,8 +353,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseTemplateArgumentLoc(Arg);
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
- DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded);
- return true;
+ return DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded);
}
void Sema::collectUnexpandedParameterPacks(TemplateArgument Arg,
@@ -597,12 +636,13 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc,
return false;
}
-unsigned Sema::getNumArgumentsInExpansion(QualType T,
+llvm::Optional<unsigned> Sema::getNumArgumentsInExpansion(QualType T,
const MultiLevelTemplateArgumentList &TemplateArgs) {
QualType Pattern = cast<PackExpansionType>(T)->getPattern();
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(Pattern);
+ llvm::Optional<unsigned> Result;
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
// Compute the depth and index for this parameter pack.
unsigned Depth;
@@ -621,9 +661,14 @@ unsigned Sema::getNumArgumentsInExpansion(QualType T,
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation
= CurrentInstantiationScope->findInstantiationOf(
Unexpanded[I].first.get<NamedDecl *>());
- if (Instantiation->is<DeclArgumentPack *>())
- return Instantiation->get<DeclArgumentPack *>()->size();
-
+ if (Instantiation->is<Decl*>())
+ // The pattern refers to an unexpanded pack. We're not ready to expand
+ // this pack yet.
+ return llvm::Optional<unsigned>();
+
+ unsigned Size = Instantiation->get<DeclArgumentPack *>()->size();
+ assert((!Result || *Result == Size) && "inconsistent pack sizes");
+ Result = Size;
continue;
}
@@ -631,13 +676,17 @@ unsigned Sema::getNumArgumentsInExpansion(QualType T,
}
if (Depth >= TemplateArgs.getNumLevels() ||
!TemplateArgs.hasTemplateArgument(Depth, Index))
- continue;
+ // The pattern refers to an unknown template argument. We're not ready to
+ // expand this pack yet.
+ return llvm::Optional<unsigned>();
// Determine the size of the argument pack.
- return TemplateArgs(Depth, Index).pack_size();
+ unsigned Size = TemplateArgs(Depth, Index).pack_size();
+ assert((!Result || *Result == Size) && "inconsistent pack sizes");
+ Result = Size;
}
- llvm_unreachable("No unexpanded parameter packs in type expansion.");
+ return Result;
}
bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 1400e7e..20fe036 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -35,19 +35,19 @@
using namespace clang;
/// isOmittedBlockReturnType - Return true if this declarator is missing a
-/// return type because this is a omitted return type on a block literal.
+/// return type because this is a omitted return type on a block literal.
static bool isOmittedBlockReturnType(const Declarator &D) {
if (D.getContext() != Declarator::BlockLiteralContext ||
D.getDeclSpec().hasTypeSpecifier())
return false;
-
+
if (D.getNumTypeObjects() == 0)
return true; // ^{ ... }
-
+
if (D.getNumTypeObjects() == 1 &&
D.getTypeObject(0).Kind == DeclaratorChunk::Function)
return true; // ^(int X, float Y) { ... }
-
+
return false;
}
@@ -59,12 +59,12 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
unsigned diagID = 0;
switch (attr.getKind()) {
- case AttributeList::AT_objc_gc:
+ case AttributeList::AT_ObjCGC:
diagID = diag::warn_pointer_attribute_wrong_type;
useExpansionLoc = true;
break;
- case AttributeList::AT_objc_ownership:
+ case AttributeList::AT_ObjCOwnership:
diagID = diag::warn_objc_object_attribute_wrong_type;
useExpansionLoc = true;
break;
@@ -93,19 +93,19 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
// objc_gc applies to Objective-C pointers or, otherwise, to the
// smallest available pointer type (i.e. 'void*' in 'void**').
#define OBJC_POINTER_TYPE_ATTRS_CASELIST \
- case AttributeList::AT_objc_gc: \
- case AttributeList::AT_objc_ownership
+ case AttributeList::AT_ObjCGC: \
+ case AttributeList::AT_ObjCOwnership
// Function type attributes.
#define FUNCTION_TYPE_ATTRS_CASELIST \
- case AttributeList::AT_noreturn: \
- case AttributeList::AT_cdecl: \
- case AttributeList::AT_fastcall: \
- case AttributeList::AT_stdcall: \
- case AttributeList::AT_thiscall: \
- case AttributeList::AT_pascal: \
- case AttributeList::AT_regparm: \
- case AttributeList::AT_pcs \
+ case AttributeList::AT_NoReturn: \
+ case AttributeList::AT_CDecl: \
+ case AttributeList::AT_FastCall: \
+ case AttributeList::AT_StdCall: \
+ case AttributeList::AT_ThisCall: \
+ case AttributeList::AT_Pascal: \
+ case AttributeList::AT_Regparm: \
+ case AttributeList::AT_Pcs \
namespace {
/// An object which stores processing state for the entire
@@ -284,9 +284,9 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
static bool handleObjCPointerTypeAttr(TypeProcessingState &state,
AttributeList &attr, QualType &type) {
- if (attr.getKind() == AttributeList::AT_objc_gc)
+ if (attr.getKind() == AttributeList::AT_ObjCGC)
return handleObjCGCTypeAttr(state, attr, type);
- assert(attr.getKind() == AttributeList::AT_objc_ownership);
+ assert(attr.getKind() == AttributeList::AT_ObjCOwnership);
return handleObjCOwnershipTypeAttr(state, attr, type);
}
@@ -412,7 +412,7 @@ static void distributeFunctionTypeAttr(TypeProcessingState &state,
continue;
}
}
-
+
diagnoseBadTypeAttribute(state.getSema(), attr, type);
}
@@ -505,7 +505,7 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType);
break;
- case AttributeList::AT_ns_returns_retained:
+ case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
break;
// fallthrough
@@ -554,7 +554,8 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state,
// ...and *prepend* it to the declarator.
declarator.AddInnermostTypeInfo(DeclaratorChunk::getFunction(
/*proto*/ true,
- /*variadic*/ false, SourceLocation(),
+ /*variadic*/ false,
+ /*ambiguous*/ false, SourceLocation(),
/*args*/ 0, 0,
/*type quals*/ 0,
/*ref-qualifier*/true, SourceLocation(),
@@ -573,7 +574,8 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state,
/// \brief Convert the specified declspec to the appropriate type
/// object.
-/// \param D the declarator containing the declaration specifier.
+/// \param state Specifies the declarator containing the declaration specifier
+/// to be converted, along with other associated processing state.
/// \returns The type described by the declaration specifiers. This function
/// never returns null.
static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
@@ -586,7 +588,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
SourceLocation DeclLoc = declarator.getIdentifierLoc();
if (DeclLoc.isInvalid())
DeclLoc = DS.getLocStart();
-
+
ASTContext &Context = S.Context;
QualType Result;
@@ -639,7 +641,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
Result = Context.getObjCObjectPointerType(Result);
break;
}
-
+
// If this is a missing declspec in a block literal return context, then it
// is inferred from the return statements inside the block.
// The declspec is always missing in a lambda expr context; it is either
@@ -695,7 +697,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
case DeclSpec::TSW_long: Result = Context.LongTy; break;
case DeclSpec::TSW_longlong:
Result = Context.LongLongTy;
-
+
// long long is a C99 feature.
if (!S.getLangOpts().C99)
S.Diag(DS.getTypeSpecWidthLoc(),
@@ -710,7 +712,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
case DeclSpec::TSW_long: Result = Context.UnsignedLongTy; break;
case DeclSpec::TSW_longlong:
Result = Context.UnsignedLongLongTy;
-
+
// long long is a C99 feature.
if (!S.getLangOpts().C99)
S.Diag(DS.getTypeSpecWidthLoc(),
@@ -762,10 +764,10 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
// If the type is deprecated or unavailable, diagnose it.
S.DiagnoseUseOfDecl(D, DS.getTypeSpecTypeNameLoc());
-
+
assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 &&
DS.getTypeSpecSign() == 0 && "No qualifiers on tag names!");
-
+
// TypeQuals handled by caller.
Result = Context.getTypeDeclType(D);
@@ -858,7 +860,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
Result = Context.IntTy;
declarator.setInvalidType(true);
}
- break;
+ break;
case DeclSpec::TST_auto: {
// TypeQuals handled by caller.
@@ -878,7 +880,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
Result = Context.IntTy;
declarator.setInvalidType(true);
}
- break;
+ break;
case DeclSpec::TST_error:
Result = Context.IntTy;
@@ -983,15 +985,15 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
// C90 6.5.3 constraints: "The same type qualifier shall not appear more
// than once in the same specifier-list or qualifier-list, either directly
// or via one or more typedefs."
- if (!S.getLangOpts().C99 && !S.getLangOpts().CPlusPlus
+ if (!S.getLangOpts().C99 && !S.getLangOpts().CPlusPlus
&& TypeQuals & Result.getCVRQualifiers()) {
if (TypeQuals & DeclSpec::TQ_const && Result.isConstQualified()) {
- S.Diag(DS.getConstSpecLoc(), diag::ext_duplicate_declspec)
+ S.Diag(DS.getConstSpecLoc(), diag::ext_duplicate_declspec)
<< "const";
}
if (TypeQuals & DeclSpec::TQ_volatile && Result.isVolatileQualified()) {
- S.Diag(DS.getVolatileSpecLoc(), diag::ext_duplicate_declspec)
+ S.Diag(DS.getVolatileSpecLoc(), diag::ext_duplicate_declspec)
<< "volatile";
}
@@ -1036,7 +1038,7 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc,
if (!PTy->getPointeeType()->isIncompleteOrObjectType()) {
DiagID = diag::err_typecheck_invalid_restrict_invalid_pointee;
ProblemTy = T->getAs<PointerType>()->getPointeeType();
- }
+ }
} else if (!Ty->isDependentType()) {
// FIXME: this deserves a proper diagnostic
DiagID = diag::err_typecheck_invalid_restrict_invalid_pointee;
@@ -1083,7 +1085,7 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type,
// If we are in an unevaluated context, like sizeof, skip adding a
// qualification.
- } else if (S.ExprEvalContexts.back().Context == Sema::Unevaluated) {
+ } else if (S.isUnevaluatedContext()) {
return type;
// If that failed, give an error and recover using __strong. __strong
@@ -1157,14 +1159,14 @@ QualType Sema::BuildPointerType(QualType T,
QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue,
SourceLocation Loc,
DeclarationName Entity) {
- assert(Context.getCanonicalType(T) != Context.OverloadTy &&
+ assert(Context.getCanonicalType(T) != Context.OverloadTy &&
"Unresolved overloaded function type");
-
+
// C++0x [dcl.ref]p6:
- // If a typedef (7.1.3), a type template-parameter (14.3.1), or a
- // decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a
- // type T, an attempt to create the type "lvalue reference to cv TR" creates
- // the type "lvalue reference to T", while an attempt to create the type
+ // If a typedef (7.1.3), a type template-parameter (14.3.1), or a
+ // decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a
+ // type T, an attempt to create the type "lvalue reference to cv TR" creates
+ // the type "lvalue reference to T", while an attempt to create the type
// "rvalue reference to cv TR" creates the type TR.
bool LValueRef = SpelledAsLValue || T->getAs<LValueReferenceType>();
@@ -1205,9 +1207,20 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue,
static bool isArraySizeVLA(Sema &S, Expr *ArraySize, llvm::APSInt &SizeVal) {
// If the size is an ICE, it certainly isn't a VLA. If we're in a GNU mode
// (like gnu99, but not c99) accept any evaluatable value as an extension.
- return S.VerifyIntegerConstantExpression(
- ArraySize, &SizeVal, S.PDiag(), S.LangOpts.GNUMode,
- S.PDiag(diag::ext_vla_folded_to_constant)).isInvalid();
+ class VLADiagnoser : public Sema::VerifyICEDiagnoser {
+ public:
+ VLADiagnoser() : Sema::VerifyICEDiagnoser(true) {}
+
+ virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) {
+ }
+
+ virtual void diagnoseFold(Sema &S, SourceLocation Loc, SourceRange SR) {
+ S.Diag(Loc, diag::ext_vla_folded_to_constant) << SR;
+ }
+ } Diagnoser;
+
+ return S.VerifyIntegerConstantExpression(ArraySize, &SizeVal, Diagnoser,
+ S.LangOpts.GNUMode).isInvalid();
}
@@ -1219,9 +1232,7 @@ static bool isArraySizeVLA(Sema &S, Expr *ArraySize, llvm::APSInt &SizeVal) {
///
/// \param ArraySize Expression describing the size of the array.
///
-/// \param Loc The location of the entity whose type involves this
-/// array type or, if there is no such entity, the location of the
-/// type that will have array type.
+/// \param Brackets The range from the opening '[' to the closing ']'.
///
/// \param Entity The name of the entity that involves the array
/// type, if known.
@@ -1236,25 +1247,30 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
if (getLangOpts().CPlusPlus) {
// C++ [dcl.array]p1:
// T is called the array element type; this type shall not be a reference
- // type, the (possibly cv-qualified) type void, a function type or an
+ // type, the (possibly cv-qualified) type void, a function type or an
// abstract class type.
//
+ // C++ [dcl.array]p3:
+ // When several "array of" specifications are adjacent, [...] only the
+ // first of the constant expressions that specify the bounds of the arrays
+ // may be omitted.
+ //
// Note: function types are handled in the common path with C.
if (T->isReferenceType()) {
Diag(Loc, diag::err_illegal_decl_array_of_references)
<< getPrintableNameForEntity(Entity) << T;
return QualType();
}
-
- if (T->isVoidType()) {
+
+ if (T->isVoidType() || T->isIncompleteArrayType()) {
Diag(Loc, diag::err_illegal_decl_array_incomplete_type) << T;
return QualType();
}
-
- if (RequireNonAbstractType(Brackets.getBegin(), T,
+
+ if (RequireNonAbstractType(Brackets.getBegin(), T,
diag::err_array_of_abstract_type))
return QualType();
-
+
} else {
// C99 6.7.5.2p1: If the element type is an incomplete or function type,
// reject it (e.g. void ary[7], struct foo ary[7], void ary[7]())
@@ -1350,7 +1366,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
if (ConstVal == 0) {
// GCC accepts zero sized static arrays. We allow them when
// we're not in a SFINAE context.
- Diag(ArraySize->getLocStart(),
+ Diag(ArraySize->getLocStart(),
isSFINAEContext()? diag::err_typecheck_zero_array_size
: diag::ext_typecheck_zero_array_size)
<< ArraySize->getSourceRange();
@@ -1361,9 +1377,9 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
<< ArraySize->getSourceRange();
ASM = ArrayType::Normal;
}
- } else if (!T->isDependentType() && !T->isVariablyModifiedType() &&
+ } else if (!T->isDependentType() && !T->isVariablyModifiedType() &&
!T->isIncompleteType()) {
- // Is the array too large?
+ // Is the array too large?
unsigned ActiveSizeBits
= ConstantArrayType::getNumAddressingBits(Context, T, ConstVal);
if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context))
@@ -1371,7 +1387,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
<< ConstVal.toString(10)
<< ArraySize->getSourceRange();
}
-
+
T = Context.getConstantArrayType(T, ConstVal, ASM, Quals);
}
// If this is not C99, extwarn about VLA's and C99 array size modifiers.
@@ -1379,13 +1395,13 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
if (T->isVariableArrayType()) {
// Prohibit the use of non-POD types in VLAs.
QualType BaseT = Context.getBaseElementType(T);
- if (!T->isDependentType() &&
+ if (!T->isDependentType() &&
!BaseT.isPODType(Context) &&
!BaseT->isObjCLifetimeType()) {
Diag(Loc, diag::err_vla_non_pod)
<< BaseT;
return QualType();
- }
+ }
// Prohibit the use of VLAs during template argument deduction.
else if (isSFINAEContext()) {
Diag(Loc, diag::err_vla_in_sfinae);
@@ -1480,7 +1496,7 @@ QualType Sema::BuildFunctionType(QualType T,
SourceLocation Loc, DeclarationName Entity,
FunctionType::ExtInfo Info) {
if (T->isArrayType() || T->isFunctionType()) {
- Diag(Loc, diag::err_func_returning_array_function)
+ Diag(Loc, diag::err_func_returning_array_function)
<< T->isFunctionType() << T;
return QualType();
}
@@ -1581,18 +1597,14 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class,
///
/// \param T The type to which we'll be building a block pointer.
///
-/// \param CVR The cvr-qualifiers to be applied to the block pointer type.
-///
-/// \param Loc The location of the entity whose type involves this
-/// block pointer type or, if there is no such entity, the location of the
-/// type that will have block pointer type.
+/// \param Loc The source location, used for diagnostics.
///
/// \param Entity The name of the entity that involves the block pointer
/// type, if known.
///
/// \returns A suitable block pointer type, if there are no
/// errors. Otherwise, returns a NULL type.
-QualType Sema::BuildBlockPointerType(QualType T,
+QualType Sema::BuildBlockPointerType(QualType T,
SourceLocation Loc,
DeclarationName Entity) {
if (!T->isFunctionType()) {
@@ -1688,7 +1700,7 @@ static void inferARCWriteback(TypeProcessingState &state,
// Otherwise, modify the type in-place.
Qualifiers qs;
-
+
if (declSpecType->isObjCARCImplicitlyUnretainedType())
qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone);
else
@@ -1711,7 +1723,7 @@ static void inferARCWriteback(TypeProcessingState &state,
return;
for (const AttributeList *attr = chunk.getAttrs(); attr;
attr = attr->getNext())
- if (attr->getKind() == AttributeList::AT_objc_ownership)
+ if (attr->getKind() == AttributeList::AT_ObjCOwnership)
return;
transferARCOwnershipToDeclaratorChunk(state, Qualifiers::OCL_Autoreleasing,
@@ -1786,7 +1798,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
case UnqualifiedId::IK_LiteralOperatorId:
case UnqualifiedId::IK_TemplateId:
T = ConvertDeclSpecToType(state);
-
+
if (!D.isInvalidType() && D.getDeclSpec().isTypeSpecOwned()) {
OwnedTagDecl = cast<TagDecl>(D.getDeclSpec().getRepAsDecl());
// Owned declaration is embedded in declarator.
@@ -1798,14 +1810,16 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
case UnqualifiedId::IK_ConstructorTemplateId:
case UnqualifiedId::IK_DestructorName:
// Constructors and destructors don't have return types. Use
- // "void" instead.
+ // "void" instead.
T = SemaRef.Context.VoidTy;
+ if (AttributeList *attrs = D.getDeclSpec().getAttributes().getList())
+ processTypeAttrs(state, T, true, attrs);
break;
case UnqualifiedId::IK_ConversionFunctionId:
// The result type of a conversion function is the type that it
// converts to.
- T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId,
+ T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId,
&ReturnTypeInfo);
break;
}
@@ -1890,7 +1904,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
DeclaratorChunk &DeclType = D.getTypeObject(chunkIndex);
if (DeclType.Kind == DeclaratorChunk::Function) {
const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
- if (FTI.TrailingReturnType) {
+ if (FTI.hasTrailingReturnType()) {
Error = -1;
break;
}
@@ -2029,6 +2043,102 @@ static void checkQualifiedFunction(Sema &S, QualType T,
<< getFunctionQualifiersAsString(T->castAs<FunctionProtoType>());
}
+/// Produce an approprioate diagnostic for an ambiguity between a function
+/// declarator and a C++ direct-initializer.
+static void warnAboutAmbiguousFunction(Sema &S, Declarator &D,
+ DeclaratorChunk &DeclType, QualType RT) {
+ const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
+ assert(FTI.isAmbiguous && "no direct-initializer / function ambiguity");
+
+ // If the return type is void there is no ambiguity.
+ if (RT->isVoidType())
+ return;
+
+ // An initializer for a non-class type can have at most one argument.
+ if (!RT->isRecordType() && FTI.NumArgs > 1)
+ return;
+
+ // An initializer for a reference must have exactly one argument.
+ if (RT->isReferenceType() && FTI.NumArgs != 1)
+ return;
+
+ // Only warn if this declarator is declaring a function at block scope, and
+ // doesn't have a storage class (such as 'extern') specified.
+ if (!D.isFunctionDeclarator() ||
+ D.getFunctionDefinitionKind() != FDK_Declaration ||
+ !S.CurContext->isFunctionOrMethod() ||
+ D.getDeclSpec().getStorageClassSpecAsWritten()
+ != DeclSpec::SCS_unspecified)
+ return;
+
+ // Inside a condition, a direct initializer is not permitted. We allow one to
+ // be parsed in order to give better diagnostics in condition parsing.
+ if (D.getContext() == Declarator::ConditionContext)
+ return;
+
+ SourceRange ParenRange(DeclType.Loc, DeclType.EndLoc);
+
+ S.Diag(DeclType.Loc,
+ FTI.NumArgs ? diag::warn_parens_disambiguated_as_function_declaration
+ : diag::warn_empty_parens_are_function_decl)
+ << ParenRange;
+
+ // If the declaration looks like:
+ // T var1,
+ // f();
+ // and name lookup finds a function named 'f', then the ',' was
+ // probably intended to be a ';'.
+ if (!D.isFirstDeclarator() && D.getIdentifier()) {
+ FullSourceLoc Comma(D.getCommaLoc(), S.SourceMgr);
+ FullSourceLoc Name(D.getIdentifierLoc(), S.SourceMgr);
+ if (Comma.getFileID() != Name.getFileID() ||
+ Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) {
+ LookupResult Result(S, D.getIdentifier(), SourceLocation(),
+ Sema::LookupOrdinaryName);
+ if (S.LookupName(Result, S.getCurScope()))
+ S.Diag(D.getCommaLoc(), diag::note_empty_parens_function_call)
+ << FixItHint::CreateReplacement(D.getCommaLoc(), ";")
+ << D.getIdentifier();
+ }
+ }
+
+ if (FTI.NumArgs > 0) {
+ // For a declaration with parameters, eg. "T var(T());", suggest adding parens
+ // around the first parameter to turn the declaration into a variable
+ // declaration.
+ SourceRange Range = FTI.ArgInfo[0].Param->getSourceRange();
+ SourceLocation B = Range.getBegin();
+ SourceLocation E = S.PP.getLocForEndOfToken(Range.getEnd());
+ // FIXME: Maybe we should suggest adding braces instead of parens
+ // in C++11 for classes that don't have an initializer_list constructor.
+ S.Diag(B, diag::note_additional_parens_for_variable_declaration)
+ << FixItHint::CreateInsertion(B, "(")
+ << FixItHint::CreateInsertion(E, ")");
+ } else {
+ // For a declaration without parameters, eg. "T var();", suggest replacing the
+ // parens with an initializer to turn the declaration into a variable
+ // declaration.
+ const CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
+
+ // Empty parens mean value-initialization, and no parens mean
+ // default initialization. These are equivalent if the default
+ // constructor is user-provided or if zero-initialization is a
+ // no-op.
+ if (RD && RD->hasDefinition() &&
+ (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor()))
+ S.Diag(DeclType.Loc, diag::note_empty_parens_default_ctor)
+ << FixItHint::CreateRemoval(ParenRange);
+ else {
+ std::string Init = S.getFixItZeroInitializerForType(RT);
+ if (Init.empty() && S.LangOpts.CPlusPlus0x)
+ Init = "{}";
+ if (!Init.empty())
+ S.Diag(DeclType.Loc, diag::note_empty_parens_zero_initialize)
+ << FixItHint::CreateReplacement(ParenRange, Init);
+ }
+ }
+}
+
static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
QualType declSpecType,
TypeSourceInfo *TInfo) {
@@ -2165,12 +2275,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// trailing-return-type is only required if we're declaring a function,
// and not, for instance, a pointer to a function.
if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto &&
- !FTI.TrailingReturnType && chunkIndex == 0) {
+ !FTI.hasTrailingReturnType() && chunkIndex == 0) {
S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
diag::err_auto_missing_trailing_return);
T = Context.IntTy;
D.setInvalidType(true);
- } else if (FTI.TrailingReturnType) {
+ } else if (FTI.hasTrailingReturnType()) {
// T must be exactly 'auto' at this point. See CWG issue 681.
if (isa<ParenType>(T)) {
S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
@@ -2184,10 +2294,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
<< T << D.getDeclSpec().getSourceRange();
D.setInvalidType(true);
}
-
- T = S.GetTypeFromParser(
- ParsedType::getFromOpaquePtr(FTI.TrailingReturnType),
- &TInfo);
+ T = S.GetTypeFromParser(FTI.getTrailingReturnType(), &TInfo);
+ if (T.isNull()) {
+ // An error occurred parsing the trailing return type.
+ T = Context.IntTy;
+ D.setInvalidType(true);
+ }
}
}
@@ -2259,6 +2371,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
<< (D.getContext() == Declarator::AliasDeclContext ||
D.getContext() == Declarator::AliasTemplateContext);
+ // If we see "T var();" or "T var(T());" at block scope, it is probably
+ // an attempt to initialize a variable, not a function declaration.
+ if (FTI.isAmbiguous)
+ warnAboutAmbiguousFunction(S, D, DeclType, T);
+
if (!FTI.NumArgs && !FTI.isVariadic && !LangOpts.CPlusPlus) {
// Simple void foo(), where the incoming T is the result type.
T = Context.getFunctionNoProtoType(T);
@@ -2270,7 +2387,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
bool Overloadable = false;
for (const AttributeList *Attrs = D.getAttributes();
Attrs; Attrs = Attrs->getNext()) {
- if (Attrs->getKind() == AttributeList::AT_overloadable) {
+ if (Attrs->getKind() == AttributeList::AT_Overloadable) {
Overloadable = true;
break;
}
@@ -2290,12 +2407,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
FunctionProtoType::ExtProtoInfo EPI;
EPI.Variadic = FTI.isVariadic;
- EPI.HasTrailingReturn = FTI.TrailingReturnType;
+ EPI.HasTrailingReturn = FTI.hasTrailingReturnType();
EPI.TypeQuals = FTI.TypeQuals;
EPI.RefQualifier = !FTI.hasRefQualifier()? RQ_None
: FTI.RefQualifierIsLValueRef? RQ_LValue
: RQ_RValue;
-
+
// Otherwise, we have a function with an argument list that is
// potentially variadic.
SmallVector<QualType, 16> ArgTys;
@@ -2311,7 +2428,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
assert(!ArgTy.isNull() && "Couldn't parse type?");
// Adjust the parameter type.
- assert((ArgTy == Context.getAdjustedParameterType(ArgTy)) &&
+ assert((ArgTy == Context.getAdjustedParameterType(ArgTy)) &&
"Unadjusted type?");
// Look for 'void'. void is allowed only as a single argument to a
@@ -2374,7 +2491,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
SmallVector<ParsedType, 2> DynamicExceptions;
SmallVector<SourceRange, 2> DynamicExceptionRanges;
Expr *NoexceptExpr = 0;
-
+
if (FTI.getExceptionSpecType() == EST_Dynamic) {
// FIXME: It's rather inefficient to have to split into two vectors
// here.
@@ -2388,14 +2505,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
} else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
NoexceptExpr = FTI.NoexceptExpr;
}
-
+
S.checkExceptionSpecification(FTI.getExceptionSpecType(),
DynamicExceptions,
DynamicExceptionRanges,
NoexceptExpr,
Exceptions,
EPI);
-
+
if (FTI.getExceptionSpecType() == EST_None &&
ImplicitlyNoexcept && chunkIndex == 0) {
// Only the outermost chunk is marked noexcept, of course.
@@ -2475,7 +2592,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
const FunctionProtoType *FnTy = T->getAs<FunctionProtoType>();
assert(FnTy && "Why oh why is there not a FunctionProtoType here?");
- // C++ 8.3.5p4:
+ // C++ 8.3.5p4:
// A cv-qualifier-seq shall only be part of the function type
// for a nonstatic member function, the function type to which a pointer
// to member refers, or the top-level function type of a function typedef
@@ -2503,7 +2620,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// Rebuild function type adding a 'const' qualifier.
FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
EPI.TypeQuals |= DeclSpec::TQ_const;
- T = Context.getFunctionType(FnTy->getResultType(),
+ T = Context.getFunctionType(FnTy->getResultType(),
FnTy->arg_type_begin(),
FnTy->getNumArgs(), EPI);
}
@@ -2540,7 +2657,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// RemovalLocs.push_back(Chunk.Fun.getRestrictQualifierLoc());
if (!RemovalLocs.empty()) {
std::sort(RemovalLocs.begin(), RemovalLocs.end(),
- SourceManager::LocBeforeThanCompare(S.getSourceManager()));
+ BeforeThanCompare<SourceLocation>(S.getSourceManager()));
RemovalRange = SourceRange(RemovalLocs.front(), RemovalLocs.back());
Loc = RemovalLocs.front();
}
@@ -2556,7 +2673,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
EPI.TypeQuals = 0;
EPI.RefQualifier = RQ_None;
- T = Context.getFunctionType(FnTy->getResultType(),
+ T = Context.getFunctionType(FnTy->getResultType(),
FnTy->arg_type_begin(),
FnTy->getNumArgs(), EPI);
}
@@ -2572,31 +2689,31 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// C++0x [dcl.constexpr]p9:
// A constexpr specifier used in an object declaration declares the object
- // as const.
+ // as const.
if (D.getDeclSpec().isConstexprSpecified() && T->isObjectType()) {
T.addConst();
}
- // If there was an ellipsis in the declarator, the declaration declares a
+ // If there was an ellipsis in the declarator, the declaration declares a
// parameter pack whose type may be a pack expansion type.
if (D.hasEllipsis() && !T.isNull()) {
// C++0x [dcl.fct]p13:
- // A declarator-id or abstract-declarator containing an ellipsis shall
+ // A declarator-id or abstract-declarator containing an ellipsis shall
// only be used in a parameter-declaration. Such a parameter-declaration
// is a parameter pack (14.5.3). [...]
switch (D.getContext()) {
case Declarator::PrototypeContext:
// C++0x [dcl.fct]p13:
- // [...] When it is part of a parameter-declaration-clause, the
- // parameter pack is a function parameter pack (14.5.3). The type T
+ // [...] When it is part of a parameter-declaration-clause, the
+ // parameter pack is a function parameter pack (14.5.3). The type T
// of the declarator-id of the function parameter pack shall contain
- // a template parameter pack; each template parameter pack in T is
+ // a template parameter pack; each template parameter pack in T is
// expanded by the function parameter pack.
//
// We represent function parameter packs as function parameters whose
// type is a pack expansion.
if (!T->containsUnexpandedParameterPack()) {
- S.Diag(D.getEllipsisLoc(),
+ S.Diag(D.getEllipsisLoc(),
diag::err_function_parameter_pack_without_parameter_packs)
<< T << D.getSourceRange();
D.setEllipsisLoc(SourceLocation());
@@ -2604,10 +2721,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
T = Context.getPackExpansionType(T, llvm::Optional<unsigned>());
}
break;
-
+
case Declarator::TemplateParamContext:
// C++0x [temp.param]p15:
- // If a template-parameter is a [...] is a parameter-declaration that
+ // If a template-parameter is a [...] is a parameter-declaration that
// declares a parameter pack (8.3.5), then the template-parameter is a
// template parameter pack (14.5.3).
//
@@ -2622,7 +2739,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
? diag::warn_cxx98_compat_variadic_templates
: diag::ext_variadic_templates);
break;
-
+
case Declarator::FileContext:
case Declarator::KNRTypeListContext:
case Declarator::ObjCParameterContext: // FIXME: special diagnostic here?
@@ -2675,7 +2792,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S) {
if (D.isPrototypeContext() && getLangOpts().ObjCAutoRefCount)
inferARCWriteback(state, T);
-
+
return GetFullTypeForDeclarator(state, T, ReturnTypeInfo);
}
@@ -2700,7 +2817,7 @@ static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state,
DeclaratorChunk &chunk = D.getTypeObject(chunkIndex);
for (const AttributeList *attr = chunk.getAttrs(); attr;
attr = attr->getNext())
- if (attr->getKind() == AttributeList::AT_objc_ownership)
+ if (attr->getKind() == AttributeList::AT_ObjCOwnership)
return;
const char *attrStr = 0;
@@ -2718,14 +2835,13 @@ static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state,
.create(&S.Context.Idents.get("objc_ownership"), SourceLocation(),
/*scope*/ 0, SourceLocation(),
&S.Context.Idents.get(attrStr), SourceLocation(),
- /*args*/ 0, 0,
- /*declspec*/ false, /*C++0x*/ false);
+ /*args*/ 0, 0, AttributeList::AS_GNU);
spliceAttrIntoList(*attr, chunk.getAttrListRef());
// TODO: mark whether we did this inference?
}
-/// \brief Used for transfering ownership in casts resulting in l-values.
+/// \brief Used for transferring ownership in casts resulting in l-values.
static void transferARCOwnership(TypeProcessingState &state,
QualType &declSpecTy,
Qualifiers::ObjCLifetime ownership) {
@@ -2763,7 +2879,7 @@ static void transferARCOwnership(TypeProcessingState &state,
if (inner == -1)
return;
- DeclaratorChunk &chunk = D.getTypeObject(inner);
+ DeclaratorChunk &chunk = D.getTypeObject(inner);
if (chunk.Kind == DeclaratorChunk::Pointer) {
if (declSpecTy->isObjCRetainableType())
return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership);
@@ -2797,33 +2913,33 @@ TypeSourceInfo *Sema::GetTypeForDeclaratorCast(Declarator &D, QualType FromTy) {
static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
switch (kind) {
case AttributedType::attr_address_space:
- return AttributeList::AT_address_space;
+ return AttributeList::AT_AddressSpace;
case AttributedType::attr_regparm:
- return AttributeList::AT_regparm;
+ return AttributeList::AT_Regparm;
case AttributedType::attr_vector_size:
- return AttributeList::AT_vector_size;
+ return AttributeList::AT_VectorSize;
case AttributedType::attr_neon_vector_type:
- return AttributeList::AT_neon_vector_type;
+ return AttributeList::AT_NeonVectorType;
case AttributedType::attr_neon_polyvector_type:
- return AttributeList::AT_neon_polyvector_type;
+ return AttributeList::AT_NeonPolyVectorType;
case AttributedType::attr_objc_gc:
- return AttributeList::AT_objc_gc;
+ return AttributeList::AT_ObjCGC;
case AttributedType::attr_objc_ownership:
- return AttributeList::AT_objc_ownership;
+ return AttributeList::AT_ObjCOwnership;
case AttributedType::attr_noreturn:
- return AttributeList::AT_noreturn;
+ return AttributeList::AT_NoReturn;
case AttributedType::attr_cdecl:
- return AttributeList::AT_cdecl;
+ return AttributeList::AT_CDecl;
case AttributedType::attr_fastcall:
- return AttributeList::AT_fastcall;
+ return AttributeList::AT_FastCall;
case AttributedType::attr_stdcall:
- return AttributeList::AT_stdcall;
+ return AttributeList::AT_StdCall;
case AttributedType::attr_thiscall:
- return AttributeList::AT_thiscall;
+ return AttributeList::AT_ThisCall;
case AttributedType::attr_pascal:
- return AttributeList::AT_pascal;
+ return AttributeList::AT_Pascal;
case AttributedType::attr_pcs:
- return AttributeList::AT_pcs;
+ return AttributeList::AT_Pcs;
}
llvm_unreachable("unexpected attribute kind!");
}
@@ -2856,7 +2972,7 @@ namespace {
const DeclSpec &DS;
public:
- TypeSpecLocFiller(ASTContext &Context, const DeclSpec &DS)
+ TypeSpecLocFiller(ASTContext &Context, const DeclSpec &DS)
: Context(Context), DS(DS) {}
void VisitAttributedTypeLoc(AttributedTypeLoc TL) {
@@ -2871,6 +2987,10 @@ namespace {
}
void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
TL.setNameLoc(DS.getTypeSpecTypeLoc());
+ // FIXME. We should have DS.getTypeSpecTypeEndLoc(). But, it requires
+ // addition field. What we have is good enough for dispay of location
+ // of 'fixit' on interface name.
+ TL.setNameEndLoc(DS.getLocEnd());
}
void VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {
// Handle the base type, which might not have been written explicitly.
@@ -3000,7 +3120,7 @@ namespace {
void VisitAtomicTypeLoc(AtomicTypeLoc TL) {
TL.setKWLoc(DS.getTypeSpecTypeLoc());
TL.setParensRange(DS.getTypeofParensRange());
-
+
TypeSourceInfo *TInfo = 0;
Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo);
TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc());
@@ -3104,7 +3224,7 @@ namespace {
assert(Chunk.Kind == DeclaratorChunk::Function);
TL.setLocalRangeBegin(Chunk.Loc);
TL.setLocalRangeEnd(Chunk.EndLoc);
- TL.setTrailingReturn(!!Chunk.Fun.TrailingReturnType);
+ TL.setTrailingReturn(Chunk.Fun.hasTrailingReturnType());
const DeclaratorChunk::FunctionTypeInfo &FTI = Chunk.Fun;
for (unsigned i = 0, e = TL.getNumArgs(), tpi = 0; i != e; ++i) {
@@ -3142,9 +3262,9 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
// Handle parameter packs whose type is a pack expansion.
if (isa<PackExpansionType>(T)) {
cast<PackExpansionTypeLoc>(CurrTL).setEllipsisLoc(D.getEllipsisLoc());
- CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc();
+ CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc();
}
-
+
for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) {
while (isa<AttributedTypeLoc>(CurrTL)) {
AttributedTypeLoc TL = cast<AttributedTypeLoc>(CurrTL);
@@ -3155,7 +3275,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
DeclaratorLocFiller(Context, D.getTypeObject(i)).Visit(CurrTL);
CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc();
}
-
+
// If we have different source information for the return type, use
// that. This really only applies to C++ conversion functions.
if (ReturnTypeInfo) {
@@ -3165,7 +3285,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
} else {
TypeSpecLocFiller(Context, D.getDeclSpec()).Visit(CurrTL);
}
-
+
return TInfo;
}
@@ -3174,7 +3294,7 @@ ParsedType Sema::CreateParsedType(QualType T, TypeSourceInfo *TInfo) {
// FIXME: LocInfoTypes are "transient", only needed for passing to/from Parser
// and Sema during declaration parsing. Try deallocating/caching them when
// it's appropriate, instead of allocating them and keeping them around.
- LocInfoType *LocT = (LocInfoType*)BumpAlloc.Allocate(sizeof(LocInfoType),
+ LocInfoType *LocT = (LocInfoType*)BumpAlloc.Allocate(sizeof(LocInfoType),
TypeAlignment);
new (LocT) LocInfoType(T, TInfo);
assert(LocT->getTypeClass() != T->getTypeClass() &&
@@ -3304,13 +3424,13 @@ static bool hasDirectOwnershipQualifier(QualType type) {
// X *__strong (...)
} else if (const ParenType *paren = dyn_cast<ParenType>(type)) {
type = paren->getInnerType();
-
+
// That's it for things we want to complain about. In particular,
// we do not want to look through typedefs, typeof(expr),
// typeof(type), or any other way that the type is somehow
// abstracted.
} else {
-
+
return false;
}
}
@@ -3439,8 +3559,8 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
attr.setInvalid();
return true;
}
-
- // Forbid __weak for class objects marked as
+
+ // Forbid __weak for class objects marked as
// objc_arc_weak_reference_unavailable
if (lifetime == Qualifiers::OCL_Weak) {
QualType T = type;
@@ -3450,12 +3570,12 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl();
if (Class->isArcWeakrefUnavailable()) {
S.Diag(AttrLoc, diag::err_arc_unsupported_weak_class);
- S.Diag(ObjT->getInterfaceDecl()->getLocation(),
+ S.Diag(ObjT->getInterfaceDecl()->getLocation(),
diag::note_class_declared);
}
}
}
-
+
return true;
}
@@ -3654,7 +3774,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state,
FunctionTypeUnwrapper unwrapped(S, type);
- if (attr.getKind() == AttributeList::AT_noreturn) {
+ if (attr.getKind() == AttributeList::AT_NoReturn) {
if (S.CheckNoReturnAttr(attr))
return true;
@@ -3670,7 +3790,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state,
// ns_returns_retained is not always a type attribute, but if we got
// here, we're treating it as one right now.
- if (attr.getKind() == AttributeList::AT_ns_returns_retained) {
+ if (attr.getKind() == AttributeList::AT_NSReturnsRetained) {
assert(S.getLangOpts().ObjCAutoRefCount &&
"ns_returns_retained treated as type attribute in non-ARC");
if (attr.getNumArgs()) return true;
@@ -3685,7 +3805,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state,
return true;
}
- if (attr.getKind() == AttributeList::AT_regparm) {
+ if (attr.getKind() == AttributeList::AT_Regparm) {
unsigned value;
if (S.CheckRegparmAttr(attr, value))
return true;
@@ -3705,7 +3825,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state,
return true;
}
- FunctionType::ExtInfo EI =
+ FunctionType::ExtInfo EI =
unwrapped.get()->getExtInfo().withRegParm(value);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
return true;
@@ -3860,11 +3980,11 @@ static void HandleVectorSizeAttr(QualType& CurType, const AttributeList &Attr,
/// \brief Process the OpenCL-like ext_vector_type attribute when it occurs on
/// a type.
-static void HandleExtVectorTypeAttr(QualType &CurType,
- const AttributeList &Attr,
+static void HandleExtVectorTypeAttr(QualType &CurType,
+ const AttributeList &Attr,
Sema &S) {
Expr *sizeExpr;
-
+
// Special case where the argument is a template id.
if (Attr.getParameterName()) {
CXXScopeSpec SS;
@@ -3876,7 +3996,7 @@ static void HandleExtVectorTypeAttr(QualType &CurType,
id, false, false);
if (Size.isInvalid())
return;
-
+
sizeExpr = Size.get();
} else {
// check the attribute arguments.
@@ -3886,7 +4006,7 @@ static void HandleExtVectorTypeAttr(QualType &CurType,
}
sizeExpr = Attr.getArg(0);
}
-
+
// Create the vector type.
QualType T = S.BuildExtVectorType(CurType, sizeExpr, Attr.getLoc());
if (!T.isNull())
@@ -3973,12 +4093,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
switch (attr.getKind()) {
default: break;
- case AttributeList::AT_may_alias:
+ case AttributeList::AT_MayAlias:
// FIXME: This attribute needs to actually be handled, but if we ignore
// it it breaks large amounts of Linux software.
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_address_space:
+ case AttributeList::AT_AddressSpace:
HandleAddressSpaceTypeAttribute(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
@@ -3987,35 +4107,42 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
distributeObjCPointerTypeAttr(state, attr, type);
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_vector_size:
+ case AttributeList::AT_VectorSize:
HandleVectorSizeAttr(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_ext_vector_type:
+ case AttributeList::AT_ExtVectorType:
if (state.getDeclarator().getDeclSpec().getStorageClassSpec()
!= DeclSpec::SCS_typedef)
HandleExtVectorTypeAttr(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_neon_vector_type:
+ case AttributeList::AT_NeonVectorType:
HandleNeonVectorTypeAttr(type, attr, state.getSema(),
VectorType::NeonVector, "neon_vector_type");
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_neon_polyvector_type:
+ case AttributeList::AT_NeonPolyVectorType:
HandleNeonVectorTypeAttr(type, attr, state.getSema(),
VectorType::NeonPolyVector,
"neon_polyvector_type");
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_opencl_image_access:
+ case AttributeList::AT_OpenCLImageAccess:
HandleOpenCLImageAccessAttribute(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
- case AttributeList::AT_ns_returns_retained:
+ case AttributeList::AT_Win64:
+ case AttributeList::AT_Ptr32:
+ case AttributeList::AT_Ptr64:
+ // FIXME: don't ignore these
+ attr.setUsedAsTypeAttr();
+ break;
+
+ case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
- break;
+ break;
// fallthrough into the function attrs
FUNCTION_TYPE_ATTRS_CASELIST:
@@ -4043,14 +4170,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
/// case of a reference type, the referred-to type).
///
/// \param E The expression whose type is required to be complete.
-/// \param PD The partial diagnostic that will be printed out if the type cannot
-/// be completed.
+/// \param Diagnoser The object that will emit a diagnostic if the type is
+/// incomplete.
///
/// \returns \c true if the type of \p E is incomplete and diagnosed, \c false
/// otherwise.
-bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
- std::pair<SourceLocation,
- PartialDiagnostic> Note) {
+bool Sema::RequireCompleteExprType(Expr *E, TypeDiagnoser &Diagnoser){
QualType T = E->getType();
// Fast path the case where the type is already complete.
@@ -4065,7 +4190,7 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
if (Var->isStaticDataMember() &&
Var->getInstantiatedFromStaticDataMember()) {
-
+
MemberSpecializationInfo *MSInfo = Var->getMemberSpecializationInfo();
assert(MSInfo && "Missing member specialization information?");
if (MSInfo->getTemplateSpecializationKind()
@@ -4073,15 +4198,15 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
// If we don't already have a point of instantiation, this is it.
if (MSInfo->getPointOfInstantiation().isInvalid()) {
MSInfo->setPointOfInstantiation(E->getLocStart());
-
- // This is a modification of an existing AST node. Notify
+
+ // This is a modification of an existing AST node. Notify
// listeners.
if (ASTMutationListener *L = getASTMutationListener())
L->StaticDataMemberInstantiated(Var);
}
-
+
InstantiateStaticDataMemberDefinition(E->getExprLoc(), Var);
-
+
// Update the type to the newly instantiated definition's type both
// here and within the expression.
if (VarDecl *Def = Var->getDefinition()) {
@@ -4091,7 +4216,7 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
E->setType(T);
}
}
-
+
// We still go on to try to complete the type independently, as it
// may also require instantiations or diagnostics if it remains
// incomplete.
@@ -4107,7 +4232,26 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
if (const ReferenceType *Ref = T->getAs<ReferenceType>())
T = Ref->getPointeeType();
- return RequireCompleteType(E->getExprLoc(), T, PD, Note);
+ return RequireCompleteType(E->getExprLoc(), T, Diagnoser);
+}
+
+namespace {
+ struct TypeDiagnoserDiag : Sema::TypeDiagnoser {
+ unsigned DiagID;
+
+ TypeDiagnoserDiag(unsigned DiagID)
+ : Sema::TypeDiagnoser(DiagID == 0), DiagID(DiagID) {}
+
+ virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) {
+ if (Suppressed) return;
+ S.Diag(Loc, DiagID) << T;
+ }
+ };
+}
+
+bool Sema::RequireCompleteExprType(Expr *E, unsigned DiagID) {
+ TypeDiagnoserDiag Diagnoser(DiagID);
+ return RequireCompleteExprType(E, Diagnoser);
}
/// @brief Ensure that the type T is a complete type.
@@ -4125,17 +4269,10 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD,
///
/// @param T The type that this routine is examining for completeness.
///
-/// @param PD The partial diagnostic that will be printed out if T is not a
-/// complete type.
-///
/// @returns @c true if @p T is incomplete and a diagnostic was emitted,
/// @c false otherwise.
bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD,
- std::pair<SourceLocation,
- PartialDiagnostic> Note) {
- unsigned diag = PD.getDiagID();
-
+ TypeDiagnoser &Diagnoser) {
// FIXME: Add this assertion to make sure we always get instantiation points.
// assert(!Loc.isInvalid() && "Invalid location in RequireCompleteType");
// FIXME: Add this assertion to help us flush out problems with
@@ -4148,9 +4285,9 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
NamedDecl *Def = 0;
if (!T->isIncompleteType(&Def)) {
// If we know about the definition but it is not visible, complain.
- if (diag != 0 && Def && !LookupResult::isVisible(Def)) {
+ if (!Diagnoser.Suppressed && Def && !LookupResult::isVisible(Def)) {
// Suppress this error outside of a SFINAE context if we've already
- // emitted the error once for this type. There's no usefulness in
+ // emitted the error once for this type. There's no usefulness in
// repeating the diagnostic.
// FIXME: Add a Fix-It that imports the corresponding module or includes
// the header.
@@ -4159,13 +4296,13 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
Diag(Def->getLocation(), diag::note_previous_definition);
}
}
-
+
return false;
}
const TagType *Tag = T->getAs<TagType>();
const ObjCInterfaceType *IFace = 0;
-
+
if (Tag) {
// Avoid diagnosing invalid decls as incomplete.
if (Tag->getDecl()->isInvalidDecl())
@@ -4182,7 +4319,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
// Avoid diagnosing invalid decls as incomplete.
if (IFace->getDecl()->isInvalidDecl())
return true;
-
+
// Give the external AST source a chance to complete the type.
if (IFace->getDecl()->hasExternalLexicalStorage()) {
Context.getExternalSource()->CompleteType(IFace->getDecl());
@@ -4190,7 +4327,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
return false;
}
}
-
+
// If we have a class template specialization or a class member of a
// class template specialization, or an array with known size of such,
// try to instantiate it.
@@ -4204,7 +4341,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
if (ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared)
return InstantiateClassTemplateSpecialization(Loc, ClassTemplateSpec,
TSK_ImplicitInstantiation,
- /*Complain=*/diag != 0);
+ /*Complain=*/!Diagnoser.Suppressed);
} else if (CXXRecordDecl *Rec
= dyn_cast<CXXRecordDecl>(Record->getDecl())) {
CXXRecordDecl *Pattern = Rec->getInstantiatedFromMemberClass();
@@ -4216,21 +4353,17 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
return InstantiateClass(Loc, Rec, Pattern,
getTemplateInstantiationArgs(Rec),
TSK_ImplicitInstantiation,
- /*Complain=*/diag != 0);
+ /*Complain=*/!Diagnoser.Suppressed);
}
}
}
- if (diag == 0)
+ if (Diagnoser.Suppressed)
return true;
-
+
// We have an incomplete type. Produce a diagnostic.
- Diag(Loc, PD) << T;
-
- // If we have a note, produce it.
- if (!Note.first.isInvalid())
- Diag(Note.first, Note.second);
-
+ Diagnoser.diagnose(*this, Loc, T);
+
// If the type was a forward declaration of a class/struct/union
// type, produce a note.
if (Tag && !Tag->getDecl()->isInvalidDecl())
@@ -4238,7 +4371,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
Tag->isBeingDefined() ? diag::note_type_being_defined
: diag::note_forward_declaration)
<< QualType(Tag, 0);
-
+
// If the Objective-C class was a forward declaration, produce a note.
if (IFace && !IFace->getDecl()->isInvalidDecl())
Diag(IFace->getDecl()->getLocation(), diag::note_forward_class);
@@ -4247,15 +4380,9 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
}
bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD) {
- return RequireCompleteType(Loc, T, PD,
- std::make_pair(SourceLocation(), PDiag(0)));
-}
-
-bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
unsigned DiagID) {
- return RequireCompleteType(Loc, T, PDiag(DiagID),
- std::make_pair(SourceLocation(), PDiag(0)));
+ TypeDiagnoserDiag Diagnoser(DiagID);
+ return RequireCompleteType(Loc, T, Diagnoser);
}
/// @brief Ensure that the type T is a literal type.
@@ -4272,13 +4399,12 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
///
/// @param T The type that this routine is examining for literalness.
///
-/// @param PD The partial diagnostic that will be printed out if T is not a
-/// literal type.
+/// @param Diagnoser Emits a diagnostic if T is not a literal type.
///
/// @returns @c true if @p T is not a literal type and a diagnostic was emitted,
/// @c false otherwise.
bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
- const PartialDiagnostic &PD) {
+ TypeDiagnoser &Diagnoser) {
assert(!T->isDependentType() && "type should not be dependent");
QualType ElemType = Context.getBaseElementType(T);
@@ -4287,10 +4413,10 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
if (T->isLiteralType())
return false;
- if (PD.getDiagID() == 0)
+ if (Diagnoser.Suppressed)
return true;
- Diag(Loc, PD) << T;
+ Diagnoser.diagnose(*this, Loc, T);
if (T->isVariableArrayType())
return true;
@@ -4301,9 +4427,13 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
- // FIXME: Better diagnostic for incomplete class?
- if (!RD->isCompleteDefinition())
+ // A partially-defined class type can't be a literal type, because a literal
+ // class type must have a trivial destructor (which can't be checked until
+ // the class definition is complete).
+ if (!RD->isCompleteDefinition()) {
+ RequireCompleteType(Loc, ElemType, diag::note_non_literal_incomplete, T);
return true;
+ }
// If the class has virtual base classes, then it's not an aggregate, and
// cannot have any constexpr constructors or a trivial default constructor,
@@ -4331,11 +4461,11 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
}
for (CXXRecordDecl::field_iterator I = RD->field_begin(),
E = RD->field_end(); I != E; ++I) {
- if (!(*I)->getType()->isLiteralType() ||
- (*I)->getType().isVolatileQualified()) {
- Diag((*I)->getLocation(), diag::note_non_literal_field)
- << RD << (*I) << (*I)->getType()
- << (*I)->getType().isVolatileQualified();
+ if (!I->getType()->isLiteralType() ||
+ I->getType().isVolatileQualified()) {
+ Diag(I->getLocation(), diag::note_non_literal_field)
+ << RD << *I << I->getType()
+ << I->getType().isVolatileQualified();
return true;
}
}
@@ -4355,6 +4485,11 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
return true;
}
+bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, unsigned DiagID) {
+ TypeDiagnoserDiag Diagnoser(DiagID);
+ return RequireLiteralType(Loc, T, Diagnoser);
+}
+
/// \brief Retrieve a version of the type 'T' that is elaborated by Keyword
/// and qualified by the nested-name-specifier contained in SS.
QualType Sema::getElaboratedType(ElaboratedTypeKeyword Keyword,
@@ -4396,8 +4531,8 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
// The type denoted by decltype(e) is defined as follows:
//
// - if e is an unparenthesized id-expression or an unparenthesized class
- // member access (5.2.5), decltype(e) is the type of the entity named
- // by e. If there is no such entity, or if e names a set of overloaded
+ // member access (5.2.5), decltype(e) is the type of the entity named
+ // by e. If there is no such entity, or if e names a set of overloaded
// functions, the program is ill-formed;
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
if (const ValueDecl *VD = dyn_cast<ValueDecl>(DRE->getDecl()))
@@ -4407,7 +4542,7 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
if (const FieldDecl *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
return FD->getType();
}
-
+
// C++11 [expr.lambda.prim]p18:
// Every occurrence of decltype((x)) where x is a possibly
// parenthesized id-expression that names an entity of automatic
@@ -4433,16 +4568,16 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
// [...]
QualType T = E->getType();
switch (E->getValueKind()) {
- // - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the
+ // - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the
// type of e;
case VK_XValue: T = S.Context.getRValueReferenceType(T); break;
- // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the
+ // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the
// type of e;
case VK_LValue: T = S.Context.getLValueReferenceType(T); break;
// - otherwise, decltype(e) is the type of e.
case VK_RValue: break;
}
-
+
return T;
}
@@ -4450,7 +4585,7 @@ QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc) {
ExprResult ER = CheckPlaceholderExpr(E);
if (ER.isInvalid()) return QualType();
E = ER.take();
-
+
return Context.getDecltypeType(E, getDecltypeForExpr(*this, E));
}
@@ -4482,8 +4617,7 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
if (!T->isDependentType()) {
// FIXME: It isn't entirely clear whether incomplete atomic types
// are allowed or not; for simplicity, ban them for the moment.
- if (RequireCompleteType(Loc, T,
- PDiag(diag::err_atomic_specifier_bad_type) << 0))
+ if (RequireCompleteType(Loc, T, diag::err_atomic_specifier_bad_type, 0))
return QualType();
int DisallowedKind = -1;
diff --git a/lib/Sema/TargetAttributesSema.cpp b/lib/Sema/TargetAttributesSema.cpp
index 8b19be7..25ace95 100644
--- a/lib/Sema/TargetAttributesSema.cpp
+++ b/lib/Sema/TargetAttributesSema.cpp
@@ -151,6 +151,18 @@ static void HandleX86ForceAlignArgPointerAttr(Decl *D,
S.Context));
}
+DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range) {
+ if (D->hasAttr<DLLExportAttr>()) {
+ Diag(Range.getBegin(), diag::warn_attribute_ignored) << "dllimport";
+ return NULL;
+ }
+
+ if (D->hasAttr<DLLImportAttr>())
+ return NULL;
+
+ return ::new (Context) DLLImportAttr(Range, Context);
+}
+
static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// check the attribute arguments.
if (Attr.getNumArgs() != 0) {
@@ -159,13 +171,8 @@ static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
}
// Attribute can be applied only to functions or variables.
- if (isa<VarDecl>(D)) {
- D->addAttr(::new (S.Context) DLLImportAttr(Attr.getLoc(), S.Context));
- return;
- }
-
FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
- if (!FD) {
+ if (!FD && !isa<VarDecl>(D)) {
// Apparently Visual C++ thinks it is okay to not emit a warning
// in this case, so only emit a warning when -fms-extensions is not
// specified.
@@ -177,27 +184,26 @@ static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// Currently, the dllimport attribute is ignored for inlined functions.
// Warning is emitted.
- if (FD->isInlineSpecified()) {
+ if (FD && FD->isInlineSpecified()) {
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport";
return;
}
- // The attribute is also overridden by a subsequent declaration as dllexport.
- // Warning is emitted.
- for (AttributeList *nextAttr = Attr.getNext(); nextAttr;
- nextAttr = nextAttr->getNext()) {
- if (nextAttr->getKind() == AttributeList::AT_dllexport) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport";
- return;
- }
- }
+ DLLImportAttr *NewAttr = S.mergeDLLImportAttr(D, Attr.getRange());
+ if (NewAttr)
+ D->addAttr(NewAttr);
+}
- if (D->getAttr<DLLExportAttr>()) {
- S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport";
- return;
+DLLExportAttr *Sema::mergeDLLExportAttr(Decl *D, SourceRange Range) {
+ if (DLLImportAttr *Import = D->getAttr<DLLImportAttr>()) {
+ Diag(Import->getLocation(), diag::warn_attribute_ignored) << "dllimport";
+ D->dropAttr<DLLImportAttr>();
}
- D->addAttr(::new (S.Context) DLLImportAttr(Attr.getLoc(), S.Context));
+ if (D->hasAttr<DLLExportAttr>())
+ return NULL;
+
+ return ::new (Context) DLLExportAttr(Range, Context);
}
static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
@@ -208,13 +214,8 @@ static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
}
// Attribute can be applied only to functions or variables.
- if (isa<VarDecl>(D)) {
- D->addAttr(::new (S.Context) DLLExportAttr(Attr.getLoc(), S.Context));
- return;
- }
-
FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
- if (!FD) {
+ if (!FD && !isa<VarDecl>(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
<< Attr.getName() << 2 /*variable and function*/;
return;
@@ -222,13 +223,15 @@ static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
// Currently, the dllexport attribute is ignored for inlined functions, unless
// the -fkeep-inline-functions flag has been used. Warning is emitted;
- if (FD->isInlineSpecified()) {
+ if (FD && FD->isInlineSpecified()) {
// FIXME: ... unless the -fkeep-inline-functions flag has been used.
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllexport";
return;
}
- D->addAttr(::new (S.Context) DLLExportAttr(Attr.getLoc(), S.Context));
+ DLLExportAttr *NewAttr = S.mergeDLLExportAttr(D, Attr.getRange());
+ if (NewAttr)
+ D->addAttr(NewAttr);
}
namespace {
@@ -241,9 +244,9 @@ namespace {
if (Triple.getOS() == llvm::Triple::Win32 ||
Triple.getOS() == llvm::Triple::MinGW32) {
switch (Attr.getKind()) {
- case AttributeList::AT_dllimport: HandleDLLImportAttr(D, Attr, S);
+ case AttributeList::AT_DLLImport: HandleDLLImportAttr(D, Attr, S);
return true;
- case AttributeList::AT_dllexport: HandleDLLExportAttr(D, Attr, S);
+ case AttributeList::AT_DLLExport: HandleDLLExportAttr(D, Attr, S);
return true;
default: break;
}
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index a66378e..90d5840 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -98,25 +98,25 @@ class TreeTransform {
class ForgetPartiallySubstitutedPackRAII {
Derived &Self;
TemplateArgument Old;
-
+
public:
ForgetPartiallySubstitutedPackRAII(Derived &Self) : Self(Self) {
Old = Self.ForgetPartiallySubstitutedPack();
}
-
+
~ForgetPartiallySubstitutedPackRAII() {
Self.RememberPartiallySubstitutedPack(Old);
}
};
-
+
protected:
Sema &SemaRef;
-
+
/// \brief The set of local declarations that have been transformed, for
/// cases where we are forced to build new declarations within the transformer
/// rather than in the subclass (e.g., lambda closure types).
llvm::DenseMap<Decl *, Decl *> TransformedLocalDecls;
-
+
public:
/// \brief Initializes a new tree transformer.
TreeTransform(Sema &SemaRef) : SemaRef(SemaRef) { }
@@ -177,7 +177,7 @@ public:
DeclarationName Entity) : Self(Self) {
OldLocation = Self.getDerived().getBaseLocation();
OldEntity = Self.getDerived().getBaseEntity();
-
+
if (Location.isValid())
Self.getDerived().setBase(Location, Entity);
}
@@ -207,7 +207,7 @@ public:
bool DropCallArgument(Expr *E) {
return E->isDefaultArgument();
}
-
+
/// \brief Determine whether we should expand a pack expansion with the
/// given set of parameter packs into separate arguments by repeatedly
/// transforming the pattern.
@@ -221,12 +221,9 @@ public:
/// \param PatternRange The source range that covers the entire pattern of
/// the pack expansion.
///
- /// \param Unexpanded The set of unexpanded parameter packs within the
+ /// \param Unexpanded The set of unexpanded parameter packs within the
/// pattern.
///
- /// \param NumUnexpanded The number of unexpanded parameter packs in
- /// \p Unexpanded.
- ///
/// \param ShouldExpand Will be set to \c true if the transformer should
/// expand the corresponding pack expansions into separate arguments. When
/// set, \c NumExpansions must also be set.
@@ -244,9 +241,9 @@ public:
/// The callee must set this value when \c ShouldExpand is \c true; it may
/// set this value in other cases.
///
- /// \returns true if an error occurred (e.g., because the parameter packs
- /// are to be instantiated with arguments of different lengths), false
- /// otherwise. If false, \c ShouldExpand (and possibly \c NumExpansions)
+ /// \returns true if an error occurred (e.g., because the parameter packs
+ /// are to be instantiated with arguments of different lengths), false
+ /// otherwise. If false, \c ShouldExpand (and possibly \c NumExpansions)
/// must be set.
bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
SourceRange PatternRange,
@@ -257,7 +254,7 @@ public:
ShouldExpand = false;
return false;
}
-
+
/// \brief "Forget" about the partially-substituted pack template argument,
/// when performing an instantiation that must preserve the parameter pack
/// use.
@@ -266,18 +263,18 @@ public:
TemplateArgument ForgetPartiallySubstitutedPack() {
return TemplateArgument();
}
-
+
/// \brief "Remember" the partially-substituted pack template argument
/// after performing an instantiation that must preserve the parameter pack
/// use.
///
/// This routine is meant to be overridden by the template instantiator.
void RememberPartiallySubstitutedPack(TemplateArgument Arg) { }
-
+
/// \brief Note to the derived class when a function parameter pack is
/// being expanded.
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { }
-
+
/// \brief Transforms the given type into another type.
///
/// By default, this routine transforms a type by creating a
@@ -328,8 +325,8 @@ public:
/// \brief Transform the given list of expressions.
///
- /// This routine transforms a list of expressions by invoking
- /// \c TransformExpr() for each subexpression. However, it also provides
+ /// This routine transforms a list of expressions by invoking
+ /// \c TransformExpr() for each subexpression. However, it also provides
/// support for variadic templates by expanding any pack expansions (if the
/// derived class permits such expansion) along the way. When pack expansions
/// are present, the number of outputs may not equal the number of inputs.
@@ -339,7 +336,7 @@ public:
/// \param NumInputs The number of expressions in \c Inputs.
///
/// \param IsCall If \c true, then this transform is being performed on
- /// function-call arguments, and any arguments that should be dropped, will
+ /// function-call arguments, and any arguments that should be dropped, will
/// be.
///
/// \param Outputs The transformed input expressions will be added to this
@@ -352,61 +349,61 @@ public:
bool TransformExprs(Expr **Inputs, unsigned NumInputs, bool IsCall,
SmallVectorImpl<Expr *> &Outputs,
bool *ArgChanged = 0);
-
+
/// \brief Transform the given declaration, which is referenced from a type
/// or expression.
///
/// By default, acts as the identity function on declarations, unless the
/// transformer has had to transform the declaration itself. Subclasses
/// may override this function to provide alternate behavior.
- Decl *TransformDecl(SourceLocation Loc, Decl *D) {
+ Decl *TransformDecl(SourceLocation Loc, Decl *D) {
llvm::DenseMap<Decl *, Decl *>::iterator Known
= TransformedLocalDecls.find(D);
if (Known != TransformedLocalDecls.end())
return Known->second;
-
- return D;
+
+ return D;
}
- /// \brief Transform the attributes associated with the given declaration and
+ /// \brief Transform the attributes associated with the given declaration and
/// place them on the new declaration.
///
/// By default, this operation does nothing. Subclasses may override this
/// behavior to transform attributes.
void transformAttrs(Decl *Old, Decl *New) { }
-
+
/// \brief Note that a local declaration has been transformed by this
/// transformer.
///
- /// Local declarations are typically transformed via a call to
+ /// Local declarations are typically transformed via a call to
/// TransformDefinition. However, in some cases (e.g., lambda expressions),
/// the transformer itself has to transform the declarations. This routine
/// can be overridden by a subclass that keeps track of such mappings.
void transformedLocalDecl(Decl *Old, Decl *New) {
TransformedLocalDecls[Old] = New;
}
-
+
/// \brief Transform the definition of the given declaration.
///
/// By default, invokes TransformDecl() to transform the declaration.
/// Subclasses may override this function to provide alternate behavior.
- Decl *TransformDefinition(SourceLocation Loc, Decl *D) {
- return getDerived().TransformDecl(Loc, D);
+ Decl *TransformDefinition(SourceLocation Loc, Decl *D) {
+ return getDerived().TransformDecl(Loc, D);
}
/// \brief Transform the given declaration, which was the first part of a
/// nested-name-specifier in a member access expression.
///
- /// This specific declaration transformation only applies to the first
+ /// This specific declaration transformation only applies to the first
/// identifier in a nested-name-specifier of a member access expression, e.g.,
/// the \c T in \c x->T::member
///
/// By default, invokes TransformDecl() to transform the declaration.
/// Subclasses may override this function to provide alternate behavior.
- NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc) {
- return cast_or_null<NamedDecl>(getDerived().TransformDecl(Loc, D));
+ NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc) {
+ return cast_or_null<NamedDecl>(getDerived().TransformDecl(Loc, D));
}
-
+
/// \brief Transform the given nested-name-specifier with source-location
/// information.
///
@@ -436,7 +433,7 @@ public:
///
/// \param NameLoc The source location of the template name.
///
- /// \param ObjectType If we're translating a template name within a member
+ /// \param ObjectType If we're translating a template name within a member
/// access expression, this is the type of the object whose member template
/// is being referenced.
///
@@ -466,7 +463,7 @@ public:
/// \brief Transform the given set of template arguments.
///
- /// By default, this operation transforms all of the template arguments
+ /// By default, this operation transforms all of the template arguments
/// in the input set using \c TransformTemplateArgument(), and appends
/// the transformed arguments to the output list.
///
@@ -490,9 +487,9 @@ public:
/// \brief Transform the given set of template arguments.
///
- /// By default, this operation transforms all of the template arguments
+ /// By default, this operation transforms all of the template arguments
/// in the input set using \c TransformTemplateArgument(), and appends
- /// the transformed arguments to the output list.
+ /// the transformed arguments to the output list.
///
/// \param First An iterator to the first template argument.
///
@@ -530,18 +527,18 @@ public:
StmtResult
TransformSEHHandler(Stmt *Handler);
- QualType
+ QualType
TransformTemplateSpecializationType(TypeLocBuilder &TLB,
TemplateSpecializationTypeLoc TL,
TemplateName Template);
- QualType
+ QualType
TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
DependentTemplateSpecializationTypeLoc TL,
TemplateName Template,
CXXScopeSpec &SS);
- QualType
+ QualType
TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
DependentTemplateSpecializationTypeLoc TL,
NestedNameSpecifierLoc QualifierLoc);
@@ -574,6 +571,9 @@ public:
StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
ExprResult TransformCXXNamedCastExpr(CXXNamedCastExpr *E);
+ /// \brief Transform the captures and body of a lambda expression.
+ ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator);
+
#define STMT(Node, Parent) \
StmtResult Transform##Node(Node *S);
#define EXPR(Node, Parent) \
@@ -784,8 +784,8 @@ public:
ElaboratedTypeKeyword Keyword,
NestedNameSpecifierLoc QualifierLoc,
QualType Named) {
- return SemaRef.Context.getElaboratedType(Keyword,
- QualifierLoc.getNestedNameSpecifier(),
+ return SemaRef.Context.getElaboratedType(Keyword,
+ QualifierLoc.getNestedNameSpecifier(),
Named);
}
@@ -804,30 +804,30 @@ public:
// TODO: avoid TemplateName abstraction
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
- TemplateName InstName
+ TemplateName InstName
= getDerived().RebuildTemplateName(SS, *Name, NameLoc, QualType(), 0);
-
+
if (InstName.isNull())
return QualType();
-
+
// If it's still dependent, make a dependent specialization.
if (InstName.getAsDependentTemplateName())
- return SemaRef.Context.getDependentTemplateSpecializationType(Keyword,
- QualifierLoc.getNestedNameSpecifier(),
- Name,
+ return SemaRef.Context.getDependentTemplateSpecializationType(Keyword,
+ QualifierLoc.getNestedNameSpecifier(),
+ Name,
Args);
-
+
// Otherwise, make an elaborated type wrapping a non-dependent
// specialization.
QualType T =
getDerived().RebuildTemplateSpecializationType(InstName, NameLoc, Args);
if (T.isNull()) return QualType();
-
+
if (Keyword == ETK_None && QualifierLoc.getNestedNameSpecifier() == 0)
return T;
-
- return SemaRef.Context.getElaboratedType(Keyword,
- QualifierLoc.getNestedNameSpecifier(),
+
+ return SemaRef.Context.getElaboratedType(Keyword,
+ QualifierLoc.getNestedNameSpecifier(),
T);
}
@@ -847,8 +847,8 @@ public:
if (QualifierLoc.getNestedNameSpecifier()->isDependent()) {
// If the name is still dependent, just build a new dependent name type.
if (!SemaRef.computeDeclContext(SS))
- return SemaRef.Context.getDependentNameType(Keyword,
- QualifierLoc.getNestedNameSpecifier(),
+ return SemaRef.Context.getDependentNameType(Keyword,
+ QualifierLoc.getNestedNameSpecifier(),
Id);
}
@@ -875,15 +875,15 @@ public:
case LookupResult::NotFound:
case LookupResult::NotFoundInCurrentInstantiation:
break;
-
+
case LookupResult::Found:
Tag = Result.getAsSingle<TagDecl>();
break;
-
+
case LookupResult::FoundOverloaded:
case LookupResult::FoundUnresolvedValue:
llvm_unreachable("Tag lookup cannot find non-tags");
-
+
case LookupResult::Ambiguous:
// Let the LookupResult structure handle ambiguities.
return QualType();
@@ -925,8 +925,8 @@ public:
// Build the elaborated-type-specifier type.
QualType T = SemaRef.Context.getTypeDeclType(Tag);
- return SemaRef.Context.getElaboratedType(Keyword,
- QualifierLoc.getNestedNameSpecifier(),
+ return SemaRef.Context.getElaboratedType(Keyword,
+ QualifierLoc.getNestedNameSpecifier(),
T);
}
@@ -934,7 +934,7 @@ public:
///
/// By default, builds a new PackExpansionType type from the given pattern.
/// Subclasses may override this routine to provide different behavior.
- QualType RebuildPackExpansionType(QualType Pattern,
+ QualType RebuildPackExpansionType(QualType Pattern,
SourceRange PatternRange,
SourceLocation EllipsisLoc,
llvm::Optional<unsigned> NumExpansions) {
@@ -984,7 +984,7 @@ public:
QualType ObjectType);
/// \brief Build a new template name given a template template parameter pack
- /// and the
+ /// and the
///
/// By default, performs semantic analysis to determine whether the name can
/// be resolved to a specific template, then builds the appropriate kind of
@@ -1053,7 +1053,8 @@ public:
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
- StmtResult RebuildAttributedStmt(SourceLocation AttrLoc, const AttrVec &Attrs,
+ StmtResult RebuildAttributedStmt(SourceLocation AttrLoc,
+ ArrayRef<const Attr*> Attrs,
Stmt *SubStmt) {
return SemaRef.ActOnAttributedStmt(AttrLoc, Attrs, SubStmt);
}
@@ -1063,7 +1064,7 @@ public:
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
StmtResult RebuildIfStmt(SourceLocation IfLoc, Sema::FullExprArg Cond,
- VarDecl *CondVar, Stmt *Then,
+ VarDecl *CondVar, Stmt *Then,
SourceLocation ElseLoc, Stmt *Else) {
return getSema().ActOnIfStmt(IfLoc, Cond, CondVar, Then, ElseLoc, Else);
}
@@ -1074,7 +1075,7 @@ public:
/// Subclasses may override this routine to provide different behavior.
StmtResult RebuildSwitchStmtStart(SourceLocation SwitchLoc,
Expr *Cond, VarDecl *CondVar) {
- return getSema().ActOnStartOfSwitchStmt(SwitchLoc, Cond,
+ return getSema().ActOnStartOfSwitchStmt(SwitchLoc, Cond,
CondVar);
}
@@ -1112,10 +1113,10 @@ public:
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
StmtResult RebuildForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
- Stmt *Init, Sema::FullExprArg Cond,
+ Stmt *Init, Sema::FullExprArg Cond,
VarDecl *CondVar, Sema::FullExprArg Inc,
SourceLocation RParenLoc, Stmt *Body) {
- return getSema().ActOnForStmt(ForLoc, LParenLoc, Init, Cond,
+ return getSema().ActOnForStmt(ForLoc, LParenLoc, Init, Cond,
CondVar, Inc, RParenLoc, Body);
}
@@ -1173,13 +1174,24 @@ public:
MultiExprArg Clobbers,
SourceLocation RParenLoc,
bool MSAsm) {
- return getSema().ActOnAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs,
+ return getSema().ActOnAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs,
NumInputs, Names, move(Constraints),
Exprs, AsmString, Clobbers,
RParenLoc, MSAsm);
}
- /// \brief Build a new Objective-C @try statement.
+ /// \brief Build a new MS style inline asm statement.
+ ///
+ /// By default, performs semantic analysis to build the new statement.
+ /// Subclasses may override this routine to provide different behavior.
+ StmtResult RebuildMSAsmStmt(SourceLocation AsmLoc,
+ ArrayRef<Token> AsmToks,
+ ArrayRef<unsigned> LineEnds,
+ SourceLocation EndLoc) {
+ return getSema().ActOnMSAsmStmt(AsmLoc, AsmToks, LineEnds, EndLoc);
+ }
+
+ /// \brief Build a new Objective-C \@try statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1202,8 +1214,8 @@ public:
ExceptionDecl->getLocation(),
ExceptionDecl->getIdentifier());
}
-
- /// \brief Build a new Objective-C @catch statement.
+
+ /// \brief Build a new Objective-C \@catch statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1214,8 +1226,8 @@ public:
return getSema().ActOnObjCAtCatchStmt(AtLoc, RParenLoc,
Var, Body);
}
-
- /// \brief Build a new Objective-C @finally statement.
+
+ /// \brief Build a new Objective-C \@finally statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1223,8 +1235,8 @@ public:
Stmt *Body) {
return getSema().ActOnObjCAtFinallyStmt(AtLoc, Body);
}
-
- /// \brief Build a new Objective-C @throw statement.
+
+ /// \brief Build a new Objective-C \@throw statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1232,8 +1244,8 @@ public:
Expr *Operand) {
return getSema().BuildObjCAtThrowStmt(AtLoc, Operand);
}
-
- /// \brief Rebuild the operand to an Objective-C @synchronized statement.
+
+ /// \brief Rebuild the operand to an Objective-C \@synchronized statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1242,7 +1254,7 @@ public:
return getSema().ActOnObjCAtSynchronizedOperand(atLoc, object);
}
- /// \brief Build a new Objective-C @synchronized statement.
+ /// \brief Build a new Objective-C \@synchronized statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1251,7 +1263,7 @@ public:
return getSema().ActOnObjCAtSynchronizedStmt(AtLoc, Object, Body);
}
- /// \brief Build a new Objective-C @autoreleasepool statement.
+ /// \brief Build a new Objective-C \@autoreleasepool statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
@@ -1260,16 +1272,6 @@ public:
return getSema().ActOnObjCAutoreleasePoolStmt(AtLoc, Body);
}
- /// \brief Build the collection operand to a new Objective-C fast
- /// enumeration statement.
- ///
- /// By default, performs semantic analysis to build the new statement.
- /// Subclasses may override this routine to provide different behavior.
- ExprResult RebuildObjCForCollectionOperand(SourceLocation forLoc,
- Expr *collection) {
- return getSema().ActOnObjCForCollectionOperand(forLoc, collection);
- }
-
/// \brief Build a new Objective-C fast enumeration statement.
///
/// By default, performs semantic analysis to build the new statement.
@@ -1280,13 +1282,16 @@ public:
Expr *Collection,
SourceLocation RParenLoc,
Stmt *Body) {
- return getSema().ActOnObjCForCollectionStmt(ForLoc, LParenLoc,
- Element,
+ StmtResult ForEachStmt = getSema().ActOnObjCForCollectionStmt(ForLoc, LParenLoc,
+ Element,
Collection,
- RParenLoc,
- Body);
+ RParenLoc);
+ if (ForEachStmt.isInvalid())
+ return StmtError();
+
+ return getSema().FinishObjCForCollectionStmt(ForEachStmt.take(), Body);
}
-
+
/// \brief Build a new C++ exception declaration.
///
/// By default, performs semantic analysis to build the new decaration.
@@ -1342,7 +1347,7 @@ public:
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
- StmtResult RebuildMSDependentExistsStmt(SourceLocation KeywordLoc,
+ StmtResult RebuildMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
NestedNameSpecifierLoc QualifierLoc,
DeclarationNameInfo NameInfo,
@@ -1358,7 +1363,7 @@ public:
StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body) {
return getSema().FinishCXXForRangeStmt(ForRange, Body);
}
-
+
StmtResult RebuildSEHTryStmt(bool IsCXXTry,
SourceLocation TryLoc,
Stmt *TryBlock,
@@ -1448,8 +1453,8 @@ public:
return getSema().BuildBuiltinOffsetOf(OperatorLoc, Type, Components,
NumComponents, RParenLoc);
}
-
- /// \brief Build a new sizeof, alignof or vec_step expression with a
+
+ /// \brief Build a new sizeof, alignof or vec_step expression with a
/// type argument.
///
/// By default, performs semantic analysis to build the new expression.
@@ -1637,7 +1642,7 @@ public:
= SemaRef.ActOnInitList(LBraceLoc, move(Inits), RBraceLoc);
if (Result.isInvalid() || ResultTy->isDependentType())
return move(Result);
-
+
// Patch in the result type we were given, which may have been computed
// when the initial InitListExpr was built.
InitListExpr *ILE = cast<InitListExpr>((Expr *)Result.get());
@@ -1887,7 +1892,7 @@ public:
SourceLocation TypeidLoc,
TypeSourceInfo *Operand,
SourceLocation RParenLoc) {
- return getSema().BuildCXXTypeId(TypeInfoType, TypeidLoc, Operand,
+ return getSema().BuildCXXTypeId(TypeInfoType, TypeidLoc, Operand,
RParenLoc);
}
@@ -1912,7 +1917,7 @@ public:
SourceLocation TypeidLoc,
TypeSourceInfo *Operand,
SourceLocation RParenLoc) {
- return getSema().BuildCXXUuidof(TypeInfoType, TypeidLoc, Operand,
+ return getSema().BuildCXXUuidof(TypeInfoType, TypeidLoc, Operand,
RParenLoc);
}
@@ -1956,7 +1961,7 @@ public:
/// By default, builds a new default-argument expression, which does not
/// require any semantic analysis. Subclasses may override this routine to
/// provide different behavior.
- ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc,
+ ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc,
ParmVarDecl *Param) {
return getSema().Owned(CXXDefaultArgExpr::Create(getSema().Context, Loc,
Param));
@@ -2046,7 +2051,7 @@ public:
SourceLocation RParenLoc) {
return getSema().BuildTypeTrait(Trait, StartLoc, Args, RParenLoc);
}
-
+
/// \brief Build a new array type trait expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -2117,10 +2122,10 @@ public:
CXXConstructExpr::ConstructionKind ConstructKind,
SourceRange ParenRange) {
ASTOwningVector<Expr*> ConvertedArgs(SemaRef);
- if (getSema().CompleteConstructorCall(Constructor, move(Args), Loc,
+ if (getSema().CompleteConstructorCall(Constructor, move(Args), Loc,
ConvertedArgs))
return ExprError();
-
+
return getSema().BuildCXXConstructExpr(Loc, T, Constructor, IsElidable,
move_arg(ConvertedArgs),
HadMultipleCandidates,
@@ -2211,31 +2216,39 @@ public:
}
/// \brief Build a new expression to compute the length of a parameter pack.
- ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc, NamedDecl *Pack,
- SourceLocation PackLoc,
+ ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc, NamedDecl *Pack,
+ SourceLocation PackLoc,
SourceLocation RParenLoc,
llvm::Optional<unsigned> Length) {
if (Length)
- return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(),
- OperatorLoc, Pack, PackLoc,
+ return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(),
+ OperatorLoc, Pack, PackLoc,
RParenLoc, *Length);
-
- return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(),
- OperatorLoc, Pack, PackLoc,
+
+ return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(),
+ OperatorLoc, Pack, PackLoc,
RParenLoc);
}
+ /// \brief Build a new Objective-C boxed expression.
+ ///
+ /// By default, performs semantic analysis to build the new expression.
+ /// Subclasses may override this routine to provide different behavior.
+ ExprResult RebuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) {
+ return getSema().BuildObjCBoxedExpr(SR, ValueExpr);
+ }
+
/// \brief Build a new Objective-C array literal.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildObjCArrayLiteral(SourceRange Range,
Expr **Elements, unsigned NumElements) {
- return getSema().BuildObjCArrayLiteral(Range,
+ return getSema().BuildObjCArrayLiteral(Range,
MultiExprArg(Elements, NumElements));
}
-
- ExprResult RebuildObjCSubscriptRefExpr(SourceLocation RB,
+
+ ExprResult RebuildObjCSubscriptRefExpr(SourceLocation RB,
Expr *Base, Expr *Key,
ObjCMethodDecl *getterMethod,
ObjCMethodDecl *setterMethod) {
@@ -2252,8 +2265,8 @@ public:
unsigned NumElements) {
return getSema().BuildObjCDictionaryLiteral(Range, Elements, NumElements);
}
-
- /// \brief Build a new Objective-C @encode expression.
+
+ /// \brief Build a new Objective-C \@encode expression.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
@@ -2269,7 +2282,7 @@ public:
Selector Sel,
ArrayRef<SourceLocation> SelectorLocs,
ObjCMethodDecl *Method,
- SourceLocation LBracLoc,
+ SourceLocation LBracLoc,
MultiExprArg Args,
SourceLocation RBracLoc) {
return SemaRef.BuildClassMessage(ReceiverTypeInfo,
@@ -2284,7 +2297,7 @@ public:
Selector Sel,
ArrayRef<SourceLocation> SelectorLocs,
ObjCMethodDecl *Method,
- SourceLocation LBracLoc,
+ SourceLocation LBracLoc,
MultiExprArg Args,
SourceLocation RBracLoc) {
return SemaRef.BuildInstanceMessage(Receiver,
@@ -2312,15 +2325,15 @@ public:
false);
if (Result.isInvalid() || Base.isInvalid())
return ExprError();
-
+
if (Result.get())
return move(Result);
-
+
return getSema().BuildMemberReferenceExpr(Base.get(), Base.get()->getType(),
/*FIXME:*/IvarLoc, IsArrow,
SS, SourceLocation(),
/*FirstQualifierInScope=*/0,
- R,
+ R,
/*TemplateArgs=*/0);
}
@@ -2328,7 +2341,7 @@ public:
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
- ExprResult RebuildObjCPropertyRefExpr(Expr *BaseArg,
+ ExprResult RebuildObjCPropertyRefExpr(Expr *BaseArg,
ObjCPropertyDecl *Property,
SourceLocation PropertyLoc) {
CXXScopeSpec SS;
@@ -2341,18 +2354,18 @@ public:
SS, 0, false);
if (Result.isInvalid() || Base.isInvalid())
return ExprError();
-
+
if (Result.get())
return move(Result);
-
+
return getSema().BuildMemberReferenceExpr(Base.get(), Base.get()->getType(),
- /*FIXME:*/PropertyLoc, IsArrow,
+ /*FIXME:*/PropertyLoc, IsArrow,
SS, SourceLocation(),
/*FirstQualifierInScope=*/0,
- R,
+ R,
/*TemplateArgs=*/0);
}
-
+
/// \brief Build a new Objective-C property reference expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -2384,18 +2397,18 @@ public:
SS, 0, false);
if (Result.isInvalid() || Base.isInvalid())
return ExprError();
-
+
if (Result.get())
return move(Result);
-
+
return getSema().BuildMemberReferenceExpr(Base.get(), Base.get()->getType(),
/*FIXME:*/IsaLoc, IsArrow,
SS, SourceLocation(),
/*FirstQualifierInScope=*/0,
- R,
+ R,
/*TemplateArgs=*/0);
}
-
+
/// \brief Build a new shuffle vector expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -2437,7 +2450,7 @@ public:
/// \brief Build a new template argument pack expansion.
///
/// By default, performs semantic analysis to build a new pack expansion
- /// for a template argument. Subclasses may override this routine to provide
+ /// for a template argument. Subclasses may override this routine to provide
/// different behavior.
TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern,
SourceLocation EllipsisLoc,
@@ -2449,10 +2462,10 @@ public:
EllipsisLoc, NumExpansions);
if (Result.isInvalid())
return TemplateArgumentLoc();
-
+
return TemplateArgumentLoc(Result.get(), Result.get());
}
-
+
case TemplateArgument::Template:
return TemplateArgumentLoc(TemplateArgument(
Pattern.getArgument().getAsTemplate(),
@@ -2460,16 +2473,16 @@ public:
Pattern.getTemplateQualifierLoc(),
Pattern.getTemplateNameLoc(),
EllipsisLoc);
-
+
case TemplateArgument::Null:
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::Pack:
case TemplateArgument::TemplateExpansion:
llvm_unreachable("Pack expansion pattern has no parameter packs");
-
+
case TemplateArgument::Type:
- if (TypeSourceInfo *Expansion
+ if (TypeSourceInfo *Expansion
= getSema().CheckPackExpansion(Pattern.getTypeSourceInfo(),
EllipsisLoc,
NumExpansions))
@@ -2477,14 +2490,14 @@ public:
Expansion);
break;
}
-
+
return TemplateArgumentLoc();
}
-
+
/// \brief Build a new expression pack expansion.
///
/// By default, performs semantic analysis to build a new pack expansion
- /// for an expression. Subclasses may override this routine to provide
+ /// for an expression. Subclasses may override this routine to provide
/// different behavior.
ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
llvm::Optional<unsigned> NumExpansions) {
@@ -2573,8 +2586,8 @@ ExprResult TreeTransform<Derived>::TransformExpr(Expr *E) {
}
template<typename Derived>
-bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
- unsigned NumInputs,
+bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
+ unsigned NumInputs,
bool IsCall,
SmallVectorImpl<Expr *> &Outputs,
bool *ArgChanged) {
@@ -2583,17 +2596,17 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
if (IsCall && getDerived().DropCallArgument(Inputs[I])) {
if (ArgChanged)
*ArgChanged = true;
-
+
break;
}
-
+
if (PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(Inputs[I])) {
Expr *Pattern = Expansion->getPattern();
-
+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
-
+
// Determine whether the set of unexpanded parameter packs can and should
// be expanded.
bool Expand = true;
@@ -2607,22 +2620,22 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
Expand, RetainExpansion,
NumExpansions))
return true;
-
+
if (!Expand) {
// The transform has determined that we should perform a simple
- // transformation on the pack expansion, producing another pack
+ // transformation on the pack expansion, producing another pack
// expansion.
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
ExprResult OutPattern = getDerived().TransformExpr(Pattern);
if (OutPattern.isInvalid())
return true;
-
- ExprResult Out = getDerived().RebuildPackExpansion(OutPattern.get(),
+
+ ExprResult Out = getDerived().RebuildPackExpansion(OutPattern.get(),
Expansion->getEllipsisLoc(),
NumExpansions);
if (Out.isInvalid())
return true;
-
+
if (ArgChanged)
*ArgChanged = true;
Outputs.push_back(Out.get());
@@ -2632,7 +2645,7 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
// Record right away that the argument was changed. This needs
// to happen even if the array expands to nothing.
if (ArgChanged) *ArgChanged = true;
-
+
// The transform has determined that we should perform an elementwise
// expansion of the pattern. Do so.
for (unsigned I = 0; I != *NumExpansions; ++I) {
@@ -2647,23 +2660,23 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
if (Out.isInvalid())
return true;
}
-
+
Outputs.push_back(Out.get());
}
-
+
continue;
}
-
+
ExprResult Result = getDerived().TransformExpr(Inputs[I]);
if (Result.isInvalid())
return true;
-
+
if (Result.get() != Inputs[I] && ArgChanged)
*ArgChanged = true;
-
- Outputs.push_back(Result.get());
+
+ Outputs.push_back(Result.get());
}
-
+
return false;
}
@@ -2674,7 +2687,7 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
QualType ObjectType,
NamedDecl *FirstQualifierInScope) {
SmallVector<NestedNameSpecifierLoc, 4> Qualifiers;
- for (NestedNameSpecifierLoc Qualifier = NNS; Qualifier;
+ for (NestedNameSpecifierLoc Qualifier = NNS; Qualifier;
Qualifier = Qualifier.getPrefix())
Qualifiers.push_back(Qualifier);
@@ -2682,19 +2695,19 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
while (!Qualifiers.empty()) {
NestedNameSpecifierLoc Q = Qualifiers.pop_back_val();
NestedNameSpecifier *QNNS = Q.getNestedNameSpecifier();
-
+
switch (QNNS->getKind()) {
case NestedNameSpecifier::Identifier:
- if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/0,
+ if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/0,
*QNNS->getAsIdentifier(),
- Q.getLocalBeginLoc(),
+ Q.getLocalBeginLoc(),
Q.getLocalEndLoc(),
- ObjectType, false, SS,
+ ObjectType, false, SS,
FirstQualifierInScope, false))
return NestedNameSpecifierLoc();
-
+
break;
-
+
case NestedNameSpecifier::Namespace: {
NamespaceDecl *NS
= cast_or_null<NamespaceDecl>(
@@ -2704,35 +2717,35 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
SS.Extend(SemaRef.Context, NS, Q.getLocalBeginLoc(), Q.getLocalEndLoc());
break;
}
-
+
case NestedNameSpecifier::NamespaceAlias: {
NamespaceAliasDecl *Alias
= cast_or_null<NamespaceAliasDecl>(
getDerived().TransformDecl(Q.getLocalBeginLoc(),
QNNS->getAsNamespaceAlias()));
- SS.Extend(SemaRef.Context, Alias, Q.getLocalBeginLoc(),
+ SS.Extend(SemaRef.Context, Alias, Q.getLocalBeginLoc(),
Q.getLocalEndLoc());
break;
}
-
+
case NestedNameSpecifier::Global:
// There is no meaningful transformation that one could perform on the
// global scope.
SS.MakeGlobal(SemaRef.Context, Q.getBeginLoc());
break;
-
+
case NestedNameSpecifier::TypeSpecWithTemplate:
case NestedNameSpecifier::TypeSpec: {
TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType,
FirstQualifierInScope, SS);
-
+
if (!TL)
return NestedNameSpecifierLoc();
-
+
if (TL.getType()->isDependentType() || TL.getType()->isRecordType() ||
- (SemaRef.getLangOpts().CPlusPlus0x &&
+ (SemaRef.getLangOpts().CPlusPlus0x &&
TL.getType()->isEnumeralType())) {
- assert(!TL.getType().hasLocalQualifiers() &&
+ assert(!TL.getType().hasLocalQualifiers() &&
"Can't get cv-qualifiers here");
if (TL.getType()->isEnumeralType())
SemaRef.Diag(TL.getBeginLoc(),
@@ -2745,24 +2758,24 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
// error because a previous error should have already been emitted.
TypedefTypeLoc* TTL = dyn_cast<TypedefTypeLoc>(&TL);
if (!TTL || !TTL->getTypedefNameDecl()->isInvalidDecl()) {
- SemaRef.Diag(TL.getBeginLoc(), diag::err_nested_name_spec_non_tag)
+ SemaRef.Diag(TL.getBeginLoc(), diag::err_nested_name_spec_non_tag)
<< TL.getType() << SS.getRange();
}
return NestedNameSpecifierLoc();
}
}
-
+
// The qualifier-in-scope and object type only apply to the leftmost entity.
FirstQualifierInScope = 0;
ObjectType = QualType();
}
-
+
// Don't rebuild the nested-name-specifier if we don't have to.
- if (SS.getScopeRep() == NNS.getNestedNameSpecifier() &&
+ if (SS.getScopeRep() == NNS.getNestedNameSpecifier() &&
!getDerived().AlwaysRebuild())
return NNS;
-
- // If we can re-use the source-location data from the original
+
+ // If we can re-use the source-location data from the original
// nested-name-specifier, do so.
if (SS.location_size() == NNS.getDataLength() &&
memcmp(SS.location_data(), NNS.getOpaqueData(), SS.location_size()) == 0)
@@ -2833,60 +2846,60 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) {
TemplateDecl *Template = QTN->getTemplateDecl();
assert(Template && "qualified template name must refer to a template");
-
+
TemplateDecl *TransTemplate
- = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc,
+ = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc,
Template));
if (!TransTemplate)
return TemplateName();
-
+
if (!getDerived().AlwaysRebuild() &&
SS.getScopeRep() == QTN->getQualifier() &&
TransTemplate == Template)
return Name;
-
+
return getDerived().RebuildTemplateName(SS, QTN->hasTemplateKeyword(),
TransTemplate);
}
-
+
if (DependentTemplateName *DTN = Name.getAsDependentTemplateName()) {
if (SS.getScopeRep()) {
// These apply to the scope specifier, not the template.
ObjectType = QualType();
FirstQualifierInScope = 0;
- }
-
+ }
+
if (!getDerived().AlwaysRebuild() &&
SS.getScopeRep() == DTN->getQualifier() &&
ObjectType.isNull())
return Name;
-
+
if (DTN->isIdentifier()) {
return getDerived().RebuildTemplateName(SS,
- *DTN->getIdentifier(),
+ *DTN->getIdentifier(),
NameLoc,
ObjectType,
FirstQualifierInScope);
}
-
+
return getDerived().RebuildTemplateName(SS, DTN->getOperator(), NameLoc,
ObjectType);
}
-
+
if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
TemplateDecl *TransTemplate
- = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc,
+ = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc,
Template));
if (!TransTemplate)
return TemplateName();
-
+
if (!getDerived().AlwaysRebuild() &&
TransTemplate == Template)
return Name;
-
+
return TemplateName(TransTemplate);
}
-
+
if (SubstTemplateTemplateParmPackStorage *SubstPack
= Name.getAsSubstTemplateTemplateParmPack()) {
TemplateTemplateParmDecl *TransParam
@@ -2894,15 +2907,15 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
getDerived().TransformDecl(NameLoc, SubstPack->getParameterPack()));
if (!TransParam)
return TemplateName();
-
+
if (!getDerived().AlwaysRebuild() &&
TransParam == SubstPack->getParameterPack())
return Name;
-
- return getDerived().RebuildTemplateName(TransParam,
+
+ return getDerived().RebuildTemplateName(TransParam,
SubstPack->getArgumentPack());
}
-
+
// These should be getting filtered out before they reach the AST.
llvm_unreachable("overloaded function decl survived to here");
}
@@ -2920,7 +2933,7 @@ void TreeTransform<Derived>::InventTemplateArgumentLoc(
case TemplateArgument::Type:
Output = TemplateArgumentLoc(Arg,
SemaRef.Context.getTrivialTypeSourceInfo(Arg.getAsType(), Loc));
-
+
break;
case TemplateArgument::Template:
@@ -2931,16 +2944,16 @@ void TreeTransform<Derived>::InventTemplateArgumentLoc(
Builder.MakeTrivial(SemaRef.Context, DTN->getQualifier(), Loc);
else if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName())
Builder.MakeTrivial(SemaRef.Context, QTN->getQualifier(), Loc);
-
+
if (Arg.getKind() == TemplateArgument::Template)
- Output = TemplateArgumentLoc(Arg,
+ Output = TemplateArgumentLoc(Arg,
Builder.getWithLocInContext(SemaRef.Context),
Loc);
else
- Output = TemplateArgumentLoc(Arg,
+ Output = TemplateArgumentLoc(Arg,
Builder.getWithLocInContext(SemaRef.Context),
Loc, Loc);
-
+
break;
}
@@ -3008,7 +3021,7 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
if (!QualifierLoc)
return true;
}
-
+
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
TemplateName Template
@@ -3016,7 +3029,7 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
Input.getTemplateNameLoc());
if (Template.isNull())
return true;
-
+
Output = TemplateArgumentLoc(TemplateArgument(Template), QualifierLoc,
Input.getTemplateNameLoc());
return false;
@@ -3063,8 +3076,8 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
= new (getSema().Context) TemplateArgument[TransformedArgs.size()];
std::copy(TransformedArgs.begin(), TransformedArgs.end(),
TransformedArgsPtr);
- Output = TemplateArgumentLoc(TemplateArgument(TransformedArgsPtr,
- TransformedArgs.size()),
+ Output = TemplateArgumentLoc(TemplateArgument(TransformedArgsPtr,
+ TransformedArgs.size()),
Input.getLocInfo());
return false;
}
@@ -3080,48 +3093,48 @@ template<typename Derived, typename InputIterator>
class TemplateArgumentLocInventIterator {
TreeTransform<Derived> &Self;
InputIterator Iter;
-
+
public:
typedef TemplateArgumentLoc value_type;
typedef TemplateArgumentLoc reference;
typedef typename std::iterator_traits<InputIterator>::difference_type
difference_type;
typedef std::input_iterator_tag iterator_category;
-
+
class pointer {
TemplateArgumentLoc Arg;
-
+
public:
explicit pointer(TemplateArgumentLoc Arg) : Arg(Arg) { }
-
+
const TemplateArgumentLoc *operator->() const { return &Arg; }
};
-
+
TemplateArgumentLocInventIterator() { }
-
+
explicit TemplateArgumentLocInventIterator(TreeTransform<Derived> &Self,
InputIterator Iter)
: Self(Self), Iter(Iter) { }
-
+
TemplateArgumentLocInventIterator &operator++() {
++Iter;
return *this;
}
-
+
TemplateArgumentLocInventIterator operator++(int) {
TemplateArgumentLocInventIterator Old(*this);
++(*this);
return Old;
}
-
+
reference operator*() const {
TemplateArgumentLoc Result;
Self.InventTemplateArgumentLoc(*Iter, Result);
return Result;
}
-
+
pointer operator->() const { return pointer(**this); }
-
+
friend bool operator==(const TemplateArgumentLocInventIterator &X,
const TemplateArgumentLocInventIterator &Y) {
return X.Iter == Y.Iter;
@@ -3132,7 +3145,7 @@ public:
return X.Iter != Y.Iter;
}
};
-
+
template<typename Derived>
template<typename InputIterator>
bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First,
@@ -3141,39 +3154,39 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First,
for (; First != Last; ++First) {
TemplateArgumentLoc Out;
TemplateArgumentLoc In = *First;
-
+
if (In.getArgument().getKind() == TemplateArgument::Pack) {
// Unpack argument packs, which we translate them into separate
// arguments.
// FIXME: We could do much better if we could guarantee that the
// TemplateArgumentLocInfo for the pack expansion would be usable for
// all of the template arguments in the argument pack.
- typedef TemplateArgumentLocInventIterator<Derived,
+ typedef TemplateArgumentLocInventIterator<Derived,
TemplateArgument::pack_iterator>
PackLocIterator;
- if (TransformTemplateArguments(PackLocIterator(*this,
+ if (TransformTemplateArguments(PackLocIterator(*this,
In.getArgument().pack_begin()),
PackLocIterator(*this,
In.getArgument().pack_end()),
Outputs))
return true;
-
+
continue;
}
-
+
if (In.getArgument().isPackExpansion()) {
// We have a pack expansion, for which we will be substituting into
// the pattern.
SourceLocation Ellipsis;
llvm::Optional<unsigned> OrigNumExpansions;
TemplateArgumentLoc Pattern
- = In.getPackExpansionPattern(Ellipsis, OrigNumExpansions,
+ = In.getPackExpansionPattern(Ellipsis, OrigNumExpansions,
getSema().Context);
-
+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
-
+
// Determine whether the set of unexpanded parameter packs can and should
// be expanded.
bool Expand = true;
@@ -3182,29 +3195,29 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First,
if (getDerived().TryExpandParameterPacks(Ellipsis,
Pattern.getSourceRange(),
Unexpanded,
- Expand,
+ Expand,
RetainExpansion,
NumExpansions))
return true;
-
+
if (!Expand) {
// The transform has determined that we should perform a simple
- // transformation on the pack expansion, producing another pack
+ // transformation on the pack expansion, producing another pack
// expansion.
TemplateArgumentLoc OutPattern;
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
if (getDerived().TransformTemplateArgument(Pattern, OutPattern))
return true;
-
+
Out = getDerived().RebuildPackExpansion(OutPattern, Ellipsis,
NumExpansions);
if (Out.getArgument().isNull())
return true;
-
+
Outputs.addArgument(Out);
continue;
}
-
+
// The transform has determined that we should perform an elementwise
// expansion of the pattern. Do so.
for (unsigned I = 0; I != *NumExpansions; ++I) {
@@ -3212,43 +3225,43 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First,
if (getDerived().TransformTemplateArgument(Pattern, Out))
return true;
-
+
if (Out.getArgument().containsUnexpandedParameterPack()) {
Out = getDerived().RebuildPackExpansion(Out, Ellipsis,
OrigNumExpansions);
if (Out.getArgument().isNull())
return true;
}
-
+
Outputs.addArgument(Out);
}
-
+
// If we're supposed to retain a pack expansion, do so by temporarily
// forgetting the partially-substituted parameter pack.
if (RetainExpansion) {
ForgetPartiallySubstitutedPackRAII Forget(getDerived());
-
+
if (getDerived().TransformTemplateArgument(Pattern, Out))
return true;
-
+
Out = getDerived().RebuildPackExpansion(Out, Ellipsis,
OrigNumExpansions);
if (Out.getArgument().isNull())
return true;
-
+
Outputs.addArgument(Out);
}
-
+
continue;
}
-
- // The simple case:
+
+ // The simple case:
if (getDerived().TransformTemplateArgument(In, Out))
return true;
-
+
Outputs.addArgument(Out);
}
-
+
return false;
}
@@ -3266,7 +3279,7 @@ QualType TreeTransform<Derived>::TransformType(QualType T) {
// eventually turn into transformations on TypeLocs.
TypeSourceInfo *DI = getSema().Context.getTrivialTypeSourceInfo(T,
getDerived().getBaseLocation());
-
+
TypeSourceInfo *NewDI = getDerived().TransformType(DI);
if (!NewDI)
@@ -3336,19 +3349,19 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB,
if (!Result->isObjCLifetimeType() && !Result->isDependentType())
Quals.removeObjCLifetime();
else if (Result.getObjCLifetime()) {
- // Objective-C ARC:
+ // Objective-C ARC:
// A lifetime qualifier applied to a substituted template parameter
// overrides the lifetime qualifier from the template argument.
- if (const SubstTemplateTypeParmType *SubstTypeParam
+ if (const SubstTemplateTypeParmType *SubstTypeParam
= dyn_cast<SubstTemplateTypeParmType>(Result)) {
QualType Replacement = SubstTypeParam->getReplacementType();
Qualifiers Qs = Replacement.getQualifiers();
Qs.removeObjCLifetime();
- Replacement
+ Replacement
= SemaRef.Context.getQualifiedType(Replacement.getUnqualifiedType(),
Qs);
Result = SemaRef.Context.getSubstTemplateTypeParmType(
- SubstTypeParam->getReplacedParameter(),
+ SubstTypeParam->getReplacedParameter(),
Replacement);
TLB.TypeWasModifiedSafely(Result);
} else {
@@ -3357,7 +3370,7 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB,
SourceRange R = TLB.getTemporaryTypeLoc(Result).getSourceRange();
SemaRef.Diag(R.getBegin(), diag::err_attr_objc_ownership_redundant)
<< Result << R;
-
+
Quals.removeObjCLifetime();
}
}
@@ -3380,37 +3393,37 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeLoc TL,
QualType T = TL.getType();
if (getDerived().AlreadyTransformed(T))
return TL;
-
+
TypeLocBuilder TLB;
QualType Result;
-
+
if (isa<TemplateSpecializationType>(T)) {
TemplateSpecializationTypeLoc SpecTL
= cast<TemplateSpecializationTypeLoc>(TL);
-
+
TemplateName Template =
getDerived().TransformTemplateName(SS,
SpecTL.getTypePtr()->getTemplateName(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup);
- if (Template.isNull())
+ if (Template.isNull())
return TypeLoc();
-
- Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL,
+
+ Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL,
Template);
} else if (isa<DependentTemplateSpecializationType>(T)) {
DependentTemplateSpecializationTypeLoc SpecTL
= cast<DependentTemplateSpecializationTypeLoc>(TL);
-
+
TemplateName Template
- = getDerived().RebuildTemplateName(SS,
- *SpecTL.getTypePtr()->getIdentifier(),
+ = getDerived().RebuildTemplateName(SS,
+ *SpecTL.getTypePtr()->getIdentifier(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup);
if (Template.isNull())
return TypeLoc();
-
- Result = getDerived().TransformDependentTemplateSpecializationType(TLB,
+
+ Result = getDerived().TransformDependentTemplateSpecializationType(TLB,
SpecTL,
Template,
SS);
@@ -3418,10 +3431,10 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeLoc TL,
// Nothing special needs to be done for these.
Result = getDerived().TransformType(TLB, TL);
}
-
- if (Result.isNull())
+
+ if (Result.isNull())
return TypeLoc();
-
+
return TLB.getTypeSourceInfo(SemaRef.Context, Result)->getTypeLoc();
}
@@ -3432,42 +3445,42 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeSourceInfo *TSInfo,
NamedDecl *UnqualLookup,
CXXScopeSpec &SS) {
// FIXME: Painfully copy-paste from the above!
-
+
QualType T = TSInfo->getType();
if (getDerived().AlreadyTransformed(T))
return TSInfo;
-
+
TypeLocBuilder TLB;
QualType Result;
-
+
TypeLoc TL = TSInfo->getTypeLoc();
if (isa<TemplateSpecializationType>(T)) {
TemplateSpecializationTypeLoc SpecTL
= cast<TemplateSpecializationTypeLoc>(TL);
-
+
TemplateName Template
= getDerived().TransformTemplateName(SS,
SpecTL.getTypePtr()->getTemplateName(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup);
- if (Template.isNull())
+ if (Template.isNull())
return 0;
-
- Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL,
+
+ Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL,
Template);
} else if (isa<DependentTemplateSpecializationType>(T)) {
DependentTemplateSpecializationTypeLoc SpecTL
= cast<DependentTemplateSpecializationTypeLoc>(TL);
-
+
TemplateName Template
- = getDerived().RebuildTemplateName(SS,
- *SpecTL.getTypePtr()->getIdentifier(),
+ = getDerived().RebuildTemplateName(SS,
+ *SpecTL.getTypePtr()->getIdentifier(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup);
if (Template.isNull())
return 0;
-
- Result = getDerived().TransformDependentTemplateSpecializationType(TLB,
+
+ Result = getDerived().TransformDependentTemplateSpecializationType(TLB,
SpecTL,
Template,
SS);
@@ -3475,10 +3488,10 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeSourceInfo *TSInfo,
// Nothing special needs to be done for these.
Result = getDerived().TransformType(TLB, TL);
}
-
- if (Result.isNull())
+
+ if (Result.isNull())
return 0;
-
+
return TLB.getTypeSourceInfo(SemaRef.Context, Result);
}
@@ -3509,8 +3522,8 @@ QualType TreeTransform<Derived>::TransformComplexType(TypeLocBuilder &TLB,
template<typename Derived>
QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB,
PointerTypeLoc TL) {
- QualType PointeeType
- = getDerived().TransformType(TLB, TL.getPointeeLoc());
+ QualType PointeeType
+ = getDerived().TransformType(TLB, TL.getPointeeLoc());
if (PointeeType.isNull())
return QualType();
@@ -3521,7 +3534,7 @@ QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB,
// resulting pointer type is an ObjCObjectPointerType, not a
// PointerType.
Result = SemaRef.Context.getObjCObjectPointerType(PointeeType);
-
+
ObjCObjectPointerTypeLoc NewT = TLB.push<ObjCObjectPointerTypeLoc>(Result);
NewT.setStarLoc(TL.getStarLoc());
return Result;
@@ -3533,14 +3546,14 @@ QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB,
if (Result.isNull())
return QualType();
}
-
+
// Objective-C ARC can add lifetime qualifiers to the type that we're
// pointing to.
TLB.TypeWasModifiedSafely(Result->getPointeeType());
-
+
PointerTypeLoc NewT = TLB.push<PointerTypeLoc>(Result);
NewT.setSigilLoc(TL.getSigilLoc());
- return Result;
+ return Result;
}
template<typename Derived>
@@ -3548,14 +3561,14 @@ QualType
TreeTransform<Derived>::TransformBlockPointerType(TypeLocBuilder &TLB,
BlockPointerTypeLoc TL) {
QualType PointeeType
- = getDerived().TransformType(TLB, TL.getPointeeLoc());
- if (PointeeType.isNull())
- return QualType();
-
- QualType Result = TL.getType();
- if (getDerived().AlwaysRebuild() ||
- PointeeType != TL.getPointeeLoc().getType()) {
- Result = getDerived().RebuildBlockPointerType(PointeeType,
+ = getDerived().TransformType(TLB, TL.getPointeeLoc());
+ if (PointeeType.isNull())
+ return QualType();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() ||
+ PointeeType != TL.getPointeeLoc().getType()) {
+ Result = getDerived().RebuildBlockPointerType(PointeeType,
TL.getSigilLoc());
if (Result.isNull())
return QualType();
@@ -3725,7 +3738,7 @@ QualType TreeTransform<Derived>::TransformIncompleteArrayType(
if (Result.isNull())
return QualType();
}
-
+
IncompleteArrayTypeLoc NewTL = TLB.push<IncompleteArrayTypeLoc>(Result);
NewTL.setLBracketLoc(TL.getLBracketLoc());
NewTL.setRBracketLoc(TL.getRBracketLoc());
@@ -3762,7 +3775,7 @@ TreeTransform<Derived>::TransformVariableArrayType(TypeLocBuilder &TLB,
if (Result.isNull())
return QualType();
}
-
+
VariableArrayTypeLoc NewTL = TLB.push<VariableArrayTypeLoc>(Result);
NewTL.setLBracketLoc(TL.getLBracketLoc());
NewTL.setRBracketLoc(TL.getRBracketLoc());
@@ -3879,7 +3892,7 @@ QualType TreeTransform<Derived>::TransformVectorType(TypeLocBuilder &TLB,
if (Result.isNull())
return QualType();
}
-
+
VectorTypeLoc NewTL = TLB.push<VectorTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
@@ -3903,7 +3916,7 @@ QualType TreeTransform<Derived>::TransformExtVectorType(TypeLocBuilder &TLB,
if (Result.isNull())
return QualType();
}
-
+
ExtVectorTypeLoc NewTL = TLB.push<ExtVectorTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
@@ -3918,29 +3931,29 @@ TreeTransform<Derived>::TransformFunctionTypeParam(ParmVarDecl *OldParm,
bool ExpectParameterPack) {
TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo();
TypeSourceInfo *NewDI = 0;
-
+
if (NumExpansions && isa<PackExpansionType>(OldDI->getType())) {
- // If we're substituting into a pack expansion type and we know the
+ // If we're substituting into a pack expansion type and we know the
// length we want to expand to, just substitute for the pattern.
TypeLoc OldTL = OldDI->getTypeLoc();
PackExpansionTypeLoc OldExpansionTL = cast<PackExpansionTypeLoc>(OldTL);
-
+
TypeLocBuilder TLB;
TypeLoc NewTL = OldDI->getTypeLoc();
TLB.reserve(NewTL.getFullDataSize());
-
- QualType Result = getDerived().TransformType(TLB,
+
+ QualType Result = getDerived().TransformType(TLB,
OldExpansionTL.getPatternLoc());
if (Result.isNull())
return 0;
-
- Result = RebuildPackExpansionType(Result,
- OldExpansionTL.getPatternLoc().getSourceRange(),
+
+ Result = RebuildPackExpansionType(Result,
+ OldExpansionTL.getPatternLoc().getSourceRange(),
OldExpansionTL.getEllipsisLoc(),
NumExpansions);
if (Result.isNull())
return 0;
-
+
PackExpansionTypeLoc NewExpansionTL
= TLB.push<PackExpansionTypeLoc>(Result);
NewExpansionTL.setEllipsisLoc(OldExpansionTL.getEllipsisLoc());
@@ -4002,27 +4015,27 @@ bool TreeTransform<Derived>::
NumExpansions = OrigNumExpansions;
if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
Pattern.getSourceRange(),
- Unexpanded,
- ShouldExpand,
+ Unexpanded,
+ ShouldExpand,
RetainExpansion,
NumExpansions)) {
return true;
}
-
+
if (ShouldExpand) {
// Expand the function parameter pack into multiple, separate
// parameters.
getDerived().ExpandingFunctionParameterPack(OldParm);
for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
- ParmVarDecl *NewParm
+ ParmVarDecl *NewParm
= getDerived().TransformFunctionTypeParam(OldParm,
indexAdjustment++,
OrigNumExpansions,
/*ExpectParameterPack=*/false);
if (!NewParm)
return true;
-
+
OutParamTypes.push_back(NewParm->getType());
if (PVars)
PVars->push_back(NewParm);
@@ -4032,14 +4045,14 @@ bool TreeTransform<Derived>::
// forgetting the partially-substituted parameter pack.
if (RetainExpansion) {
ForgetPartiallySubstitutedPackRAII Forget(getDerived());
- ParmVarDecl *NewParm
+ ParmVarDecl *NewParm
= getDerived().TransformFunctionTypeParam(OldParm,
indexAdjustment++,
OrigNumExpansions,
/*ExpectParameterPack=*/false);
if (!NewParm)
return true;
-
+
OutParamTypes.push_back(NewParm->getType());
if (PVars)
PVars->push_back(NewParm);
@@ -4054,8 +4067,8 @@ bool TreeTransform<Derived>::
// We're done with the pack expansion.
continue;
}
-
- // We'll substitute the parameter now without expanding the pack
+
+ // We'll substitute the parameter now without expanding the pack
// expansion.
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
NewParm = getDerived().TransformFunctionTypeParam(OldParm,
@@ -4071,7 +4084,7 @@ bool TreeTransform<Derived>::
if (!NewParm)
return true;
-
+
OutParamTypes.push_back(NewParm->getType());
if (PVars)
PVars->push_back(NewParm);
@@ -4084,26 +4097,26 @@ bool TreeTransform<Derived>::
bool IsPackExpansion = false;
llvm::Optional<unsigned> NumExpansions;
QualType NewType;
- if (const PackExpansionType *Expansion
+ if (const PackExpansionType *Expansion
= dyn_cast<PackExpansionType>(OldType)) {
// We have a function parameter pack that may need to be expanded.
QualType Pattern = Expansion->getPattern();
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
-
+
// Determine whether we should expand the parameter packs.
bool ShouldExpand = false;
bool RetainExpansion = false;
if (getDerived().TryExpandParameterPacks(Loc, SourceRange(),
- Unexpanded,
- ShouldExpand,
+ Unexpanded,
+ ShouldExpand,
RetainExpansion,
NumExpansions)) {
return true;
}
-
+
if (ShouldExpand) {
- // Expand the function parameter pack into multiple, separate
+ // Expand the function parameter pack into multiple, separate
// parameters.
for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
@@ -4115,11 +4128,11 @@ bool TreeTransform<Derived>::
if (PVars)
PVars->push_back(0);
}
-
+
// We're done with the pack expansion.
continue;
}
-
+
// If we're supposed to retain a pack expansion, do so by temporarily
// forgetting the partially-substituted parameter pack.
if (RetainExpansion) {
@@ -4127,13 +4140,13 @@ bool TreeTransform<Derived>::
QualType NewType = getDerived().TransformType(Pattern);
if (NewType.isNull())
return true;
-
+
OutParamTypes.push_back(NewType);
if (PVars)
PVars->push_back(0);
}
- // We'll substitute the parameter now without expanding the pack
+ // We'll substitute the parameter now without expanding the pack
// expansion.
OldType = Expansion->getPattern();
IsPackExpansion = true;
@@ -4142,14 +4155,14 @@ bool TreeTransform<Derived>::
} else {
NewType = getDerived().TransformType(OldType);
}
-
+
if (NewType.isNull())
return true;
if (IsPackExpansion)
NewType = getSema().Context.getPackExpansionType(NewType,
NumExpansions);
-
+
OutParamTypes.push_back(NewType);
if (PVars)
PVars->push_back(0);
@@ -4193,22 +4206,22 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
QualType ResultType;
if (TL.getTrailingReturn()) {
- if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(),
+ if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(),
TL.getParmArray(),
TL.getNumArgs(),
- TL.getTypePtr()->arg_type_begin(),
+ TL.getTypePtr()->arg_type_begin(),
ParamTypes, &ParamDecls))
return QualType();
{
// C++11 [expr.prim.general]p3:
- // If a declaration declares a member function or member function
- // template of a class X, the expression this is a prvalue of type
+ // If a declaration declares a member function or member function
+ // template of a class X, the expression this is a prvalue of type
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
- // and the end of the function-definition, member-declarator, or
+ // and the end of the function-definition, member-declarator, or
// declarator.
Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals);
-
+
ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
if (ResultType.isNull())
return QualType();
@@ -4219,10 +4232,10 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
if (ResultType.isNull())
return QualType();
- if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(),
+ if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(),
TL.getParmArray(),
TL.getNumArgs(),
- TL.getTypePtr()->arg_type_begin(),
+ TL.getTypePtr()->arg_type_begin(),
ParamTypes, &ParamDecls))
return QualType();
}
@@ -4533,7 +4546,7 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType(
TypeLocBuilder &TLB,
SubstTemplateTypeParmTypeLoc TL) {
const SubstTemplateTypeParmType *T = TL.getTypePtr();
-
+
// Substitute into the replacement type, which itself might involve something
// that needs to be transformed. This only tends to occur with default
// template arguments of template template parameters.
@@ -4541,13 +4554,13 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType(
QualType Replacement = getDerived().TransformType(T->getReplacementType());
if (Replacement.isNull())
return QualType();
-
+
// Always canonicalize the replacement type.
Replacement = SemaRef.Context.getCanonicalType(Replacement);
QualType Result
- = SemaRef.Context.getSubstTemplateTypeParmType(T->getReplacedParameter(),
+ = SemaRef.Context.getSubstTemplateTypeParmType(T->getReplacedParameter(),
Replacement);
-
+
// Propagate type-source information.
SubstTemplateTypeParmTypeLoc NewTL
= TLB.push<SubstTemplateTypeParmTypeLoc>(Result);
@@ -4605,7 +4618,7 @@ QualType TreeTransform<Derived>::TransformAtomicType(TypeLocBuilder &TLB,
}
namespace {
- /// \brief Simple iterator that traverses the template arguments in a
+ /// \brief Simple iterator that traverses the template arguments in a
/// container that provides a \c getArgLoc() member function.
///
/// This iterator is intended to be used with the iterator form of
@@ -4614,63 +4627,63 @@ namespace {
class TemplateArgumentLocContainerIterator {
ArgLocContainer *Container;
unsigned Index;
-
+
public:
typedef TemplateArgumentLoc value_type;
typedef TemplateArgumentLoc reference;
typedef int difference_type;
typedef std::input_iterator_tag iterator_category;
-
+
class pointer {
TemplateArgumentLoc Arg;
-
+
public:
explicit pointer(TemplateArgumentLoc Arg) : Arg(Arg) { }
-
+
const TemplateArgumentLoc *operator->() const {
return &Arg;
}
};
-
-
+
+
TemplateArgumentLocContainerIterator() {}
-
+
TemplateArgumentLocContainerIterator(ArgLocContainer &Container,
unsigned Index)
: Container(&Container), Index(Index) { }
-
+
TemplateArgumentLocContainerIterator &operator++() {
++Index;
return *this;
}
-
+
TemplateArgumentLocContainerIterator operator++(int) {
TemplateArgumentLocContainerIterator Old(*this);
++(*this);
return Old;
}
-
+
TemplateArgumentLoc operator*() const {
return Container->getArgLoc(Index);
}
-
+
pointer operator->() const {
return pointer(Container->getArgLoc(Index));
}
-
+
friend bool operator==(const TemplateArgumentLocContainerIterator &X,
const TemplateArgumentLocContainerIterator &Y) {
return X.Container == Y.Container && X.Index == Y.Index;
}
-
+
friend bool operator!=(const TemplateArgumentLocContainerIterator &X,
const TemplateArgumentLocContainerIterator &Y) {
return !(X == Y);
}
};
}
-
-
+
+
template <typename Derived>
QualType TreeTransform<Derived>::TransformTemplateSpecializationType(
TypeLocBuilder &TLB,
@@ -4681,7 +4694,7 @@ QualType TreeTransform<Derived>::TransformTemplateSpecializationType(
NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc());
typedef TemplateArgumentLocContainerIterator<TemplateSpecializationTypeLoc>
ArgIterator;
- if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0),
+ if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0),
ArgIterator(TL, TL.getNumArgs()),
NewTemplateArgs))
return QualType();
@@ -4736,13 +4749,13 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType(
NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc());
typedef TemplateArgumentLocContainerIterator<
DependentTemplateSpecializationTypeLoc> ArgIterator;
- if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0),
+ if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0),
ArgIterator(TL, TL.getNumArgs()),
NewTemplateArgs))
return QualType();
-
+
// FIXME: maybe don't rebuild if all the template arguments are the same.
-
+
if (DependentTemplateName *DTN = Template.getAsDependentTemplateName()) {
QualType Result
= getSema().Context.getDependentTemplateSpecializationType(
@@ -4750,7 +4763,7 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType(
DTN->getQualifier(),
DTN->getIdentifier(),
NewTemplateArgs);
-
+
DependentTemplateSpecializationTypeLoc NewTL
= TLB.push<DependentTemplateSpecializationTypeLoc>(Result);
NewTL.setElaboratedKeywordLoc(TL.getElaboratedKeywordLoc());
@@ -4763,12 +4776,12 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType(
NewTL.setArgLocInfo(i, NewTemplateArgs[i].getLocInfo());
return Result;
}
-
- QualType Result
+
+ QualType Result
= getDerived().RebuildTemplateSpecializationType(Template,
TL.getTemplateNameLoc(),
NewTemplateArgs);
-
+
if (!Result.isNull()) {
/// FIXME: Wrap this in an elaborated-type-specifier?
TemplateSpecializationTypeLoc NewTL
@@ -4780,7 +4793,7 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType(
for (unsigned i = 0, e = NewTemplateArgs.size(); i != e; ++i)
NewTL.setArgLocInfo(i, NewTemplateArgs[i].getLocInfo());
}
-
+
return Result;
}
@@ -4793,7 +4806,7 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB,
NestedNameSpecifierLoc QualifierLoc;
// NOTE: the qualifier in an ElaboratedType is optional.
if (TL.getQualifierLoc()) {
- QualifierLoc
+ QualifierLoc
= getDerived().TransformNestedNameSpecifierLoc(TL.getQualifierLoc());
if (!QualifierLoc)
return QualType();
@@ -4825,7 +4838,7 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB,
QualifierLoc != TL.getQualifierLoc() ||
NamedT != T->getNamedType()) {
Result = getDerived().RebuildElaboratedType(TL.getElaboratedKeywordLoc(),
- T->getKeyword(),
+ T->getKeyword(),
QualifierLoc, NamedT);
if (Result.isNull())
return QualType();
@@ -4942,7 +4955,7 @@ QualType TreeTransform<Derived>::
if (!QualifierLoc)
return QualType();
}
-
+
return getDerived()
.TransformDependentTemplateSpecializationType(TLB, TL, QualifierLoc);
}
@@ -4953,18 +4966,18 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
DependentTemplateSpecializationTypeLoc TL,
NestedNameSpecifierLoc QualifierLoc) {
const DependentTemplateSpecializationType *T = TL.getTypePtr();
-
+
TemplateArgumentListInfo NewTemplateArgs;
NewTemplateArgs.setLAngleLoc(TL.getLAngleLoc());
NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc());
-
+
typedef TemplateArgumentLocContainerIterator<
DependentTemplateSpecializationTypeLoc> ArgIterator;
if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0),
ArgIterator(TL, TL.getNumArgs()),
NewTemplateArgs))
return QualType();
-
+
QualType Result
= getDerived().RebuildDependentTemplateSpecializationType(T->getKeyword(),
QualifierLoc,
@@ -4973,10 +4986,10 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
NewTemplateArgs);
if (Result.isNull())
return QualType();
-
+
if (const ElaboratedType *ElabT = dyn_cast<ElaboratedType>(Result)) {
QualType NamedT = ElabT->getNamedType();
-
+
// Copy information relevant to the template specialization.
TemplateSpecializationTypeLoc NamedTL
= TLB.push<TemplateSpecializationTypeLoc>(NamedT);
@@ -4986,7 +4999,7 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
NamedTL.setRAngleLoc(TL.getRAngleLoc());
for (unsigned I = 0, E = NewTemplateArgs.size(); I != E; ++I)
NamedTL.setArgLocInfo(I, NewTemplateArgs[I].getLocInfo());
-
+
// Copy information relevant to the elaborated type.
ElaboratedTypeLoc NewTL = TLB.push<ElaboratedTypeLoc>(Result);
NewTL.setElaboratedKeywordLoc(TL.getElaboratedKeywordLoc());
@@ -5018,22 +5031,22 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
template<typename Derived>
QualType TreeTransform<Derived>::TransformPackExpansionType(TypeLocBuilder &TLB,
PackExpansionTypeLoc TL) {
- QualType Pattern
- = getDerived().TransformType(TLB, TL.getPatternLoc());
+ QualType Pattern
+ = getDerived().TransformType(TLB, TL.getPatternLoc());
if (Pattern.isNull())
return QualType();
-
- QualType Result = TL.getType();
+
+ QualType Result = TL.getType();
if (getDerived().AlwaysRebuild() ||
Pattern != TL.getPatternLoc().getType()) {
- Result = getDerived().RebuildPackExpansionType(Pattern,
+ Result = getDerived().RebuildPackExpansionType(Pattern,
TL.getPatternLoc().getSourceRange(),
TL.getEllipsisLoc(),
TL.getTypePtr()->getNumExpansions());
if (Result.isNull())
return QualType();
}
-
+
PackExpansionTypeLoc NewT = TLB.push<PackExpansionTypeLoc>(Result);
NewT.setEllipsisLoc(TL.getEllipsisLoc());
return Result;
@@ -5217,7 +5230,7 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) {
ExprResult Cond;
VarDecl *ConditionVar = 0;
if (S->getConditionVariable()) {
- ConditionVar
+ ConditionVar
= cast_or_null<VarDecl>(
getDerived().TransformDefinition(
S->getConditionVariable()->getLocation(),
@@ -5226,25 +5239,25 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) {
return StmtError();
} else {
Cond = getDerived().TransformExpr(S->getCond());
-
+
if (Cond.isInvalid())
return StmtError();
-
+
// Convert the condition to a boolean value.
if (S->getCond()) {
- ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getIfLoc(),
+ ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getIfLoc(),
Cond.get());
if (CondE.isInvalid())
return StmtError();
-
+
Cond = CondE.get();
}
}
-
+
Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.take()));
if (!S->getConditionVariable() && S->getCond() && !FullCond.get())
return StmtError();
-
+
// Transform the "then" branch.
StmtResult Then = getDerived().TransformStmt(S->getThen());
if (Then.isInvalid())
@@ -5274,7 +5287,7 @@ TreeTransform<Derived>::TransformSwitchStmt(SwitchStmt *S) {
ExprResult Cond;
VarDecl *ConditionVar = 0;
if (S->getConditionVariable()) {
- ConditionVar
+ ConditionVar
= cast_or_null<VarDecl>(
getDerived().TransformDefinition(
S->getConditionVariable()->getLocation(),
@@ -5283,7 +5296,7 @@ TreeTransform<Derived>::TransformSwitchStmt(SwitchStmt *S) {
return StmtError();
} else {
Cond = getDerived().TransformExpr(S->getCond());
-
+
if (Cond.isInvalid())
return StmtError();
}
@@ -5312,7 +5325,7 @@ TreeTransform<Derived>::TransformWhileStmt(WhileStmt *S) {
ExprResult Cond;
VarDecl *ConditionVar = 0;
if (S->getConditionVariable()) {
- ConditionVar
+ ConditionVar
= cast_or_null<VarDecl>(
getDerived().TransformDefinition(
S->getConditionVariable()->getLocation(),
@@ -5321,13 +5334,13 @@ TreeTransform<Derived>::TransformWhileStmt(WhileStmt *S) {
return StmtError();
} else {
Cond = getDerived().TransformExpr(S->getCond());
-
+
if (Cond.isInvalid())
return StmtError();
if (S->getCond()) {
// Convert the condition to a boolean value.
- ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getWhileLoc(),
+ ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getWhileLoc(),
Cond.get());
if (CondE.isInvalid())
return StmtError();
@@ -5366,7 +5379,7 @@ TreeTransform<Derived>::TransformDoStmt(DoStmt *S) {
ExprResult Cond = getDerived().TransformExpr(S->getCond());
if (Cond.isInvalid())
return StmtError();
-
+
if (!getDerived().AlwaysRebuild() &&
Cond.get() == S->getCond() &&
Body.get() == S->getBody())
@@ -5389,7 +5402,7 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) {
ExprResult Cond;
VarDecl *ConditionVar = 0;
if (S->getConditionVariable()) {
- ConditionVar
+ ConditionVar
= cast_or_null<VarDecl>(
getDerived().TransformDefinition(
S->getConditionVariable()->getLocation(),
@@ -5398,13 +5411,13 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) {
return StmtError();
} else {
Cond = getDerived().TransformExpr(S->getCond());
-
+
if (Cond.isInvalid())
return StmtError();
if (S->getCond()) {
// Convert the condition to a boolean value.
- ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getForLoc(),
+ ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getForLoc(),
Cond.get());
if (CondE.isInvalid())
return StmtError();
@@ -5413,7 +5426,7 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) {
}
}
- Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.take()));
+ Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.take()));
if (!S->getConditionVariable() && S->getCond() && !FullCond.get())
return StmtError();
@@ -5450,7 +5463,7 @@ TreeTransform<Derived>::TransformGotoStmt(GotoStmt *S) {
S->getLabel());
if (!LD)
return StmtError();
-
+
// Goto statements must always be rebuilt, to resolve the label.
return getDerived().RebuildGotoStmt(S->getGotoLoc(), S->getLabelLoc(),
cast<LabelDecl>(LD));
@@ -5524,7 +5537,7 @@ TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) {
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformAsmStmt(AsmStmt *S) {
-
+
ASTOwningVector<Expr*> Constraints(getSema());
ASTOwningVector<Expr*> Exprs(getSema());
SmallVector<IdentifierInfo *, 4> Names;
@@ -5533,43 +5546,43 @@ TreeTransform<Derived>::TransformAsmStmt(AsmStmt *S) {
ASTOwningVector<Expr*> Clobbers(getSema());
bool ExprsChanged = false;
-
+
// Go through the outputs.
for (unsigned I = 0, E = S->getNumOutputs(); I != E; ++I) {
Names.push_back(S->getOutputIdentifier(I));
-
+
// No need to transform the constraint literal.
Constraints.push_back(S->getOutputConstraintLiteral(I));
-
+
// Transform the output expr.
Expr *OutputExpr = S->getOutputExpr(I);
ExprResult Result = getDerived().TransformExpr(OutputExpr);
if (Result.isInvalid())
return StmtError();
-
+
ExprsChanged |= Result.get() != OutputExpr;
-
+
Exprs.push_back(Result.get());
}
-
+
// Go through the inputs.
for (unsigned I = 0, E = S->getNumInputs(); I != E; ++I) {
Names.push_back(S->getInputIdentifier(I));
-
+
// No need to transform the constraint literal.
Constraints.push_back(S->getInputConstraintLiteral(I));
-
+
// Transform the input expr.
Expr *InputExpr = S->getInputExpr(I);
ExprResult Result = getDerived().TransformExpr(InputExpr);
if (Result.isInvalid())
return StmtError();
-
+
ExprsChanged |= Result.get() != InputExpr;
-
+
Exprs.push_back(Result.get());
}
-
+
if (!getDerived().AlwaysRebuild() && !ExprsChanged)
return SemaRef.Owned(S);
@@ -5594,6 +5607,18 @@ TreeTransform<Derived>::TransformAsmStmt(AsmStmt *S) {
S->isMSAsm());
}
+template<typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformMSAsmStmt(MSAsmStmt *S) {
+ ArrayRef<Token> AsmToks =
+ llvm::makeArrayRef(S->getAsmToks(), S->getNumAsmToks());
+ ArrayRef<unsigned> LineEnds =
+ llvm::makeArrayRef(S->getLineEnds(), S->getNumLineEnds());
+
+ // No need to transform the asm string literal.
+ return getDerived().RebuildMSAsmStmt(S->getAsmLoc(), AsmToks, LineEnds,
+ S->getEndLoc());
+}
template<typename Derived>
StmtResult
@@ -5602,7 +5627,7 @@ TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) {
StmtResult TryBody = getDerived().TransformStmt(S->getTryBody());
if (TryBody.isInvalid())
return StmtError();
-
+
// Transform the @catch statements (if present).
bool AnyCatchChanged = false;
ASTOwningVector<Stmt*> CatchStmts(SemaRef);
@@ -5614,7 +5639,7 @@ TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) {
AnyCatchChanged = true;
CatchStmts.push_back(Catch.release());
}
-
+
// Transform the @finally statement (if present).
StmtResult Finally;
if (S->getFinallyStmt()) {
@@ -5629,7 +5654,7 @@ TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) {
!AnyCatchChanged &&
Finally.get() == S->getFinallyStmt())
return SemaRef.Owned(S);
-
+
// Build a new statement.
return getDerived().RebuildObjCAtTryStmt(S->getAtTryLoc(), TryBody.get(),
move_arg(CatchStmts), Finally.get());
@@ -5647,26 +5672,26 @@ TreeTransform<Derived>::TransformObjCAtCatchStmt(ObjCAtCatchStmt *S) {
if (!TSInfo)
return StmtError();
}
-
+
QualType T;
if (TSInfo)
T = TSInfo->getType();
else {
T = getDerived().TransformType(FromVar->getType());
if (T.isNull())
- return StmtError();
+ return StmtError();
}
-
+
Var = getDerived().RebuildObjCExceptionDecl(FromVar, TSInfo, T);
if (!Var)
return StmtError();
}
-
+
StmtResult Body = getDerived().TransformStmt(S->getCatchBody());
if (Body.isInvalid())
return StmtError();
-
- return getDerived().RebuildObjCAtCatchStmt(S->getAtCatchLoc(),
+
+ return getDerived().RebuildObjCAtCatchStmt(S->getAtCatchLoc(),
S->getRParenLoc(),
Var, Body.get());
}
@@ -5678,7 +5703,7 @@ TreeTransform<Derived>::TransformObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
StmtResult Body = getDerived().TransformStmt(S->getFinallyBody());
if (Body.isInvalid())
return StmtError();
-
+
// If nothing changed, just retain this statement.
if (!getDerived().AlwaysRebuild() &&
Body.get() == S->getFinallyBody())
@@ -5698,11 +5723,11 @@ TreeTransform<Derived>::TransformObjCAtThrowStmt(ObjCAtThrowStmt *S) {
if (Operand.isInvalid())
return StmtError();
}
-
+
if (!getDerived().AlwaysRebuild() &&
Operand.get() == S->getThrowExpr())
return getSema().Owned(S);
-
+
return getDerived().RebuildObjCAtThrowStmt(S->getThrowLoc(), Operand.get());
}
@@ -5719,12 +5744,12 @@ TreeTransform<Derived>::TransformObjCAtSynchronizedStmt(
Object.get());
if (Object.isInvalid())
return StmtError();
-
+
// Transform the body.
StmtResult Body = getDerived().TransformStmt(S->getSynchBody());
if (Body.isInvalid())
return StmtError();
-
+
// If nothing change, just retain the current statement.
if (!getDerived().AlwaysRebuild() &&
Object.get() == S->getSynchExpr() &&
@@ -5744,7 +5769,7 @@ TreeTransform<Derived>::TransformObjCAutoreleasePoolStmt(
StmtResult Body = getDerived().TransformStmt(S->getSubStmt());
if (Body.isInvalid())
return StmtError();
-
+
// If nothing changed, just retain this statement.
if (!getDerived().AlwaysRebuild() &&
Body.get() == S->getSubStmt())
@@ -5763,28 +5788,24 @@ TreeTransform<Derived>::TransformObjCForCollectionStmt(
StmtResult Element = getDerived().TransformStmt(S->getElement());
if (Element.isInvalid())
return StmtError();
-
+
// Transform the collection expression.
ExprResult Collection = getDerived().TransformExpr(S->getCollection());
if (Collection.isInvalid())
return StmtError();
- Collection = getDerived().RebuildObjCForCollectionOperand(S->getForLoc(),
- Collection.take());
- if (Collection.isInvalid())
- return StmtError();
-
+
// Transform the body.
StmtResult Body = getDerived().TransformStmt(S->getBody());
if (Body.isInvalid())
return StmtError();
-
+
// If nothing changed, just retain this statement.
if (!getDerived().AlwaysRebuild() &&
Element.get() == S->getElement() &&
Collection.get() == S->getCollection() &&
Body.get() == S->getBody())
return SemaRef.Owned(S);
-
+
// Build a new statement.
return getDerived().RebuildObjCForCollectionStmt(S->getForLoc(),
/*FIXME:*/S->getForLoc(),
@@ -5931,7 +5952,7 @@ TreeTransform<Derived>::TransformMSDependentExistsStmt(
// Transform the nested-name-specifier, if any.
NestedNameSpecifierLoc QualifierLoc;
if (S->getQualifierLoc()) {
- QualifierLoc
+ QualifierLoc
= getDerived().TransformNestedNameSpecifierLoc(S->getQualifierLoc());
if (!QualifierLoc)
return StmtError();
@@ -5950,7 +5971,7 @@ TreeTransform<Derived>::TransformMSDependentExistsStmt(
QualifierLoc == S->getQualifierLoc() &&
NameInfo.getName() == S->getNameInfo().getName())
return S;
-
+
// Determine whether this name exists, if we can.
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
@@ -5959,32 +5980,32 @@ TreeTransform<Derived>::TransformMSDependentExistsStmt(
case Sema::IER_Exists:
if (S->isIfExists())
break;
-
+
return new (getSema().Context) NullStmt(S->getKeywordLoc());
case Sema::IER_DoesNotExist:
if (S->isIfNotExists())
break;
-
+
return new (getSema().Context) NullStmt(S->getKeywordLoc());
-
+
case Sema::IER_Dependent:
Dependent = true;
break;
-
+
case Sema::IER_Error:
return StmtError();
}
-
+
// We need to continue with the instantiation, so do so now.
StmtResult SubStmt = getDerived().TransformCompoundStmt(S->getSubStmt());
if (SubStmt.isInvalid())
return StmtError();
-
+
// If we have resolved the name, just transform to the substatement.
if (!Dependent)
return SubStmt;
-
+
// The name is still dependent, so build a dependent expression again.
return getDerived().RebuildMSDependentExistsStmt(S->getKeywordLoc(),
S->isIfExists(),
@@ -6101,7 +6122,7 @@ TreeTransform<Derived>::TransformDeclRefExpr(DeclRefExpr *E) {
return ExprError();
}
- return getDerived().RebuildDeclRefExpr(QualifierLoc, ND, NameInfo,
+ return getDerived().RebuildDeclRefExpr(QualifierLoc, ND, NameInfo,
TemplateArgs);
}
@@ -6213,12 +6234,12 @@ TreeTransform<Derived>::TransformOffsetOfExpr(OffsetOfExpr *E) {
TypeSourceInfo *Type = getDerived().TransformType(E->getTypeSourceInfo());
if (!Type)
return ExprError();
-
+
// Transform all of the components into components similar to what the
// parser uses.
- // FIXME: It would be slightly more efficient in the non-dependent case to
- // just map FieldDecls, rather than requiring the rebuilder to look for
- // the fields again. However, __builtin_offsetof is rare enough in
+ // FIXME: It would be slightly more efficient in the non-dependent case to
+ // just map FieldDecls, rather than requiring the rebuilder to look for
+ // the fields again. However, __builtin_offsetof is rare enough in
// template code that we don't care.
bool ExprChanged = false;
typedef Sema::OffsetOfComponent Component;
@@ -6236,36 +6257,36 @@ TreeTransform<Derived>::TransformOffsetOfExpr(OffsetOfExpr *E) {
ExprResult Index = getDerived().TransformExpr(FromIndex);
if (Index.isInvalid())
return ExprError();
-
+
ExprChanged = ExprChanged || Index.get() != FromIndex;
Comp.isBrackets = true;
Comp.U.E = Index.get();
break;
}
-
+
case Node::Field:
case Node::Identifier:
Comp.isBrackets = false;
Comp.U.IdentInfo = ON.getFieldName();
if (!Comp.U.IdentInfo)
continue;
-
+
break;
-
+
case Node::Base:
// Will be recomputed during the rebuild.
continue;
}
-
+
Components.push_back(Comp);
}
-
+
// If nothing changed, retain the existing expression.
if (!getDerived().AlwaysRebuild() &&
Type == E->getTypeSourceInfo() &&
!ExprChanged)
return SemaRef.Owned(E);
-
+
// Build a new offsetof expression.
return getDerived().RebuildOffsetOfExpr(E->getOperatorLoc(), Type,
Components.data(), Components.size(),
@@ -6373,10 +6394,10 @@ TreeTransform<Derived>::TransformCallExpr(CallExpr *E) {
// Transform arguments.
bool ArgChanged = false;
ASTOwningVector<Expr*> Args(SemaRef);
- if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
+ if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgChanged))
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() &&
Callee.get() == E->getCallee() &&
!ArgChanged)
@@ -6401,7 +6422,7 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) {
if (E->hasQualifier()) {
QualifierLoc
= getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc());
-
+
if (!QualifierLoc)
return ExprError();
}
@@ -6429,7 +6450,7 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) {
Member == E->getMemberDecl() &&
FoundDecl == E->getFoundDecl() &&
!E->hasExplicitTemplateArgs()) {
-
+
// Mark it referenced in the new context regardless.
// FIXME: this is a bit instantiation-specific.
SemaRef.MarkMemberReferenced(E);
@@ -6446,7 +6467,7 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) {
TransArgs))
return ExprError();
}
-
+
// FIXME: Bogus source location for the operator
SourceLocation FakeOperatorLoc
= SemaRef.PP.getLocForEndOfToken(E->getBase()->getSourceRange().getEnd());
@@ -6564,7 +6585,7 @@ TreeTransform<Derived>::TransformCStyleCastExpr(CStyleCastExpr *E) {
TypeSourceInfo *Type = getDerived().TransformType(E->getTypeInfoAsWritten());
if (!Type)
return ExprError();
-
+
ExprResult SubExpr
= getDerived().TransformExpr(E->getSubExprAsWritten());
if (SubExpr.isInvalid())
@@ -6632,10 +6653,10 @@ TreeTransform<Derived>::TransformInitListExpr(InitListExpr *E) {
bool InitChanged = false;
ASTOwningVector<Expr*, 4> Inits(SemaRef);
- if (getDerived().TransformExprs(E->getInits(), E->getNumInits(), false,
+ if (getDerived().TransformExprs(E->getInits(), E->getNumInits(), false,
Inits, &InitChanged))
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() && !InitChanged)
return SemaRef.Owned(E);
@@ -6716,7 +6737,7 @@ ExprResult
TreeTransform<Derived>::TransformImplicitValueInitExpr(
ImplicitValueInitExpr *E) {
TemporaryBase Rebase(*this, E->getLocStart(), DeclarationName());
-
+
// FIXME: Will we ever have proper type location here? Will we actually
// need to transform the type?
QualType T = getDerived().TransformType(E->getType());
@@ -6758,7 +6779,7 @@ TreeTransform<Derived>::TransformParenListExpr(ParenListExpr *E) {
if (TransformExprs(E->getExprs(), E->getNumExprs(), true, Inits,
&ArgumentChanged))
return ExprError();
-
+
return getDerived().RebuildParenListExpr(E->getLParenLoc(),
move_arg(Inits),
E->getRParenLoc());
@@ -6776,13 +6797,13 @@ TreeTransform<Derived>::TransformAddrLabelExpr(AddrLabelExpr *E) {
E->getLabel());
if (!LD)
return ExprError();
-
+
return getDerived().RebuildAddrLabelExpr(E->getAmpAmpLoc(), E->getLabelLoc(),
cast<LabelDecl>(LD));
}
template<typename Derived>
-ExprResult
+ExprResult
TreeTransform<Derived>::TransformStmtExpr(StmtExpr *E) {
SemaRef.ActOnStartStmtExpr();
StmtResult SubStmt
@@ -6845,7 +6866,7 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
case OO_Array_New:
case OO_Array_Delete:
llvm_unreachable("new and delete operators cannot use CXXOperatorCallExpr");
-
+
case OO_Call: {
// This is a call to an object's operator().
assert(E->getNumArgs() >= 1 && "Object call is missing arguments");
@@ -6862,7 +6883,7 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
// Transform the call arguments.
ASTOwningVector<Expr*> Args(SemaRef);
- if (getDerived().TransformExprs(E->getArgs() + 1, E->getNumArgs() - 1, true,
+ if (getDerived().TransformExprs(E->getArgs() + 1, E->getNumArgs() - 1, true,
Args))
return ExprError();
@@ -6937,7 +6958,7 @@ TreeTransform<Derived>::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) {
// Transform arguments.
bool ArgChanged = false;
ASTOwningVector<Expr*> Args(SemaRef);
- if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
+ if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgChanged))
return ExprError();
@@ -6960,7 +6981,7 @@ TreeTransform<Derived>::TransformCXXNamedCastExpr(CXXNamedCastExpr *E) {
TypeSourceInfo *Type = getDerived().TransformType(E->getTypeInfoAsWritten());
if (!Type)
return ExprError();
-
+
ExprResult SubExpr
= getDerived().TransformExpr(E->getSubExprAsWritten());
if (SubExpr.isInvalid())
@@ -7140,7 +7161,7 @@ TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) {
getSema().CheckCXXThisCapture(E->getLocStart());
return SemaRef.Owned(E);
}
-
+
return getDerived().RebuildCXXThisExpr(E->getLocStart(), T, E->isImplicit());
}
@@ -7182,12 +7203,12 @@ TreeTransform<Derived>::TransformCXXScalarValueInitExpr(
TypeSourceInfo *T = getDerived().TransformType(E->getTypeSourceInfo());
if (!T)
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() &&
T == E->getTypeSourceInfo())
return SemaRef.Owned(E);
- return getDerived().RebuildCXXScalarValueInitExpr(T,
+ return getDerived().RebuildCXXScalarValueInitExpr(T,
/*FIXME:*/T->getTypeLoc().getEndLoc(),
E->getRParenLoc());
}
@@ -7209,7 +7230,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) {
// Transform the placement arguments (if any).
bool ArgumentChanged = false;
ASTOwningVector<Expr*> PlacementArgs(SemaRef);
- if (getDerived().TransformExprs(E->getPlacementArgs(),
+ if (getDerived().TransformExprs(E->getPlacementArgs(),
E->getNumPlacementArgs(), true,
PlacementArgs, &ArgumentChanged))
return ExprError();
@@ -7240,7 +7261,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) {
if (!OperatorDelete)
return ExprError();
}
-
+
if (!getDerived().AlwaysRebuild() &&
AllocTypeInfo == E->getAllocatedTypeSourceInfo() &&
ArraySize.get() == E->getArraySize() &&
@@ -7254,7 +7275,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) {
SemaRef.MarkFunctionReferenced(E->getLocStart(), OperatorNew);
if (OperatorDelete)
SemaRef.MarkFunctionReferenced(E->getLocStart(), OperatorDelete);
-
+
if (E->isArray() && !E->getAllocatedType()->isDependentType()) {
QualType ElementType
= SemaRef.Context.getBaseElementType(E->getAllocatedType());
@@ -7281,9 +7302,9 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) {
// Do nothing
} else if (const ConstantArrayType *ConsArrayT
= dyn_cast<ConstantArrayType>(ArrayT)) {
- ArraySize
+ ArraySize
= SemaRef.Owned(IntegerLiteral::Create(SemaRef.Context,
- ConsArrayT->getSize(),
+ ConsArrayT->getSize(),
SemaRef.Context.getSizeType(),
/*FIXME:*/E->getLocStart()));
AllocType = ConsArrayT->getElementType();
@@ -7325,7 +7346,7 @@ TreeTransform<Derived>::TransformCXXDeleteExpr(CXXDeleteExpr *E) {
if (!OperatorDelete)
return ExprError();
}
-
+
if (!getDerived().AlwaysRebuild() &&
Operand.get() == E->getArgument() &&
OperatorDelete == E->getOperatorDelete()) {
@@ -7333,17 +7354,17 @@ TreeTransform<Derived>::TransformCXXDeleteExpr(CXXDeleteExpr *E) {
// FIXME: instantiation-specific.
if (OperatorDelete)
SemaRef.MarkFunctionReferenced(E->getLocStart(), OperatorDelete);
-
+
if (!E->getArgument()->isTypeDependent()) {
QualType Destroyed = SemaRef.Context.getBaseElementType(
E->getDestroyedType());
if (const RecordType *DestroyedRec = Destroyed->getAs<RecordType>()) {
CXXRecordDecl *Record = cast<CXXRecordDecl>(DestroyedRec->getDecl());
- SemaRef.MarkFunctionReferenced(E->getLocStart(),
+ SemaRef.MarkFunctionReferenced(E->getLocStart(),
SemaRef.LookupDestructor(Record));
}
}
-
+
return SemaRef.Owned(E);
}
@@ -7363,14 +7384,14 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr(
ParsedType ObjectTypePtr;
bool MayBePseudoDestructor = false;
- Base = SemaRef.ActOnStartCXXMemberReference(0, Base.get(),
+ Base = SemaRef.ActOnStartCXXMemberReference(0, Base.get(),
E->getOperatorLoc(),
E->isArrow()? tok::arrow : tok::period,
ObjectTypePtr,
MayBePseudoDestructor);
if (Base.isInvalid())
return ExprError();
-
+
QualType ObjectType = ObjectTypePtr.get();
NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc();
if (QualifierLoc) {
@@ -7405,7 +7426,7 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr(
false);
if (!T)
return ExprError();
-
+
Destroyed
= SemaRef.Context.getTrivialTypeSourceInfo(SemaRef.GetTypeFromParser(T),
E->getDestroyedTypeLoc());
@@ -7417,7 +7438,7 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr(
if (!ScopeTypeInfo)
return ExprError();
}
-
+
return getDerived().RebuildCXXPseudoDestructorExpr(Base.get(),
E->getOperatorLoc(),
E->isArrow(),
@@ -7473,10 +7494,10 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(
= getDerived().TransformNestedNameSpecifierLoc(Old->getQualifierLoc());
if (!QualifierLoc)
return ExprError();
-
+
SS.Adopt(QualifierLoc);
- }
-
+ }
+
if (Old->getNamingClass()) {
CXXRecordDecl *NamingClass
= cast_or_null<CXXRecordDecl>(getDerived().TransformDecl(
@@ -7484,7 +7505,7 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(
Old->getNamingClass()));
if (!NamingClass)
return ExprError();
-
+
R.setNamingClass(NamingClass);
}
@@ -7559,7 +7580,7 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
QualType To = getDerived().TransformType(TLB, FromTL);
if (To.isNull())
return ExprError();
-
+
if (To == From->getType())
Args.push_back(From);
else {
@@ -7568,15 +7589,15 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
}
continue;
}
-
+
ArgChanged = true;
-
+
// We have a pack expansion. Instantiate it.
- PackExpansionTypeLoc ExpansionTL = cast<PackExpansionTypeLoc>(FromTL);
+ PackExpansionTypeLoc ExpansionTL = cast<PackExpansionTypeLoc>(FromTL);
TypeLoc PatternTL = ExpansionTL.getPatternLoc();
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
SemaRef.collectUnexpandedParameterPacks(PatternTL, Unexpanded);
-
+
// Determine whether the set of unexpanded parameter packs can and should
// be expanded.
bool Expand = true;
@@ -7590,13 +7611,13 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
Expand, RetainExpansion,
NumExpansions))
return ExprError();
-
+
if (!Expand) {
// The transform has determined that we should perform a simple
- // transformation on the pack expansion, producing another pack
+ // transformation on the pack expansion, producing another pack
// expansion.
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
-
+
TypeLocBuilder TLB;
TLB.reserve(From->getTypeLoc().getFullDataSize());
@@ -7604,13 +7625,13 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
if (To.isNull())
return ExprError();
- To = getDerived().RebuildPackExpansionType(To,
+ To = getDerived().RebuildPackExpansionType(To,
PatternTL.getSourceRange(),
ExpansionTL.getEllipsisLoc(),
NumExpansions);
if (To.isNull())
return ExprError();
-
+
PackExpansionTypeLoc ToExpansionTL
= TLB.push<PackExpansionTypeLoc>(To);
ToExpansionTL.setEllipsisLoc(ExpansionTL.getEllipsisLoc());
@@ -7630,34 +7651,34 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
Args.push_back(TLB.getTypeSourceInfo(SemaRef.Context, To));
}
-
+
if (!RetainExpansion)
continue;
-
+
// If we're supposed to retain a pack expansion, do so by temporarily
// forgetting the partially-substituted parameter pack.
ForgetPartiallySubstitutedPackRAII Forget(getDerived());
TypeLocBuilder TLB;
TLB.reserve(From->getTypeLoc().getFullDataSize());
-
+
QualType To = getDerived().TransformType(TLB, PatternTL);
if (To.isNull())
return ExprError();
-
- To = getDerived().RebuildPackExpansionType(To,
+
+ To = getDerived().RebuildPackExpansionType(To,
PatternTL.getSourceRange(),
ExpansionTL.getEllipsisLoc(),
NumExpansions);
if (To.isNull())
return ExprError();
-
+
PackExpansionTypeLoc ToExpansionTL
= TLB.push<PackExpansionTypeLoc>(To);
ToExpansionTL.setEllipsisLoc(ExpansionTL.getEllipsisLoc());
Args.push_back(TLB.getTypeSourceInfo(SemaRef.Context, To));
}
-
+
if (!getDerived().AlwaysRebuild() && !ArgChanged)
return SemaRef.Owned(E);
@@ -7783,10 +7804,10 @@ TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) {
bool ArgumentChanged = false;
ASTOwningVector<Expr*> Args(SemaRef);
- if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
+ if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() &&
T == E->getType() &&
Constructor == E->getConstructor() &&
@@ -7837,7 +7858,7 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
CXXConstructorDecl *Constructor
= cast_or_null<CXXConstructorDecl>(
- getDerived().TransformDecl(E->getLocStart(),
+ getDerived().TransformDecl(E->getLocStart(),
E->getConstructor()));
if (!Constructor)
return ExprError();
@@ -7845,7 +7866,7 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
bool ArgumentChanged = false;
ASTOwningVector<Expr*> Args(SemaRef);
Args.reserve(E->getNumArgs());
- if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
+ if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
@@ -7857,7 +7878,7 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
SemaRef.MarkFunctionReferenced(E->getLocStart(), Constructor);
return SemaRef.MaybeBindToTemporary(E);
}
-
+
return getDerived().RebuildCXXTemporaryObjectExpr(T,
/*FIXME:*/T->getTypeLoc().getEndLoc(),
move_arg(Args),
@@ -7872,40 +7893,38 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
= getSema().createLambdaClosureType(E->getIntroducerRange(),
/*KnownDependent=*/false);
getDerived().transformedLocalDecl(E->getLambdaClass(), Class);
-
+
// Transform the type of the lambda parameters and start the definition of
// the lambda itself.
TypeSourceInfo *MethodTy
- = TransformType(E->getCallOperator()->getTypeSourceInfo());
+ = TransformType(E->getCallOperator()->getTypeSourceInfo());
if (!MethodTy)
return ExprError();
// Transform lambda parameters.
- bool Invalid = false;
llvm::SmallVector<QualType, 4> ParamTypes;
llvm::SmallVector<ParmVarDecl *, 4> Params;
if (getDerived().TransformFunctionTypeParams(E->getLocStart(),
E->getCallOperator()->param_begin(),
E->getCallOperator()->param_size(),
0, ParamTypes, &Params))
- Invalid = true;
+ return ExprError();
// Build the call operator.
- // Note: Once a lambda mangling number and context declaration have been
- // assigned, they never change.
- unsigned ManglingNumber = E->getLambdaClass()->getLambdaManglingNumber();
- Decl *ContextDecl = E->getLambdaClass()->getLambdaContextDecl();
CXXMethodDecl *CallOperator
= getSema().startLambdaDefinition(Class, E->getIntroducerRange(),
- MethodTy,
+ MethodTy,
E->getCallOperator()->getLocEnd(),
- Params, ManglingNumber, ContextDecl);
+ Params);
getDerived().transformAttrs(E->getCallOperator(), CallOperator);
-
- // FIXME: Instantiation-specific.
- CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(),
- TSK_ImplicitInstantiation);
+ return getDerived().TransformLambdaScope(E, CallOperator);
+}
+
+template<typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformLambdaScope(LambdaExpr *E,
+ CXXMethodDecl *CallOperator) {
// Introduce the context of the call operator.
Sema::ContextRAII SavedContext(getSema(), CallOperator);
@@ -7916,10 +7935,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->hasExplicitParameters(),
E->hasExplicitResultType(),
E->isMutable());
-
+
// Transform captures.
+ bool Invalid = false;
bool FinishedExplicitCaptures = false;
- for (LambdaExpr::capture_iterator C = E->capture_begin(),
+ for (LambdaExpr::capture_iterator C = E->capture_begin(),
CEnd = E->capture_end();
C != CEnd; ++C) {
// When we hit the first implicit capture, tell Sema that we've finished
@@ -7928,13 +7948,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
getSema().finishLambdaExplicitCaptures(LSI);
FinishedExplicitCaptures = true;
}
-
+
// Capturing 'this' is trivial.
if (C->capturesThis()) {
getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit());
continue;
}
-
+
// Determine the capture kind for Sema.
Sema::TryCaptureKind Kind
= C->isImplicit()? Sema::TryCapture_Implicit
@@ -7947,13 +7967,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
bool ShouldExpand = false;
bool RetainExpansion = false;
llvm::Optional<unsigned> NumExpansions;
- if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(),
- C->getLocation(),
+ if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(),
+ C->getLocation(),
Unexpanded,
ShouldExpand, RetainExpansion,
NumExpansions))
return ExprError();
-
+
if (ShouldExpand) {
// The transform has determined that we should perform an expansion;
// transform and capture each of the arguments.
@@ -7962,31 +7982,31 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
VarDecl *CapturedVar
- = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(),
+ = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(),
Pack));
if (!CapturedVar) {
Invalid = true;
continue;
}
-
+
// Capture the transformed variable.
- getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind);
- }
+ getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind);
+ }
continue;
}
-
+
EllipsisLoc = C->getEllipsisLoc();
}
-
+
// Transform the captured variable.
VarDecl *CapturedVar
- = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(),
+ = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(),
C->getCapturedVar()));
if (!CapturedVar) {
Invalid = true;
continue;
}
-
+
// Capture the transformed variable.
getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind);
}
@@ -7996,10 +8016,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
// Enter a new evaluation context to insulate the lambda from any
// cleanups from the enclosing full-expression.
- getSema().PushExpressionEvaluationContext(Sema::PotentiallyEvaluated);
+ getSema().PushExpressionEvaluationContext(Sema::PotentiallyEvaluated);
if (Invalid) {
- getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0,
+ getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0,
/*IsInstantiation=*/true);
return ExprError();
}
@@ -8007,12 +8027,12 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
// Instantiate the body of the lambda expression.
StmtResult Body = getDerived().TransformStmt(E->getBody());
if (Body.isInvalid()) {
- getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0,
+ getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0,
/*IsInstantiation=*/true);
- return ExprError();
+ return ExprError();
}
- return getSema().ActOnLambdaExpr(E->getLocStart(), Body.take(),
+ return getSema().ActOnLambdaExpr(E->getLocStart(), Body.take(),
/*CurScope=*/0, /*IsInstantiation=*/true);
}
@@ -8027,10 +8047,10 @@ TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
bool ArgumentChanged = false;
ASTOwningVector<Expr*> Args(SemaRef);
Args.reserve(E->arg_size());
- if (getDerived().TransformExprs(E->arg_begin(), E->arg_size(), true, Args,
+ if (getDerived().TransformExprs(E->arg_begin(), E->arg_size(), true, Args,
&ArgumentChanged))
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() &&
T == E->getTypeSourceInfo() &&
!ArgumentChanged)
@@ -8209,16 +8229,16 @@ TreeTransform<Derived>::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old)
// Determine the naming class.
if (Old->getNamingClass()) {
- CXXRecordDecl *NamingClass
+ CXXRecordDecl *NamingClass
= cast_or_null<CXXRecordDecl>(getDerived().TransformDecl(
Old->getMemberLoc(),
Old->getNamingClass()));
if (!NamingClass)
return ExprError();
-
+
R.setNamingClass(NamingClass);
}
-
+
TemplateArgumentListInfo TransArgs;
if (Old->hasExplicitTemplateArgs()) {
TransArgs.setLAngleLoc(Old->getLAngleLoc());
@@ -8234,7 +8254,7 @@ TreeTransform<Derived>::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old)
// base (and therefore couldn't do the check) and a
// nested-name-qualifier (and therefore could do the lookup).
NamedDecl *FirstQualifierInScope = 0;
-
+
return getDerived().RebuildUnresolvedMemberExpr(Base.get(),
BaseType,
Old->getOperatorLoc(),
@@ -8267,7 +8287,7 @@ TreeTransform<Derived>::TransformPackExpansionExpr(PackExpansionExpr *E) {
ExprResult Pattern = getDerived().TransformExpr(E->getPattern());
if (Pattern.isInvalid())
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() && Pattern.get() == E->getPattern())
return SemaRef.Owned(E);
@@ -8285,33 +8305,33 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
// Note: None of the implementations of TryExpandParameterPacks can ever
// produce a diagnostic when given only a single unexpanded parameter pack,
- // so
+ // so
UnexpandedParameterPack Unexpanded(E->getPack(), E->getPackLoc());
bool ShouldExpand = false;
bool RetainExpansion = false;
llvm::Optional<unsigned> NumExpansions;
- if (getDerived().TryExpandParameterPacks(E->getOperatorLoc(), E->getPackLoc(),
+ if (getDerived().TryExpandParameterPacks(E->getOperatorLoc(), E->getPackLoc(),
Unexpanded,
ShouldExpand, RetainExpansion,
NumExpansions))
return ExprError();
-
+
if (RetainExpansion)
return SemaRef.Owned(E);
-
+
NamedDecl *Pack = E->getPack();
if (!ShouldExpand) {
- Pack = cast_or_null<NamedDecl>(getDerived().TransformDecl(E->getPackLoc(),
+ Pack = cast_or_null<NamedDecl>(getDerived().TransformDecl(E->getPackLoc(),
Pack));
if (!Pack)
return ExprError();
}
-
+
// We now know the length of the parameter pack, so build a new expression
// that stores that length.
- return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), Pack,
- E->getPackLoc(), E->getRParenLoc(),
+ return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), Pack,
+ E->getPackLoc(), E->getRParenLoc(),
NumExpansions);
}
@@ -8337,7 +8357,7 @@ TreeTransform<Derived>::TransformMaterializeTemporaryExpr(
MaterializeTemporaryExpr *E) {
return getDerived().TransformExpr(E->GetTemporaryExpr());
}
-
+
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformObjCStringLiteral(ObjCStringLiteral *E) {
@@ -8352,8 +8372,16 @@ TreeTransform<Derived>::TransformObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) {
template<typename Derived>
ExprResult
-TreeTransform<Derived>::TransformObjCNumericLiteral(ObjCNumericLiteral *E) {
- return SemaRef.MaybeBindToTemporary(E);
+TreeTransform<Derived>::TransformObjCBoxedExpr(ObjCBoxedExpr *E) {
+ ExprResult SubExpr = getDerived().TransformExpr(E->getSubExpr());
+ if (SubExpr.isInvalid())
+ return ExprError();
+
+ if (!getDerived().AlwaysRebuild() &&
+ SubExpr.get() == E->getSubExpr())
+ return SemaRef.Owned(E);
+
+ return getDerived().RebuildObjCBoxedExpr(E->getSourceRange(), SubExpr.get());
}
template<typename Derived>
@@ -8362,13 +8390,13 @@ TreeTransform<Derived>::TransformObjCArrayLiteral(ObjCArrayLiteral *E) {
// Transform each of the elements.
llvm::SmallVector<Expr *, 8> Elements;
bool ArgChanged = false;
- if (getDerived().TransformExprs(E->getElements(), E->getNumElements(),
+ if (getDerived().TransformExprs(E->getElements(), E->getNumElements(),
/*IsCall=*/false, Elements, &ArgChanged))
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() && !ArgChanged)
return SemaRef.MaybeBindToTemporary(E);
-
+
return getDerived().RebuildObjCArrayLiteral(E->getSourceRange(),
Elements.data(),
Elements.size());
@@ -8377,13 +8405,13 @@ TreeTransform<Derived>::TransformObjCArrayLiteral(ObjCArrayLiteral *E) {
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformObjCDictionaryLiteral(
- ObjCDictionaryLiteral *E) {
+ ObjCDictionaryLiteral *E) {
// Transform each of the elements.
llvm::SmallVector<ObjCDictionaryElement, 8> Elements;
bool ArgChanged = false;
for (unsigned I = 0, N = E->getNumElements(); I != N; ++I) {
ObjCDictionaryElement OrigElement = E->getKeyValueElement(I);
-
+
if (OrigElement.isPackExpansion()) {
// This key/value element is a pack expansion.
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
@@ -8408,7 +8436,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
if (!Expand) {
// The transform has determined that we should perform a simple
- // transformation on the pack expansion, producing another pack
+ // transformation on the pack expansion, producing another pack
// expansion.
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
ExprResult Key = getDerived().TransformExpr(OrigElement.Key);
@@ -8421,11 +8449,11 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
ExprResult Value = getDerived().TransformExpr(OrigElement.Value);
if (Value.isInvalid())
return ExprError();
-
+
if (Value.get() != OrigElement.Value)
ArgChanged = true;
- ObjCDictionaryElement Expansion = {
+ ObjCDictionaryElement Expansion = {
Key.get(), Value.get(), OrigElement.EllipsisLoc, NumExpansions
};
Elements.push_back(Expansion);
@@ -8435,7 +8463,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
// Record right away that the argument was changed. This needs
// to happen even if the array expands to nothing.
ArgChanged = true;
-
+
// The transform has determined that we should perform an elementwise
// expansion of the pattern. Do so.
for (unsigned I = 0; I != *NumExpansions; ++I) {
@@ -8448,7 +8476,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
if (Value.isInvalid())
return ExprError();
- ObjCDictionaryElement Element = {
+ ObjCDictionaryElement Element = {
Key.get(), Value.get(), SourceLocation(), NumExpansions
};
@@ -8457,7 +8485,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
if (Key.get()->containsUnexpandedParameterPack() ||
Value.get()->containsUnexpandedParameterPack())
Element.EllipsisLoc = OrigElement.EllipsisLoc;
-
+
Elements.push_back(Element);
}
@@ -8469,25 +8497,25 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
ExprResult Key = getDerived().TransformExpr(OrigElement.Key);
if (Key.isInvalid())
return ExprError();
-
+
if (Key.get() != OrigElement.Key)
ArgChanged = true;
-
+
// Transform and check value.
ExprResult Value
= getDerived().TransformExpr(OrigElement.Value);
if (Value.isInvalid())
return ExprError();
-
+
if (Value.get() != OrigElement.Value)
ArgChanged = true;
-
- ObjCDictionaryElement Element = {
+
+ ObjCDictionaryElement Element = {
Key.get(), Value.get(), SourceLocation(), llvm::Optional<unsigned>()
};
Elements.push_back(Element);
}
-
+
if (!getDerived().AlwaysRebuild() && !ArgChanged)
return SemaRef.MaybeBindToTemporary(E);
@@ -8531,22 +8559,22 @@ TransformObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) {
template<typename Derived>
ExprResult TreeTransform<Derived>::
TransformObjCBridgedCastExpr(ObjCBridgedCastExpr *E) {
- TypeSourceInfo *TSInfo
+ TypeSourceInfo *TSInfo
= getDerived().TransformType(E->getTypeInfoAsWritten());
if (!TSInfo)
return ExprError();
-
+
ExprResult Result = getDerived().TransformExpr(E->getSubExpr());
- if (Result.isInvalid())
+ if (Result.isInvalid())
return ExprError();
-
+
if (!getDerived().AlwaysRebuild() &&
TSInfo == E->getTypeInfoAsWritten() &&
Result.get() == E->getSubExpr())
return SemaRef.Owned(E);
-
+
return SemaRef.BuildObjCBridgedCast(E->getLParenLoc(), E->getBridgeKind(),
- E->getBridgeKeywordLoc(), TSInfo,
+ E->getBridgeKeywordLoc(), TSInfo,
Result.get());
}
@@ -8557,17 +8585,17 @@ TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) {
bool ArgChanged = false;
ASTOwningVector<Expr*> Args(SemaRef);
Args.reserve(E->getNumArgs());
- if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), false, Args,
+ if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), false, Args,
&ArgChanged))
return ExprError();
-
+
if (E->getReceiverKind() == ObjCMessageExpr::Class) {
// Class message: transform the receiver type.
TypeSourceInfo *ReceiverTypeInfo
= getDerived().TransformType(E->getClassReceiverTypeInfo());
if (!ReceiverTypeInfo)
return ExprError();
-
+
// If nothing changed, just retain the existing message send.
if (!getDerived().AlwaysRebuild() &&
ReceiverTypeInfo == E->getClassReceiverTypeInfo() && !ArgChanged)
@@ -8597,7 +8625,7 @@ TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) {
if (!getDerived().AlwaysRebuild() &&
Receiver.get() == E->getInstanceReceiver() && !ArgChanged)
return SemaRef.MaybeBindToTemporary(E);
-
+
// Build a new instance message send.
SmallVector<SourceLocation, 16> SelLocs;
E->getSelectorLocs(SelLocs);
@@ -8631,12 +8659,12 @@ TreeTransform<Derived>::TransformObjCIvarRefExpr(ObjCIvarRefExpr *E) {
return ExprError();
// We don't need to transform the ivar; it will never change.
-
+
// If nothing changed, just retain the existing expression.
if (!getDerived().AlwaysRebuild() &&
Base.get() == E->getBase())
return SemaRef.Owned(E);
-
+
return getDerived().RebuildObjCIvarRefExpr(Base.get(), E->getDecl(),
E->getLocation(),
E->isArrow(), E->isFreeIvar());
@@ -8649,14 +8677,14 @@ TreeTransform<Derived>::TransformObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
// retain the existing expression.
if (!E->isObjectReceiver())
return SemaRef.Owned(E);
-
+
// Transform the base expression.
ExprResult Base = getDerived().TransformExpr(E->getBase());
if (Base.isInvalid())
return ExprError();
-
+
// We don't need to transform the property; it will never change.
-
+
// If nothing changed, just retain the existing expression.
if (!getDerived().AlwaysRebuild() &&
Base.get() == E->getBase())
@@ -8692,7 +8720,7 @@ TreeTransform<Derived>::TransformObjCSubscriptRefExpr(ObjCSubscriptRefExpr *E) {
Key.get() == E->getKeyExpr() && Base.get() == E->getBaseExpr())
return SemaRef.Owned(E);
- return getDerived().RebuildObjCSubscriptRefExpr(E->getRBracket(),
+ return getDerived().RebuildObjCSubscriptRefExpr(E->getRBracket(),
Base.get(), Key.get(),
E->getAtIndexMethodDecl(),
E->setAtIndexMethodDecl());
@@ -8705,12 +8733,12 @@ TreeTransform<Derived>::TransformObjCIsaExpr(ObjCIsaExpr *E) {
ExprResult Base = getDerived().TransformExpr(E->getBase());
if (Base.isInvalid())
return ExprError();
-
+
// If nothing changed, just retain the existing expression.
if (!getDerived().AlwaysRebuild() &&
Base.get() == E->getBase())
return SemaRef.Owned(E);
-
+
return getDerived().RebuildObjCIsaExpr(Base.get(), E->getIsaMemberLoc(),
E->isArrow());
}
@@ -8721,7 +8749,7 @@ TreeTransform<Derived>::TransformShuffleVectorExpr(ShuffleVectorExpr *E) {
bool ArgumentChanged = false;
ASTOwningVector<Expr*> SubExprs(SemaRef);
SubExprs.reserve(E->getNumSubExprs());
- if (getDerived().TransformExprs(E->getSubExprs(), E->getNumSubExprs(), false,
+ if (getDerived().TransformExprs(E->getSubExprs(), E->getNumSubExprs(), false,
SubExprs, &ArgumentChanged))
return ExprError();
@@ -8738,17 +8766,17 @@ template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) {
BlockDecl *oldBlock = E->getBlockDecl();
-
+
SemaRef.ActOnBlockStart(E->getCaretLocation(), /*Scope=*/0);
BlockScopeInfo *blockScope = SemaRef.getCurBlock();
blockScope->TheDecl->setIsVariadic(oldBlock->isVariadic());
blockScope->TheDecl->setBlockMissingReturnType(
oldBlock->blockMissingReturnType());
-
+
SmallVector<ParmVarDecl*, 4> params;
SmallVector<QualType, 4> paramTypes;
-
+
// Parameter substitution.
if (getDerived().TransformFunctionTypeParams(E->getCaretLocation(),
oldBlock->param_begin(),
@@ -8764,8 +8792,8 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) {
// Don't allow returning a objc interface by value.
if (exprResultType->isObjCObjectType()) {
- getSema().Diag(E->getCaretLocation(),
- diag::err_object_cannot_be_passed_returned_by_value)
+ getSema().Diag(E->getCaretLocation(),
+ diag::err_object_cannot_be_passed_returned_by_value)
<< 0 << exprResultType;
getSema().ActOnBlockError(E->getCaretLocation(), /*Scope=*/0);
return ExprError();
@@ -8788,7 +8816,7 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) {
blockScope->HasImplicitReturnType = false;
blockScope->ReturnType = exprResultType;
}
-
+
// Transform the body
StmtResult body = getDerived().TransformStmt(E->getBody());
if (body.isInvalid()) {
@@ -8846,7 +8874,7 @@ TreeTransform<Derived>::TransformAtomicExpr(AtomicExpr *E) {
return getDerived().RebuildAtomicExpr(E->getBuiltinLoc(), move_arg(SubExprs),
RetTy, E->getOp(), E->getRParenLoc());
}
-
+
//===----------------------------------------------------------------------===//
// Type reconstruction
//===----------------------------------------------------------------------===//
@@ -9028,7 +9056,7 @@ QualType TreeTransform<Derived>::RebuildUnresolvedUsingType(Decl *D) {
// A valid resolved using typename decl points to exactly one type decl.
assert(++Using->shadow_begin() == Using->shadow_end());
Ty = cast<TypeDecl>((*Using->shadow_begin())->getTargetDecl());
-
+
} else {
assert(isa<UnresolvedUsingTypenameDecl>(D) &&
"UnresolvedUsingTypenameDecl transformed to non-using decl");
@@ -9123,7 +9151,7 @@ TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
Template);
return Template.template getAsVal<TemplateName>();
}
-
+
template<typename Derived>
ExprResult
TreeTransform<Derived>::RebuildCXXOperatorCallExpr(OverloadedOperatorKind Op,
@@ -9223,7 +9251,7 @@ TreeTransform<Derived>::RebuildCXXOperatorCallExpr(OverloadedOperatorKind Op,
}
template<typename Derived>
-ExprResult
+ExprResult
TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base,
SourceLocation OperatorLoc,
bool isArrow,
@@ -9235,7 +9263,7 @@ TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base,
QualType BaseType = Base->getType();
if (Base->isTypeDependent() || Destroyed.getIdentifier() ||
(!isArrow && !BaseType->getAs<RecordType>()) ||
- (isArrow && BaseType->getAs<PointerType>() &&
+ (isArrow && BaseType->getAs<PointerType>() &&
!BaseType->getAs<PointerType>()->getPointeeType()
->template getAs<RecordType>())){
// This pseudo-destructor expression is still a pseudo-destructor.
@@ -9252,7 +9280,11 @@ TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base,
DeclarationNameInfo NameInfo(Name, Destroyed.getLocation());
NameInfo.setNamedTypeInfo(DestroyedType);
- // FIXME: the ScopeType should be tacked onto SS.
+ // The scope type is now known to be a valid nested name specifier
+ // component. Tack it on to the end of the nested name specifier.
+ if (ScopeType)
+ SS.Extend(SemaRef.Context, SourceLocation(),
+ ScopeType->getTypeLoc(), CCLoc);
SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller.
return getSema().BuildMemberReferenceExpr(Base, BaseType,
diff --git a/lib/Serialization/ASTCommon.h b/lib/Serialization/ASTCommon.h
index 16db8e3..eacb39d 100644
--- a/lib/Serialization/ASTCommon.h
+++ b/lib/Serialization/ASTCommon.h
@@ -50,6 +50,8 @@ TypeID MakeTypeID(ASTContext &Context, QualType T, IdxForTypeTy IdxForType) {
return TypeIdx(PREDEF_TYPE_AUTO_DEDUCT).asTypeID(FastQuals);
if (T == Context.AutoRRefDeductTy)
return TypeIdx(PREDEF_TYPE_AUTO_RREF_DEDUCT).asTypeID(FastQuals);
+ if (T == Context.VaListTagTy)
+ return TypeIdx(PREDEF_TYPE_VA_LIST_TAG).asTypeID(FastQuals);
return IdxForType(T).asTypeID(FastQuals);
}
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index fd0c171..beef338 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -90,6 +90,12 @@ PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts) {
#define BENIGN_LANGOPT(Name, Bits, Default, Description)
#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description)
#include "clang/Basic/LangOptions.def"
+
+ if (PPLangOpts.ObjCRuntime != LangOpts.ObjCRuntime) {
+ Reader.Diag(diag::err_pch_langopt_value_mismatch)
+ << "target Objective-C runtime";
+ return true;
+ }
return false;
}
@@ -829,7 +835,7 @@ bool ASTReader::ParseLineTable(ModuleFile &F,
Entries.push_back(LineEntry::get(FileOffset, LineNo, FilenameID,
FileKind, IncludeOffset));
}
- LineTable.AddEntry(FID, Entries);
+ LineTable.AddEntry(FileID::get(FID), Entries);
}
return false;
@@ -1121,8 +1127,9 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(int ID) {
// This is the module's main file.
IncludeLoc = getImportLocation(F);
}
- FileID FID = SourceMgr.createFileID(File, IncludeLoc,
- (SrcMgr::CharacteristicKind)Record[2],
+ SrcMgr::CharacteristicKind
+ FileCharacter = (SrcMgr::CharacteristicKind)Record[2];
+ FileID FID = SourceMgr.createFileID(File, IncludeLoc, FileCharacter,
ID, BaseOffset + Record[0]);
SrcMgr::FileInfo &FileInfo =
const_cast<SrcMgr::FileInfo&>(SourceMgr.getSLocEntry(FID).getFile());
@@ -1139,7 +1146,8 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(int ID) {
}
const SrcMgr::ContentCache *ContentCache
- = SourceMgr.getOrCreateContentCache(File);
+ = SourceMgr.getOrCreateContentCache(File,
+ /*isSystemFile=*/FileCharacter != SrcMgr::C_User);
if (OverriddenBuffer && !ContentCache->BufferOverridden &&
ContentCache->ContentsEntry == ContentCache->OrigEntry) {
unsigned Code = SLocEntryCursor.ReadCode();
@@ -1737,6 +1745,17 @@ ASTReader::ReadASTBlock(ModuleFile &F) {
}
break;
+ case COMMENTS_BLOCK_ID: {
+ llvm::BitstreamCursor C = Stream;
+ if (Stream.SkipBlock() ||
+ ReadBlockAbbrevs(C, COMMENTS_BLOCK_ID)) {
+ Error("malformed comments block in AST file");
+ return Failure;
+ }
+ CommentsCursors.push_back(std::make_pair(C, &F));
+ break;
+ }
+
default:
if (!Stream.SkipBlock())
break;
@@ -2473,6 +2492,26 @@ ASTReader::ASTReadResult ASTReader::validateFileEntries(ModuleFile &M) {
Error("source location entry is incorrect");
return Failure;
}
+
+ off_t StoredSize = (off_t)Record[4];
+ time_t StoredTime = (time_t)Record[5];
+
+ // Check if there was a request to override the contents of the file
+ // that was part of the precompiled header. Overridding such a file
+ // can lead to problems when lexing using the source locations from the
+ // PCH.
+ SourceManager &SM = getSourceManager();
+ if (SM.isFileOverridden(File)) {
+ Error(diag::err_fe_pch_file_overridden, Filename);
+ // After emitting the diagnostic, recover by disabling the override so
+ // that the original file will be used.
+ SM.disableFileContentsOverride(File);
+ // The FileEntry is a virtual file entry with the size of the contents
+ // that would override the original contents. Set it to the original's
+ // size/time.
+ FileMgr.modifyFileEntry(const_cast<FileEntry*>(File),
+ StoredSize, StoredTime);
+ }
// The stat info from the FileEntry came from the cached stat
// info of the PCH, so we cannot trust it.
@@ -2482,12 +2521,12 @@ ASTReader::ASTReadResult ASTReader::validateFileEntries(ModuleFile &M) {
StatBuf.st_mtime = File->getModificationTime();
}
- if (((off_t)Record[4] != StatBuf.st_size
+ if ((StoredSize != StatBuf.st_size
#if !defined(LLVM_ON_WIN32)
// In our regression testing, the Windows file system seems to
// have inconsistent modification times that sometimes
// erroneously trigger this error-handling path.
- || (time_t)Record[5] != StatBuf.st_mtime
+ || StoredTime != StatBuf.st_mtime
#endif
)) {
Error(diag::err_fe_pch_file_modified, Filename);
@@ -2831,11 +2870,6 @@ void ASTReader::InitializeContext() {
// Load the special types.
if (SpecialTypes.size() >= NumSpecialTypeIDs) {
- if (Context.getBuiltinVaListType().isNull()) {
- Context.setBuiltinVaListType(
- GetType(SpecialTypes[SPECIAL_TYPE_BUILTIN_VA_LIST]));
- }
-
if (unsigned String = SpecialTypes[SPECIAL_TYPE_CF_CONSTANT_STRING]) {
if (!Context.CFConstantStringTypeDecl)
Context.setCFConstantStringType(GetType(String));
@@ -2975,7 +3009,7 @@ std::string ASTReader::getOriginalSourceFile(const std::string &ASTFileName,
OwningPtr<llvm::MemoryBuffer> Buffer;
Buffer.reset(FileMgr.getBufferForFile(ASTFileName, &ErrStr));
if (!Buffer) {
- Diags.Report(diag::err_fe_unable_to_read_pch_file) << ErrStr;
+ Diags.Report(diag::err_fe_unable_to_read_pch_file) << ASTFileName << ErrStr;
return std::string();
}
@@ -3297,8 +3331,7 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) {
/// them to the AST listener if one is set.
///
/// \returns true if the listener deems the file unacceptable, false otherwise.
-bool ASTReader::ParseLanguageOptions(
- const SmallVectorImpl<uint64_t> &Record) {
+bool ASTReader::ParseLanguageOptions(const RecordData &Record) {
if (Listener) {
LangOptions LangOpts;
unsigned Idx = 0;
@@ -3307,6 +3340,10 @@ bool ASTReader::ParseLanguageOptions(
#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \
LangOpts.set##Name(static_cast<LangOptions::Type>(Record[Idx++]));
#include "clang/Basic/LangOptions.def"
+
+ ObjCRuntime::Kind runtimeKind = (ObjCRuntime::Kind) Record[Idx++];
+ VersionTuple runtimeVersion = ReadVersionTuple(Record, Idx);
+ LangOpts.ObjCRuntime = ObjCRuntime(runtimeKind, runtimeVersion);
unsigned Length = Record[Idx++];
LangOpts.CurrentModule.assign(Record.begin() + Idx,
@@ -3869,6 +3906,8 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
} else if (EST == EST_Uninstantiated) {
EPI.ExceptionSpecDecl = ReadDeclAs<FunctionDecl>(*Loc.F, Record, Idx);
EPI.ExceptionSpecTemplate = ReadDeclAs<FunctionDecl>(*Loc.F, Record, Idx);
+ } else if (EST == EST_Unevaluated) {
+ EPI.ExceptionSpecDecl = ReadDeclAs<FunctionDecl>(*Loc.F, Record, Idx);
}
return Context.getFunctionType(ResultType, ParamTypes.data(), NumParams,
EPI);
@@ -4124,7 +4163,6 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
class clang::TypeLocReader : public TypeLocVisitor<TypeLocReader> {
ASTReader &Reader;
ModuleFile &F;
- llvm::BitstreamCursor &DeclsCursor;
const ASTReader::RecordData &Record;
unsigned &Idx;
@@ -4141,7 +4179,7 @@ class clang::TypeLocReader : public TypeLocVisitor<TypeLocReader> {
public:
TypeLocReader(ASTReader &Reader, ModuleFile &F,
const ASTReader::RecordData &Record, unsigned &Idx)
- : Reader(Reader), F(F), DeclsCursor(F.DeclsCursor), Record(Record), Idx(Idx)
+ : Reader(Reader), F(F), Record(Record), Idx(Idx)
{ }
// We want compile-time assurance that we've enumerated all of
@@ -4427,6 +4465,9 @@ QualType ASTReader::GetType(TypeID ID) {
T = Context.ARCUnbridgedCastTy;
break;
+ case PREDEF_TYPE_VA_LIST_TAG:
+ T = Context.getVaListTagType();
+ break;
}
assert(!T.isNull() && "Unknown predefined type");
@@ -4627,13 +4668,18 @@ Decl *ASTReader::GetDecl(DeclID ID) {
case PREDEF_DECL_OBJC_INSTANCETYPE_ID:
return Context.getObjCInstanceTypeDecl();
+
+ case PREDEF_DECL_BUILTIN_VA_LIST_ID:
+ return Context.getBuiltinVaListDecl();
}
}
unsigned Index = ID - NUM_PREDEF_DECL_IDS;
if (Index >= DeclsLoaded.size()) {
+ assert(0 && "declaration ID out-of-range for AST file");
Error("declaration ID out-of-range for AST file");
+ return 0;
}
if (!DeclsLoaded[Index]) {
@@ -4839,7 +4885,6 @@ namespace {
class DeclContextNameLookupVisitor {
ASTReader &Reader;
llvm::SmallVectorImpl<const DeclContext *> &Contexts;
- const DeclContext *DC;
DeclarationName Name;
SmallVectorImpl<NamedDecl *> &Decls;
@@ -4941,7 +4986,6 @@ namespace {
class DeclContextAllNamesVisitor {
ASTReader &Reader;
llvm::SmallVectorImpl<const DeclContext *> &Contexts;
- const DeclContext *DC;
llvm::DenseMap<DeclarationName, SmallVector<NamedDecl *, 8> > &Decls;
public:
@@ -5025,6 +5069,7 @@ void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) {
I = Decls.begin(), E = Decls.end(); I != E; ++I) {
SetExternalVisibleDeclsForName(DC, I->first, I->second);
}
+ const_cast<DeclContext *>(DC)->setHasExternalVisibleStorage(false);
}
/// \brief Under non-PCH compilation the consumer receives the objc methods
@@ -5887,7 +5932,7 @@ ASTReader::ReadTemplateArgument(ModuleFile &F,
case TemplateArgument::Integral: {
llvm::APSInt Value = ReadAPSInt(Record, Idx);
QualType T = readType(F, Record, Idx);
- return TemplateArgument(Value, T);
+ return TemplateArgument(Context, Value, T);
}
case TemplateArgument::Template:
return TemplateArgument(ReadTemplateName(F, Record, Idx));
@@ -6227,18 +6272,72 @@ IdentifierTable &ASTReader::getIdentifierTable() {
/// \brief Record that the given ID maps to the given switch-case
/// statement.
void ASTReader::RecordSwitchCaseID(SwitchCase *SC, unsigned ID) {
- assert(SwitchCaseStmts[ID] == 0 && "Already have a SwitchCase with this ID");
- SwitchCaseStmts[ID] = SC;
+ assert((*CurrSwitchCaseStmts)[ID] == 0 &&
+ "Already have a SwitchCase with this ID");
+ (*CurrSwitchCaseStmts)[ID] = SC;
}
/// \brief Retrieve the switch-case statement with the given ID.
SwitchCase *ASTReader::getSwitchCaseWithID(unsigned ID) {
- assert(SwitchCaseStmts[ID] != 0 && "No SwitchCase with this ID");
- return SwitchCaseStmts[ID];
+ assert((*CurrSwitchCaseStmts)[ID] != 0 && "No SwitchCase with this ID");
+ return (*CurrSwitchCaseStmts)[ID];
}
void ASTReader::ClearSwitchCaseIDs() {
- SwitchCaseStmts.clear();
+ CurrSwitchCaseStmts->clear();
+}
+
+void ASTReader::ReadComments() {
+ std::vector<RawComment *> Comments;
+ for (SmallVectorImpl<std::pair<llvm::BitstreamCursor,
+ serialization::ModuleFile *> >::iterator
+ I = CommentsCursors.begin(),
+ E = CommentsCursors.end();
+ I != E; ++I) {
+ llvm::BitstreamCursor &Cursor = I->first;
+ serialization::ModuleFile &F = *I->second;
+ SavedStreamPosition SavedPosition(Cursor);
+
+ RecordData Record;
+ while (true) {
+ unsigned Code = Cursor.ReadCode();
+ if (Code == llvm::bitc::END_BLOCK)
+ break;
+
+ if (Code == llvm::bitc::ENTER_SUBBLOCK) {
+ // No known subblocks, always skip them.
+ Cursor.ReadSubBlockID();
+ if (Cursor.SkipBlock()) {
+ Error("malformed block record in AST file");
+ return;
+ }
+ continue;
+ }
+
+ if (Code == llvm::bitc::DEFINE_ABBREV) {
+ Cursor.ReadAbbrevRecord();
+ continue;
+ }
+
+ // Read a record.
+ Record.clear();
+ switch ((CommentRecordTypes) Cursor.ReadRecord(Code, Record)) {
+ case COMMENTS_RAW_COMMENT: {
+ unsigned Idx = 0;
+ SourceRange SR = ReadSourceRange(F, Record, Idx);
+ RawComment::CommentKind Kind =
+ (RawComment::CommentKind) Record[Idx++];
+ bool IsTrailingComment = Record[Idx++];
+ bool IsAlmostTrailingComment = Record[Idx++];
+ Comments.push_back(new (Context) RawComment(SR, Kind,
+ IsTrailingComment,
+ IsAlmostTrailingComment));
+ break;
+ }
+ }
+ }
+ }
+ Context.Comments.addCommentsToFront(Comments);
}
void ASTReader::finishPendingActions() {
@@ -6353,7 +6452,8 @@ ASTReader::ASTReader(Preprocessor &PP, ASTContext &Context,
DisableValidation(DisableValidation),
DisableStatCache(DisableStatCache),
AllowASTWithCompilerErrors(AllowASTWithCompilerErrors),
- CurrentGeneration(0), NumStatHits(0), NumStatMisses(0),
+ CurrentGeneration(0), CurrSwitchCaseStmts(&SwitchCaseStmts),
+ NumStatHits(0), NumStatMisses(0),
NumSLocEntriesRead(0), TotalNumSLocEntries(0),
NumStatementsRead(0), TotalNumStatements(0), NumMacrosRead(0),
TotalNumMacros(0), NumSelectorsRead(0), NumMethodPoolEntriesRead(0),
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 8dd53ee..cb21f82 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -25,6 +25,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace clang::serialization;
@@ -629,6 +630,10 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) {
if (Record[Idx++]) {
// In practice, this won't be executed (since method definitions
// don't occur in header files).
+ // Switch case IDs for this method body.
+ ASTReader::SwitchCaseMapTy SwitchCaseStmtsForObjCMethod;
+ SaveAndRestore<ASTReader::SwitchCaseMapTy *>
+ SCFOM(Reader.CurrSwitchCaseStmts, &SwitchCaseStmtsForObjCMethod);
MD->setBody(Reader.ReadStmt(F));
MD->setSelfDecl(ReadDeclAs<ImplicitParamDecl>(Record, Idx));
MD->setCmdDecl(ReadDeclAs<ImplicitParamDecl>(Record, Idx));
@@ -637,6 +642,7 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) {
MD->setVariadic(Record[Idx++]);
MD->setSynthesized(Record[Idx++]);
MD->setDefined(Record[Idx++]);
+ MD->IsOverriding = Record[Idx++];
MD->IsRedeclaration = Record[Idx++];
MD->HasRedeclaration = Record[Idx++];
@@ -649,7 +655,7 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) {
MD->SetRelatedResultType(Record[Idx++]);
MD->setResultType(Reader.readType(F, Record, Idx));
MD->setResultTypeSourceInfo(GetTypeSourceInfo(Record, Idx));
- MD->setEndLoc(ReadSourceLocation(Record, Idx));
+ MD->DeclEndLoc = ReadSourceLocation(Record, Idx);
unsigned NumParams = Record[Idx++];
SmallVector<ParmVarDecl *, 16> Params;
Params.reserve(NumParams);
@@ -797,7 +803,6 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) {
ProtoLocs.push_back(ReadSourceLocation(Record, Idx));
CD->setProtocolList(ProtoRefs.data(), NumProtoRefs, ProtoLocs.data(),
Reader.getContext());
- CD->setHasSynthBitfield(Record[Idx++]);
}
void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) {
@@ -843,7 +848,6 @@ void ASTDeclReader::VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
D->setIvarRBraceLoc(ReadSourceLocation(Record, Idx));
llvm::tie(D->IvarInitializers, D->NumIvarInitializers)
= Reader.ReadCXXCtorInitializers(F, Record, Idx);
- D->setHasSynthBitfield(Record[Idx++]);
}
@@ -859,12 +863,11 @@ void ASTDeclReader::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
VisitDeclaratorDecl(FD);
- FD->setMutable(Record[Idx++]);
- int BitWidthOrInitializer = Record[Idx++];
- if (BitWidthOrInitializer == 1)
- FD->setBitWidth(Reader.ReadExpr(F));
- else if (BitWidthOrInitializer == 2)
- FD->setInClassInitializer(Reader.ReadExpr(F));
+ FD->Mutable = Record[Idx++];
+ if (int BitWidthOrInitializer = Record[Idx++]) {
+ FD->InitializerOrBitWidth.setInt(BitWidthOrInitializer - 1);
+ FD->InitializerOrBitWidth.setPointer(Reader.ReadExpr(F));
+ }
if (!FD->getDeclName()) {
if (FieldDecl *Tmpl = ReadDeclAs<FieldDecl>(Record, Idx))
Reader.getContext().setInstantiatedFromUnnamedFieldDecl(FD, Tmpl);
@@ -1085,14 +1088,11 @@ void ASTDeclReader::ReadCXXDefinitionData(
Data.HasPublicFields = Record[Idx++];
Data.HasMutableFields = Record[Idx++];
Data.HasOnlyCMembers = Record[Idx++];
+ Data.HasInClassInitializer = Record[Idx++];
Data.HasTrivialDefaultConstructor = Record[Idx++];
Data.HasConstexprNonCopyMoveConstructor = Record[Idx++];
Data.DefaultedDefaultConstructorIsConstexpr = Record[Idx++];
- Data.DefaultedCopyConstructorIsConstexpr = Record[Idx++];
- Data.DefaultedMoveConstructorIsConstexpr = Record[Idx++];
Data.HasConstexprDefaultConstructor = Record[Idx++];
- Data.HasConstexprCopyConstructor = Record[Idx++];
- Data.HasConstexprMoveConstructor = Record[Idx++];
Data.HasTrivialCopyConstructor = Record[Idx++];
Data.HasTrivialMoveConstructor = Record[Idx++];
Data.HasTrivialCopyAssignment = Record[Idx++];
@@ -1242,7 +1242,6 @@ void ASTDeclReader::VisitImportDecl(ImportDecl *D) {
SourceLocation *StoredLocs = reinterpret_cast<SourceLocation *>(D + 1);
for (unsigned I = 0, N = Record.back(); I != N; ++I)
StoredLocs[I] = ReadSourceLocation(Record, Idx);
- ++Idx;
}
void ASTDeclReader::VisitAccessSpecDecl(AccessSpecDecl *D) {
@@ -1500,7 +1499,8 @@ void ASTDeclReader::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) {
VisitDecl(D);
- D->AssertExpr = Reader.ReadExpr(F);
+ D->AssertExprAndFailed.setPointer(Reader.ReadExpr(F));
+ D->AssertExprAndFailed.setInt(Record[Idx++]);
D->Message = cast<StringLiteral>(Reader.ReadExpr(F));
D->RParenLoc = ReadSourceLocation(Record, Idx);
}
@@ -1528,7 +1528,7 @@ ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
// We temporarily set the first (canonical) declaration as the previous one
// which is the one that matters and mark the real previous DeclID to be
// loaded & attached later on.
- D->RedeclLink = typename Redeclarable<T>::PreviousDeclLink(FirstDecl);
+ D->RedeclLink = Redeclarable<T>::PreviousDeclLink(FirstDecl);
}
// Note that this declaration has been deserialized.
@@ -1556,8 +1556,7 @@ void ASTDeclReader::mergeRedeclarable(Redeclarable<T> *D,
// Have our redeclaration link point back at the canonical declaration
// of the existing declaration, so that this declaration has the
// appropriate canonical declaration.
- D->RedeclLink
- = typename Redeclarable<T>::PreviousDeclLink(ExistingCanon);
+ D->RedeclLink = Redeclarable<T>::PreviousDeclLink(ExistingCanon);
// When we merge a namespace, update its pointer to the first namespace.
if (NamespaceDecl *Namespace
@@ -1799,22 +1798,22 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) {
void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *previous) {
assert(D && previous);
if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
- TD->RedeclLink.setPointer(cast<TagDecl>(previous));
+ TD->RedeclLink.setNext(cast<TagDecl>(previous));
} else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- FD->RedeclLink.setPointer(cast<FunctionDecl>(previous));
+ FD->RedeclLink.setNext(cast<FunctionDecl>(previous));
} else if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
- VD->RedeclLink.setPointer(cast<VarDecl>(previous));
+ VD->RedeclLink.setNext(cast<VarDecl>(previous));
} else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) {
- TD->RedeclLink.setPointer(cast<TypedefNameDecl>(previous));
+ TD->RedeclLink.setNext(cast<TypedefNameDecl>(previous));
} else if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) {
- ID->RedeclLink.setPointer(cast<ObjCInterfaceDecl>(previous));
+ ID->RedeclLink.setNext(cast<ObjCInterfaceDecl>(previous));
} else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) {
- PD->RedeclLink.setPointer(cast<ObjCProtocolDecl>(previous));
+ PD->RedeclLink.setNext(cast<ObjCProtocolDecl>(previous));
} else if (NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D)) {
- ND->RedeclLink.setPointer(cast<NamespaceDecl>(previous));
+ ND->RedeclLink.setNext(cast<NamespaceDecl>(previous));
} else {
RedeclarableTemplateDecl *TD = cast<RedeclarableTemplateDecl>(D);
- TD->RedeclLink.setPointer(cast<RedeclarableTemplateDecl>(previous));
+ TD->RedeclLink.setNext(cast<RedeclarableTemplateDecl>(previous));
}
}
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 007ecee..c5325b5 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Serialization/ASTReader.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/StmtVisitor.h"
@@ -161,9 +162,13 @@ void ASTStmtReader::VisitLabelStmt(LabelStmt *S) {
void ASTStmtReader::VisitAttributedStmt(AttributedStmt *S) {
VisitStmt(S);
+ uint64_t NumAttrs = Record[Idx++];
AttrVec Attrs;
Reader.ReadAttributes(F, Attrs, Record, Idx);
- S->Attrs = Attrs;
+ (void)NumAttrs;
+ assert(NumAttrs == S->NumAttrs);
+ assert(NumAttrs == Attrs.size());
+ std::copy(Attrs.begin(), Attrs.end(), S->Attrs);
S->SubStmt = Reader.ReadSubStmt();
S->AttrLoc = ReadSourceLocation(Record, Idx);
}
@@ -317,6 +322,11 @@ void ASTStmtReader::VisitAsmStmt(AsmStmt *S) {
Clobbers.data(), NumClobbers);
}
+void ASTStmtReader::VisitMSAsmStmt(MSAsmStmt *S) {
+ // FIXME: Statement reader not yet implemented for MS style inline asm.
+ VisitStmt(S);
+}
+
void ASTStmtReader::VisitExpr(Expr *E) {
VisitStmt(E);
E->setType(Reader.readType(F, Record, Idx));
@@ -816,12 +826,12 @@ void ASTStmtReader::VisitObjCStringLiteral(ObjCStringLiteral *E) {
E->setAtLoc(ReadSourceLocation(Record, Idx));
}
-void ASTStmtReader::VisitObjCNumericLiteral(ObjCNumericLiteral *E) {
+void ASTStmtReader::VisitObjCBoxedExpr(ObjCBoxedExpr *E) {
VisitExpr(E);
// could be one of several IntegerLiteral, FloatLiteral, etc.
- E->Number = Reader.ReadSubStmt();
- E->ObjCNumericLiteralMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx);
- E->AtLoc = ReadSourceLocation(Record, Idx);
+ E->SubExpr = Reader.ReadSubStmt();
+ E->BoxingMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx);
+ E->Range = ReadSourceRange(Record, Idx);
}
void ASTStmtReader::VisitObjCArrayLiteral(ObjCArrayLiteral *E) {
@@ -873,6 +883,7 @@ void ASTStmtReader::VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
VisitExpr(E);
E->setProtocol(ReadDeclAs<ObjCProtocolDecl>(Record, Idx));
E->setAtLoc(ReadSourceLocation(Record, Idx));
+ E->ProtoLoc = ReadSourceLocation(Record, Idx);
E->setRParenLoc(ReadSourceLocation(Record, Idx));
}
@@ -1074,7 +1085,8 @@ void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
VisitCallExpr(E);
- E->setOperator((OverloadedOperatorKind)Record[Idx++]);
+ E->Operator = (OverloadedOperatorKind)Record[Idx++];
+ E->Range = Reader.ReadSourceRange(F, Record, Idx);
}
void ASTStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) {
@@ -1640,7 +1652,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
break;
case STMT_ATTRIBUTED:
- S = new (Context) AttributedStmt(Empty);
+ S = AttributedStmt::CreateEmpty(
+ Context,
+ /*NumAttrs*/Record[ASTStmtReader::NumStmtFields]);
break;
case STMT_IF:
@@ -1888,8 +1902,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
case EXPR_OBJC_STRING_LITERAL:
S = new (Context) ObjCStringLiteral(Empty);
break;
- case EXPR_OBJC_NUMERIC_LITERAL:
- S = new (Context) ObjCNumericLiteral(Empty);
+ case EXPR_OBJC_BOXED_EXPRESSION:
+ S = new (Context) ObjCBoxedExpr(Empty);
break;
case EXPR_OBJC_ARRAY_LITERAL:
S = ObjCArrayLiteral::CreateEmpty(Context,
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 36933a9..b7718c4 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -198,6 +198,8 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) {
} else if (T->getExceptionSpecType() == EST_Uninstantiated) {
Writer.AddDeclRef(T->getExceptionSpecDecl(), Record);
Writer.AddDeclRef(T->getExceptionSpecTemplate(), Record);
+ } else if (T->getExceptionSpecType() == EST_Unevaluated) {
+ Writer.AddDeclRef(T->getExceptionSpecDecl(), Record);
}
Code = TYPE_FUNCTION_PROTO;
}
@@ -699,7 +701,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
RECORD(EXPR_BLOCK);
RECORD(EXPR_GENERIC_SELECTION);
RECORD(EXPR_OBJC_STRING_LITERAL);
- RECORD(EXPR_OBJC_NUMERIC_LITERAL);
+ RECORD(EXPR_OBJC_BOXED_EXPRESSION);
RECORD(EXPR_OBJC_ARRAY_LITERAL);
RECORD(EXPR_OBJC_DICTIONARY_LITERAL);
RECORD(EXPR_OBJC_ENCODE);
@@ -1081,6 +1083,9 @@ void ASTWriter::WriteLanguageOptions(const LangOptions &LangOpts) {
#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \
Record.push_back(static_cast<unsigned>(LangOpts.get##Name()));
#include "clang/Basic/LangOptions.def"
+
+ Record.push_back((unsigned) LangOpts.ObjCRuntime.getKind());
+ AddVersionTuple(LangOpts.ObjCRuntime.getVersion(), Record);
Record.push_back(LangOpts.CurrentModule.size());
Record.append(LangOpts.CurrentModule.begin(), LangOpts.CurrentModule.end());
@@ -1242,15 +1247,14 @@ namespace {
// Trait used for the on-disk hash table of header search information.
class HeaderFileInfoTrait {
ASTWriter &Writer;
- const HeaderSearch &HS;
// Keep track of the framework names we've used during serialization.
SmallVector<char, 128> FrameworkStringData;
llvm::StringMap<unsigned> FrameworkNameOffset;
public:
- HeaderFileInfoTrait(ASTWriter &Writer, const HeaderSearch &HS)
- : Writer(Writer), HS(HS) { }
+ HeaderFileInfoTrait(ASTWriter &Writer)
+ : Writer(Writer) { }
typedef const char *key_type;
typedef key_type key_type_ref;
@@ -1335,7 +1339,7 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS, StringRef isysroot) {
if (FilesByUID.size() > HS.header_file_size())
FilesByUID.resize(HS.header_file_size());
- HeaderFileInfoTrait GeneratorTrait(*this, HS);
+ HeaderFileInfoTrait GeneratorTrait(*this);
OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator;
SmallVector<const char *, 4> SavedStrings;
unsigned NumHeaderSearchEntries = 0;
@@ -1605,11 +1609,11 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
for (LineTableInfo::iterator L = LineTable.begin(), LEnd = LineTable.end();
L != LEnd; ++L) {
// Only emit entries for local files.
- if (L->first < 0)
+ if (L->first.ID < 0)
continue;
// Emit the file ID
- Record.push_back(L->first);
+ Record.push_back(L->first.ID);
// Emit the line entries
Record.push_back(L->second.size());
@@ -2241,6 +2245,23 @@ void ASTWriter::WriteFileDeclIDsMap() {
Stream.EmitRecordWithBlob(AbbrevCode, Record, data(FileSortedIDs));
}
+void ASTWriter::WriteComments() {
+ Stream.EnterSubblock(COMMENTS_BLOCK_ID, 3);
+ ArrayRef<RawComment *> RawComments = Context->Comments.getComments();
+ RecordData Record;
+ for (ArrayRef<RawComment *>::iterator I = RawComments.begin(),
+ E = RawComments.end();
+ I != E; ++I) {
+ Record.clear();
+ AddSourceRange((*I)->getSourceRange(), Record);
+ Record.push_back((*I)->getKind());
+ Record.push_back((*I)->isTrailingComment());
+ Record.push_back((*I)->isAlmostTrailingComment());
+ Stream.EmitRecord(COMMENTS_RAW_COMMENT, Record);
+ }
+ Stream.ExitBlock();
+}
+
//===----------------------------------------------------------------------===//
// Global Method Pool and Selector Serialization
//===----------------------------------------------------------------------===//
@@ -3067,10 +3088,12 @@ void ASTWriter::WriteMergedDecls() {
//===----------------------------------------------------------------------===//
/// \brief Write a record containing the given attributes.
-void ASTWriter::WriteAttributes(const AttrVec &Attrs, RecordDataImpl &Record) {
+void ASTWriter::WriteAttributes(ArrayRef<const Attr*> Attrs,
+ RecordDataImpl &Record) {
Record.push_back(Attrs.size());
- for (AttrVec::const_iterator i = Attrs.begin(), e = Attrs.end(); i != e; ++i){
- const Attr * A = *i;
+ for (ArrayRef<const Attr *>::iterator i = Attrs.begin(),
+ e = Attrs.end(); i != e; ++i){
+ const Attr *A = *i;
Record.push_back(A->getKind()); // FIXME: stable encoding, target attrs
AddSourceRange(A->getRange(), Record);
@@ -3121,7 +3144,8 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) {
ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream)
: Stream(Stream), Context(0), PP(0), Chain(0), WritingModule(0),
- WritingAST(false), ASTHasCompilerErrors(false),
+ WritingAST(false), DoneWritingDeclsAndTypes(false),
+ ASTHasCompilerErrors(false),
FirstDeclID(NUM_PREDEF_DECL_IDS), NextDeclID(FirstDeclID),
FirstTypeID(NUM_PREDEF_TYPE_IDS), NextTypeID(FirstTypeID),
FirstIdentID(NUM_PREDEF_IDENT_IDS), NextIdentID(FirstIdentID),
@@ -3213,7 +3237,9 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
DeclIDs[Context.UInt128Decl] = PREDEF_DECL_UNSIGNED_INT_128_ID;
if (Context.ObjCInstanceTypeDecl)
DeclIDs[Context.ObjCInstanceTypeDecl] = PREDEF_DECL_OBJC_INSTANCETYPE_ID;
-
+ if (Context.BuiltinVaListDecl)
+ DeclIDs[Context.getBuiltinVaListDecl()] = PREDEF_DECL_BUILTIN_VA_LIST_ID;
+
if (!Chain) {
// Make sure that we emit IdentifierInfos (and any attached
// declarations) for builtins. We don't need to do this when we're
@@ -3379,13 +3405,20 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
Record.push_back(reinterpret_cast<uint64_t>(NS));
}
}
-
+
+ // Make sure visible decls, added to DeclContexts previously loaded from
+ // an AST file, are registered for serialization.
+ for (SmallVector<const Decl *, 16>::iterator
+ I = UpdatingVisibleDecls.begin(),
+ E = UpdatingVisibleDecls.end(); I != E; ++I) {
+ GetDeclRef(*I);
+ }
+
// Resolve any declaration pointers within the declaration updates block.
ResolveDeclUpdatesBlocks();
// Form the record of special types.
RecordData SpecialTypes;
- AddTypeRef(Context.getBuiltinVaListType(), SpecialTypes);
AddTypeRef(Context.getRawCFConstantStringType(), SpecialTypes);
AddTypeRef(Context.getFILEType(), SpecialTypes);
AddTypeRef(Context.getjmp_bufType(), SpecialTypes);
@@ -3413,8 +3446,11 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
}
Stream.ExitBlock();
+ DoneWritingDeclsAndTypes = true;
+
WriteFileDeclIDsMap();
WriteSourceManagerBlock(Context.getSourceManager(), PP, isysroot);
+ WriteComments();
if (Chain) {
// Write the mapping information describing our module dependencies and how
@@ -3798,6 +3834,11 @@ TypeIdx ASTWriter::GetOrCreateTypeIdx(QualType T) {
TypeIdx &Idx = TypeIdxs[T];
if (Idx.getIndex() == 0) {
+ if (DoneWritingDeclsAndTypes) {
+ assert(0 && "New type seen after serializing all the types to emit!");
+ return TypeIdx();
+ }
+
// We haven't seen this type before. Assign it a new ID and put it
// into the queue of types to emit.
Idx = TypeIdx(NextTypeID++);
@@ -3835,6 +3876,11 @@ DeclID ASTWriter::GetDeclRef(const Decl *D) {
assert(!(reinterpret_cast<uintptr_t>(D) & 0x01) && "Invalid decl pointer");
DeclID &ID = DeclIDs[D];
if (ID == 0) {
+ if (DoneWritingDeclsAndTypes) {
+ assert(0 && "New decl seen after serializing all the decls to emit!");
+ return 0;
+ }
+
// We haven't seen this declaration before. Give it a new ID and
// enqueue it in the list of declarations to emit.
ID = NextDeclID++;
@@ -4148,7 +4194,7 @@ void ASTWriter::AddTemplateArgument(const TemplateArgument &Arg,
AddDeclRef(Arg.getAsDecl(), Record);
break;
case TemplateArgument::Integral:
- AddAPSInt(*Arg.getAsIntegral(), Record);
+ AddAPSInt(Arg.getAsIntegral(), Record);
AddTypeRef(Arg.getIntegralType(), Record);
break;
case TemplateArgument::Template:
@@ -4310,14 +4356,11 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
Record.push_back(Data.HasPublicFields);
Record.push_back(Data.HasMutableFields);
Record.push_back(Data.HasOnlyCMembers);
+ Record.push_back(Data.HasInClassInitializer);
Record.push_back(Data.HasTrivialDefaultConstructor);
Record.push_back(Data.HasConstexprNonCopyMoveConstructor);
Record.push_back(Data.DefaultedDefaultConstructorIsConstexpr);
- Record.push_back(Data.DefaultedCopyConstructorIsConstexpr);
- Record.push_back(Data.DefaultedMoveConstructorIsConstexpr);
Record.push_back(Data.HasConstexprDefaultConstructor);
- Record.push_back(Data.HasConstexprCopyConstructor);
- Record.push_back(Data.HasConstexprMoveConstructor);
Record.push_back(Data.HasTrivialCopyConstructor);
Record.push_back(Data.HasTrivialMoveConstructor);
Record.push_back(Data.HasTrivialCopyAssignment);
@@ -4459,6 +4502,7 @@ void ASTWriter::AddedVisibleDecl(const DeclContext *DC, const Decl *D) {
return; // Not a source decl added to a DeclContext from PCH.
AddUpdatedDeclContext(DC);
+ UpdatingVisibleDecls.push_back(D);
}
void ASTWriter::AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) {
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index 1ee3ac4..602943b 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -151,7 +151,8 @@ void ASTDeclWriter::VisitDecl(Decl *D) {
Record.push_back(D->isInvalidDecl());
Record.push_back(D->hasAttrs());
if (D->hasAttrs())
- Writer.WriteAttributes(D->getAttrs(), Record);
+ Writer.WriteAttributes(ArrayRef<const Attr*>(D->getAttrs().begin(),
+ D->getAttrs().size()), Record);
Record.push_back(D->isImplicit());
Record.push_back(D->isUsed(false));
Record.push_back(D->isReferenced());
@@ -417,6 +418,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) {
Record.push_back(D->isVariadic());
Record.push_back(D->isSynthesized());
Record.push_back(D->isDefined());
+ Record.push_back(D->IsOverriding);
Record.push_back(D->IsRedeclaration);
Record.push_back(D->HasRedeclaration);
@@ -559,7 +561,6 @@ void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) {
PL = D->protocol_loc_begin(), PLEnd = D->protocol_loc_end();
PL != PLEnd; ++PL)
Writer.AddSourceLocation(*PL, Record);
- Record.push_back(D->hasSynthBitfield());
Code = serialization::DECL_OBJC_CATEGORY;
}
@@ -607,7 +608,6 @@ void ASTDeclWriter::VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
Writer.AddSourceLocation(D->getIvarRBraceLoc(), Record);
Writer.AddCXXCtorInitializers(D->IvarInitializers, D->NumIvarInitializers,
Record);
- Record.push_back(D->hasSynthBitfield());
Code = serialization::DECL_OBJC_IMPLEMENTATION;
}
@@ -625,11 +625,13 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
VisitDeclaratorDecl(D);
Record.push_back(D->isMutable());
- Record.push_back(D->getBitWidth()? 1 : D->hasInClassInitializer() ? 2 : 0);
- if (D->getBitWidth())
- Writer.AddStmt(D->getBitWidth());
- else if (D->hasInClassInitializer())
- Writer.AddStmt(D->getInClassInitializer());
+ if (D->InitializerOrBitWidth.getInt() != ICIS_NoInit ||
+ D->InitializerOrBitWidth.getPointer()) {
+ Record.push_back(D->InitializerOrBitWidth.getInt() + 1);
+ Writer.AddStmt(D->InitializerOrBitWidth.getPointer());
+ } else {
+ Record.push_back(0);
+ }
if (!D->getDeclName())
Writer.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D), Record);
@@ -1054,7 +1056,7 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
VisitRedeclarableTemplateDecl(D);
if (D->isFirstDeclaration()) {
- typedef llvm::FoldingSet<ClassTemplateSpecializationDecl> CTSDSetTy;
+ typedef llvm::FoldingSetVector<ClassTemplateSpecializationDecl> CTSDSetTy;
CTSDSetTy &CTSDSet = D->getSpecializations();
Record.push_back(CTSDSet.size());
for (CTSDSetTy::iterator I=CTSDSet.begin(), E = CTSDSet.end(); I!=E; ++I) {
@@ -1062,7 +1064,8 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
Writer.AddDeclRef(&*I, Record);
}
- typedef llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> CTPSDSetTy;
+ typedef llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>
+ CTPSDSetTy;
CTPSDSetTy &CTPSDSet = D->getPartialSpecializations();
Record.push_back(CTPSDSet.size());
for (CTPSDSetTy::iterator I=CTPSDSet.begin(), E=CTPSDSet.end(); I!=E; ++I) {
@@ -1146,7 +1149,7 @@ void ASTDeclWriter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
// Write the function specialization declarations.
Record.push_back(D->getSpecializations().size());
- for (llvm::FoldingSet<FunctionTemplateSpecializationInfo>::iterator
+ for (llvm::FoldingSetVector<FunctionTemplateSpecializationInfo>::iterator
I = D->getSpecializations().begin(),
E = D->getSpecializations().end() ; I != E; ++I) {
assert(I->Function->isCanonicalDecl() &&
@@ -1217,6 +1220,7 @@ void ASTDeclWriter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) {
VisitDecl(D);
Writer.AddStmt(D->getAssertExpr());
+ Record.push_back(D->isFailed());
Writer.AddStmt(D->getMessage());
Writer.AddSourceLocation(D->getRParenLoc(), Record);
Code = serialization::DECL_STATIC_ASSERT;
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 1e31211..f63388f 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Serialization/ASTWriter.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@@ -108,6 +109,7 @@ void ASTStmtWriter::VisitLabelStmt(LabelStmt *S) {
void ASTStmtWriter::VisitAttributedStmt(AttributedStmt *S) {
VisitStmt(S);
+ Record.push_back(S->getAttrs().size());
Writer.WriteAttributes(S->getAttrs(), Record);
Writer.AddStmt(S->getSubStmt());
Writer.AddSourceLocation(S->getAttrLoc(), Record);
@@ -249,6 +251,11 @@ void ASTStmtWriter::VisitAsmStmt(AsmStmt *S) {
Code = serialization::STMT_ASM;
}
+void ASTStmtWriter::VisitMSAsmStmt(MSAsmStmt *S) {
+ // FIXME: Statement writer not yet implemented for MS style inline asm.
+ VisitStmt(S);
+}
+
void ASTStmtWriter::VisitExpr(Expr *E) {
VisitStmt(E);
Writer.AddTypeRef(E->getType(), Record);
@@ -777,12 +784,12 @@ void ASTStmtWriter::VisitObjCStringLiteral(ObjCStringLiteral *E) {
Code = serialization::EXPR_OBJC_STRING_LITERAL;
}
-void ASTStmtWriter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) {
+void ASTStmtWriter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) {
VisitExpr(E);
- Writer.AddStmt(E->getNumber());
- Writer.AddDeclRef(E->getObjCNumericLiteralMethod(), Record);
- Writer.AddSourceLocation(E->getAtLoc(), Record);
- Code = serialization::EXPR_OBJC_NUMERIC_LITERAL;
+ Writer.AddStmt(E->getSubExpr());
+ Writer.AddDeclRef(E->getBoxingMethod(), Record);
+ Writer.AddSourceRange(E->getSourceRange(), Record);
+ Code = serialization::EXPR_OBJC_BOXED_EXPRESSION;
}
void ASTStmtWriter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) {
@@ -837,6 +844,7 @@ void ASTStmtWriter::VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
VisitExpr(E);
Writer.AddDeclRef(E->getProtocol(), Record);
Writer.AddSourceLocation(E->getAtLoc(), Record);
+ Writer.AddSourceLocation(E->ProtoLoc, Record);
Writer.AddSourceLocation(E->getRParenLoc(), Record);
Code = serialization::EXPR_OBJC_PROTOCOL_EXPR;
}
@@ -1045,6 +1053,7 @@ void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
VisitCallExpr(E);
Record.push_back(E->getOperator());
+ Writer.AddSourceRange(E->Range, Record);
Code = serialization::EXPR_CXX_OPERATOR_CALL;
}
diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt
index 04c5382..20999e1 100644
--- a/lib/Serialization/CMakeLists.txt
+++ b/lib/Serialization/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(LLVM_USED_LIBS clangSema)
-
add_clang_library(clangSerialization
ASTCommon.h
ASTReaderInternals.h
@@ -18,10 +16,18 @@ add_clang_library(clangSerialization
add_dependencies(clangSerialization
ClangAttrClasses
ClangAttrList
+ ClangAttrParsedAttrList
ClangAttrPCHRead
ClangAttrPCHWrite
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
ClangDiagnosticLex
ClangDiagnosticSema
ClangDiagnosticSerialization
- ClangDeclNodes
- ClangStmtNodes)
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangSerialization
+ clangSema
+ )
diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp
index ab66e98..c582cfc 100644
--- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp
@@ -15,6 +15,7 @@
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -23,40 +24,32 @@ using namespace ento;
namespace {
class AttrNonNullChecker
- : public Checker< check::PreStmt<CallExpr> > {
+ : public Checker< check::PreCall > {
mutable OwningPtr<BugType> BT;
public:
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
};
} // end anonymous namespace
-void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
+void AttrNonNullChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
-
- // Check if the callee has a 'nonnull' attribute.
- SVal X = state->getSVal(CE->getCallee(), LCtx);
-
- const FunctionDecl *FD = X.getAsFunctionDecl();
+ const Decl *FD = Call.getDecl();
if (!FD)
return;
- const NonNullAttr* Att = FD->getAttr<NonNullAttr>();
+ const NonNullAttr *Att = FD->getAttr<NonNullAttr>();
if (!Att)
return;
- // Iterate through the arguments of CE and check them for null.
- unsigned idx = 0;
-
- for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E;
- ++I, ++idx) {
+ ProgramStateRef state = C.getState();
+ // Iterate through the arguments of CE and check them for null.
+ for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) {
if (!Att->isNonNull(idx))
continue;
- SVal V = state->getSVal(*I, LCtx);
+ SVal V = Call.getArgSVal(idx);
DefinedSVal *DV = dyn_cast<DefinedSVal>(&V);
// If the value is unknown or undefined, we can't perform this check.
@@ -65,11 +58,16 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
if (!isa<Loc>(*DV)) {
// If the argument is a union type, we want to handle a potential
- // transparent_unoin GCC extension.
- QualType T = (*I)->getType();
+ // transparent_union GCC extension.
+ const Expr *ArgE = Call.getArgExpr(idx);
+ if (!ArgE)
+ continue;
+
+ QualType T = ArgE->getType();
const RecordType *UT = T->getAsUnionType();
if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
continue;
+
if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) {
nonloc::CompoundVal::iterator CSV_I = CSV->begin();
assert(CSV_I != CSV->end());
@@ -78,8 +76,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
assert(++CSV_I == CSV->end());
if (!DV)
continue;
- }
- else {
+ } else {
// FIXME: Handle LazyCompoundVals?
continue;
}
@@ -106,10 +103,9 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
"'nonnull' parameter", errorNode);
// Highlight the range of the argument that was null.
- const Expr *arg = *I;
- R->addRange(arg->getSourceRange());
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode,
- arg, R));
+ R->addRange(Call.getArgSourceRange(idx));
+ if (const Expr *ArgE = Call.getArgExpr(idx))
+ bugreporter::addTrackNullOrUndefValueVisitor(errorNode, ArgE, R);
// Emit the bug report.
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 6dd0a8c..955e79a 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -17,18 +17,20 @@
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/AST/ASTContext.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
using namespace clang;
using namespace ento;
@@ -44,21 +46,40 @@ public:
// Utility functions.
//===----------------------------------------------------------------------===//
-static const char* GetReceiverNameType(const ObjCMessage &msg) {
+static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
- return ID->getIdentifier()->getNameStart();
- return 0;
+ return ID->getIdentifier()->getName();
+ return StringRef();
}
-static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
- StringRef ClassName) {
- if (ID->getIdentifier()->getName() == ClassName)
- return true;
+enum FoundationClass {
+ FC_None,
+ FC_NSArray,
+ FC_NSDictionary,
+ FC_NSEnumerator,
+ FC_NSOrderedSet,
+ FC_NSSet,
+ FC_NSString
+};
+
+static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
+ static llvm::StringMap<FoundationClass> Classes;
+ if (Classes.empty()) {
+ Classes["NSArray"] = FC_NSArray;
+ Classes["NSDictionary"] = FC_NSDictionary;
+ Classes["NSEnumerator"] = FC_NSEnumerator;
+ Classes["NSOrderedSet"] = FC_NSOrderedSet;
+ Classes["NSSet"] = FC_NSSet;
+ Classes["NSString"] = FC_NSString;
+ }
- if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
- return isReceiverClassOrSuperclass(Super, ClassName);
+ // FIXME: Should we cache this at all?
+ FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
+ if (result == FC_None)
+ if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
+ return findKnownClass(Super);
- return false;
+ return result;
}
static inline bool isNil(SVal X) {
@@ -74,15 +95,15 @@ namespace {
mutable OwningPtr<APIMisuse> BT;
void WarnNilArg(CheckerContext &C,
- const ObjCMessage &msg, unsigned Arg) const;
+ const ObjCMethodCall &msg, unsigned Arg) const;
public:
- void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
};
}
void NilArgChecker::WarnNilArg(CheckerContext &C,
- const ObjCMessage &msg,
+ const ObjCMethodCall &msg,
unsigned int Arg) const
{
if (!BT)
@@ -91,7 +112,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C,
if (ExplodedNode *N = C.generateSink()) {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
- os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
+ os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
<< msg.getSelector().getAsString() << "' cannot be nil";
BugReport *R = new BugReport(*BT, os.str(), N);
@@ -100,13 +121,13 @@ void NilArgChecker::WarnNilArg(CheckerContext &C,
}
}
-void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
+void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
if (!ID)
return;
- if (isReceiverClassOrSuperclass(ID, "NSString")) {
+ if (findKnownClass(ID) == FC_NSString) {
Selector S = msg.getSelector();
if (S.isUnarySelector())
@@ -130,7 +151,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
Name == "compare:options:range:locale:" ||
Name == "componentsSeparatedByCharactersInSet:" ||
Name == "initWithFormat:") {
- if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
+ if (isNil(msg.getArgSVal(0)))
WarnNilArg(C, msg, 0);
}
}
@@ -411,8 +432,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
BugReport *report = new BugReport(*BT, description, N);
report->addRange(Arg->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Arg, report);
C.EmitReport(report);
return;
}
@@ -434,11 +454,11 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
mutable OwningPtr<BugType> BT;
public:
- void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
};
}
-void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
+void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
if (!BT) {
@@ -490,18 +510,18 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
mutable Selector initWithObjectsAndKeysS;
mutable OwningPtr<BugType> BT;
- bool isVariadicMessage(const ObjCMessage &msg) const;
+ bool isVariadicMessage(const ObjCMethodCall &msg) const;
public:
- void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
};
}
/// isVariadicMessage - Returns whether the given message is a variadic message,
/// where all arguments must be Objective-C types.
bool
-VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
- const ObjCMethodDecl *MD = msg.getMethodDecl();
+VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
+ const ObjCMethodDecl *MD = msg.getDecl();
if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
return false;
@@ -517,53 +537,35 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
// gains that this analysis gives.
const ObjCInterfaceDecl *Class = MD->getClassInterface();
- // -[NSArray initWithObjects:]
- if (isReceiverClassOrSuperclass(Class, "NSArray") &&
- S == initWithObjectsS)
- return true;
-
- // -[NSDictionary initWithObjectsAndKeys:]
- if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
- S == initWithObjectsAndKeysS)
- return true;
-
- // -[NSSet initWithObjects:]
- if (isReceiverClassOrSuperclass(Class, "NSSet") &&
- S == initWithObjectsS)
- return true;
-
- // -[NSOrderedSet initWithObjects:]
- if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
- S == initWithObjectsS)
- return true;
+ switch (findKnownClass(Class)) {
+ case FC_NSArray:
+ case FC_NSOrderedSet:
+ case FC_NSSet:
+ return S == initWithObjectsS;
+ case FC_NSDictionary:
+ return S == initWithObjectsAndKeysS;
+ default:
+ return false;
+ }
} else {
const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
- // -[NSArray arrayWithObjects:]
- if (isReceiverClassOrSuperclass(Class, "NSArray") &&
- S == arrayWithObjectsS)
- return true;
-
- // -[NSDictionary dictionaryWithObjectsAndKeys:]
- if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
- S == dictionaryWithObjectsAndKeysS)
- return true;
-
- // -[NSSet setWithObjects:]
- if (isReceiverClassOrSuperclass(Class, "NSSet") &&
- S == setWithObjectsS)
- return true;
-
- // -[NSOrderedSet orderedSetWithObjects:]
- if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
- S == orderedSetWithObjectsS)
- return true;
+ switch (findKnownClass(Class)) {
+ case FC_NSArray:
+ return S == arrayWithObjectsS;
+ case FC_NSOrderedSet:
+ return S == orderedSetWithObjectsS;
+ case FC_NSSet:
+ return S == setWithObjectsS;
+ case FC_NSDictionary:
+ return S == dictionaryWithObjectsAndKeysS;
+ default:
+ return false;
+ }
}
-
- return false;
}
-void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
+void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
if (!BT) {
BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
@@ -599,7 +601,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
ProgramStateRef state = C.getState();
for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
- QualType ArgTy = msg.getArgType(I);
+ QualType ArgTy = msg.getArgExpr(I)->getType();
if (ArgTy->isObjCObjectPointerType())
continue;
@@ -608,8 +610,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
continue;
// Ignore pointer constants.
- if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
- state)))
+ if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
continue;
// Ignore pointer types annotated with 'NSObject' attribute.
@@ -621,9 +622,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
continue;
// Generate only one error node to use for all bug reports.
- if (!errorNode.hasValue()) {
+ if (!errorNode.hasValue())
errorNode = C.addTransition();
- }
if (!errorNode.getValue())
continue;
@@ -631,23 +631,93 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
- if (const char *TypeName = GetReceiverNameType(msg))
+ StringRef TypeName = GetReceiverInterfaceName(msg);
+ if (!TypeName.empty())
os << "Argument to '" << TypeName << "' method '";
else
os << "Argument to method '";
os << msg.getSelector().getAsString()
- << "' should be an Objective-C pointer type, not '"
- << ArgTy.getAsString() << "'";
+ << "' should be an Objective-C pointer type, not '";
+ ArgTy.print(os, C.getLangOpts());
+ os << "'";
- BugReport *R = new BugReport(*BT, os.str(),
- errorNode.getValue());
+ BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
R->addRange(msg.getArgSourceRange(I));
C.EmitReport(R);
}
}
//===----------------------------------------------------------------------===//
+// Improves the modeling of loops over Cocoa collections.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ObjCLoopChecker
+ : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
+
+public:
+ void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
+};
+}
+
+static bool isKnownNonNilCollectionType(QualType T) {
+ const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
+ if (!PT)
+ return false;
+
+ const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
+ if (!ID)
+ return false;
+
+ switch (findKnownClass(ID)) {
+ case FC_NSArray:
+ case FC_NSDictionary:
+ case FC_NSEnumerator:
+ case FC_NSOrderedSet:
+ case FC_NSSet:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Check if this is the branch for the end of the loop.
+ SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
+ if (CollectionSentinel.isZeroConstant())
+ return;
+
+ // See if the collection is one where we /know/ the elements are non-nil.
+ const Expr *Collection = FCS->getCollection();
+ if (!isKnownNonNilCollectionType(Collection->getType()))
+ return;
+
+ // FIXME: Copied from ExprEngineObjC.
+ const Stmt *Element = FCS->getElement();
+ SVal ElementVar;
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
+ const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
+ assert(ElemDecl->getInit() == 0);
+ ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
+ } else {
+ ElementVar = State->getSVal(Element, C.getLocationContext());
+ }
+
+ if (!isa<Loc>(ElementVar))
+ return;
+
+ // Go ahead and assume the value is non-nil.
+ SVal Val = State->getSVal(cast<Loc>(ElementVar));
+ State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
+ C.addTransition(State);
+}
+
+
+//===----------------------------------------------------------------------===//
// Check registration.
//===----------------------------------------------------------------------===//
@@ -670,3 +740,7 @@ void ento::registerClassReleaseChecker(CheckerManager &mgr) {
void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<VariadicMethodTypeChecker>();
}
+
+void ento::registerObjCLoopChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCLoopChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index a377ca9..7fe51d3 100644
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -3,8 +3,6 @@ clang_tablegen(Checkers.inc -gen-clang-sa-checkers
SOURCE Checkers.td
TARGET ClangSACheckers)
-set(LLVM_USED_LIBS clangBasic clangAST clangStaticAnalyzerCore)
-
add_clang_library(clangStaticAnalyzerCheckers
AdjustedReturnValueChecker.cpp
AnalyzerStatsChecker.cpp
@@ -31,10 +29,11 @@ add_clang_library(clangStaticAnalyzerCheckers
DebugCheckers.cpp
DereferenceChecker.cpp
DivZeroChecker.cpp
+ DynamicTypePropagation.cpp
+ ExprInspectionChecker.cpp
FixedAddressChecker.cpp
GenericTaintChecker.cpp
IdempotentOperationChecker.cpp
- IteratorsChecker.cpp
LLVMConventionsChecker.cpp
MacOSKeychainAPIChecker.cpp
MacOSXAPIChecker.cpp
@@ -59,6 +58,7 @@ add_clang_library(clangStaticAnalyzerCheckers
StackAddrEscapeChecker.cpp
StreamChecker.cpp
TaintTesterChecker.cpp
+ TraversalChecker.cpp
UndefBranchChecker.cpp
UndefCapturedBlockVarChecker.cpp
UndefResultChecker.cpp
@@ -74,7 +74,15 @@ add_dependencies(clangStaticAnalyzerCheckers
clangStaticAnalyzerCore
ClangAttrClasses
ClangAttrList
+ ClangCommentNodes
ClangDeclNodes
+ ClangDiagnosticCommon
ClangStmtNodes
ClangSACheckers
)
+
+target_link_libraries(clangStaticAnalyzerCheckers
+ clangBasic
+ clangAST
+ clangStaticAnalyzerCore
+ )
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 9eb7edf..483082a 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -66,7 +66,7 @@ public:
const StoreManager::InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
typedef void (CStringChecker::*FnCheck)(CheckerContext &,
const CallExpr *) const;
@@ -252,8 +252,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
BugReport *report = new BugReport(*BT, os.str(), N);
report->addRange(S->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, S, report);
C.EmitReport(report);
return NULL;
}
@@ -901,9 +900,10 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
// If the size is zero, there won't be any actual memory access, so
// just bind the return value to the destination buffer and return.
- if (stateZeroSize) {
+ if (stateZeroSize && !stateNonZeroSize) {
stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal);
C.addTransition(stateZeroSize);
+ return;
}
// If the size can be nonzero, we have to check the other arguments.
@@ -1403,6 +1403,24 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// For strncpy, this is just checking that lenVal <= sizeof(dst)
// (Yes, strncpy and strncat differ in how they treat termination.
// strncat ALWAYS terminates, but strncpy doesn't.)
+
+ // We need a special case for when the copy size is zero, in which
+ // case strncpy will do no work at all. Our bounds check uses n-1
+ // as the last element accessed, so n == 0 is problematic.
+ ProgramStateRef StateZeroSize, StateNonZeroSize;
+ llvm::tie(StateZeroSize, StateNonZeroSize) =
+ assumeZero(C, state, *lenValNL, sizeTy);
+
+ // If the size is known to be zero, we're done.
+ if (StateZeroSize && !StateNonZeroSize) {
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
+ C.addTransition(StateZeroSize);
+ return;
+ }
+
+ // Otherwise, go ahead and figure out the last element we'll touch.
+ // We don't record the non-zero assumption here because we can't
+ // be sure. We won't warn on a possible zero.
NonLoc one = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy));
maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
one, sizeTy);
@@ -1876,7 +1894,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
CStringLength::EntryMap Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return state;
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index f601431..30f45c7 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -15,8 +15,8 @@
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"
@@ -27,35 +27,37 @@ using namespace ento;
namespace {
class CallAndMessageChecker
- : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > {
+ : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage,
+ check::PreCall > {
mutable OwningPtr<BugType> BT_call_null;
mutable OwningPtr<BugType> BT_call_undef;
+ mutable OwningPtr<BugType> BT_cxx_call_null;
+ mutable OwningPtr<BugType> BT_cxx_call_undef;
mutable OwningPtr<BugType> BT_call_arg;
mutable OwningPtr<BugType> BT_msg_undef;
mutable OwningPtr<BugType> BT_objc_prop_undef;
+ mutable OwningPtr<BugType> BT_objc_subscript_undef;
mutable OwningPtr<BugType> BT_msg_arg;
mutable OwningPtr<BugType> BT_msg_ret;
public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
- static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg,
- const char *BT_desc, OwningPtr<BugType> &BT);
- static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange,
- const Expr *argEx,
- const bool checkUninitFields,
- const char *BT_desc,
- OwningPtr<BugType> &BT);
-
- static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
- void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg,
+ static bool PreVisitProcessArg(CheckerContext &C, SVal V,
+ SourceRange argRange, const Expr *argEx,
+ bool IsFirstArgument, bool checkUninitFields,
+ const CallEvent &Call, OwningPtr<BugType> &BT);
+
+ static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
+ void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
ExplodedNode *N) const;
void HandleNilReceiver(CheckerContext &C,
ProgramStateRef state,
- ObjCMessage msg) const;
+ const ObjCMethodCall &msg) const;
static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) {
if (!BT)
@@ -64,55 +66,63 @@ private:
};
} // end anonymous namespace
-void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
- const CallExpr *CE) {
+void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
+ const Expr *BadE) {
ExplodedNode *N = C.generateSink();
if (!N)
return;
BugReport *R = new BugReport(*BT, BT->getName(), N);
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- bugreporter::GetCalleeExpr(N), R));
+ if (BadE) {
+ R->addRange(BadE->getSourceRange());
+ bugreporter::addTrackNullOrUndefValueVisitor(N, BadE, R);
+ }
C.EmitReport(R);
}
-void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C,
- CallOrObjCMessage callOrMsg,
- const char *BT_desc,
- OwningPtr<BugType> &BT) {
- // Don't check for uninitialized field values in arguments if the
- // caller has a body that is available and we have the chance to inline it.
- // This is a hack, but is a reasonable compromise betweens sometimes warning
- // and sometimes not depending on if we decide to inline a function.
- const Decl *D = callOrMsg.getDecl();
- const bool checkUninitFields =
- !(C.getAnalysisManager().shouldInlineCall() &&
- (D && D->getBody()));
-
- for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i)
- if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i),
- callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i),
- checkUninitFields,
- BT_desc, BT))
- return;
+StringRef describeUninitializedArgumentInCall(const CallEvent &Call,
+ bool IsFirstArgument) {
+ switch (Call.getKind()) {
+ case CE_ObjCMessage: {
+ const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
+ switch (Msg.getMessageKind()) {
+ case OCM_Message:
+ return "Argument in message expression is an uninitialized value";
+ case OCM_PropertyAccess:
+ assert(Msg.isSetter() && "Getters have no args");
+ return "Argument for property setter is an uninitialized value";
+ case OCM_Subscript:
+ if (Msg.isSetter() && IsFirstArgument)
+ return "Argument for subscript setter is an uninitialized value";
+ return "Subscript index is an uninitialized value";
+ }
+ llvm_unreachable("Unknown message kind.");
+ }
+ case CE_Block:
+ return "Block call argument is an uninitialized value";
+ default:
+ return "Function call argument is an uninitialized value";
+ }
}
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SVal V, SourceRange argRange,
const Expr *argEx,
- const bool checkUninitFields,
- const char *BT_desc,
+ bool IsFirstArgument,
+ bool checkUninitFields,
+ const CallEvent &Call,
OwningPtr<BugType> &BT) {
if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT(BT_desc, BT);
+ LazyInit_BT("Uninitialized argument value", BT);
// Generate a report for this bug.
- BugReport *R = new BugReport(*BT, BT->getName(), N);
+ StringRef Desc = describeUninitializedArgumentInCall(Call,
+ IsFirstArgument);
+ BugReport *R = new BugReport(*BT, Desc, N);
R->addRange(argRange);
if (argEx)
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx,
- R));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, argEx, R);
C.EmitReport(R);
}
return true;
@@ -128,14 +138,13 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
public:
SmallVector<const FieldDecl *, 10> FieldChain;
private:
- ASTContext &C;
StoreManager &StoreMgr;
MemRegionManager &MrMgr;
Store store;
public:
- FindUninitializedField(ASTContext &c, StoreManager &storeMgr,
+ FindUninitializedField(StoreManager &storeMgr,
MemRegionManager &mrMgr, Store s)
- : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
+ : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
bool Find(const TypedValueRegion *R) {
QualType T = R->getValueType();
@@ -146,7 +155,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
RD->field_begin(), E = RD->field_end(); I!=E; ++I) {
const FieldRegion *FR = MrMgr.getFieldRegion(*I, R);
FieldChain.push_back(*I);
- T = (*I)->getType();
+ T = I->getType();
if (T->getAsStructureType()) {
if (Find(FR))
return true;
@@ -165,14 +174,13 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
};
const LazyCompoundValData *D = LV->getCVData();
- FindUninitializedField F(C.getASTContext(),
- C.getState()->getStateManager().getStoreManager(),
+ FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
C.getSValBuilder().getRegionManager(),
D->getStore());
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT(BT_desc, BT);
+ LazyInit_BT("Uninitialized argument value", BT);
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";
@@ -212,87 +220,137 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const{
const Expr *Callee = CE->getCallee()->IgnoreParens();
+ ProgramStateRef State = C.getState();
const LocationContext *LCtx = C.getLocationContext();
- SVal L = C.getState()->getSVal(Callee, LCtx);
+ SVal L = State->getSVal(Callee, LCtx);
if (L.isUndef()) {
if (!BT_call_undef)
BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
"uninitalized pointer value"));
- EmitBadCall(BT_call_undef.get(), C, CE);
+ emitBadCall(BT_call_undef.get(), C, Callee);
return;
}
- if (isa<loc::ConcreteInt>(L)) {
+ ProgramStateRef StNonNull, StNull;
+ llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(L));
+
+ // FIXME: Do we want to record the non-null assumption here?
+ if (StNull && !StNonNull) {
if (!BT_call_null)
BT_call_null.reset(
new BuiltinBug("Called function pointer is null (null dereference)"));
- EmitBadCall(BT_call_null.get(), C, CE);
+ emitBadCall(BT_call_null.get(), C, Callee);
+ }
+}
+
+void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // If this is a call to a C++ method, check if the callee is null or
+ // undefined.
+ if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
+ SVal V = CC->getCXXThisVal();
+ if (V.isUndef()) {
+ if (!BT_cxx_call_undef)
+ BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is "
+ "uninitialized"));
+ emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
+ return;
+ }
+
+ ProgramStateRef State = C.getState();
+ ProgramStateRef StNonNull, StNull;
+ llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V));
+
+ // FIXME: Do we want to record the non-null assumption here?
+ if (StNull && !StNonNull) {
+ if (!BT_cxx_call_null)
+ BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer "
+ "is null"));
+ emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
+ return;
+ }
}
- PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState(), LCtx),
- "Function call argument is an uninitialized value",
- BT_call_arg);
+ // Don't check for uninitialized field values in arguments if the
+ // caller has a body that is available and we have the chance to inline it.
+ // This is a hack, but is a reasonable compromise betweens sometimes warning
+ // and sometimes not depending on if we decide to inline a function.
+ const Decl *D = Call.getDecl();
+ const bool checkUninitFields =
+ !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
+
+ OwningPtr<BugType> *BT;
+ if (isa<ObjCMethodCall>(Call))
+ BT = &BT_msg_arg;
+ else
+ BT = &BT_call_arg;
+
+ for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i)
+ if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
+ Call.getArgExpr(i), /*IsFirstArgument=*/i == 0,
+ checkUninitFields, Call, *BT))
+ return;
}
-void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
+void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
+ SVal recVal = msg.getReceiverSVal();
+ if (recVal.isUndef()) {
+ if (ExplodedNode *N = C.generateSink()) {
+ BugType *BT = 0;
+ switch (msg.getMessageKind()) {
+ case OCM_Message:
+ if (!BT_msg_undef)
+ BT_msg_undef.reset(new BuiltinBug("Receiver in message expression "
+ "is an uninitialized value"));
+ BT = BT_msg_undef.get();
+ break;
+ case OCM_PropertyAccess:
+ if (!BT_objc_prop_undef)
+ BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
+ "uninitialized object "
+ "pointer"));
+ BT = BT_objc_prop_undef.get();
+ break;
+ case OCM_Subscript:
+ if (!BT_objc_subscript_undef)
+ BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an "
+ "uninitialized object "
+ "pointer"));
+ BT = BT_objc_subscript_undef.get();
+ break;
+ }
+ assert(BT && "Unknown message kind.");
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
+ BugReport *R = new BugReport(*BT, BT->getName(), N);
+ const ObjCMessageExpr *ME = msg.getOriginExpr();
+ R->addRange(ME->getReceiverRange());
- // FIXME: Handle 'super'?
- if (const Expr *receiver = msg.getInstanceReceiver()) {
- SVal recVal = state->getSVal(receiver, LCtx);
- if (recVal.isUndef()) {
- if (ExplodedNode *N = C.generateSink()) {
- BugType *BT = 0;
- if (msg.isPureMessageExpr()) {
- if (!BT_msg_undef)
- BT_msg_undef.reset(new BuiltinBug("Receiver in message expression "
- "is an uninitialized value"));
- BT = BT_msg_undef.get();
- }
- else {
- if (!BT_objc_prop_undef)
- BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
- "uninitialized object pointer"));
- BT = BT_objc_prop_undef.get();
- }
- BugReport *R =
- new BugReport(*BT, BT->getName(), N);
- R->addRange(receiver->getSourceRange());
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- receiver,
- R));
- C.EmitReport(R);
- }
+ // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
+ if (const Expr *ReceiverE = ME->getInstanceReceiver())
+ bugreporter::addTrackNullOrUndefValueVisitor(N, ReceiverE, R);
+ C.EmitReport(R);
+ }
+ return;
+ } else {
+ // Bifurcate the state into nil and non-nil ones.
+ DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
+
+ ProgramStateRef state = C.getState();
+ ProgramStateRef notNilState, nilState;
+ llvm::tie(notNilState, nilState) = state->assume(receiverVal);
+
+ // Handle receiver must be nil.
+ if (nilState && !notNilState) {
+ HandleNilReceiver(C, state, msg);
return;
- } else {
- // Bifurcate the state into nil and non-nil ones.
- DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
-
- ProgramStateRef notNilState, nilState;
- llvm::tie(notNilState, nilState) = state->assume(receiverVal);
-
- // Handle receiver must be nil.
- if (nilState && !notNilState) {
- HandleNilReceiver(C, state, msg);
- return;
- }
}
}
-
- const char *bugDesc = msg.isPropertySetter() ?
- "Argument for property setter is an uninitialized value"
- : "Argument in message expression is an uninitialized value";
- // Check for any arguments that are uninitialized/undefined.
- PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx),
- bugDesc, BT_msg_arg);
}
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
- const ObjCMessage &msg,
+ const ObjCMethodCall &msg,
ExplodedNode *N) const {
if (!BT_msg_ret)
@@ -300,18 +358,20 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
new BuiltinBug("Receiver in message expression is "
"'nil' and returns a garbage value"));
+ const ObjCMessageExpr *ME = msg.getOriginExpr();
+
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
- os << "The receiver of message '" << msg.getSelector().getAsString()
- << "' is nil and returns a value of type '"
- << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage";
+ os << "The receiver of message '" << ME->getSelector().getAsString()
+ << "' is nil and returns a value of type '";
+ msg.getResultType().print(os, C.getLangOpts());
+ os << "' that will be garbage";
BugReport *report = new BugReport(*BT_msg_ret, os.str(), N);
- if (const Expr *receiver = msg.getInstanceReceiver()) {
- report->addRange(receiver->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- receiver,
- report));
+ report->addRange(ME->getReceiverRange());
+ // FIXME: This won't track "self" in messages to super.
+ if (const Expr *receiver = ME->getInstanceReceiver()) {
+ bugreporter::addTrackNullOrUndefValueVisitor(N, receiver, report);
}
C.EmitReport(report);
}
@@ -324,25 +384,25 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
ProgramStateRef state,
- ObjCMessage msg) const {
+ const ObjCMethodCall &Msg) const {
ASTContext &Ctx = C.getASTContext();
// Check the return type of the message expression. A message to nil will
// return different values depending on the return type and the architecture.
- QualType RetTy = msg.getType(Ctx);
+ QualType RetTy = Msg.getResultType();
CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
const LocationContext *LCtx = C.getLocationContext();
if (CanRetTy->isStructureOrClassType()) {
// Structure returns are safe since the compiler zeroes them out.
- SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx));
- C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V));
+ SVal V = C.getSValBuilder().makeZeroVal(RetTy);
+ C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));
return;
}
// Other cases: check if sizeof(return type) > sizeof(void*)
if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
- .isConsumedExpr(msg.getMessageExpr())) {
+ .isConsumedExpr(Msg.getOriginExpr())) {
// Compute: sizeof(void *) and sizeof(return type)
const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
@@ -355,7 +415,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
Ctx.LongLongTy == CanRetTy ||
Ctx.UnsignedLongLongTy == CanRetTy))) {
if (ExplodedNode *N = C.generateSink(state))
- emitNilReceiverBug(C, msg, N);
+ emitNilReceiverBug(C, Msg, N);
return;
}
@@ -372,8 +432,8 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
// it most likely isn't nil. We should assume the semantics
// of this case unless we have *a lot* more knowledge.
//
- SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx));
- C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V));
+ SVal V = C.getSValBuilder().makeZeroVal(RetTy);
+ C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 133204a..7a25865 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -215,10 +215,10 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
E = D->propimpl_end(); I!=E; ++I) {
// We can only check the synthesized properties
- if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
+ if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
continue;
- ObjCIvarDecl *ID = (*I)->getPropertyIvarDecl();
+ ObjCIvarDecl *ID = I->getPropertyIvarDecl();
if (!ID)
continue;
@@ -226,7 +226,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars
continue;
- const ObjCPropertyDecl *PD = (*I)->getPropertyDecl();
+ const ObjCPropertyDecl *PD = I->getPropertyDecl();
if (!PD)
continue;
@@ -261,7 +261,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
}
PathDiagnosticLocation SDLoc =
- PathDiagnosticLocation::createBegin((*I), BR.getSourceManager());
+ PathDiagnosticLocation::createBegin(*I, BR.getSourceManager());
BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC,
os.str(), SDLoc);
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index dde9071..b8b7c36 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -31,6 +31,7 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) {
T.getOS() == llvm::Triple::FreeBSD ||
T.getOS() == llvm::Triple::NetBSD ||
T.getOS() == llvm::Triple::OpenBSD ||
+ T.getOS() == llvm::Triple::Bitrig ||
T.getOS() == llvm::Triple::DragonFly;
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
index 843502f..0e9efaa 100644
--- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -37,6 +37,8 @@ class CheckerDocumentation : public Checker< check::PreStmt<DeclStmt>,
check::PostStmt<CallExpr>,
check::PreObjCMessage,
check::PostObjCMessage,
+ check::PreCall,
+ check::PostCall,
check::BranchCondition,
check::Location,
check::Bind,
@@ -72,14 +74,41 @@ public:
/// which does not include the control flow statements such as IfStmt. The
/// callback can be specialized to be called with any subclass of Stmt.
///
- /// check::PostStmt<DeclStmt>
+ /// check::PostStmt<CallExpr>
void checkPostStmt(const CallExpr *DS, CheckerContext &C) const;
- /// \brief Pre-visit the Objective C messages.
- void checkPreObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {}
+ /// \brief Pre-visit the Objective C message.
+ ///
+ /// This will be called before the analyzer core processes the method call.
+ /// This is called for any action which produces an Objective-C message send,
+ /// including explicit message syntax and property access.
+ ///
+ /// check::PreObjCMessage
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
+
+ /// \brief Post-visit the Objective C message.
+ /// \sa checkPreObjCMessage()
+ ///
+ /// check::PostObjCMessage
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
- /// \brief Post-visit the Objective C messages.
- void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {}
+ /// \brief Pre-visit an abstract "call" event.
+ ///
+ /// This is used for checkers that want to check arguments or attributed
+ /// behavior for functions and methods no matter how they are being invoked.
+ ///
+ /// Note that this includes ALL cross-body invocations, so if you want to
+ /// limit your checks to, say, function calls, you can either test for that
+ /// or fall back to the explicit callback (i.e. check::PreStmt).
+ ///
+ /// check::PreCall
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const {}
+
+ /// \brief Post-visit an abstract "call" event.
+ /// \sa checkPreObjCMessage()
+ ///
+ /// check::PostCall
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {}
/// \brief Pre-visit of the condition statement of a branch (such as IfStmt).
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
@@ -94,7 +123,7 @@ public:
///
/// check::Location
void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
- CheckerContext &C) const {}
+ CheckerContext &) const {}
/// \brief Called on binding of a value to a location.
///
@@ -103,7 +132,7 @@ public:
/// \param S The bind is performed while processing the statement S.
///
/// check::Bind
- void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {}
+ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {}
/// \brief Called whenever a symbol becomes dead.
@@ -187,24 +216,26 @@ public:
bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; }
- /// check::RegionChanges
- /// Allows tracking regions which get invalidated.
- /// \param state The current program state.
- /// \param invalidated A set of all symbols potentially touched by the change.
+ /// \brief Allows tracking regions which get invalidated.
+ ///
+ /// \param State The current program state.
+ /// \param Invalidated A set of all symbols potentially touched by the change.
/// \param ExplicitRegions The regions explicitly requested for invalidation.
/// For example, in the case of a function call, these would be arguments.
/// \param Regions The transitive closure of accessible regions,
/// i.e. all regions that may have been touched by this change.
- /// \param The call expression wrapper if the regions are invalidated by a
- /// call, 0 otherwise.
- /// Note, in order to be notified, the checker should also implement
+ /// \param Call The call expression wrapper if the regions are invalidated
+ /// by a call, 0 otherwise.
+ /// Note, in order to be notified, the checker should also implement the
/// wantsRegionChangeUpdate callback.
+ ///
+ /// check::RegionChanges
ProgramStateRef
checkRegionChanges(ProgramStateRef State,
- const StoreManager::InvalidatedSymbols *,
+ const StoreManager::InvalidatedSymbols *Invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
return State;
}
diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td
index 96a8d26..8110bd0 100644
--- a/lib/StaticAnalyzer/Checkers/Checkers.td
+++ b/lib/StaticAnalyzer/Checkers/Checkers.td
@@ -84,6 +84,10 @@ def StackAddrEscapeChecker : Checker<"StackAddressEscape">,
HelpText<"Check that addresses to stack memory do not escape the function">,
DescFile<"StackAddrEscapeChecker.cpp">;
+def DynamicTypePropagation : Checker<"DynamicTypePropagation">,
+ HelpText<"Generate dynamic type information">,
+ DescFile<"DynamicTypePropagation.cpp">;
+
} // end "core"
let ParentPackage = CoreExperimental in {
@@ -168,10 +172,6 @@ def ReturnUndefChecker : Checker<"UndefReturn">,
let ParentPackage = CplusplusExperimental in {
-def IteratorsChecker : Checker<"Iterators">,
- HelpText<"Check improper uses of STL vector iterators">,
- DescFile<"IteratorsChecker.cpp">;
-
def VirtualCallChecker : Checker<"VirtualCall">,
HelpText<"Check virtual function calls during construction or destruction">,
DescFile<"VirtualCallChecker.cpp">;
@@ -283,6 +283,10 @@ def MallocPessimistic : Checker<"Malloc">,
HelpText<"Check for memory leaks, double free, and use-after-free problems.">,
DescFile<"MallocChecker.cpp">;
+def MallocSizeofChecker : Checker<"MallocSizeof">,
+ HelpText<"Check for dubious malloc arguments involving sizeof">,
+ DescFile<"MallocSizeofChecker.cpp">;
+
} // end "unix"
let ParentPackage = UnixExperimental in {
@@ -295,10 +299,6 @@ def MallocOptimistic : Checker<"MallocWithAnnotations">,
HelpText<"Check for memory leaks, double free, and use-after-free problems. Assumes that all user-defined functions which might free a pointer are annotated.">,
DescFile<"MallocChecker.cpp">;
-def MallocSizeofChecker : Checker<"MallocSizeof">,
- HelpText<"Check for dubious malloc arguments involving sizeof">,
- DescFile<"MallocSizeofChecker.cpp">;
-
def PthreadLockChecker : Checker<"PthreadLock">,
HelpText<"Simple lock -> unlock checker">,
DescFile<"PthreadLockChecker.cpp">;
@@ -361,7 +361,7 @@ def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">,
let ParentPackage = Cocoa in {
def ObjCAtSyncChecker : Checker<"AtSync">,
- HelpText<"Check for null pointers used as mutexes for @synchronized">,
+ HelpText<"Check for nil pointers used as mutexes for @synchronized">,
DescFile<"ObjCAtSyncChecker.cpp">;
def NilArgChecker : Checker<"NilArg">,
@@ -373,8 +373,8 @@ def ClassReleaseChecker : Checker<"ClassRelease">,
DescFile<"BasicObjCFoundationChecks.cpp">;
def VariadicMethodTypeChecker : Checker<"VariadicMethodTypes">,
- HelpText<"Check for passing non-Objective-C types to variadic methods that expect "
- "only Objective-C types">,
+ HelpText<"Check for passing non-Objective-C types to variadic collection "
+ "initialization methods that expect only Objective-C types">,
DescFile<"BasicObjCFoundationChecks.cpp">;
def NSAutoreleasePoolChecker : Checker<"NSAutoreleasePool">,
@@ -393,6 +393,10 @@ def ObjCSelfInitChecker : Checker<"SelfInit">,
HelpText<"Check that 'self' is properly initialized inside an initializer method">,
DescFile<"ObjCSelfInitChecker.cpp">;
+def ObjCLoopChecker : Checker<"Loops">,
+ HelpText<"Improved modeling of loops using Cocoa collection types">,
+ DescFile<"BasicObjCFoundationChecks.cpp">;
+
def NSErrorChecker : Checker<"NSError">,
HelpText<"Check usage of NSError** parameters">,
DescFile<"NSErrorChecker.cpp">;
@@ -401,7 +405,7 @@ def RetainCountChecker : Checker<"RetainCount">,
HelpText<"Check for leaks and improper reference count management">,
DescFile<"RetainCountChecker.cpp">;
-} // end "cocoa"
+} // end "osx.cocoa"
let ParentPackage = CocoaExperimental in {
@@ -475,6 +479,14 @@ def CallGraphDumper : Checker<"DumpCallGraph">,
HelpText<"Display Call Graph">,
DescFile<"DebugCheckers.cpp">;
+def TraversalDumper : Checker<"DumpTraversal">,
+ HelpText<"Print branch conditions as they are traversed by the engine">,
+ DescFile<"TraversalChecker.cpp">;
+
+def CallDumper : Checker<"DumpCalls">,
+ HelpText<"Print calls as they are traversed by the engine">,
+ DescFile<"TraversalChecker.cpp">;
+
def AnalyzerStatsChecker : Checker<"Stats">,
HelpText<"Emit warnings with analyzer statistics">,
DescFile<"AnalyzerStatsChecker.cpp">;
@@ -483,5 +495,9 @@ def TaintTesterChecker : Checker<"TaintTest">,
HelpText<"Mark tainted symbols as such.">,
DescFile<"TaintTesterChecker.cpp">;
+def ExprInspectionChecker : Checker<"ExprInspection">,
+ HelpText<"Check the analyzer's understanding of expressions">,
+ DescFile<"ExprInspectionChecker.cpp">;
+
} // end "debug"
diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 81a2745..e98c131 100644
--- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -26,13 +26,18 @@ using namespace ento;
namespace {
class DereferenceChecker
: public Checker< check::Location,
- EventDispatcher<ImplicitNullDerefEvent> > {
+ check::Bind,
+ EventDispatcher<ImplicitNullDerefEvent> > {
mutable OwningPtr<BuiltinBug> BT_null;
mutable OwningPtr<BuiltinBug> BT_undef;
+ void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
+ bool IsBind = false) const;
+
public:
void checkLocation(SVal location, bool isLoad, const Stmt* S,
CheckerContext &C) const;
+ void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
static const MemRegion *AddDerefSource(raw_ostream &os,
SmallVectorImpl<SourceRange> &Ranges,
@@ -76,6 +81,106 @@ DereferenceChecker::AddDerefSource(raw_ostream &os,
return sourceR;
}
+void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
+ CheckerContext &C, bool IsBind) const {
+ // Generate an error node.
+ ExplodedNode *N = C.generateSink(State);
+ if (!N)
+ return;
+
+ // We know that 'location' cannot be non-null. This is what
+ // we call an "explicit" null dereference.
+ if (!BT_null)
+ BT_null.reset(new BuiltinBug("Dereference of null pointer"));
+
+ SmallString<100> buf;
+ SmallVector<SourceRange, 2> Ranges;
+
+ // Walk through lvalue casts to get the original expression
+ // that syntactically caused the load.
+ if (const Expr *expr = dyn_cast<Expr>(S))
+ S = expr->IgnoreParenLValueCasts();
+
+ const MemRegion *sourceR = 0;
+
+ if (IsBind) {
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
+ if (BO->isAssignmentOp())
+ S = BO->getRHS();
+ } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+ assert(DS->isSingleDecl() && "We process decls one by one");
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
+ if (const Expr *Init = VD->getAnyInitializer())
+ S = Init;
+ }
+ }
+
+ switch (S->getStmtClass()) {
+ case Stmt::ArraySubscriptExprClass: {
+ llvm::raw_svector_ostream os(buf);
+ os << "Array access";
+ const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
+ sourceR = AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
+ State.getPtr(), N->getLocationContext());
+ os << " results in a null pointer dereference";
+ break;
+ }
+ case Stmt::UnaryOperatorClass: {
+ llvm::raw_svector_ostream os(buf);
+ os << "Dereference of null pointer";
+ const UnaryOperator *U = cast<UnaryOperator>(S);
+ sourceR = AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
+ State.getPtr(), N->getLocationContext(), true);
+ break;
+ }
+ case Stmt::MemberExprClass: {
+ const MemberExpr *M = cast<MemberExpr>(S);
+ if (M->isArrow()) {
+ llvm::raw_svector_ostream os(buf);
+ os << "Access to field '" << M->getMemberNameInfo()
+ << "' results in a dereference of a null pointer";
+ sourceR = AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
+ State.getPtr(), N->getLocationContext(), true);
+ }
+ break;
+ }
+ case Stmt::ObjCIvarRefExprClass: {
+ const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
+ if (const DeclRefExpr *DR =
+ dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ llvm::raw_svector_ostream os(buf);
+ os << "Instance variable access (via '" << VD->getName()
+ << "') results in a null pointer dereference";
+ }
+ }
+ Ranges.push_back(IV->getSourceRange());
+ break;
+ }
+ default:
+ break;
+ }
+
+ BugReport *report =
+ new BugReport(*BT_null,
+ buf.empty() ? BT_null->getDescription() : buf.str(),
+ N);
+
+ bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N),
+ report);
+
+ for (SmallVectorImpl<SourceRange>::iterator
+ I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
+ report->addRange(*I);
+
+ if (sourceR) {
+ report->markInteresting(sourceR);
+ report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR)));
+ }
+
+ C.EmitReport(report);
+}
+
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
CheckerContext &C) const {
// Check for dereference of an undefined value.
@@ -86,8 +191,10 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
BugReport *report =
new BugReport(*BT_undef, BT_undef->getDescription(), N);
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- bugreporter::GetDerefExpr(N), report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N,
+ bugreporter::GetDerefExpr(N),
+ report);
+ report->disablePathPruning();
C.EmitReport(report);
}
return;
@@ -100,115 +207,81 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
return;
ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
+
ProgramStateRef notNullState, nullState;
llvm::tie(notNullState, nullState) = state->assume(location);
// The explicit NULL case.
if (nullState) {
if (!notNullState) {
- // Generate an error node.
- ExplodedNode *N = C.generateSink(nullState);
- if (!N)
- return;
-
- // We know that 'location' cannot be non-null. This is what
- // we call an "explicit" null dereference.
- if (!BT_null)
- BT_null.reset(new BuiltinBug("Dereference of null pointer"));
-
- SmallString<100> buf;
- SmallVector<SourceRange, 2> Ranges;
-
- // Walk through lvalue casts to get the original expression
- // that syntactically caused the load.
- if (const Expr *expr = dyn_cast<Expr>(S))
- S = expr->IgnoreParenLValueCasts();
-
- const MemRegion *sourceR = 0;
-
- switch (S->getStmtClass()) {
- case Stmt::ArraySubscriptExprClass: {
- llvm::raw_svector_ostream os(buf);
- os << "Array access";
- const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
- sourceR =
- AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
- state.getPtr(), LCtx);
- os << " results in a null pointer dereference";
- break;
- }
- case Stmt::UnaryOperatorClass: {
- llvm::raw_svector_ostream os(buf);
- os << "Dereference of null pointer";
- const UnaryOperator *U = cast<UnaryOperator>(S);
- sourceR =
- AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
- state.getPtr(), LCtx, true);
- break;
- }
- case Stmt::MemberExprClass: {
- const MemberExpr *M = cast<MemberExpr>(S);
- if (M->isArrow()) {
- llvm::raw_svector_ostream os(buf);
- os << "Access to field '" << M->getMemberNameInfo()
- << "' results in a dereference of a null pointer";
- sourceR =
- AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
- state.getPtr(), LCtx, true);
- }
- break;
- }
- case Stmt::ObjCIvarRefExprClass: {
- const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
- if (const DeclRefExpr *DR =
- dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
- llvm::raw_svector_ostream os(buf);
- os << "Instance variable access (via '" << VD->getName()
- << "') results in a null pointer dereference";
- }
- }
- Ranges.push_back(IV->getSourceRange());
- break;
- }
- default:
- break;
- }
+ reportBug(nullState, S, C);
+ return;
+ }
- BugReport *report =
- new BugReport(*BT_null,
- buf.empty() ? BT_null->getDescription():buf.str(),
- N);
+ // Otherwise, we have the case where the location could either be
+ // null or not-null. Record the error node as an "implicit" null
+ // dereference.
+ if (ExplodedNode *N = C.generateSink(nullState)) {
+ ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
+ dispatchEvent(event);
+ }
+ }
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- bugreporter::GetDerefExpr(N), report));
+ // From this point forward, we know that the location is not null.
+ C.addTransition(notNullState);
+}
- for (SmallVectorImpl<SourceRange>::iterator
- I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
- report->addRange(*I);
+void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
+ CheckerContext &C) const {
+ // If we're binding to a reference, check if the value is known to be null.
+ if (V.isUndef())
+ return;
- if (sourceR) {
- report->markInteresting(sourceR);
- report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR)));
- }
+ const MemRegion *MR = L.getAsRegion();
+ const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
+ if (!TVR)
+ return;
- C.EmitReport(report);
+ if (!TVR->getValueType()->isReferenceType())
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ ProgramStateRef StNonNull, StNull;
+ llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V));
+
+ if (StNull) {
+ if (!StNonNull) {
+ reportBug(StNull, S, C, /*isBind=*/true);
return;
}
- else {
- // Otherwise, we have the case where the location could either be
- // null or not-null. Record the error node as an "implicit" null
- // dereference.
- if (ExplodedNode *N = C.generateSink(nullState)) {
- ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
- dispatchEvent(event);
- }
+
+ // At this point the value could be either null or non-null.
+ // Record this as an "implicit" null dereference.
+ if (ExplodedNode *N = C.generateSink(StNull)) {
+ ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
+ &C.getBugReporter() };
+ dispatchEvent(event);
}
}
- // From this point forward, we know that the location is not null.
- C.addTransition(notNullState);
+ // Unlike a regular null dereference, initializing a reference with a
+ // dereferenced null pointer does not actually cause a runtime exception in
+ // Clang's implementation of references.
+ //
+ // int &r = *p; // safe??
+ // if (p != NULL) return; // uh-oh
+ // r = 5; // trap here
+ //
+ // The standard says this is invalid as soon as we try to create a "null
+ // reference" (there is no such thing), but turning this into an assumption
+ // that 'p' is never null will not match our actual runtime behavior.
+ // So we do not record this assumption, allowing us to warn on the last line
+ // of this example.
+ //
+ // We do need to add a transition because we may have generated a sink for
+ // the "implicit" null dereference.
+ C.addTransition(State, this);
}
void ento::registerDereferenceChecker(CheckerManager &mgr) {
diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 2627f0c..dcf6a86 100644
--- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -42,8 +42,9 @@ void DivZeroChecker::reportBug(const char *Msg,
BugReport *R =
new BugReport(*BT, Msg, N);
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- bugreporter::GetDenomExpr(N), R));
+ bugreporter::addTrackNullOrUndefValueVisitor(N,
+ bugreporter::GetDenomExpr(N),
+ R);
C.EmitReport(R);
}
}
@@ -57,8 +58,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
Op != BO_RemAssign)
return;
- if (!B->getRHS()->getType()->isIntegerType() ||
- !B->getRHS()->getType()->isScalarType())
+ if (!B->getRHS()->getType()->isScalarType())
return;
SVal Denom = C.getState()->getSVal(B->getRHS(), C.getLocationContext());
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
new file mode 100644
index 0000000..fea5733
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -0,0 +1,179 @@
+//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker defines the rules for dynamic type gathering and propagation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/Basic/Builtins.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DynamicTypePropagation:
+ public Checker< check::PostCall,
+ check::PostStmt<ImplicitCastExpr> > {
+ const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
+ CheckerContext &C) const;
+
+ /// \brief Return a better dynamic type if one can be derived from the cast.
+ const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
+ CheckerContext &C) const;
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const;
+};
+}
+
+void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // We can obtain perfect type info for return values from some calls.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
+
+ // Get the returned value if it's a region.
+ SVal Result = C.getSVal(Call.getOriginExpr());
+ const MemRegion *RetReg = Result.getAsRegion();
+ if (!RetReg)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ switch (Msg->getMethodFamily()) {
+ default:
+ break;
+
+ // We assume that the type of the object returned by alloc and new are the
+ // pointer to the object of the class specified in the receiver of the
+ // message.
+ case OMF_alloc:
+ case OMF_new: {
+ // Get the type of object that will get created.
+ const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
+ const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
+ if (!ObjTy)
+ return;
+ QualType DynResTy =
+ C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
+ C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false));
+ break;
+ }
+ case OMF_init: {
+ // Assume, the result of the init method has the same dynamic type as
+ // the receiver and propagate the dynamic type info.
+ const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
+ if (!RecReg)
+ return;
+ DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
+ C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType));
+ break;
+ }
+ }
+ }
+}
+
+void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
+ CheckerContext &C) const {
+ // We only track dynamic type info for regions.
+ const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
+ if (!ToR)
+ return;
+
+ switch (CastE->getCastKind()) {
+ default:
+ break;
+ case CK_BitCast:
+ // Only handle ObjCObjects for now.
+ if (const Type *NewTy = getBetterObjCType(CastE, C))
+ C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0)));
+ break;
+ }
+ return;
+}
+
+const ObjCObjectType *
+DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
+ CheckerContext &C) const {
+ if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
+ if (const ObjCObjectType *ObjTy
+ = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
+ return ObjTy;
+ }
+
+ if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
+ if (const ObjCObjectType *ObjTy
+ = MsgE->getSuperType()->getAs<ObjCObjectType>())
+ return ObjTy;
+ }
+
+ const Expr *RecE = MsgE->getInstanceReceiver();
+ if (!RecE)
+ return 0;
+
+ RecE= RecE->IgnoreParenImpCasts();
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
+ const StackFrameContext *SFCtx = C.getStackFrame();
+ // Are we calling [self alloc]? If this is self, get the type of the
+ // enclosing ObjC class.
+ if (DRE->getDecl() == SFCtx->getSelfDecl()) {
+ if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
+ if (const ObjCObjectType *ObjTy =
+ dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
+ return ObjTy;
+ }
+ }
+ return 0;
+}
+
+// Return a better dynamic type if one can be derived from the cast.
+// Compare the current dynamic type of the region and the new type to which we
+// are casting. If the new type is lower in the inheritance hierarchy, pick it.
+const ObjCObjectPointerType *
+DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
+ CheckerContext &C) const {
+ const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
+ assert(ToR);
+
+ // Get the old and new types.
+ const ObjCObjectPointerType *NewTy =
+ CastE->getType()->getAs<ObjCObjectPointerType>();
+ if (!NewTy)
+ return 0;
+ QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType();
+ if (OldDTy.isNull()) {
+ return NewTy;
+ }
+ const ObjCObjectPointerType *OldTy =
+ OldDTy->getAs<ObjCObjectPointerType>();
+ if (!OldTy)
+ return 0;
+
+ // Id the old type is 'id', the new one is more precise.
+ if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
+ return NewTy;
+
+ // Return new if it's a subclass of old.
+ const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl();
+ const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl();
+ if (ToI && FromI && FromI->isSuperClassOf(ToI))
+ return NewTy;
+
+ return 0;
+}
+
+void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
+ mgr.registerChecker<DynamicTypePropagation>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
new file mode 100644
index 0000000..7acf223
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -0,0 +1,122 @@
+//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ExprInspectionChecker : public Checker< eval::Call > {
+ mutable OwningPtr<BugType> BT;
+
+ void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
+
+ typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
+ CheckerContext &C) const;
+
+public:
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+};
+}
+
+bool ExprInspectionChecker::evalCall(const CallExpr *CE,
+ CheckerContext &C) const {
+ // These checks should have no effect on the surrounding environment
+ // (globals should not be invalidated, etc), hence the use of evalCall.
+ FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
+ .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
+ .Case("clang_analyzer_checkInlined",
+ &ExprInspectionChecker::analyzerCheckInlined)
+ .Default(0);
+
+ if (!Handler)
+ return false;
+
+ (this->*Handler)(CE, C);
+ return true;
+}
+
+static const char *getArgumentValueString(const CallExpr *CE,
+ CheckerContext &C) {
+ if (CE->getNumArgs() == 0)
+ return "Missing assertion argument";
+
+ ExplodedNode *N = C.getPredecessor();
+ const LocationContext *LC = N->getLocationContext();
+ ProgramStateRef State = N->getState();
+
+ const Expr *Assertion = CE->getArg(0);
+ SVal AssertionVal = State->getSVal(Assertion, LC);
+
+ if (AssertionVal.isUndef())
+ return "UNDEFINED";
+
+ ProgramStateRef StTrue, StFalse;
+ llvm::tie(StTrue, StFalse) =
+ State->assume(cast<DefinedOrUnknownSVal>(AssertionVal));
+
+ if (StTrue) {
+ if (StFalse)
+ return "UNKNOWN";
+ else
+ return "TRUE";
+ } else {
+ if (StFalse)
+ return "FALSE";
+ else
+ llvm_unreachable("Invalid constraint; neither true or false.");
+ }
+}
+
+void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.getPredecessor();
+ const LocationContext *LC = N->getLocationContext();
+
+ // A specific instantiation of an inlined function may have more constrained
+ // values than can generally be assumed. Skip the check.
+ if (LC->getCurrentStackFrame()->getParent() != 0)
+ return;
+
+ if (!BT)
+ BT.reset(new BugType("Checking analyzer assumptions", "debug"));
+
+ BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
+ C.EmitReport(R);
+}
+
+void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.getPredecessor();
+ const LocationContext *LC = N->getLocationContext();
+
+ // An inlined function could conceivably also be analyzed as a top-level
+ // function. We ignore this case and only emit a message (TRUE or FALSE)
+ // when we are analyzing it as an inlined function. This means that
+ // clang_analyzer_checkInlined(true) should always print TRUE, but
+ // clang_analyzer_checkInlined(false) should never actually print anything.
+ if (LC->getCurrentStackFrame()->getParent() == 0)
+ return;
+
+ if (!BT)
+ BT.reset(new BugType("Checking analyzer assumptions", "debug"));
+
+ BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
+ C.EmitReport(R);
+}
+
+void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ExprInspectionChecker>();
+}
+
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 135b81d..afb862c 100644
--- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -273,7 +273,7 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
// Skipping the following functions, since they might be used for cleansing
// or smart memory copy:
- // - memccpy - copying untill hitting a special character.
+ // - memccpy - copying until hitting a special character.
return TaintPropagationRule();
}
@@ -299,6 +299,9 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
CheckerContext &C) const {
ProgramStateRef State = 0;
const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || FDecl->getKind() != Decl::Function)
+ return;
+
StringRef Name = C.getCalleeName(FDecl);
if (Name.empty())
return;
@@ -372,7 +375,11 @@ void GenericTaintChecker::addSourcesPost(const CallExpr *CE,
CheckerContext &C) const {
// Define the attack surface.
// Set the evaluation function by switching on the callee name.
- StringRef Name = C.getCalleeName(CE);
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || FDecl->getKind() != Decl::Function)
+ return;
+
+ StringRef Name = C.getCalleeName(FDecl);
if (Name.empty())
return;
FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
@@ -406,6 +413,9 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{
return true;
const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || FDecl->getKind() != Decl::Function)
+ return false;
+
StringRef Name = C.getCalleeName(FDecl);
if (Name.empty())
return false;
@@ -549,7 +559,6 @@ ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE,
if (CE->getNumArgs() < 2)
return State;
- SVal x = State->getSVal(CE->getArg(1), C.getLocationContext());
// All arguments except for the very first one should get taint.
for (unsigned int i = 1; i < CE->getNumArgs(); ++i) {
// The arguments are pointer arguments. The data they are pointing at is
diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
index c08f163..9d0b83f 100644
--- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
@@ -106,6 +106,7 @@ private:
typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData>
AssumptionMap;
mutable AssumptionMap hash;
+ mutable OwningPtr<BugType> BT;
};
}
@@ -343,7 +344,9 @@ void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B,
void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G,
BugReporter &BR,
ExprEngine &Eng) const {
- BugType *BT = new BugType("Idempotent operation", "Dead code");
+ if (!BT)
+ BT.reset(new BugType("Idempotent operation", "Dead code"));
+
// Iterate over the hash to see if we have any paths with definite
// idempotent operations.
for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) {
diff --git a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp
deleted file mode 100644
index b0bac33..0000000
--- a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-//=== IteratorsChecker.cpp - Check for Invalidated Iterators ------*- C++ -*----
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This defines IteratorsChecker, a number of small checks for conditions
-// leading to invalid iterators being used.
-// FIXME: Currently only supports 'vector' and 'deque'
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/AST/DeclTemplate.h"
-#include "clang/Basic/SourceManager.h"
-#include "ClangSACheckers.h"
-#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/Type.h"
-#include "clang/AST/PrettyPrinter.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/StringSwitch.h"
-
-
-using namespace clang;
-using namespace ento;
-
-// This is the state associated with each iterator which includes both the
-// kind of state and the instance used to initialize it.
-// FIXME: add location where invalidated for better error reporting.
-namespace {
-class RefState {
- enum Kind { BeginValid, EndValid, Invalid, Undefined, Unknown } K;
- const void *VR;
-
-public:
- RefState(Kind k, const void *vr) : K(k), VR(vr) {}
-
- bool isValid() const { return K == BeginValid || K == EndValid; }
- bool isInvalid() const { return K == Invalid; }
- bool isUndefined() const { return K == Undefined; }
- bool isUnknown() const { return K == Unknown; }
- const MemRegion *getMemRegion() const {
- if (K == BeginValid || K == EndValid)
- return(const MemRegion *)VR;
- return 0;
- }
- const MemberExpr *getMemberExpr() const {
- if (K == Invalid)
- return(const MemberExpr *)VR;
- return 0;
- }
-
- bool operator==(const RefState &X) const {
- return K == X.K && VR == X.VR;
- }
-
- static RefState getBeginValid(const MemRegion *vr) {
- assert(vr);
- return RefState(BeginValid, vr);
- }
- static RefState getEndValid(const MemRegion *vr) {
- assert(vr);
- return RefState(EndValid, vr);
- }
- static RefState getInvalid( const MemberExpr *ME ) {
- return RefState(Invalid, ME);
- }
- static RefState getUndefined( void ) {
- return RefState(Undefined, 0);
- }
- static RefState getUnknown( void ) {
- return RefState(Unknown, 0);
- }
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(K);
- ID.AddPointer(VR);
- }
-};
-
-enum RefKind { NoKind, VectorKind, VectorIteratorKind };
-
-class IteratorsChecker :
- public Checker<check::PreStmt<CXXOperatorCallExpr>,
- check::PreStmt<DeclStmt>,
- check::PreStmt<CXXMemberCallExpr>,
- check::PreStmt<CallExpr> >
- {
- // Used when parsing iterators and vectors and deques.
- BuiltinBug *BT_Invalid, *BT_Undefined, *BT_Incompatible;
-
-public:
- IteratorsChecker() :
- BT_Invalid(0), BT_Undefined(0), BT_Incompatible(0)
- {}
- static void *getTag() { static int tag; return &tag; }
-
- // Checker entry points.
- void checkPreStmt(const CXXOperatorCallExpr *OCE,
- CheckerContext &C) const;
-
- void checkPreStmt(const DeclStmt *DS,
- CheckerContext &C) const;
-
- void checkPreStmt(const CXXMemberCallExpr *MCE,
- CheckerContext &C) const;
-
- void checkPreStmt(const CallExpr *CE,
- CheckerContext &C) const;
-
-private:
- ProgramStateRef handleAssign(ProgramStateRef state,
- const Expr *lexp,
- const Expr *rexp,
- const LocationContext *LC) const;
-
- ProgramStateRef handleAssign(ProgramStateRef state,
- const MemRegion *MR,
- const Expr *rexp,
- const LocationContext *LC) const;
-
- ProgramStateRef invalidateIterators(ProgramStateRef state,
- const MemRegion *MR,
- const MemberExpr *ME) const;
-
- void checkExpr(CheckerContext &C, const Expr *E) const;
-
- void checkArgs(CheckerContext &C, const CallExpr *CE) const;
-
- const MemRegion *getRegion(ProgramStateRef state,
- const Expr *E,
- const LocationContext *LC) const;
-
- const DeclRefExpr *getDeclRefExpr(const Expr *E) const;
-};
-
-class IteratorState {
-public:
- typedef llvm::ImmutableMap<const MemRegion *, RefState> EntryMap;
-};
-} //end anonymous namespace
-
-namespace clang {
- namespace ento {
- template <>
- struct ProgramStateTrait<IteratorState>
- : public ProgramStatePartialTrait<IteratorState::EntryMap> {
- static void *GDMIndex() { return IteratorsChecker::getTag(); }
- };
- }
-}
-
-void ento::registerIteratorsChecker(CheckerManager &mgr) {
- mgr.registerChecker<IteratorsChecker>();
-}
-
-// ===============================================
-// Utility functions used by visitor functions
-// ===============================================
-
-// check a templated type for std::vector or std::deque
-static RefKind getTemplateKind(const NamedDecl *td) {
- const DeclContext *dc = td->getDeclContext();
- const NamespaceDecl *nameSpace = dyn_cast<NamespaceDecl>(dc);
- if (!nameSpace || !isa<TranslationUnitDecl>(nameSpace->getDeclContext())
- || nameSpace->getName() != "std")
- return NoKind;
-
- StringRef name = td->getName();
- return llvm::StringSwitch<RefKind>(name)
- .Cases("vector", "deque", VectorKind)
- .Default(NoKind);
-}
-
-static RefKind getTemplateKind(const DeclContext *dc) {
- if (const ClassTemplateSpecializationDecl *td =
- dyn_cast<ClassTemplateSpecializationDecl>(dc))
- return getTemplateKind(cast<NamedDecl>(td));
- return NoKind;
-}
-
-static RefKind getTemplateKind(const TypedefType *tdt) {
- const TypedefNameDecl *td = tdt->getDecl();
- RefKind parentKind = getTemplateKind(td->getDeclContext());
- if (parentKind == VectorKind) {
- return llvm::StringSwitch<RefKind>(td->getName())
- .Cases("iterator",
- "const_iterator",
- "reverse_iterator", VectorIteratorKind)
- .Default(NoKind);
- }
- return NoKind;
-}
-
-static RefKind getTemplateKind(const TemplateSpecializationType *tsp) {
- const TemplateName &tname = tsp->getTemplateName();
- TemplateDecl *td = tname.getAsTemplateDecl();
- if (!td)
- return NoKind;
- return getTemplateKind(td);
-}
-
-static RefKind getTemplateKind(QualType T) {
- if (const TemplateSpecializationType *tsp =
- T->getAs<TemplateSpecializationType>()) {
- return getTemplateKind(tsp);
- }
- if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(T)) {
- QualType namedType = ET->getNamedType();
- if (const TypedefType *tdt = namedType->getAs<TypedefType>())
- return getTemplateKind(tdt);
- if (const TemplateSpecializationType *tsp =
- namedType->getAs<TemplateSpecializationType>()) {
- return getTemplateKind(tsp);
- }
- }
- return NoKind;
-}
-
-// Iterate through our map and invalidate any iterators that were
-// initialized fromt the specified instance MemRegion.
-ProgramStateRef IteratorsChecker::invalidateIterators(ProgramStateRef state,
- const MemRegion *MR, const MemberExpr *ME) const {
- IteratorState::EntryMap Map = state->get<IteratorState>();
- if (Map.isEmpty())
- return state;
-
- // Loop over the entries in the current state.
- // The key doesn't change, so the map iterators won't change.
- for (IteratorState::EntryMap::iterator I = Map.begin(), E = Map.end();
- I != E; ++I) {
- RefState RS = I.getData();
- if (RS.getMemRegion() == MR)
- state = state->set<IteratorState>(I.getKey(), RefState::getInvalid(ME));
- }
-
- return state;
-}
-
-// Handle assigning to an iterator where we don't have the LValue MemRegion.
-ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state,
- const Expr *lexp, const Expr *rexp, const LocationContext *LC) const {
- // Skip the cast if present.
- if (const MaterializeTemporaryExpr *M
- = dyn_cast<MaterializeTemporaryExpr>(lexp))
- lexp = M->GetTemporaryExpr();
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(lexp))
- lexp = ICE->getSubExpr();
- SVal sv = state->getSVal(lexp, LC);
- const MemRegion *MR = sv.getAsRegion();
- if (!MR)
- return state;
- RefKind kind = getTemplateKind(lexp->getType());
-
- // If assigning to a vector, invalidate any iterators currently associated.
- if (kind == VectorKind)
- return invalidateIterators(state, MR, 0);
-
- // Make sure that we are assigning to an iterator.
- if (getTemplateKind(lexp->getType()) != VectorIteratorKind)
- return state;
- return handleAssign(state, MR, rexp, LC);
-}
-
-// handle assigning to an iterator
-ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state,
- const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const {
- // Assume unknown until we find something definite.
- state = state->set<IteratorState>(MR, RefState::getUnknown());
- if (const MaterializeTemporaryExpr *M
- = dyn_cast<MaterializeTemporaryExpr>(rexp))
- rexp = M->GetTemporaryExpr();
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(rexp))
- rexp = ICE->getSubExpr();
- // Need to handle three cases: MemberCall, copy, copy with addition.
- if (const CallExpr *CE = dyn_cast<CallExpr>(rexp)) {
- // Handle MemberCall.
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee())) {
- const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase());
- if (!DRE)
- return state;
- // Verify that the type is std::vector<T>.
- if (getTemplateKind(DRE->getType()) != VectorKind)
- return state;
- // Now get the MemRegion associated with the instance.
- const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
- if (!VD)
- return state;
- const MemRegion *IMR = state->getRegion(VD, LC);
- if (!IMR)
- return state;
- // Finally, see if it is one of the calls that will create
- // a valid iterator and mark it if so, else mark as Unknown.
- StringRef mName = ME->getMemberDecl()->getName();
-
- if (llvm::StringSwitch<bool>(mName)
- .Cases("begin", "insert", "erase", true).Default(false)) {
- return state->set<IteratorState>(MR, RefState::getBeginValid(IMR));
- }
- if (mName == "end")
- return state->set<IteratorState>(MR, RefState::getEndValid(IMR));
-
- return state->set<IteratorState>(MR, RefState::getUnknown());
- }
- }
- // Handle straight copy from another iterator.
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(rexp)) {
- if (getTemplateKind(DRE->getType()) != VectorIteratorKind)
- return state;
- // Now get the MemRegion associated with the instance.
- const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
- if (!VD)
- return state;
- const MemRegion *IMR = state->getRegion(VD, LC);
- if (!IMR)
- return state;
- // Get the RefState of the iterator being copied.
- const RefState *RS = state->get<IteratorState>(IMR);
- if (!RS)
- return state;
- // Use it to set the state of the LValue.
- return state->set<IteratorState>(MR, *RS);
- }
- // If we have operator+ or operator- ...
- if (const CXXOperatorCallExpr *OCE = dyn_cast<CXXOperatorCallExpr>(rexp)) {
- OverloadedOperatorKind Kind = OCE->getOperator();
- if (Kind == OO_Plus || Kind == OO_Minus) {
- // Check left side of tree for a valid value.
- state = handleAssign( state, MR, OCE->getArg(0), LC);
- const RefState *RS = state->get<IteratorState>(MR);
- // If found, return it.
- if (!RS->isUnknown())
- return state;
- // Otherwise return what we find in the right side.
- return handleAssign(state, MR, OCE->getArg(1), LC);
- }
- }
- // Fall through if nothing matched.
- return state;
-}
-
-// Iterate through the arguments looking for an Invalid or Undefined iterator.
-void IteratorsChecker::checkArgs(CheckerContext &C, const CallExpr *CE) const {
- for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end();
- I != E; ++I) {
- checkExpr(C, *I);
- }
-}
-
-// Get the DeclRefExpr associated with the expression.
-const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const {
- // If it is a CXXConstructExpr, need to get the subexpression.
- if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) {
- if (CE->getNumArgs()== 1) {
- CXXConstructorDecl *CD = CE->getConstructor();
- if (CD->isTrivial())
- E = CE->getArg(0);
- }
- }
- if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E))
- E = M->GetTemporaryExpr();
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
- E = ICE->getSubExpr();
- // If it isn't one of our types, don't do anything.
- if (getTemplateKind(E->getType()) != VectorIteratorKind)
- return NULL;
- return dyn_cast<DeclRefExpr>(E);
-}
-
-// Get the MemRegion associated with the expresssion.
-const MemRegion *IteratorsChecker::getRegion(ProgramStateRef state,
- const Expr *E, const LocationContext *LC) const {
- const DeclRefExpr *DRE = getDeclRefExpr(E);
- if (!DRE)
- return NULL;
- const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
- if (!VD)
- return NULL;
- // return the MemRegion associated with the iterator
- return state->getRegion(VD, LC);
-}
-
-// Check the expression and if it is an iterator, generate a diagnostic
-// if the iterator is not valid.
-// FIXME: this method can generate new nodes, and subsequent logic should
-// use those nodes. We also cannot create multiple nodes at one ProgramPoint
-// with the same tag.
-void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const {
- ProgramStateRef state = C.getState();
- const MemRegion *MR = getRegion(state, E, C.getLocationContext());
- if (!MR)
- return;
-
- // Get the state associated with the iterator.
- const RefState *RS = state->get<IteratorState>(MR);
- if (!RS)
- return;
- if (RS->isInvalid()) {
- if (ExplodedNode *N = C.addTransition()) {
- if (!BT_Invalid)
- // FIXME: We are eluding constness here.
- const_cast<IteratorsChecker*>(this)->BT_Invalid = new BuiltinBug("");
-
- std::string msg;
- const MemberExpr *ME = RS->getMemberExpr();
- if (ME) {
- std::string name = ME->getMemberNameInfo().getAsString();
- msg = "Attempt to use an iterator made invalid by call to '" +
- name + "'";
- }
- else {
- msg = "Attempt to use an iterator made invalid by copying another "
- "container to its container";
- }
-
- BugReport *R = new BugReport(*BT_Invalid, msg, N);
- R->addRange(getDeclRefExpr(E)->getSourceRange());
- C.EmitReport(R);
- }
- }
- else if (RS->isUndefined()) {
- if (ExplodedNode *N = C.addTransition()) {
- if (!BT_Undefined)
- // FIXME: We are eluding constness here.
- const_cast<IteratorsChecker*>(this)->BT_Undefined =
- new BuiltinBug("Use of iterator that is not defined");
-
- BugReport *R = new BugReport(*BT_Undefined,
- BT_Undefined->getDescription(), N);
- R->addRange(getDeclRefExpr(E)->getSourceRange());
- C.EmitReport(R);
- }
- }
-}
-
-// ===============================================
-// Path analysis visitor functions
-// ===============================================
-
-// For a generic Call, just check the args for bad iterators.
-void IteratorsChecker::checkPreStmt(const CallExpr *CE,
- CheckerContext &C) const{
-
- // FIXME: These checks are to currently work around a bug
- // in CheckerManager.
- if (isa<CXXOperatorCallExpr>(CE))
- return;
- if (isa<CXXMemberCallExpr>(CE))
- return;
-
- checkArgs(C, CE);
-}
-
-// Handle operator calls. First, if it is operator=, check the argument,
-// and handle assigning and set target state appropriately. Otherwise, for
-// other operators, check the args for bad iterators and handle comparisons.
-void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE,
- CheckerContext &C) const
-{
- const LocationContext *LC = C.getLocationContext();
- ProgramStateRef state = C.getState();
- OverloadedOperatorKind Kind = OCE->getOperator();
- if (Kind == OO_Equal) {
- checkExpr(C, OCE->getArg(1));
- state = handleAssign(state, OCE->getArg(0), OCE->getArg(1), LC);
- C.addTransition(state);
- return;
- }
- else {
- checkArgs(C, OCE);
- // If it is a compare and both are iterators, ensure that they are for
- // the same container.
- if (Kind == OO_EqualEqual || Kind == OO_ExclaimEqual ||
- Kind == OO_Less || Kind == OO_LessEqual ||
- Kind == OO_Greater || Kind == OO_GreaterEqual) {
- const MemRegion *MR0, *MR1;
- MR0 = getRegion(state, OCE->getArg(0), LC);
- if (!MR0)
- return;
- MR1 = getRegion(state, OCE->getArg(1), LC);
- if (!MR1)
- return;
- const RefState *RS0, *RS1;
- RS0 = state->get<IteratorState>(MR0);
- if (!RS0)
- return;
- RS1 = state->get<IteratorState>(MR1);
- if (!RS1)
- return;
- if (RS0->getMemRegion() != RS1->getMemRegion()) {
- if (ExplodedNode *N = C.addTransition()) {
- if (!BT_Incompatible)
- const_cast<IteratorsChecker*>(this)->BT_Incompatible =
- new BuiltinBug(
- "Cannot compare iterators from different containers");
-
- BugReport *R = new BugReport(*BT_Incompatible,
- BT_Incompatible->getDescription(), N);
- R->addRange(OCE->getSourceRange());
- C.EmitReport(R);
- }
- }
- }
- }
-}
-
-// Need to handle DeclStmts to pick up initializing of iterators and to mark
-// uninitialized ones as Undefined.
-void IteratorsChecker::checkPreStmt(const DeclStmt *DS,
- CheckerContext &C) const {
- const Decl *D = *DS->decl_begin();
- const VarDecl *VD = dyn_cast<VarDecl>(D);
- // Only care about iterators.
- if (getTemplateKind(VD->getType()) != VectorIteratorKind)
- return;
-
- // Get the MemRegion associated with the iterator and mark it as Undefined.
- ProgramStateRef state = C.getState();
- Loc VarLoc = state->getLValue(VD, C.getLocationContext());
- const MemRegion *MR = VarLoc.getAsRegion();
- if (!MR)
- return;
- state = state->set<IteratorState>(MR, RefState::getUndefined());
-
- // if there is an initializer, handle marking Valid if a proper initializer
- const Expr *InitEx = VD->getInit();
- if (InitEx) {
- // FIXME: This is too syntactic. Since 'InitEx' will be analyzed first
- // it should resolve to an SVal that we can check for validity
- // *semantically* instead of walking through the AST.
- if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(InitEx)) {
- if (CE->getNumArgs() == 1) {
- const Expr *E = CE->getArg(0);
- if (const MaterializeTemporaryExpr *M
- = dyn_cast<MaterializeTemporaryExpr>(E))
- E = M->GetTemporaryExpr();
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
- InitEx = ICE->getSubExpr();
- state = handleAssign(state, MR, InitEx, C.getLocationContext());
- }
- }
- }
- C.addTransition(state);
-}
-
-
-namespace { struct CalledReserved {}; }
-namespace clang { namespace ento {
-template<> struct ProgramStateTrait<CalledReserved>
- : public ProgramStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
- static void *GDMIndex() { static int index = 0; return &index; }
-};
-}}
-
-// on a member call, first check the args for any bad iterators
-// then, check to see if it is a call to a function that will invalidate
-// the iterators
-void IteratorsChecker::checkPreStmt(const CXXMemberCallExpr *MCE,
- CheckerContext &C) const {
- // Check the arguments.
- checkArgs(C, MCE);
- const MemberExpr *ME = dyn_cast<MemberExpr>(MCE->getCallee());
- if (!ME)
- return;
- // Make sure we have the right kind of container.
- const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase());
- if (!DRE || getTemplateKind(DRE->getType()) != VectorKind)
- return;
- SVal tsv = C.getState()->getSVal(DRE, C.getLocationContext());
- // Get the MemRegion associated with the container instance.
- const MemRegion *MR = tsv.getAsRegion();
- if (!MR)
- return;
- // If we are calling a function that invalidates iterators, mark them
- // appropriately by finding matching instances.
- ProgramStateRef state = C.getState();
- StringRef mName = ME->getMemberDecl()->getName();
- if (llvm::StringSwitch<bool>(mName)
- .Cases("insert", "reserve", "push_back", true)
- .Cases("erase", "pop_back", "clear", "resize", true)
- .Default(false)) {
- // If there was a 'reserve' call, assume iterators are good.
- if (!state->contains<CalledReserved>(MR))
- state = invalidateIterators(state, MR, ME);
- }
- // Keep track of instances that have called 'reserve'
- // note: do this after we invalidate any iterators by calling
- // 'reserve' itself.
- if (mName == "reserve")
- state = state->add<CalledReserved>(MR);
-
- if (state != C.getState())
- C.addTransition(state);
-}
-
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index cb976e0..969f2dd 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -290,7 +290,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
unsigned idx = InvalidIdx;
ProgramStateRef State = C.getState();
- StringRef funName = C.getCalleeName(CE);
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return;
+
+ StringRef funName = C.getCalleeName(FD);
if (funName.empty())
return;
@@ -446,7 +450,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- StringRef funName = C.getCalleeName(CE);
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return;
+
+ StringRef funName = C.getCalleeName(FD);
// If a value has been allocated, add it to the set for tracking.
unsigned idx = getTrackedFunctionIndex(funName, true);
@@ -528,9 +536,11 @@ MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N,
}
ProgramPoint P = AllocNode->getLocation();
- if (!isa<StmtPoint>(P))
- return 0;
- return cast<clang::PostStmt>(P).getStmt();
+ if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P))
+ return Exit->getCalleeContext()->getCallSite();
+ if (clang::PostStmt *PS = dyn_cast<clang::PostStmt>(&P))
+ return PS->getStmt();
+ return 0;
}
BugReport *MacOSKeychainAPIChecker::
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 8bce88a..dfcedf6 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -18,7 +18,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@@ -34,15 +34,21 @@ using namespace ento;
namespace {
class RefState {
- enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped,
+ enum Kind { // Reference to allocated memory.
+ Allocated,
+ // Reference to released/freed memory.
+ Released,
+ // The responsibility for freeing resources has transfered from
+ // this reference. A relinquished symbol should not be freed.
Relinquished } K;
const Stmt *S;
public:
RefState(Kind k, const Stmt *s) : K(k), S(s) {}
- bool isAllocated() const { return K == AllocateUnchecked; }
+ bool isAllocated() const { return K == Allocated; }
bool isReleased() const { return K == Released; }
+ bool isRelinquished() const { return K == Relinquished; }
const Stmt *getStmt() const { return S; }
@@ -50,14 +56,10 @@ public:
return K == X.K && S == X.S;
}
- static RefState getAllocateUnchecked(const Stmt *s) {
- return RefState(AllocateUnchecked, s);
- }
- static RefState getAllocateFailed() {
- return RefState(AllocateFailed, 0);
+ static RefState getAllocated(const Stmt *s) {
+ return RefState(Allocated, s);
}
static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
- static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); }
static RefState getRelinquished(const Stmt *s) {
return RefState(Relinquished, s);
}
@@ -90,6 +92,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
check::PreStmt<CallExpr>,
check::PostStmt<CallExpr>,
check::PostStmt<BlockExpr>,
+ check::PreObjCMessage,
check::Location,
check::Bind,
eval::Assume,
@@ -117,6 +120,7 @@ public:
void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndPath(CheckerContext &C) const;
@@ -132,17 +136,22 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
return true;
}
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const;
+
private:
void initIdentifierInfo(ASTContext &C) const;
/// Check if this is one of the functions which can allocate/reallocate memory
/// pointed to by one of its arguments.
bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
+ bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const;
+ bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
@@ -167,20 +176,26 @@ private:
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
const OwnershipAttr* Att) const;
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef state, unsigned Num,
- bool Hold) const;
+ ProgramStateRef state, unsigned Num,
+ bool Hold) const;
+ ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
+ const Expr *ParentExpr,
+ ProgramStateRef state,
+ bool Hold) const;
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
bool FreesMemOnFailure) const;
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE);
- bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const;
+ ///\brief Check if the memory associated with this symbol was released.
+ bool isReleased(SymbolRef Sym, CheckerContext &C) const;
+
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
const Stmt *S = 0) const;
/// Check if the function is not known to us. So, for example, we could
/// conservatively assume it can free/reallocate it's pointer arguments.
- bool doesNotFreeMemory(const CallOrObjCMessage *Call,
+ bool doesNotFreeMemory(const CallEvent *Call,
ProgramStateRef State) const;
static bool SummarizeValue(raw_ostream &os, SVal V);
@@ -207,15 +222,17 @@ private:
// The allocated region symbol tracked by the main analysis.
SymbolRef Sym;
- // The mode we are in, i.e. what kind of diagnostics will be emitted.
- NotificationMode Mode;
+ // The mode we are in, i.e. what kind of diagnostics will be emitted.
+ NotificationMode Mode;
- // A symbol from when the primary region should have been reallocated.
- SymbolRef FailedReallocSymbol;
+ // A symbol from when the primary region should have been reallocated.
+ SymbolRef FailedReallocSymbol;
- public:
- MallocBugVisitor(SymbolRef S)
- : Sym(S), Mode(Normal), FailedReallocSymbol(0) {}
+ bool IsLeak;
+
+ public:
+ MallocBugVisitor(SymbolRef S, bool isLeak = false)
+ : Sym(S), Mode(Normal), FailedReallocSymbol(0), IsLeak(isLeak) {}
virtual ~MallocBugVisitor() {}
@@ -239,6 +256,15 @@ private:
(S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
}
+ inline bool isRelinquished(const RefState *S, const RefState *SPrev,
+ const Stmt *Stmt) {
+ // Did not track -> relinquished. Other state (allocated) -> relinquished.
+ return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
+ isa<ObjCPropertyRefExpr>(Stmt)) &&
+ (S && S->isRelinquished()) &&
+ (!SPrev || !SPrev->isRelinquished()));
+ }
+
inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev,
const Stmt *Stmt) {
// If the expression is not a call, and the state change is
@@ -253,6 +279,20 @@ private:
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR);
+
+ PathDiagnosticPiece* getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ BugReport &BR) {
+ if (!IsLeak)
+ return 0;
+
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createEndOfPath(EndPathNode,
+ BRC.getSourceManager());
+ // Do not add the statement itself as a range in case of leak.
+ return new PathDiagnosticEventPiece(L, BR.getDescription(), false);
+ }
+
private:
class StackHintGeneratorForReallocationFailed
: public StackHintGeneratorForSymbol {
@@ -315,44 +355,73 @@ public:
} // end anonymous namespace
void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
- if (!II_malloc)
- II_malloc = &Ctx.Idents.get("malloc");
- if (!II_free)
- II_free = &Ctx.Idents.get("free");
- if (!II_realloc)
- II_realloc = &Ctx.Idents.get("realloc");
- if (!II_reallocf)
- II_reallocf = &Ctx.Idents.get("reallocf");
- if (!II_calloc)
- II_calloc = &Ctx.Idents.get("calloc");
- if (!II_valloc)
- II_valloc = &Ctx.Idents.get("valloc");
- if (!II_strdup)
- II_strdup = &Ctx.Idents.get("strdup");
- if (!II_strndup)
- II_strndup = &Ctx.Idents.get("strndup");
+ if (II_malloc)
+ return;
+ II_malloc = &Ctx.Idents.get("malloc");
+ II_free = &Ctx.Idents.get("free");
+ II_realloc = &Ctx.Idents.get("realloc");
+ II_reallocf = &Ctx.Idents.get("reallocf");
+ II_calloc = &Ctx.Idents.get("calloc");
+ II_valloc = &Ctx.Idents.get("valloc");
+ II_strdup = &Ctx.Idents.get("strdup");
+ II_strndup = &Ctx.Idents.get("strndup");
}
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
+ if (isFreeFunction(FD, C))
+ return true;
+
+ if (isAllocationFunction(FD, C))
+ return true;
+
+ return false;
+}
+
+bool MallocChecker::isAllocationFunction(const FunctionDecl *FD,
+ ASTContext &C) const {
if (!FD)
return false;
- IdentifierInfo *FunI = FD->getIdentifier();
- if (!FunI)
- return false;
- initIdentifierInfo(C);
+ if (FD->getKind() == Decl::Function) {
+ IdentifierInfo *FunI = FD->getIdentifier();
+ initIdentifierInfo(C);
- if (FunI == II_malloc || FunI == II_free || FunI == II_realloc ||
- FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
- FunI == II_strdup || FunI == II_strndup)
- return true;
+ if (FunI == II_malloc || FunI == II_realloc ||
+ FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
+ FunI == II_strdup || FunI == II_strndup)
+ return true;
+ }
- if (Filter.CMallocOptimistic && FD->hasAttrs() &&
- FD->specific_attr_begin<OwnershipAttr>() !=
- FD->specific_attr_end<OwnershipAttr>())
- return true;
+ if (Filter.CMallocOptimistic && FD->hasAttrs())
+ for (specific_attr_iterator<OwnershipAttr>
+ i = FD->specific_attr_begin<OwnershipAttr>(),
+ e = FD->specific_attr_end<OwnershipAttr>();
+ i != e; ++i)
+ if ((*i)->getOwnKind() == OwnershipAttr::Returns)
+ return true;
+ return false;
+}
+
+bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const {
+ if (!FD)
+ return false;
+
+ if (FD->getKind() == Decl::Function) {
+ IdentifierInfo *FunI = FD->getIdentifier();
+ initIdentifierInfo(C);
+ if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf)
+ return true;
+ }
+ if (Filter.CMallocOptimistic && FD->hasAttrs())
+ for (specific_attr_iterator<OwnershipAttr>
+ i = FD->specific_attr_begin<OwnershipAttr>(),
+ e = FD->specific_attr_end<OwnershipAttr>();
+ i != e; ++i)
+ if ((*i)->getOwnKind() == OwnershipAttr::Takes ||
+ (*i)->getOwnKind() == OwnershipAttr::Holds)
+ return true;
return false;
}
@@ -361,29 +430,32 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (!FD)
return;
- initIdentifierInfo(C.getASTContext());
- IdentifierInfo *FunI = FD->getIdentifier();
- if (!FunI)
- return;
-
ProgramStateRef State = C.getState();
- if (FunI == II_malloc || FunI == II_valloc) {
- if (CE->getNumArgs() < 1)
- return;
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == II_realloc) {
- State = ReallocMem(C, CE, false);
- } else if (FunI == II_reallocf) {
- State = ReallocMem(C, CE, true);
- } else if (FunI == II_calloc) {
- State = CallocMem(C, CE);
- } else if (FunI == II_free) {
- State = FreeMemAux(C, CE, C.getState(), 0, false);
- } else if (FunI == II_strdup) {
- State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == II_strndup) {
- State = MallocUpdateRefState(C, CE, State);
- } else if (Filter.CMallocOptimistic) {
+
+ if (FD->getKind() == Decl::Function) {
+ initIdentifierInfo(C.getASTContext());
+ IdentifierInfo *FunI = FD->getIdentifier();
+
+ if (FunI == II_malloc || FunI == II_valloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (FunI == II_realloc) {
+ State = ReallocMem(C, CE, false);
+ } else if (FunI == II_reallocf) {
+ State = ReallocMem(C, CE, true);
+ } else if (FunI == II_calloc) {
+ State = CallocMem(C, CE);
+ } else if (FunI == II_free) {
+ State = FreeMemAux(C, CE, State, 0, false);
+ } else if (FunI == II_strdup) {
+ State = MallocUpdateRefState(C, CE, State);
+ } else if (FunI == II_strndup) {
+ State = MallocUpdateRefState(C, CE, State);
+ }
+ }
+
+ if (Filter.CMallocOptimistic) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
if (FD->hasAttrs())
@@ -405,6 +477,34 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
+static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) {
+ Selector S = Call.getSelector();
+ for (unsigned i = 1; i < S.getNumArgs(); ++i)
+ if (S.getNameForSlot(i).equals("freeWhenDone"))
+ if (Call.getArgSVal(i).isConstant(0))
+ return true;
+
+ return false;
+}
+
+void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call,
+ CheckerContext &C) const {
+ // If the first selector is dataWithBytesNoCopy, assume that the memory will
+ // be released with 'free' by the new object.
+ // Ex: [NSData dataWithBytesNoCopy:bytes length:10];
+ // Unless 'freeWhenDone' param set to 0.
+ // TODO: Check that the memory was allocated with malloc.
+ Selector S = Call.getSelector();
+ if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" ||
+ S.getNameForSlot(0) == "initWithBytesNoCopy" ||
+ S.getNameForSlot(0) == "initWithCharactersNoCopy") &&
+ !isFreeWhenDoneSetToZero(Call)){
+ unsigned int argIdx = 0;
+ C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx),
+ Call.getOriginExpr(), C.getState(), true));
+ }
+}
+
ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
const OwnershipAttr* Att) {
@@ -422,19 +522,27 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
const CallExpr *CE,
SVal Size, SVal Init,
ProgramStateRef state) {
- // Get the return value.
- SVal retVal = state->getSVal(CE, C.getLocationContext());
+
+ // Bind the return value to the symbolic value from the heap region.
+ // TODO: We could rewrite post visit to eval call; 'malloc' does not have
+ // side effects other than what we model here.
+ unsigned Count = C.getCurrentBlockCount();
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
+ DefinedSVal RetVal =
+ cast<DefinedSVal>(svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count));
+ state = state->BindExpr(CE, C.getLocationContext(), RetVal);
// We expect the malloc functions to return a pointer.
- if (!isa<Loc>(retVal))
+ if (!isa<Loc>(RetVal))
return 0;
// Fill the region with the initialization value.
- state = state->bindDefault(retVal, Init);
+ state = state->bindDefault(RetVal, Init);
// Set the region's extent equal to the Size parameter.
const SymbolicRegion *R =
- dyn_cast_or_null<SymbolicRegion>(retVal.getAsRegion());
+ dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());
if (!R)
return 0;
if (isa<DefinedOrUnknownSVal>(Size)) {
@@ -465,7 +573,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
assert(Sym);
// Set the symbol's state to Allocated.
- return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE));
+ return state->set<RegionState>(Sym, RefState::getAllocated(CE));
}
@@ -495,7 +603,15 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
if (CE->getNumArgs() < (Num + 1))
return 0;
- const Expr *ArgExpr = CE->getArg(Num);
+ return FreeMemAux(C, CE->getArg(Num), CE, state, Hold);
+}
+
+ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
+ const Expr *ArgExpr,
+ const Expr *ParentExpr,
+ ProgramStateRef state,
+ bool Hold) const {
+
SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
if (!isa<DefinedOrUnknownSVal>(ArgVal))
return 0;
@@ -558,20 +674,15 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
SymbolRef Sym = SR->getSymbol();
const RefState *RS = state->get<RegionState>(Sym);
- // If the symbol has not been tracked, return. This is possible when free() is
- // called on a pointer that does not get its pointee directly from malloc().
- // Full support of this requires inter-procedural analysis.
- if (!RS)
- return 0;
-
// Check double free.
- if (RS->isReleased()) {
+ if (RS && (RS->isReleased() || RS->isRelinquished())) {
if (ExplodedNode *N = C.generateSink()) {
if (!BT_DoubleFree)
BT_DoubleFree.reset(
new BugType("Double free", "Memory Error"));
BugReport *R = new BugReport(*BT_DoubleFree,
- "Attempt to free released memory", N);
+ (RS->isReleased() ? "Attempt to free released memory" :
+ "Attempt to free non-owned memory"), N);
R->addRange(ArgExpr->getSourceRange());
R->markInteresting(Sym);
R->addVisitor(new MallocBugVisitor(Sym));
@@ -582,8 +693,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// Normal free.
if (Hold)
- return state->set<RegionState>(Sym, RefState::getRelinquished(CE));
- return state->set<RegionState>(Sym, RefState::getReleased(CE));
+ return state->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr));
+ return state->set<RegionState>(Sym, RefState::getReleased(ParentExpr));
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -780,10 +891,8 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){
// The semantics of the return value are:
// If size was equal to 0, either NULL or a pointer suitable to be passed
- // to free() is returned.
- stateFree = stateFree->set<ReallocPairs>(ToPtr,
- ReallocPair(FromPtr, FreesOnFail));
- C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
+ // to free() is returned. We just free the input pointer and do not add
+ // any constrains on the output pointer.
return stateFree;
}
@@ -851,8 +960,10 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
ProgramPoint P = AllocNode->getLocation();
const Stmt *AllocationStmt = 0;
- if (isa<StmtPoint>(P))
- AllocationStmt = cast<StmtPoint>(P).getStmt();
+ if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P))
+ AllocationStmt = Exit->getCalleeContext()->getCallSite();
+ else if (StmtPoint *SP = dyn_cast<StmtPoint>(&P))
+ AllocationStmt = SP->getStmt();
return LeakInfo(AllocationStmt, ReferenceRegion);
}
@@ -884,15 +995,15 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
os << "Memory is never released; potential leak";
- if (Region) {
+ if (Region && Region->canPrintPretty()) {
os << " of memory pointed to by '";
- Region->dumpPretty(os);
- os <<'\'';
+ Region->printPretty(os);
+ os << '\'';
}
BugReport *R = new BugReport(*BT_Leak, os.str(), N, LocUsedForUniqueing);
R->markInteresting(Sym);
- R->addVisitor(new MallocBugVisitor(Sym));
+ R->addVisitor(new MallocBugVisitor(Sym, true));
C.EmitReport(R);
}
@@ -960,23 +1071,9 @@ void MallocChecker::checkEndPath(CheckerContext &C) const {
}
}
-bool MallocChecker::checkEscape(SymbolRef Sym, const Stmt *S,
- CheckerContext &C) const {
- ProgramStateRef state = C.getState();
- const RefState *RS = state->get<RegionState>(Sym);
- if (!RS)
- return false;
-
- if (RS->isAllocated()) {
- state = state->set<RegionState>(Sym, RefState::getEscaped(S));
- C.addTransition(state);
- return true;
- }
- return false;
-}
-
void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
- if (isMemFunction(C.getCalleeDecl(CE), C.getASTContext()))
+ // We will check for double free in the post visit.
+ if (isFreeFunction(C.getCalleeDecl(CE), C.getASTContext()))
return;
// Check use after free, when a freed pointer is passed to a call.
@@ -1000,7 +1097,8 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
return;
// Check if we are returning a symbol.
- SVal RetVal = C.getState()->getSVal(E, C.getLocationContext());
+ ProgramStateRef State = C.getState();
+ SVal RetVal = State->getSVal(E, C.getLocationContext());
SymbolRef Sym = RetVal.getAsSymbol();
if (!Sym)
// If we are returning a field of the allocated struct or an array element,
@@ -1011,16 +1109,18 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
if (const SymbolicRegion *BMR =
dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
Sym = BMR->getSymbol();
- if (!Sym)
- return;
// Check if we are returning freed memory.
- if (checkUseAfterFree(Sym, C, E))
- return;
+ if (Sym)
+ if (checkUseAfterFree(Sym, C, E))
+ return;
- // If this function body is not inlined, check if the symbol is escaping.
- if (C.getLocationContext()->getParent() == 0)
- checkEscape(Sym, E, C);
+ // If this function body is not inlined, stop tracking any returned symbols.
+ if (C.getLocationContext()->getParent() == 0) {
+ State =
+ State->scanReachableSymbols<StopTrackingCallback>(RetVal).getState();
+ C.addTransition(State);
+ }
}
// TODO: Blocks should be either inlined or should call invalidate regions
@@ -1063,11 +1163,15 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
C.addTransition(state);
}
-bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
- const Stmt *S) const {
+bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
assert(Sym);
const RefState *RS = C.getState()->get<RegionState>(Sym);
- if (RS && RS->isReleased()) {
+ return (RS && RS->isReleased());
+}
+
+bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const {
+ if (isReleased(Sym, C)) {
if (ExplodedNode *N = C.generateSink()) {
if (!BT_UseFree)
BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
@@ -1090,7 +1194,7 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
CheckerContext &C) const {
SymbolRef Sym = l.getLocSymbolInBase();
if (Sym)
- checkUseAfterFree(Sym, C);
+ checkUseAfterFree(Sym, C, S);
}
//===----------------------------------------------------------------------===//
@@ -1118,13 +1222,11 @@ void MallocChecker::checkBind(SVal loc, SVal val, const Stmt *S,
// To test (3), generate a new state with the binding added. If it is
// the same state, then it escapes (since the store cannot represent
// the binding).
- escapes = (state == (state->bindLoc(*regionLoc, val)));
- }
- if (!escapes) {
- // Case 4: We do not currently model what happens when a symbol is
- // assigned to a struct field, so be conservative here and let the symbol
- // go. TODO: This could definitely be improved upon.
- escapes = !isa<VarRegion>(regionLoc->getRegion());
+ // Do this only if we know that the store is not supposed to generate the
+ // same state.
+ SVal StoredVal = state->getSVal(regionLoc->getRegion());
+ if (StoredVal != val)
+ escapes = (state == (state->bindLoc(*regionLoc, val)));
}
}
@@ -1165,7 +1267,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
if (RS) {
if (RS->isReleased() && ! I.getData().IsFreeOnFailure)
state = state->set<RegionState>(ReallocSym,
- RefState::getAllocateUnchecked(RS->getStmt()));
+ RefState::getAllocated(RS->getStmt()));
}
state = state->remove<ReallocPairs>(I.getKey());
}
@@ -1175,118 +1277,30 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
}
// Check if the function is known to us. So, for example, we could
-// conservatively assume it can free/reallocate it's pointer arguments.
+// conservatively assume it can free/reallocate its pointer arguments.
// (We assume that the pointers cannot escape through calls to system
// functions not handled by this checker.)
-bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
+bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
ProgramStateRef State) const {
- if (!Call)
- return false;
+ assert(Call);
// For now, assume that any C++ call can free memory.
// TODO: If we want to be more optimistic here, we'll need to make sure that
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
- if (Call->isCXXCall())
- return false;
-
- const Decl *D = Call->getDecl();
- if (!D)
+ if (!(isa<FunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
return false;
- ASTContext &ASTC = State->getStateManager().getContext();
-
- // If it's one of the allocation functions we can reason about, we model
- // its behavior explicitly.
- if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) {
- return true;
- }
-
- // If it's not a system call, assume it frees memory.
- SourceManager &SM = ASTC.getSourceManager();
- if (!SM.isInSystemHeader(D->getLocation()))
- return false;
-
- // Process C/ObjC functions.
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- // White list the system functions whose arguments escape.
- const IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return true;
- StringRef FName = II->getName();
-
- // White list thread local storage.
- if (FName.equals("pthread_setspecific"))
- return false;
-
- // White list the 'XXXNoCopy' ObjC functions.
- if (FName.endswith("NoCopy")) {
- // Look for the deallocator argument. We know that the memory ownership
- // is not transfered only if the deallocator argument is
- // 'kCFAllocatorNull'.
- for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
- const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts();
- if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
- StringRef DeallocatorName = DE->getFoundDecl()->getName();
- if (DeallocatorName == "kCFAllocatorNull")
- return true;
- }
- }
- return false;
- }
-
- // PR12101
- // Many CoreFoundation and CoreGraphics might allow a tracked object
- // to escape.
- if (Call->isCFCGAllowingEscape(FName))
+ // Check Objective-C messages by selector name.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
+ // If it's not a framework call, or if it takes a callback, assume it
+ // can free memory.
+ if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg())
return false;
- // Associating streams with malloced buffers. The pointer can escape if
- // 'closefn' is specified (and if that function does free memory).
- // Currently, we do not inspect the 'closefn' function (PR12101).
- if (FName == "funopen")
- if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0))
- return false;
-
- // Do not warn on pointers passed to 'setbuf' when used with std streams,
- // these leaks might be intentional when setting the buffer for stdio.
- // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
- if (FName == "setbuf" || FName =="setbuffer" ||
- FName == "setlinebuf" || FName == "setvbuf") {
- if (Call->getNumArgs() >= 1)
- if (const DeclRefExpr *Arg =
- dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts()))
- if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl()))
- if (D->getCanonicalDecl()->getName().find("std")
- != StringRef::npos)
- return false;
- }
-
- // A bunch of other functions, which take ownership of a pointer (See retain
- // release checker). Not all the parameters here are invalidated, but the
- // Malloc checker cannot differentiate between them. The right way of doing
- // this would be to implement a pointer escapes callback.
- if (FName == "CVPixelBufferCreateWithBytes" ||
- FName == "CGBitmapContextCreateWithData" ||
- FName == "CVPixelBufferCreateWithPlanarBytes" ||
- FName == "OSAtomicEnqueue") {
- return false;
- }
-
- // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
- // be deallocated by NSMapRemove.
- if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
- return false;
-
- // Otherwise, assume that the function does not free memory.
- // Most system calls, do not free the memory.
- return true;
-
- // Process ObjC functions.
- } else if (const ObjCMethodDecl * ObjCD = dyn_cast<ObjCMethodDecl>(D)) {
- Selector S = ObjCD->getSelector();
+ Selector S = Msg->getSelector();
- // White list the ObjC functions which do free memory.
+ // Whitelist the ObjC methods which do free memory.
// - Anything containing 'freeWhenDone' param set to 1.
// Ex: dataWithBytesNoCopy:length:freeWhenDone.
for (unsigned i = 1; i < S.getNumArgs(); ++i) {
@@ -1299,20 +1313,111 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
}
// If the first selector ends with NoCopy, assume that the ownership is
- // transfered as well.
+ // transferred as well.
// Ex: [NSData dataWithBytesNoCopy:bytes length:10];
- if (S.getNameForSlot(0).endswith("NoCopy")) {
+ StringRef FirstSlot = S.getNameForSlot(0);
+ if (FirstSlot.endswith("NoCopy"))
+ return false;
+
+ // If the first selector starts with addPointer, insertPointer,
+ // or replacePointer, assume we are dealing with NSPointerArray or similar.
+ // This is similar to C++ containers (vector); we still might want to check
+ // that the pointers get freed by following the container itself.
+ if (FirstSlot.startswith("addPointer") ||
+ FirstSlot.startswith("insertPointer") ||
+ FirstSlot.startswith("replacePointer")) {
return false;
}
- // Otherwise, assume that the function does not free memory.
- // Most system calls, do not free the memory.
+ // Otherwise, assume that the method does not free memory.
+ // Most framework methods do not free memory.
return true;
}
- // Otherwise, assume that the function can free memory.
- return false;
+ // At this point the only thing left to handle is straight function calls.
+ const FunctionDecl *FD = cast<FunctionCall>(Call)->getDecl();
+ if (!FD)
+ return false;
+
+ ASTContext &ASTC = State->getStateManager().getContext();
+
+ // If it's one of the allocation functions we can reason about, we model
+ // its behavior explicitly.
+ if (isMemFunction(FD, ASTC))
+ return true;
+ // If it's not a system call, assume it frees memory.
+ if (!Call->isInSystemHeader())
+ return false;
+
+ // White list the system functions whose arguments escape.
+ const IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return false;
+ StringRef FName = II->getName();
+
+ // White list the 'XXXNoCopy' CoreFoundation functions.
+ // We specifically check these before
+ if (FName.endswith("NoCopy")) {
+ // Look for the deallocator argument. We know that the memory ownership
+ // is not transferred only if the deallocator argument is
+ // 'kCFAllocatorNull'.
+ for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
+ const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts();
+ if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
+ StringRef DeallocatorName = DE->getFoundDecl()->getName();
+ if (DeallocatorName == "kCFAllocatorNull")
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Associating streams with malloced buffers. The pointer can escape if
+ // 'closefn' is specified (and if that function does free memory),
+ // but it will not if closefn is not specified.
+ // Currently, we do not inspect the 'closefn' function (PR12101).
+ if (FName == "funopen")
+ if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))
+ return true;
+
+ // Do not warn on pointers passed to 'setbuf' when used with std streams,
+ // these leaks might be intentional when setting the buffer for stdio.
+ // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
+ if (FName == "setbuf" || FName =="setbuffer" ||
+ FName == "setlinebuf" || FName == "setvbuf") {
+ if (Call->getNumArgs() >= 1) {
+ const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
+ if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
+ if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))
+ if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos)
+ return false;
+ }
+ }
+
+ // A bunch of other functions which either take ownership of a pointer or
+ // wrap the result up in a struct or object, meaning it can be freed later.
+ // (See RetainCountChecker.) Not all the parameters here are invalidated,
+ // but the Malloc checker cannot differentiate between them. The right way
+ // of doing this would be to implement a pointer escapes callback.
+ if (FName == "CGBitmapContextCreate" ||
+ FName == "CGBitmapContextCreateWithData" ||
+ FName == "CVPixelBufferCreateWithBytes" ||
+ FName == "CVPixelBufferCreateWithPlanarBytes" ||
+ FName == "OSAtomicEnqueue") {
+ return false;
+ }
+
+ // Handle cases where we know a buffer's /address/ can escape.
+ // Note that the above checks handle some special cases where we know that
+ // even though the address escapes, it's still our responsibility to free the
+ // buffer.
+ if (Call->argumentsMayEscape())
+ return false;
+
+ // Otherwise, assume that the function does not free memory.
+ // Most system calls do not free the memory.
+ return true;
}
// If the symbol we are tracking is invalidated, but not explicitly (ex: the &p
@@ -1323,7 +1428,7 @@ MallocChecker::checkRegionChanges(ProgramStateRef State,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
if (!invalidated || invalidated->empty())
return State;
llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
@@ -1345,9 +1450,13 @@ MallocChecker::checkRegionChanges(ProgramStateRef State,
SymbolRef sym = *I;
if (WhitelistedSymbols.count(sym))
continue;
- // The symbol escaped.
- if (const RefState *RS = State->get<RegionState>(sym))
- State = State->set<RegionState>(sym, RefState::getEscaped(RS->getStmt()));
+ // The symbol escaped. Note, we assume that if the symbol is released,
+ // passing it out will result in a use after free. We also keep tracking
+ // relinquished symbols.
+ if (const RefState *RS = State->get<RegionState>(sym)) {
+ if (RS->isAllocated())
+ State = State->remove<RegionState>(sym);
+ }
}
return State;
}
@@ -1377,7 +1486,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
const RefState *RS = state->get<RegionState>(Sym);
const RefState *RSPrev = statePrev->get<RegionState>(Sym);
- if (!RS && !RSPrev)
+ if (!RS)
return 0;
const Stmt *S = 0;
@@ -1386,17 +1495,22 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
// Retrieve the associated statement.
ProgramPoint ProgLoc = N->getLocation();
- if (isa<StmtPoint>(ProgLoc))
- S = cast<StmtPoint>(ProgLoc).getStmt();
+ if (StmtPoint *SP = dyn_cast<StmtPoint>(&ProgLoc))
+ S = SP->getStmt();
+ else if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&ProgLoc))
+ S = Exit->getCalleeContext()->getCallSite();
// If an assumption was made on a branch, it should be caught
// here by looking at the state transition.
- if (isa<BlockEdge>(ProgLoc)) {
- const CFGBlock *srcBlk = cast<BlockEdge>(ProgLoc).getSrc();
+ else if (BlockEdge *Edge = dyn_cast<BlockEdge>(&ProgLoc)) {
+ const CFGBlock *srcBlk = Edge->getSrc();
S = srcBlk->getTerminator();
}
if (!S)
return 0;
+ // FIXME: We will eventually need to handle non-statement-based events
+ // (__attribute__((cleanup))).
+
// Find out if this is an interesting point and what is the kind.
if (Mode == Normal) {
if (isAllocated(RS, RSPrev, S)) {
@@ -1407,6 +1521,9 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
Msg = "Memory is released";
StackHint = new StackHintGeneratorForSymbol(Sym,
"Returned released memory");
+ } else if (isRelinquished(RS, RSPrev, S)) {
+ Msg = "Memory ownership is transfered";
+ StackHint = new StackHintGeneratorForSymbol(Sym, "");
} else if (isReallocFailedCheck(RS, RSPrev, S)) {
Mode = ReallocationFailed;
Msg = "Reallocation failed";
@@ -1428,11 +1545,6 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
// Is this is the first appearance of the reallocated symbol?
if (!statePrev->get<RegionState>(FailedReallocSymbol)) {
- // If we ever hit this assert, that means BugReporter has decided to skip
- // node pairs or visit them out of order.
- assert(state->get<RegionState>(FailedReallocSymbol) &&
- "Missed the reallocation point");
-
// We're at the reallocation point.
Msg = "Attempt to reallocate memory";
StackHint = new StackHintGeneratorForSymbol(Sym,
@@ -1452,6 +1564,14 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint);
}
+void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+
+ RegionStateTy RS = State->get<RegionState>();
+
+ if (!RS.isEmpty())
+ Out << "Has Malloc data" << NL;
+}
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) {\
diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index 08a9da1..6292a47 100644
--- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -118,11 +118,6 @@ public:
Visit(E->getRHS());
}
- void VisitBinAdd(const BinaryOperator *E) {
- Visit(E->getLHS());
- Visit(E->getRHS());
- }
-
void VisitImplicitCastExpr(const ImplicitCastExpr *E) {
return Visit(E->getSubExpr());
}
@@ -139,6 +134,29 @@ public:
}
};
+// Determine if the pointee and sizeof types are compatible. Here
+// we ignore constness of pointer types.
+static bool typesCompatible(ASTContext &C, QualType A, QualType B) {
+ while (true) {
+ A = A.getCanonicalType();
+ B = B.getCanonicalType();
+
+ if (A.getTypePtr() == B.getTypePtr())
+ return true;
+
+ if (const PointerType *ptrA = A->getAs<PointerType>())
+ if (const PointerType *ptrB = B->getAs<PointerType>()) {
+ A = ptrA->getPointeeType();
+ B = ptrB->getPointeeType();
+ continue;
+ }
+
+ break;
+ }
+
+ return false;
+}
+
class MallocSizeofChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
@@ -166,7 +184,7 @@ public:
continue;
QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument();
- if (!BR.getContext().hasSameUnqualifiedType(PointeeType, SizeofType)) {
+ if (!typesCompatible(BR.getContext(), PointeeType, SizeofType)) {
const TypeSourceInfo *TSI = 0;
if (i->CastedExprParent.is<const VarDecl *>()) {
TSI =
@@ -180,9 +198,8 @@ public:
OS << "Result of '"
<< i->AllocCall->getDirectCallee()->getIdentifier()->getName()
- << "' is converted to type '"
- << CastedType.getAsString() << "', whose pointee type '"
- << PointeeType.getAsString() << "' is incompatible with "
+ << "' is converted to a pointer of type '"
+ << PointeeType.getAsString() << "', which is incompatible with "
<< "sizeof operand type '" << SizeofType.getAsString() << "'";
llvm::SmallVector<SourceRange, 4> Ranges;
Ranges.push_back(i->AllocCall->getCallee()->getSourceRange());
@@ -194,7 +211,7 @@ public:
PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
BR.getSourceManager(), ADC);
- BR.EmitBasicReport(D, "allocator sizeof operand mismatch",
+ BR.EmitBasicReport(D, "Allocator sizeof operand mismatch",
categories::UnixAPI,
OS.str(),
L, Ranges.data(), Ranges.size());
diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index 4989ba8..aad3b0f 100644
--- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -20,9 +20,9 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Decl.h"
@@ -36,29 +36,20 @@ class NSAutoreleasePoolChecker
mutable Selector releaseS;
public:
- void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
};
} // end anonymous namespace
-void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg,
+void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
-
- const Expr *receiver = msg.getInstanceReceiver();
- if (!receiver)
+ if (!msg.isInstanceMessage())
return;
-
- // FIXME: Enhance with value-tracking information instead of consulting
- // the type of the expression.
- const ObjCObjectPointerType* PT =
- receiver->getType()->getAs<ObjCObjectPointerType>();
-
- if (!PT)
- return;
- const ObjCInterfaceDecl *OD = PT->getInterfaceDecl();
+
+ const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
if (!OD)
return;
- if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool"))
+ if (!OD->getIdentifier()->isStr("NSAutoreleasePool"))
return;
if (releaseS.isNull())
diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index c2d7c09..efb7072 100644
--- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -15,8 +15,8 @@
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "llvm/ADT/StringSwitch.h"
#include <cstdarg>
@@ -29,7 +29,7 @@ class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>,
check::PostObjCMessage > {
public:
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
};
}
@@ -98,7 +98,7 @@ static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) {
return (Arg == NULL);
}
-void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg,
+void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
CheckerContext &C) const {
// HACK: This entire check is to handle two messages in the Cocoa frameworks:
// -[NSAssertionHandler
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index 777e9ea..4cc92ce 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -50,8 +50,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
"for @synchronized"));
BugReport *report =
new BugReport(*BT_undef, BT_undef->getDescription(), N);
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report);
C.EmitReport(report);
}
return;
@@ -74,8 +73,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
"(no synchronization will occur)"));
BugReport *report =
new BugReport(*BT_null, BT_null->getDescription(), N);
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report);
C.EmitReport(report);
return;
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index f4655b6..2ab49ed 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -21,7 +21,6 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 97b58cf..be45da1 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -39,9 +39,9 @@
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
@@ -50,29 +50,27 @@ using namespace ento;
static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND);
static bool isInitializationMethod(const ObjCMethodDecl *MD);
-static bool isInitMessage(const ObjCMessage &msg);
+static bool isInitMessage(const ObjCMethodCall &Msg);
static bool isSelfVar(SVal location, CheckerContext &C);
namespace {
-class ObjCSelfInitChecker : public Checker< check::PreObjCMessage,
- check::PostObjCMessage,
+class ObjCSelfInitChecker : public Checker< check::PostObjCMessage,
check::PostStmt<ObjCIvarRefExpr>,
check::PreStmt<ReturnStmt>,
- check::PreStmt<CallExpr>,
- check::PostStmt<CallExpr>,
- check::Location > {
+ check::PreCall,
+ check::PostCall,
+ check::Location,
+ check::Bind > {
public:
- void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
- void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkLocation(SVal location, bool isLoad, const Stmt *S,
CheckerContext &C) const;
+ void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
- void checkPreStmt(const CallOrObjCMessage &CE, CheckerContext &C) const;
- void checkPostStmt(const CallOrObjCMessage &CE, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &CE, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
};
} // end anonymous namespace
@@ -181,7 +179,7 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C,
C.EmitReport(report);
}
-void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg,
+void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
CheckerContext &C) const {
// When encountering a message that does initialization (init rule),
// tag the return value so that we know later on that if self has this value
@@ -192,7 +190,7 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg,
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
- if (isInitMessage(msg)) {
+ if (isInitMessage(Msg)) {
// Tag the return value as the result of an initializer.
ProgramStateRef state = C.getState();
@@ -201,14 +199,11 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg,
// value out when we return from this method.
state = state->set<CalledInit>(true);
- SVal V = state->getSVal(msg.getMessageExpr(), C.getLocationContext());
+ SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext());
addSelfFlag(state, V, SelfFlag_InitRes, C);
return;
}
- CallOrObjCMessage MsgWrapper(msg, C.getState(), C.getLocationContext());
- checkPostStmt(MsgWrapper, C);
-
// We don't check for an invalid 'self' in an obj-c message expression to cut
// down false positives where logging functions get information from self
// (like its class) or doing "invalidation" on self when the initialization
@@ -239,8 +234,8 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
"'[(super or self) init...]'");
}
-// When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass
-// the SelfFlags from the object 'self' point to before the call, to the new
+// When a call receives a reference to 'self', [Pre/Post]Call pass
+// the SelfFlags from the object 'self' points to before the call to the new
// object after the call. This is to avoid invalidation of 'self' by logging
// functions.
// Another common pattern in classes with multiple initializers is to put the
@@ -255,26 +250,13 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
// Until we can use inter-procedural analysis, in such a call, transfer the
// SelfFlags to the result of the call.
-void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE,
+void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,
CheckerContext &C) const {
- CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext());
- checkPreStmt(CEWrapper, C);
-}
-
-void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE,
- CheckerContext &C) const {
- CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext());
- checkPostStmt(CEWrapper, C);
-}
-
-void ObjCSelfInitChecker::checkPreObjCMessage(ObjCMessage Msg,
- CheckerContext &C) const {
- CallOrObjCMessage MsgWrapper(Msg, C.getState(), C.getLocationContext());
- checkPreStmt(MsgWrapper, C);
-}
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
-void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE,
- CheckerContext &C) const {
ProgramStateRef state = C.getState();
unsigned NumArgs = CE.getNumArgs();
// If we passed 'self' as and argument to the call, record it in the state
@@ -296,9 +278,19 @@ void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE,
}
}
-void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE,
+void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
CheckerContext &C) const {
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
ProgramStateRef state = C.getState();
+ SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
+ if (!prevFlags)
+ return;
+ state = state->remove<PreCallSelfFlags>();
+
unsigned NumArgs = CE.getNumArgs();
for (unsigned i = 0; i < NumArgs; ++i) {
SVal argV = CE.getArgSVal(i);
@@ -306,8 +298,6 @@ void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE,
// If the address of 'self' is being passed to the call, assume that the
// 'self' after the call will have the same flags.
// EX: log(&self)
- SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
- state = state->remove<PreCallSelfFlags>();
addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C);
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
@@ -315,8 +305,6 @@ void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE,
// returns 'self'. So assign the flags, which were set on 'self' to the
// return value.
// EX: self = performMoreInitialization(self)
- SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
- state = state->remove<PreCallSelfFlags>();
const Expr *CallExpr = CE.getOriginExpr();
if (CallExpr)
addSelfFlag(state, state->getSVal(CallExpr, C.getLocationContext()),
@@ -336,6 +324,28 @@ void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C);
}
+
+void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,
+ CheckerContext &C) const {
+ // Allow assignment of anything to self. Self is a local variable in the
+ // initializer, so it is legal to assign anything to it, like results of
+ // static functions/method calls. After self is assigned something we cannot
+ // reason about, stop enforcing the rules.
+ // (Only continue checking if the assigned value should be treated as self.)
+ if ((isSelfVar(loc, C)) &&
+ !hasSelfFlag(val, SelfFlag_InitRes, C) &&
+ !hasSelfFlag(val, SelfFlag_Self, C) &&
+ !isSelfVar(val, C)) {
+
+ // Stop tracking the checker-specific state in the state.
+ ProgramStateRef State = C.getState();
+ State = State->remove<CalledInit>();
+ if (SymbolRef sym = loc.getAsSymbol())
+ State = State->remove<SelfFlag>(sym);
+ C.addTransition(State);
+ }
+}
+
// FIXME: A callback should disable checkers at the start of functions.
static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
if (!ND)
@@ -383,8 +393,8 @@ static bool isInitializationMethod(const ObjCMethodDecl *MD) {
return MD->getMethodFamily() == OMF_init;
}
-static bool isInitMessage(const ObjCMessage &msg) {
- return msg.getMethodFamily() == OMF_init;
+static bool isInitMessage(const ObjCMethodCall &Call) {
+ return Call.getMethodFamily() == OMF_init;
}
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index 4718dc7..582269c 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -47,6 +47,15 @@ static void Scan(IvarUsageMap& M, const Stmt *S) {
return;
}
+ if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
+ for (PseudoObjectExpr::const_semantics_iterator
+ i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
+ const Expr *sub = *i;
+ if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
+ sub = OVE->getSourceExpr();
+ Scan(M, sub);
+ }
+
for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
Scan(M, *I);
}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index b569e41..5503b23 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -23,10 +23,10 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableList.h"
@@ -66,8 +66,9 @@ public:
/// particular argument.
enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg,
DecRefBridgedTransfered,
+ DecRefAndStopTracking, DecRefMsgAndStopTracking,
IncRefMsg, IncRef, MakeCollectable, MayEscape,
- NewAutoreleasePool, SelfOwn, StopTracking };
+ NewAutoreleasePool, StopTracking };
namespace llvm {
template <> struct FoldingSetTrait<ArgEffect> {
@@ -351,6 +352,20 @@ struct ProgramStateTrait<RefBindings>
}
}
+static inline const RefVal *getRefBinding(ProgramStateRef State,
+ SymbolRef Sym) {
+ return State->get<RefBindings>(Sym);
+}
+
+static inline ProgramStateRef setRefBinding(ProgramStateRef State,
+ SymbolRef Sym, RefVal Val) {
+ return State->set<RefBindings>(Sym, Val);
+}
+
+static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
+ return State->remove<RefBindings>(Sym);
+}
+
//===----------------------------------------------------------------------===//
// Function/Method behavior summaries.
//===----------------------------------------------------------------------===//
@@ -431,6 +446,12 @@ public:
bool isSimple() const {
return Args.isEmpty();
}
+
+private:
+ ArgEffects getArgEffects() const { return Args; }
+ ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; }
+
+ friend class RetainSummaryManager;
};
} // end anonymous namespace
@@ -449,9 +470,6 @@ public:
ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s)
: II(d ? d->getIdentifier() : 0), S(s) {}
- ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s)
- : II(d ? d->getIdentifier() : ii), S(s) {}
-
ObjCSummaryKey(Selector s)
: II(0), S(s) {}
@@ -473,17 +491,14 @@ template <> struct DenseMapInfo<ObjCSummaryKey> {
}
static unsigned getHashValue(const ObjCSummaryKey &V) {
- return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier())
- & 0x88888888)
- | (DenseMapInfo<Selector>::getHashValue(V.getSelector())
- & 0x55555555);
+ typedef std::pair<IdentifierInfo*, Selector> PairTy;
+ return DenseMapInfo<PairTy>::getHashValue(PairTy(V.getIdentifier(),
+ V.getSelector()));
}
static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) {
- return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(),
- RHS.getIdentifier()) &&
- DenseMapInfo<Selector>::isEqual(LHS.getSelector(),
- RHS.getSelector());
+ return LHS.getIdentifier() == RHS.getIdentifier() &&
+ LHS.getSelector() == RHS.getSelector();
}
};
@@ -498,21 +513,16 @@ class ObjCSummaryCache {
public:
ObjCSummaryCache() {}
- const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName,
- Selector S) {
- // Lookup the method using the decl for the class @interface. If we
- // have no decl, lookup using the class name.
- return D ? find(D, S) : find(ClsName, S);
- }
-
const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) {
// Do a lookup with the (D,S) pair. If we find a match return
// the iterator.
ObjCSummaryKey K(D, S);
MapTy::iterator I = M.find(K);
- if (I != M.end() || !D)
+ if (I != M.end())
return I->second;
+ if (!D)
+ return NULL;
// Walk the super chain. If we find a hit with a parent, we'll end
// up returning that summary. We actually allow that key (null,S), as
@@ -628,9 +638,6 @@ class RetainSummaryManager {
ArgEffects getArgEffects();
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
-
-public:
- RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
const RetainSummary *getUnarySummary(const FunctionType* FT,
UnaryFuncKind func);
@@ -648,6 +655,10 @@ public:
return getPersistentSummary(Summ);
}
+ const RetainSummary *getDoNothingSummary() {
+ return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
+ }
+
const RetainSummary *getDefaultSummary() {
return getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing, MayEscape);
@@ -739,41 +750,32 @@ public:
InitializeMethodSummaries();
}
- const RetainSummary *getSummary(const FunctionDecl *FD);
+ const RetainSummary *getSummary(const CallEvent &Call,
+ ProgramStateRef State = 0);
- const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName,
- const ObjCInterfaceDecl *ID,
+ const RetainSummary *getFunctionSummary(const FunctionDecl *FD);
+
+ const RetainSummary *getMethodSummary(Selector S, const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy,
ObjCMethodSummariesTy &CachedSummaries);
- const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
- ProgramStateRef state,
- const LocationContext *LC);
-
- const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
- const ObjCInterfaceDecl *ID) {
- return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(),
- msg.getType(Ctx), ObjCMethodSummaries);
- }
+ const RetainSummary *getInstanceMethodSummary(const ObjCMethodCall &M,
+ ProgramStateRef State);
- const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
- const ObjCInterfaceDecl *Class = 0;
- if (!msg.isInstanceMessage())
- Class = msg.getReceiverInterface();
+ const RetainSummary *getClassMethodSummary(const ObjCMethodCall &M) {
+ assert(!M.isInstanceMessage());
+ const ObjCInterfaceDecl *Class = M.getReceiverInterface();
- return getMethodSummary(msg.getSelector(), Class->getIdentifier(),
- Class, msg.getMethodDecl(), msg.getType(Ctx),
- ObjCClassMethodSummaries);
+ return getMethodSummary(M.getSelector(), Class, M.getDecl(),
+ M.getResultType(), ObjCClassMethodSummaries);
}
/// getMethodSummary - This version of getMethodSummary is used to query
/// the summary for the current method being analyzed.
const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
- // FIXME: Eventually this should be unneeded.
const ObjCInterfaceDecl *ID = MD->getClassInterface();
Selector S = MD->getSelector();
- IdentifierInfo *ClsName = ID->getIdentifier();
QualType ResultTy = MD->getResultType();
ObjCMethodSummariesTy *CachedSummaries;
@@ -782,11 +784,11 @@ public:
else
CachedSummaries = &ObjCClassMethodSummaries;
- return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries);
+ return getMethodSummary(S, ID, MD, ResultTy, *CachedSummaries);
}
const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD,
- Selector S, QualType RetTy);
+ Selector S, QualType RetTy);
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const ObjCMethodDecl *MD);
@@ -794,11 +796,18 @@ public:
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD);
+ void updateSummaryForCall(const RetainSummary *&Summ,
+ const CallEvent &Call);
+
bool isGCEnabled() const { return GCEnabled; }
bool isARCEnabled() const { return ARCEnabled; }
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
+
+ RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
+
+ friend class RetainSummaryTemplate;
};
// Used to avoid allocating long-term (BPAlloc'd) memory for default retain
@@ -811,10 +820,8 @@ class RetainSummaryTemplate {
RetainSummary ScratchSummary;
bool Accessed;
public:
- RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base,
- RetainSummaryManager &mgr)
- : Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base),
- Accessed(false) {}
+ RetainSummaryTemplate(const RetainSummary *&real, RetainSummaryManager &mgr)
+ : Manager(mgr), RealSummary(real), ScratchSummary(*real), Accessed(false) {}
~RetainSummaryTemplate() {
if (Accessed)
@@ -886,7 +893,101 @@ static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
return FName.find("MakeCollectable") != StringRef::npos;
}
-const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
+static ArgEffect getStopTrackingEquivalent(ArgEffect E) {
+ switch (E) {
+ case DoNothing:
+ case Autorelease:
+ case DecRefBridgedTransfered:
+ case IncRef:
+ case IncRefMsg:
+ case MakeCollectable:
+ case MayEscape:
+ case NewAutoreleasePool:
+ case StopTracking:
+ return StopTracking;
+ case DecRef:
+ case DecRefAndStopTracking:
+ return DecRefAndStopTracking;
+ case DecRefMsg:
+ case DecRefMsgAndStopTracking:
+ return DecRefMsgAndStopTracking;
+ case Dealloc:
+ return Dealloc;
+ }
+
+ llvm_unreachable("Unknown ArgEffect kind");
+}
+
+void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S,
+ const CallEvent &Call) {
+ if (Call.hasNonZeroCallbackArg()) {
+ ArgEffect RecEffect = getStopTrackingEquivalent(S->getReceiverEffect());
+ ArgEffect DefEffect = getStopTrackingEquivalent(S->getDefaultArgEffect());
+
+ ArgEffects CustomArgEffects = S->getArgEffects();
+ for (ArgEffects::iterator I = CustomArgEffects.begin(),
+ E = CustomArgEffects.end();
+ I != E; ++I) {
+ ArgEffect Translated = getStopTrackingEquivalent(I->second);
+ if (Translated != DefEffect)
+ ScratchArgs = AF.add(ScratchArgs, I->first, Translated);
+ }
+
+ RetEffect RE = RetEffect::MakeNoRet();
+
+ // Special cases where the callback argument CANNOT free the return value.
+ // This can generally only happen if we know that the callback will only be
+ // called when the return value is already being deallocated.
+ if (const FunctionCall *FC = dyn_cast<FunctionCall>(&Call)) {
+ IdentifierInfo *Name = FC->getDecl()->getIdentifier();
+
+ // This callback frees the associated buffer.
+ if (Name->isStr("CGBitmapContextCreateWithData"))
+ RE = S->getRetEffect();
+ }
+
+ S = getPersistentSummary(RE, RecEffect, DefEffect);
+ }
+}
+
+const RetainSummary *
+RetainSummaryManager::getSummary(const CallEvent &Call,
+ ProgramStateRef State) {
+ const RetainSummary *Summ;
+ switch (Call.getKind()) {
+ case CE_Function:
+ Summ = getFunctionSummary(cast<FunctionCall>(Call).getDecl());
+ break;
+ case CE_CXXMember:
+ case CE_CXXMemberOperator:
+ case CE_Block:
+ case CE_CXXConstructor:
+ case CE_CXXDestructor:
+ case CE_CXXAllocator:
+ // FIXME: These calls are currently unsupported.
+ return getPersistentStopSummary();
+ case CE_ObjCMessage: {
+ const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
+ if (Msg.isInstanceMessage())
+ Summ = getInstanceMethodSummary(Msg, State);
+ else
+ Summ = getClassMethodSummary(Msg);
+ break;
+ }
+ }
+
+ updateSummaryForCall(Summ, Call);
+
+ assert(Summ && "Unknown call type?");
+ return Summ;
+}
+
+const RetainSummary *
+RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
+ // If we don't know what function we're calling, use our default summary.
+ if (!FD)
+ return getDefaultSummary();
+
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
if (I != FuncSummaries.end())
@@ -894,6 +995,7 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// No summary? Generate one.
const RetainSummary *S = 0;
+ bool AllowAnnotations = true;
do {
// We generate "stop" summaries for implicitly defined functions.
@@ -901,13 +1003,6 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
S = getPersistentStopSummary();
break;
}
- // For C++ methods, generate an implicit "stop" summary as well. We
- // can relax this once we have a clear policy for C++ methods and
- // ownership attributes.
- if (isa<CXXMethodDecl>(FD)) {
- S = getPersistentStopSummary();
- break;
- }
// [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the
// function's type.
@@ -929,18 +1024,22 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// filters.
assert(ScratchArgs.isEmpty());
- if (FName == "pthread_create") {
- // Part of: <rdar://problem/7299394>. This will be addressed
- // better with IPA.
+ if (FName == "pthread_create" || FName == "pthread_setspecific") {
+ // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>.
+ // This will be addressed better with IPA.
S = getPersistentStopSummary();
} else if (FName == "NSMakeCollectable") {
// Handle: id NSMakeCollectable(CFTypeRef)
S = (RetTy->isObjCIdType())
? getUnarySummary(FT, cfmakecollectable)
: getPersistentStopSummary();
+ // The headers on OS X 10.8 use cf_consumed/ns_returns_retained,
+ // but we can fully model NSMakeCollectable ourselves.
+ AllowAnnotations = false;
} else if (FName == "IOBSDNameMatching" ||
FName == "IOServiceMatching" ||
FName == "IOServiceNameMatching" ||
+ FName == "IORegistryEntrySearchCFProperty" ||
FName == "IORegistryEntryIDMatching" ||
FName == "IOOpenFirmwarePathMatching") {
// Part of <rdar://problem/6961230>. (IOKit)
@@ -993,6 +1092,8 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// libdispatch finalizers.
ScratchArgs = AF.add(ScratchArgs, 1, StopTracking);
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
+ } else if (FName.startswith("NSLog")) {
+ S = getDoNothingSummary();
} else if (FName.startswith("NS") &&
(FName.find("Insert") != StringRef::npos)) {
// Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
@@ -1090,8 +1191,13 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
}
while (0);
+ // If we got all the way here without any luck, use a default summary.
+ if (!S)
+ S = getDefaultSummary();
+
// Annotations override defaults.
- updateSummaryFromAnnotations(S, FD);
+ if (AllowAnnotations)
+ updateSummaryFromAnnotations(S, FD);
FuncSummaries[FD] = S;
return S;
@@ -1152,7 +1258,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
if (!FD)
return;
- RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this);
+ assert(Summ && "Must have a summary to add annotations to.");
+ RetainSummaryTemplate Template(Summ, *this);
// Effects on the parameters.
unsigned parm_idx = 0;
@@ -1200,7 +1307,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
if (!MD)
return;
- RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this);
+ assert(Summ && "Must have a valid summary to add annotations to");
+ RetainSummaryTemplate Template(Summ, *this);
bool isTrackedLoc = false;
// Effects on the receiver.
@@ -1341,10 +1449,15 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
// because the reference count is quite possibly handled by a delegate
// method.
if (S.isKeywordSelector()) {
- const std::string &str = S.getAsString();
- assert(!str.empty());
- if (StrInStrNoCase(str, "delegate:") != StringRef::npos)
- ReceiverEff = StopTracking;
+ for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) {
+ StringRef Slot = S.getNameForSlot(i);
+ if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) {
+ if (ResultEff == ObjCInitRetE)
+ ResultEff = RetEffect::MakeNoRet();
+ else
+ ReceiverEff = StopTracking;
+ }
+ }
}
if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing &&
@@ -1355,56 +1468,46 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
}
const RetainSummary *
-RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
- ProgramStateRef state,
- const LocationContext *LC) {
-
- // We need the type-information of the tracked receiver object
- // Retrieve it from the state.
- const Expr *Receiver = msg.getInstanceReceiver();
- const ObjCInterfaceDecl *ID = 0;
-
- // FIXME: Is this really working as expected? There are cases where
- // we just use the 'ID' from the message expression.
- SVal receiverV;
-
- if (Receiver) {
- receiverV = state->getSValAsScalarOrLoc(Receiver, LC);
-
- // FIXME: Eventually replace the use of state->get<RefBindings> with
- // a generic API for reasoning about the Objective-C types of symbolic
- // objects.
- if (SymbolRef Sym = receiverV.getAsLocSymbol())
- if (const RefVal *T = state->get<RefBindings>(Sym))
- if (const ObjCObjectPointerType* PT =
+RetainSummaryManager::getInstanceMethodSummary(const ObjCMethodCall &Msg,
+ ProgramStateRef State) {
+ const ObjCInterfaceDecl *ReceiverClass = 0;
+
+ // We do better tracking of the type of the object than the core ExprEngine.
+ // See if we have its type in our private state.
+ // FIXME: Eventually replace the use of state->get<RefBindings> with
+ // a generic API for reasoning about the Objective-C types of symbolic
+ // objects.
+ SVal ReceiverV = Msg.getReceiverSVal();
+ if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
+ if (const RefVal *T = getRefBinding(State, Sym))
+ if (const ObjCObjectPointerType *PT =
T->getType()->getAs<ObjCObjectPointerType>())
- ID = PT->getInterfaceDecl();
+ ReceiverClass = PT->getInterfaceDecl();
- // FIXME: this is a hack. This may or may not be the actual method
- // that is called.
- if (!ID) {
- if (const ObjCObjectPointerType *PT =
- Receiver->getType()->getAs<ObjCObjectPointerType>())
- ID = PT->getInterfaceDecl();
- }
- } else {
- // FIXME: Hack for 'super'.
- ID = msg.getReceiverInterface();
- }
+ // If we don't know what kind of object this is, fall back to its static type.
+ if (!ReceiverClass)
+ ReceiverClass = Msg.getReceiverInterface();
// FIXME: The receiver could be a reference to a class, meaning that
// we should use the class method.
- return getInstanceMethodSummary(msg, ID);
+ // id x = [NSObject class];
+ // [x performSelector:... withObject:... afterDelay:...];
+ Selector S = Msg.getSelector();
+ const ObjCMethodDecl *Method = Msg.getDecl();
+ if (!Method && ReceiverClass)
+ Method = ReceiverClass->getInstanceMethod(S);
+
+ return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(),
+ ObjCMethodSummaries);
}
const RetainSummary *
-RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName,
- const ObjCInterfaceDecl *ID,
+RetainSummaryManager::getMethodSummary(Selector S, const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD, QualType RetTy,
ObjCMethodSummariesTy &CachedSummaries) {
// Look up a summary in our summary cache.
- const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S);
+ const RetainSummary *Summ = CachedSummaries.find(ID, S);
if (!Summ) {
Summ = getStandardMethodSummary(MD, S, RetTy);
@@ -1413,7 +1516,7 @@ RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName,
updateSummaryFromAnnotations(Summ, MD);
// Memoize the summary.
- CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
+ CachedSummaries[ObjCSummaryKey(ID, S)] = Summ;
}
return Summ;
@@ -1430,29 +1533,6 @@ void RetainSummaryManager::InitializeClassMethodSummaries() {
addClassMethSummary("NSAutoreleasePool", "addObject",
getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing, Autorelease));
-
- // Create the summaries for [NSObject performSelector...]. We treat
- // these as 'stop tracking' for the arguments because they are often
- // used for delegates that can release the object. When we have better
- // inter-procedural analysis we can potentially do something better. This
- // workaround is to remove false positives.
- const RetainSummary *Summ =
- getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
- IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
- addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
- "afterDelay", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
- "afterDelay", "inModes", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
- "withObject", "waitUntilDone", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
- "withObject", "waitUntilDone", "modes", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
- "withObject", "waitUntilDone", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
- "withObject", "waitUntilDone", "modes", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground",
- "withObject", NULL);
}
void RetainSummaryManager::InitializeMethodSummaries() {
@@ -1558,6 +1638,10 @@ void RetainSummaryManager::InitializeMethodSummaries() {
//===----------------------------------------------------------------------===//
// AutoreleaseBindings - State used to track objects in autorelease pools.
//===----------------------------------------------------------------------===//
+#define AUTORELEASE_POOL_MODELING (0)
+// We do not currently have complete modeling of autorelease pools.
+
+#if AUTORELEASE_POOL_MODELING
typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts;
typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents;
@@ -1605,6 +1689,7 @@ SendAutorelease(ProgramStateRef state,
return state->set<AutoreleasePoolContents>(pool, newCnts);
}
+#endif
//===----------------------------------------------------------------------===//
// Error reporting.
@@ -1690,32 +1775,18 @@ namespace {
};
class Leak : public CFRefBug {
- const bool isReturn;
- protected:
- Leak(StringRef name, bool isRet)
- : CFRefBug(name), isReturn(isRet) {
+ public:
+ Leak(StringRef name)
+ : CFRefBug(name) {
// Leaks should not be reported if they are post-dominated by a sink.
setSuppressOnSink(true);
}
- public:
const char *getDescription() const { return ""; }
bool isLeak() const { return true; }
};
- class LeakAtReturn : public Leak {
- public:
- LeakAtReturn(StringRef name)
- : Leak(name, true) {}
- };
-
- class LeakWithinFunction : public Leak {
- public:
- LeakWithinFunction(StringRef name)
- : Leak(name, false) {}
- };
-
//===---------===//
// Bug Reports. //
//===---------===//
@@ -1854,25 +1925,21 @@ static inline bool contains(const SmallVectorImpl<ArgEffect>& V,
return false;
}
-static bool isPropertyAccess(const Stmt *S, ParentMap &PM) {
- unsigned maxDepth = 4;
- while (S && maxDepth) {
- if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) {
- if (!isa<ObjCMessageExpr>(PO->getSyntacticForm()))
- return true;
- return false;
- }
- S = PM.getParent(S);
- --maxDepth;
- }
- return false;
+static bool isNumericLiteralExpression(const Expr *E) {
+ // FIXME: This set of cases was copied from SemaExprObjC.
+ return isa<IntegerLiteral>(E) ||
+ isa<CharacterLiteral>(E) ||
+ isa<FloatingLiteral>(E) ||
+ isa<ObjCBoolLiteralExpr>(E) ||
+ isa<CXXBoolLiteralExpr>(E);
}
PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) {
-
+ // FIXME: We will eventually need to handle non-statement-based events
+ // (__attribute__((cleanup))).
if (!isa<StmtPoint>(N->getLocation()))
return NULL;
@@ -1881,11 +1948,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
ProgramStateRef CurrSt = N->getState();
const LocationContext *LCtx = N->getLocationContext();
- const RefVal* CurrT = CurrSt->get<RefBindings>(Sym);
+ const RefVal* CurrT = getRefBinding(CurrSt, Sym);
if (!CurrT) return NULL;
const RefVal &CurrV = *CurrT;
- const RefVal *PrevT = PrevSt->get<RefBindings>(Sym);
+ const RefVal *PrevT = getRefBinding(PrevSt, Sym);
// Create a string buffer to constain all the useful things we want
// to tell the user.
@@ -1903,6 +1970,24 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
else if (isa<ObjCDictionaryLiteral>(S)) {
os << "NSDictionary literal is an object with a +0 retain count";
}
+ else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
+ if (isNumericLiteralExpression(BL->getSubExpr()))
+ os << "NSNumber literal is an object with a +0 retain count";
+ else {
+ const ObjCInterfaceDecl *BoxClass = 0;
+ if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
+ BoxClass = Method->getClassInterface();
+
+ // We should always be able to find the boxing class interface,
+ // but consider this future-proofing.
+ if (BoxClass)
+ os << *BoxClass << " b";
+ else
+ os << "B";
+
+ os << "oxed expression produces an object with a +0 retain count";
+ }
+ }
else {
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
// Get the name of the callee (if it is available).
@@ -1913,10 +1998,22 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
os << "function call";
}
else {
- assert(isa<ObjCMessageExpr>(S));
- // The message expression may have between written directly or as
- // a property access. Lazily determine which case we are looking at.
- os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method");
+ assert(isa<ObjCMessageExpr>(S));
+ CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Call
+ = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+
+ switch (Call->getMessageKind()) {
+ case OCM_Message:
+ os << "Method";
+ break;
+ case OCM_PropertyAccess:
+ os << "Property";
+ break;
+ case OCM_Subscript:
+ os << "Subscript";
+ break;
+ }
}
if (CurrV.getObjKind() == RetEffect::CF) {
@@ -2143,9 +2240,8 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
while (N) {
ProgramStateRef St = N->getState();
- RefBindings B = St->get<RefBindings>();
- if (!B.lookup(Sym))
+ if (!getRefBinding(St, Sym))
break;
StoreManager::FindUniqueBinding FB(Sym);
@@ -2216,7 +2312,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
os << "allocated object";
// Get the retain count.
- const RefVal* RV = EndN->getState()->get<RefBindings>(Sym);
+ const RefVal* RV = getRefBinding(EndN->getState(), Sym);
if (RV->getKind() == RefVal::ErrorLeakReturned) {
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
@@ -2276,8 +2372,15 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
// Get the SourceLocation for the allocation site.
+ // FIXME: This will crash the analyzer if an allocation comes from an
+ // implicit call. (Currently there are no such allocations in Cocoa, though.)
+ const Stmt *AllocStmt;
ProgramPoint P = AllocNode->getLocation();
- const Stmt *AllocStmt = cast<PostStmt>(P).getStmt();
+ if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P))
+ AllocStmt = Exit->getCalleeContext()->getCallSite();
+ else
+ AllocStmt = cast<PostStmt>(P).getStmt();
+ assert(AllocStmt && "All allocations must come from explicit calls");
Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
n->getLocationContext());
// Fill in the description of the bug.
@@ -2307,11 +2410,10 @@ class RetainCountChecker
check::EndPath,
check::PostStmt<BlockExpr>,
check::PostStmt<CastExpr>,
- check::PostStmt<CallExpr>,
- check::PostStmt<CXXConstructExpr>,
check::PostStmt<ObjCArrayLiteral>,
check::PostStmt<ObjCDictionaryLiteral>,
- check::PostObjCMessage,
+ check::PostStmt<ObjCBoxedExpr>,
+ check::PostCall,
check::PreStmt<ReturnStmt>,
check::RegionChanges,
eval::Assume,
@@ -2330,7 +2432,9 @@ class RetainCountChecker
mutable OwningPtr<RetainSummaryManager> Summaries;
mutable OwningPtr<RetainSummaryManager> SummariesGC;
+#if AUTORELEASE_POOL_MODELING
mutable ARCounts::Factory ARCountFactory;
+#endif
mutable SummaryLogTy SummaryLog;
mutable bool ShouldResetSummaryLog;
@@ -2382,20 +2486,17 @@ public:
bool GCEnabled) const {
if (GCEnabled) {
if (!leakWithinFunctionGC)
- leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when "
- "using garbage "
- "collection"));
+ leakWithinFunctionGC.reset(new Leak("Leak of object when using "
+ "garbage collection"));
return leakWithinFunctionGC.get();
} else {
if (!leakWithinFunction) {
if (LOpts.getGC() == LangOptions::HybridGC) {
- leakWithinFunction.reset(new LeakWithinFunction("Leak of object when "
- "not using garbage "
- "collection (GC) in "
- "dual GC/non-GC "
- "code"));
+ leakWithinFunction.reset(new Leak("Leak of object when not using "
+ "garbage collection (GC) in "
+ "dual GC/non-GC code"));
} else {
- leakWithinFunction.reset(new LeakWithinFunction("Leak"));
+ leakWithinFunction.reset(new Leak("Leak"));
}
}
return leakWithinFunction.get();
@@ -2405,17 +2506,17 @@ public:
CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const {
if (GCEnabled) {
if (!leakAtReturnGC)
- leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when "
- "using garbage collection"));
+ leakAtReturnGC.reset(new Leak("Leak of returned object when using "
+ "garbage collection"));
return leakAtReturnGC.get();
} else {
if (!leakAtReturn) {
if (LOpts.getGC() == LangOptions::HybridGC) {
- leakAtReturn.reset(new LeakAtReturn("Leak of returned object when "
- "not using garbage collection "
- "(GC) in dual GC/non-GC code"));
+ leakAtReturn.reset(new Leak("Leak of returned object when not using "
+ "garbage collection (GC) in dual "
+ "GC/non-GC code"));
} else {
- leakAtReturn.reset(new LeakAtReturn("Leak of returned object"));
+ leakAtReturn.reset(new Leak("Leak of returned object"));
}
}
return leakAtReturn.get();
@@ -2453,13 +2554,13 @@ public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
+ void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
+ void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
CheckerContext &C) const;
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
@@ -2472,7 +2573,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
return true;
@@ -2499,8 +2600,8 @@ public:
const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
ProgramStateRef handleSymbolDeath(ProgramStateRef state,
- SymbolRef sid, RefVal V,
- SmallVectorImpl<SymbolRef> &Leaked) const;
+ SymbolRef sid, RefVal V,
+ SmallVectorImpl<SymbolRef> &Leaked) const;
std::pair<ExplodedNode *, ProgramStateRef >
handleAutoreleaseCounts(ProgramStateRef state,
@@ -2597,7 +2698,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol();
if (!Sym)
return;
- const RefVal* T = state->get<RefBindings>(Sym);
+ const RefVal* T = getRefBinding(state, Sym);
if (!T)
return;
@@ -2613,54 +2714,6 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
C.addTransition(state);
}
-void RetainCountChecker::checkPostStmt(const CallExpr *CE,
- CheckerContext &C) const {
- if (C.wasInlined)
- return;
-
- // Get the callee.
- ProgramStateRef state = C.getState();
- const Expr *Callee = CE->getCallee();
- SVal L = state->getSVal(Callee, C.getLocationContext());
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
- const RetainSummary *Summ = 0;
-
- // FIXME: Better support for blocks. For now we stop tracking anything
- // that is passed to blocks.
- // FIXME: Need to handle variables that are "captured" by the block.
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- Summ = Summaries.getPersistentStopSummary();
- } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) {
- Summ = Summaries.getSummary(FD);
- } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- if (const CXXMethodDecl *MD = me->getMethodDecl())
- Summ = Summaries.getSummary(MD);
- }
-
- if (!Summ)
- Summ = Summaries.getDefaultSummary();
-
- checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
-}
-
-void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE,
- CheckerContext &C) const {
- const CXXConstructorDecl *Ctor = CE->getConstructor();
- if (!Ctor)
- return;
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
- const RetainSummary *Summ = Summaries.getSummary(Ctor);
-
- // If we didn't get a summary, this constructor doesn't affect retain counts.
- if (!Summ)
- return;
-
- ProgramStateRef state = C.getState();
- checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
-}
-
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
const Expr *Ex) const {
ProgramStateRef state = C.getState();
@@ -2670,7 +2723,7 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C,
const Stmt *child = *it;
SVal V = state->getSVal(child, pred->getLocationContext());
if (SymbolRef sym = V.getAsSymbol())
- if (const RefVal* T = state->get<RefBindings>(sym)) {
+ if (const RefVal* T = getRefBinding(state, sym)) {
RefVal::Kind hasErr = (RefVal::Kind) 0;
state = updateSymbol(state, sym, *T, MayEscape, hasErr, C);
if (hasErr) {
@@ -2685,8 +2738,8 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C,
if (SymbolRef sym =
state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
QualType ResultTy = Ex->getType();
- state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC,
- ResultTy));
+ state = setRefBinding(state, sym,
+ RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
}
C.addTransition(state);
@@ -2704,30 +2757,34 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
processObjCLiterals(C, DL);
}
-void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg,
- CheckerContext &C) const {
- ProgramStateRef state = C.getState();
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
+void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
+ CheckerContext &C) const {
+ const ExplodedNode *Pred = C.getPredecessor();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
- const RetainSummary *Summ;
- if (Msg.isInstanceMessage()) {
- const LocationContext *LC = C.getLocationContext();
- Summ = Summaries.getInstanceMethodSummary(Msg, state, LC);
- } else {
- Summ = Summaries.getClassMethodSummary(Msg);
+ if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) {
+ QualType ResultTy = Ex->getType();
+ State = setRefBinding(State, Sym,
+ RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
}
- // If we didn't get a summary, this message doesn't affect retain counts.
- if (!Summ)
+ C.addTransition(State);
+}
+
+void RetainCountChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (C.wasInlined)
return;
- checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C);
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const RetainSummary *Summ = Summaries.getSummary(Call, C.getState());
+ checkSummary(*Summ, Call, C);
}
/// GetReturnType - Used to get the return type of a message expression or
/// function call with the intention of affixing that type to a tracked symbol.
-/// While the the return type can be queried directly from RetEx, when
+/// While the return type can be queried directly from RetEx, when
/// invoking class methods we augment to the return type to be that of
/// a pointer to the class (as opposed it just being id).
// FIXME: We may be able to do this with related result types instead.
@@ -2754,7 +2811,7 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
}
void RetainCountChecker::checkSummary(const RetainSummary &Summ,
- const CallOrObjCMessage &CallOrMsg,
+ const CallEvent &CallOrMsg,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
@@ -2767,7 +2824,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
SVal V = CallOrMsg.getArgSVal(idx);
if (SymbolRef Sym = V.getAsLocSymbol()) {
- if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C);
if (hasErr) {
ErrorRange = CallOrMsg.getArgSourceRange(idx);
@@ -2780,17 +2837,18 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
// Evaluate the effect on the message receiver.
bool ReceiverIsTracked = false;
- if (!hasErr && CallOrMsg.isObjCMessage()) {
- const LocationContext *LC = C.getLocationContext();
- SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC);
- if (SymbolRef Sym = Receiver.getAsLocSymbol()) {
- if (const RefVal *T = state->get<RefBindings>(Sym)) {
- ReceiverIsTracked = true;
- state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
- hasErr, C);
- if (hasErr) {
- ErrorRange = CallOrMsg.getReceiverSourceRange();
- ErrorSym = Sym;
+ if (!hasErr) {
+ const ObjCMethodCall *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg);
+ if (MsgInvocation) {
+ if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
+ ReceiverIsTracked = true;
+ state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
+ hasErr, C);
+ if (hasErr) {
+ ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
+ ErrorSym = Sym;
+ }
}
}
}
@@ -2827,11 +2885,11 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
if (!Sym)
break;
- // Use the result type from callOrMsg as it automatically adjusts
+ // Use the result type from the CallEvent as it automatically adjusts
// for methods/functions that return references.
- QualType ResultTy = CallOrMsg.getResultType(C.getASTContext());
- state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
- ResultTy));
+ QualType ResultTy = CallOrMsg.getResultType();
+ state = setRefBinding(state, Sym, RefVal::makeOwned(RE.getObjKind(),
+ ResultTy));
// FIXME: Add a flag to the checker where allocations are assumed to
// *not* fail. (The code below is out-of-date, though.)
@@ -2856,8 +2914,8 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
// Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *.
QualType ResultTy = GetReturnType(Ex, C.getASTContext());
- state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(),
- ResultTy));
+ state = setRefBinding(state, Sym, RefVal::makeNotOwned(RE.getObjKind(),
+ ResultTy));
break;
}
}
@@ -2895,25 +2953,37 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
switch (E) {
- default: break;
- case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break;
- case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break;
- case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break;
- case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing :
- NewAutoreleasePool; break;
+ default:
+ break;
+ case IncRefMsg:
+ E = IgnoreRetainMsg ? DoNothing : IncRef;
+ break;
+ case DecRefMsg:
+ E = IgnoreRetainMsg ? DoNothing : DecRef;
+ break;
+ case DecRefMsgAndStopTracking:
+ E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTracking;
+ break;
+ case MakeCollectable:
+ E = C.isObjCGCEnabled() ? DecRef : DoNothing;
+ break;
+ case NewAutoreleasePool:
+ E = C.isObjCGCEnabled() ? DoNothing : NewAutoreleasePool;
+ break;
}
// Handle all use-after-releases.
if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) {
V = V ^ RefVal::ErrorUseAfterRelease;
hasErr = V.getKind();
- return state->set<RefBindings>(sym, V);
+ return setRefBinding(state, sym, V);
}
switch (E) {
case DecRefMsg:
case IncRefMsg:
case MakeCollectable:
+ case DecRefMsgAndStopTracking:
llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted");
case Dealloc:
@@ -2931,7 +3001,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
// The object immediately transitions to the released state.
V = V ^ RefVal::Released;
V.clearCounts();
- return state->set<RefBindings>(sym, V);
+ return setRefBinding(state, sym, V);
case RefVal::NotOwned:
V = V ^ RefVal::ErrorDeallocNotOwned;
hasErr = V.getKind();
@@ -2941,7 +3011,10 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
case NewAutoreleasePool:
assert(!C.isObjCGCEnabled());
- return state->add<AutoreleaseStack>(sym);
+#if AUTORELEASE_POOL_MODELING
+ state = state->add<AutoreleaseStack>(sym);
+#endif
+ return state;
case MayEscape:
if (V.getKind() == RefVal::Owned) {
@@ -2959,12 +3032,16 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
return state;
// Update the autorelease counts.
+ // TODO: AutoreleasePoolContents are not currently used. We will need to
+ // call SendAutorelease after it's wired up.
+#if AUTORELEASE_POOL_MODELING
state = SendAutorelease(state, ARCountFactory, sym);
+#endif
V = V.autorelease();
break;
case StopTracking:
- return state->remove<RefBindings>(sym);
+ return removeRefBinding(state, sym);
case IncRef:
switch (V.getKind()) {
@@ -2982,11 +3059,9 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
}
break;
- case SelfOwn:
- V = V ^ RefVal::NotOwned;
- // Fall-through.
case DecRef:
case DecRefBridgedTransfered:
+ case DecRefAndStopTracking:
switch (V.getKind()) {
default:
// case 'RefVal::Released' handled above.
@@ -2997,13 +3072,18 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
if (V.getCount() == 1)
V = V ^ (E == DecRefBridgedTransfered ?
RefVal::NotOwned : RefVal::Released);
+ else if (E == DecRefAndStopTracking)
+ return removeRefBinding(state, sym);
+
V = V - 1;
break;
case RefVal::NotOwned:
- if (V.getCount() > 0)
+ if (V.getCount() > 0) {
+ if (E == DecRefAndStopTracking)
+ return removeRefBinding(state, sym);
V = V - 1;
- else {
+ } else {
V = V ^ RefVal::ErrorReleaseNotOwned;
hasErr = V.getKind();
}
@@ -3018,7 +3098,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
}
break;
}
- return state->set<RefBindings>(sym, V);
+ return setRefBinding(state, sym, V);
}
void RetainCountChecker::processNonLeakError(ProgramStateRef St,
@@ -3117,7 +3197,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// If the receiver is unknown, conjure a return value.
SValBuilder &SVB = C.getSValBuilder();
unsigned Count = C.getCurrentBlockCount();
- SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
+ RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
}
state = state->BindExpr(CE, LCtx, RetVal, false);
@@ -3126,9 +3206,9 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
if (const MemRegion *ArgRegion = RetVal.getAsRegion()) {
// Save the refcount status of the argument.
SymbolRef Sym = RetVal.getAsLocSymbol();
- RefBindings::data_type *Binding = 0;
+ const RefVal *Binding = 0;
if (Sym)
- Binding = state->get<RefBindings>(Sym);
+ Binding = getRefBinding(state, Sym);
// Invalidate the argument region.
unsigned Count = C.getCurrentBlockCount();
@@ -3136,7 +3216,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// Restore the refcount status of the argument.
if (Binding)
- state = state->set<RefBindings>(Sym, *Binding);
+ state = setRefBinding(state, Sym, *Binding);
}
C.addTransition(state);
@@ -3175,7 +3255,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
return;
// Get the reference count binding (if any).
- const RefVal *T = state->get<RefBindings>(Sym);
+ const RefVal *T = getRefBinding(state, Sym);
if (!T)
return;
@@ -3208,7 +3288,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
}
// Update the binding.
- state = state->set<RefBindings>(Sym, X);
+ state = setRefBinding(state, Sym, X);
ExplodedNode *Pred = C.addTransition(state);
// At this point we have updated the state properly.
@@ -3230,29 +3310,27 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
return;
// Get the updated binding.
- T = state->get<RefBindings>(Sym);
+ T = getRefBinding(state, Sym);
assert(T);
X = *T;
// Consult the summary of the enclosing method.
RetainSummaryManager &Summaries = getSummaryManager(C);
const Decl *CD = &Pred->getCodeDecl();
+ RetEffect RE = RetEffect::MakeNoRet();
+ // FIXME: What is the convention for blocks? Is there one?
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
- // Unlike regular functions, /all/ ObjC methods are assumed to always
- // follow Cocoa retain-count conventions, not just those with special
- // names or attributes.
const RetainSummary *Summ = Summaries.getMethodSummary(MD);
- RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
- checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
+ RE = Summ->getRetEffect();
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
+ if (!isa<CXXMethodDecl>(FD)) {
+ const RetainSummary *Summ = Summaries.getFunctionSummary(FD);
+ RE = Summ->getRetEffect();
+ }
}
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
- if (!isa<CXXMethodDecl>(FD))
- if (const RetainSummary *Summ = Summaries.getSummary(FD))
- checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X,
- Sym, state);
- }
+ checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
}
void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
@@ -3283,7 +3361,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (hasError) {
// Generate an error node.
- state = state->set<RefBindings>(Sym, X);
+ state = setRefBinding(state, Sym, X);
static SimpleProgramPointTag
ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak");
@@ -3303,7 +3381,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (RE.isOwned()) {
// Trying to return a not owned object to a caller expecting an
// owned object.
- state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned);
+ state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
static SimpleProgramPointTag
ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned");
@@ -3346,7 +3424,11 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
// To test (3), generate a new state with the binding added. If it is
// the same state, then it escapes (since the store cannot represent
// the binding).
- escapes = (state == (state->bindLoc(*regionLoc, val)));
+ // Do this only if we know that the store is not supposed to generate the
+ // same state.
+ SVal StoredVal = state->getSVal(regionLoc->getRegion());
+ if (StoredVal != val)
+ escapes = (state == (state->bindLoc(*regionLoc, val)));
}
if (!escapes) {
// Case 4: We do not currently model what happens when a symbol is
@@ -3406,7 +3488,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
if (!invalidated)
return state;
@@ -3423,7 +3505,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state,
if (WhitelistedSymbols.count(sym))
continue;
// Remove any existing reference-count binding.
- state = state->remove<RefBindings>(sym);
+ state = removeRefBinding(state, sym);
}
return state;
}
@@ -3463,7 +3545,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
V.setCount(Cnt - ACnt);
V.setAutoreleaseCount(0);
}
- state = state->set<RefBindings>(Sym, V);
+ state = setRefBinding(state, Sym, V);
ExplodedNode *N = Bd.MakeNode(state, Pred);
if (N == 0)
state = 0;
@@ -3473,7 +3555,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
// Woah! More autorelease counts then retain counts left.
// Emit hard error.
V = V ^ RefVal::ErrorOverAutorelease;
- state = state->set<RefBindings>(Sym, V);
+ state = setRefBinding(state, Sym, V);
if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) {
SmallString<128> sbuf;
@@ -3507,10 +3589,10 @@ RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
hasLeak = (V.getCount() > 0);
if (!hasLeak)
- return state->remove<RefBindings>(sid);
+ return removeRefBinding(state, sid);
Leaked.push_back(sid);
- return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
+ return setRefBinding(state, sid, V ^ RefVal::ErrorLeak);
}
ExplodedNode *
@@ -3559,7 +3641,7 @@ void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const {
// If the current LocationContext has a parent, don't check for leaks.
// We will do that later.
- // FIXME: we should instead check for imblances of the retain/releases,
+ // FIXME: we should instead check for imbalances of the retain/releases,
// and suggest annotations.
if (Ctx.getLocationContext()->getParent())
return;
@@ -3641,6 +3723,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// Debug printing of refcount bindings and autorelease pools.
//===----------------------------------------------------------------------===//
+#if AUTORELEASE_POOL_MODELING
static void PrintPool(raw_ostream &Out, SymbolRef Sym,
ProgramStateRef State) {
Out << ' ';
@@ -3654,7 +3737,6 @@ static void PrintPool(raw_ostream &Out, SymbolRef Sym,
if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym))
for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I)
Out << '(' << I.getKey() << ',' << I.getData() << ')';
-
Out << '}';
}
@@ -3664,6 +3746,7 @@ static bool UsesAutorelease(ProgramStateRef state) {
return !state->get<AutoreleaseStack>().isEmpty() ||
state->get<AutoreleasePoolContents>(SymbolRef());
}
+#endif
void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
@@ -3679,6 +3762,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
Out << NL;
}
+#if AUTORELEASE_POOL_MODELING
// Print the autorelease stack.
if (UsesAutorelease(State)) {
Out << Sep << NL << "AR pool stack:";
@@ -3690,6 +3774,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
Out << NL;
}
+#endif
}
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index 7b1f0b1..ca2a55d 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -53,9 +53,9 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
BugReport *report =
new BugReport(*BT, BT->getDescription(), N);
+ report->disablePathPruning();
report->addRange(RetE->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, RetE, report);
C.EmitReport(report);
}
diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 3745d4a..731dd66 100644
--- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -116,7 +116,7 @@ namespace ento {
bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
const FunctionDecl *FD = C.getCalleeDecl(CE);
- if (!FD)
+ if (!FD || FD->getKind() != Decl::Function)
return false;
ASTContext &Ctx = C.getASTContext();
diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
new file mode 100644
index 0000000..b97cd6c
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
@@ -0,0 +1,84 @@
+//== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These checkers print various aspects of the ExprEngine's traversal of the CFG
+// as it builds the ExplodedGraph.
+//
+//===----------------------------------------------------------------------===//
+#include "ClangSACheckers.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class TraversalDumper : public Checker< check::BranchCondition,
+ check::EndPath > {
+public:
+ void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
+ void checkEndPath(CheckerContext &C) const;
+};
+}
+
+void TraversalDumper::checkBranchCondition(const Stmt *Condition,
+ CheckerContext &C) const {
+ // Special-case Objective-C's for-in loop, which uses the entire loop as its
+ // condition. We just print the collection expression.
+ const Stmt *Parent = dyn_cast<ObjCForCollectionStmt>(Condition);
+ if (!Parent) {
+ const ParentMap &Parents = C.getLocationContext()->getParentMap();
+ Parent = Parents.getParent(Condition);
+ }
+
+ // It is mildly evil to print directly to llvm::outs() rather than emitting
+ // warnings, but this ensures things do not get filtered out by the rest of
+ // the static analyzer machinery.
+ SourceLocation Loc = Parent->getLocStart();
+ llvm::outs() << C.getSourceManager().getSpellingLineNumber(Loc) << " "
+ << Parent->getStmtClassName() << "\n";
+}
+
+void TraversalDumper::checkEndPath(CheckerContext &C) const {
+ llvm::outs() << "--END PATH--\n";
+}
+
+void ento::registerTraversalDumper(CheckerManager &mgr) {
+ mgr.registerChecker<TraversalDumper>();
+}
+
+//------------------------------------------------------------------------------
+
+namespace {
+class CallDumper : public Checker< check::PreCall > {
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+};
+}
+
+void CallDumper::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
+ unsigned Indentation = 0;
+ for (const LocationContext *LC = C.getLocationContext()->getParent();
+ LC != 0; LC = LC->getParent())
+ ++Indentation;
+
+ // It is mildly evil to print directly to llvm::outs() rather than emitting
+ // warnings, but this ensures things do not get filtered out by the rest of
+ // the static analyzer machinery.
+ llvm::outs().indent(Indentation);
+ Call.dump(llvm::outs());
+}
+
+void ento::registerCallDumper(CheckerManager &mgr) {
+ mgr.registerChecker<CallDumper>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index a30f6d5..70a33c7 100644
--- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -99,8 +99,9 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
// Emit the bug report.
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, R));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, R);
R->addRange(Ex->getSourceRange());
+ R->disablePathPruning();
Ctx.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index d57767e..675b38a 100644
--- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -94,6 +94,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
R->addRange(Ex->getSourceRange());
R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR));
+ R->disablePathPruning();
// need location of block
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index c3c9ed7..e220499 100644
--- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -76,12 +76,12 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
BugReport *report = new BugReport(*BT, OS.str(), N);
if (Ex) {
report->addRange(Ex->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report);
}
else
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, B, report);
+
+ report->disablePathPruning();
C.EmitReport(report);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index 0297c4e..6ae3c18 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -42,9 +42,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
// Generate a report for this bug.
BugReport *R = new BugReport(*BT, BT->getName(), N);
R->addRange(A->getIdx()->getSourceRange());
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
- A->getIdx(),
- R));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, A->getIdx(), R);
C.EmitReport(R);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 78f7fa6..14a884e 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -78,8 +78,9 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
BugReport *R = new BugReport(*BT, str, N);
if (ex) {
R->addRange(ex->getSourceRange());
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex, R));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, ex, R);
}
+ R->disablePathPruning();
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 60e665fe..d35455c 100644
--- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -224,8 +224,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);
report->addRange(arg->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, arg, report);
C.EmitReport(report);
return true;
@@ -256,7 +255,7 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
(void) ReportZeroByteAllocation(C, falseState, arg, fn);
return;
}
- // Assume the the value is non-zero going forward.
+ // Assume the value is non-zero going forward.
assert(trueState);
if (trueState != state)
C.addTransition(trueState);
@@ -292,7 +291,7 @@ void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
}
}
- // Assume the the value is non-zero going forward.
+ // Assume the value is non-zero going forward.
assert(trueState);
if (trueState != state)
C.addTransition(trueState);
@@ -325,7 +324,11 @@ void UnixAPIChecker::CheckVallocZero(CheckerContext &C,
void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
- StringRef FName = C.getCalleeName(CE);
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return;
+
+ StringRef FName = C.getCalleeName(FD);
if (FName.empty())
return;
diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index 38c9cc1..fab4adf 100644
--- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -69,8 +69,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind,
BugReport *report = new BugReport(*BT, os.str(), N);
report->addRange(SizeE->getSourceRange());
- report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SizeE,
- report));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, SizeE, report);
C.EmitReport(report);
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index f7c7c0c..bdc9627 100644
--- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -46,7 +46,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
visited. */
PostVisited /**< A CallExpr to this FunctionDecl is in the
worklist, and the body has been visited. */
- } K;
+ };
/// A DenseMap that records visited states of FunctionDecls.
llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp
new file mode 100644
index 0000000..884b0fa
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/APSIntType.cpp
@@ -0,0 +1,38 @@
+//===--- APSIntType.cpp - Simple record of the type of APSInts ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+
+using namespace clang;
+using namespace ento;
+
+APSIntType::RangeTestResultKind
+APSIntType::testInRange(const llvm::APSInt &Value) const {
+ // Negative numbers cannot be losslessly converted to unsigned type.
+ if (IsUnsigned && Value.isSigned() && Value.isNegative())
+ return RTR_Below;
+
+ // Signed integers can be converted to signed integers of the same width
+ // or (if positive) unsigned integers with one fewer bit.
+ // Unsigned integers can be converted to unsigned integers of the same width
+ // or signed integers with one more bit.
+ unsigned MinBits;
+ if (Value.isSigned())
+ MinBits = Value.getMinSignedBits() - IsUnsigned;
+ else
+ MinBits = Value.getActiveBits() + !IsUnsigned;
+
+ if (MinBits <= BitWidth)
+ return RTR_Within;
+
+ if (Value.isSigned() && Value.isNegative())
+ return RTR_Below;
+ else
+ return RTR_Above;
+}
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index eeaed2d..5aac640 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -25,18 +25,18 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
AnalysisPurgeMode purge,
bool eager, bool trim,
bool useUnoptimizedCFG,
- bool addImplicitDtors, bool addInitializers,
+ bool addImplicitDtors,
bool eagerlyTrimEGraph,
AnalysisIPAMode ipa,
unsigned inlineMaxStack,
unsigned inlineMaxFunctionSize,
AnalysisInliningMode IMode,
bool NoRetry)
- : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
+ : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, /*addInitializers=*/true),
Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd),
CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
CheckerMgr(checkerMgr),
- AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
+ MaxNodes(maxnodes), MaxVisit(maxvisit),
VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
EagerlyAssume(eager), TrimGraph(trim),
EagerlyTrimEGraph(eagerlyTrimEGraph),
@@ -59,7 +59,6 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
CreateStoreMgr(ParentAM.CreateStoreMgr),
CreateConstraintMgr(ParentAM.CreateConstraintMgr),
CheckerMgr(ParentAM.CheckerMgr),
- AScope(ScopeDecl),
MaxNodes(ParentAM.MaxNodes),
MaxVisit(ParentAM.MaxVisit),
VisualizeEGDot(ParentAM.VisualizeEGDot),
diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
index 2d9addd..8897756 100644
--- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/raw_ostream.h"
@@ -53,18 +54,25 @@ class BasicConstraintManager
ProgramState::IntSetTy::Factory ISetFactory;
public:
BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine)
- : SimpleConstraintManager(subengine),
+ : SimpleConstraintManager(subengine, statemgr.getBasicVals()),
ISetFactory(statemgr.getAllocator()) {}
- ProgramStateRef assumeSymNE(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
+ ProgramStateRef assumeSymEquality(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment,
+ bool Assumption);
- ProgramStateRef assumeSymEQ(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
+ ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) {
+ return assumeSymEquality(State, Sym, V, Adjustment, false);
+ }
+
+ ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) {
+ return assumeSymEquality(State, Sym, V, Adjustment, true);
+ }
ProgramStateRef assumeSymLT(ProgramStateRef state,
SymbolRef sym,
@@ -108,6 +116,9 @@ public:
ProgramStateRef removeDeadBindings(ProgramStateRef state,
SymbolReaper& SymReaper);
+ bool performTest(llvm::APSInt SymVal, llvm::APSInt Adjustment,
+ BinaryOperator::Opcode Op, llvm::APSInt ComparisonVal);
+
void print(ProgramStateRef state,
raw_ostream &Out,
const char* nl,
@@ -122,60 +133,94 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr,
return new BasicConstraintManager(statemgr, subengine);
}
-ProgramStateRef
-BasicConstraintManager::assumeSymNE(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) {
- // First, determine if sym == X, where X+Adjustment != V.
- llvm::APSInt Adjusted = V-Adjustment;
- if (const llvm::APSInt* X = getSymVal(state, sym)) {
- bool isFeasible = (*X != Adjusted);
- return isFeasible ? state : NULL;
- }
-
- // Second, determine if sym+Adjustment != V.
- if (isNotEqual(state, sym, Adjusted))
- return state;
-
- // If we reach here, sym is not a constant and we don't know if it is != V.
- // Make that assumption.
- return AddNE(state, sym, Adjusted);
+// FIXME: This is a more general utility and should live somewhere else.
+bool BasicConstraintManager::performTest(llvm::APSInt SymVal,
+ llvm::APSInt Adjustment,
+ BinaryOperator::Opcode Op,
+ llvm::APSInt ComparisonVal) {
+ APSIntType Type(Adjustment);
+ Type.apply(SymVal);
+ Type.apply(ComparisonVal);
+ SymVal += Adjustment;
+
+ assert(BinaryOperator::isComparisonOp(Op));
+ BasicValueFactory &BVF = getBasicVals();
+ const llvm::APSInt *Result = BVF.evalAPSInt(Op, SymVal, ComparisonVal);
+ assert(Result && "Comparisons should always have valid results.");
+
+ return Result->getBoolValue();
}
-ProgramStateRef
-BasicConstraintManager::assumeSymEQ(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) {
- // First, determine if sym == X, where X+Adjustment != V.
- llvm::APSInt Adjusted = V-Adjustment;
- if (const llvm::APSInt* X = getSymVal(state, sym)) {
- bool isFeasible = (*X == Adjusted);
- return isFeasible ? state : NULL;
+ProgramStateRef
+BasicConstraintManager::assumeSymEquality(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment,
+ bool Assumption) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(V) != APSIntType::RTR_Within)
+ return Assumption ? NULL : State;
+
+ // Get the symbol type.
+ BasicValueFactory &BVF = getBasicVals();
+ ASTContext &Ctx = BVF.getContext();
+ APSIntType SymbolType = BVF.getAPSIntType(Sym->getType(Ctx));
+
+ // First, see if the adjusted value is within range for the symbol.
+ llvm::APSInt Adjusted = AdjustmentType.convert(V) - Adjustment;
+ if (SymbolType.testInRange(Adjusted) != APSIntType::RTR_Within)
+ return Assumption ? NULL : State;
+
+ // Now we can do things properly in the symbol space.
+ SymbolType.apply(Adjusted);
+
+ // Second, determine if sym == X, where X+Adjustment != V.
+ if (const llvm::APSInt *X = getSymVal(State, Sym)) {
+ bool IsFeasible = (*X == Adjusted);
+ return (IsFeasible == Assumption) ? State : NULL;
}
- // Second, determine if sym+Adjustment != V.
- if (isNotEqual(state, sym, Adjusted))
- return NULL;
+ // Third, determine if we already know sym+Adjustment != V.
+ if (isNotEqual(State, Sym, Adjusted))
+ return Assumption ? NULL : State;
- // If we reach here, sym is not a constant and we don't know if it is == V.
- // Make that assumption.
- return AddEQ(state, sym, Adjusted);
+ // If we reach here, sym is not a constant and we don't know if it is != V.
+ // Make the correct assumption.
+ if (Assumption)
+ return AddEQ(State, Sym, Adjusted);
+ else
+ return AddNE(State, Sym, Adjusted);
}
// The logic for these will be handled in another ConstraintManager.
+// Approximate it here anyway by handling some edge cases.
ProgramStateRef
BasicConstraintManager::assumeSymLT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Is 'V' the smallest possible value?
- if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) {
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
+
+ // Is 'V' out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (V > ComparisonType.convert(Max)) {
+ // This path is trivially feasible.
+ return state;
+ }
+
+ // Is 'V' the smallest possible value, or out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (V <= ComparisonType.convert(Min)) {
// sym cannot be any value less than 'V'. This path is infeasible.
return NULL;
}
+ // Reject a path if the value of sym is a constant X and !(X+Adj < V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_LT, V);
+ return isFeasible ? state : NULL;
+ }
+
// FIXME: For now have assuming x < y be the same as assuming sym != V;
return assumeSymNE(state, sym, V, Adjustment);
}
@@ -185,12 +230,28 @@ BasicConstraintManager::assumeSymGT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Is 'V' the largest possible value?
- if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) {
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
+
+ // Is 'V' the largest possible value, or out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (V >= ComparisonType.convert(Max)) {
// sym cannot be any value greater than 'V'. This path is infeasible.
return NULL;
}
+ // Is 'V' out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (V < ComparisonType.convert(Min)) {
+ // This path is trivially feasible.
+ return state;
+ }
+
+ // Reject a path if the value of sym is a constant X and !(X+Adj > V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_GT, V);
+ return isFeasible ? state : NULL;
+ }
+
// FIXME: For now have assuming x > y be the same as assuming sym != V;
return assumeSymNE(state, sym, V, Adjustment);
}
@@ -200,25 +261,33 @@ BasicConstraintManager::assumeSymGE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Reject a path if the value of sym is a constant X and !(X+Adj >= V).
- if (const llvm::APSInt *X = getSymVal(state, sym)) {
- bool isFeasible = (*X >= V-Adjustment);
- return isFeasible ? state : NULL;
- }
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
- // Sym is not a constant, but it is worth looking to see if V is the
- // maximum integer value.
- if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) {
- llvm::APSInt Adjusted = V-Adjustment;
+ // Is 'V' the largest possible value, or out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ ComparisonType.apply(Max);
- // If we know that sym != V (after adjustment), then this condition
- // is infeasible since there is no other value greater than V.
- bool isFeasible = !isNotEqual(state, sym, Adjusted);
-
- // If the path is still feasible then as a consequence we know that
+ if (V > Max) {
+ // sym cannot be any value greater than 'V'. This path is infeasible.
+ return NULL;
+ } else if (V == Max) {
+ // If the path is feasible then as a consequence we know that
// 'sym+Adjustment == V' because there are no larger values.
// Add this constraint.
- return isFeasible ? AddEQ(state, sym, Adjusted) : NULL;
+ return assumeSymEQ(state, sym, V, Adjustment);
+ }
+
+ // Is 'V' out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (V < ComparisonType.convert(Min)) {
+ // This path is trivially feasible.
+ return state;
+ }
+
+ // Reject a path if the value of sym is a constant X and !(X+Adj >= V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_GE, V);
+ return isFeasible ? state : NULL;
}
return state;
@@ -229,25 +298,33 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Reject a path if the value of sym is a constant X and !(X+Adj <= V).
- if (const llvm::APSInt* X = getSymVal(state, sym)) {
- bool isFeasible = (*X <= V-Adjustment);
- return isFeasible ? state : NULL;
- }
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
- // Sym is not a constant, but it is worth looking to see if V is the
- // minimum integer value.
- if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) {
- llvm::APSInt Adjusted = V-Adjustment;
+ // Is 'V' out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (V > ComparisonType.convert(Max)) {
+ // This path is trivially feasible.
+ return state;
+ }
- // If we know that sym != V (after adjustment), then this condition
- // is infeasible since there is no other value less than V.
- bool isFeasible = !isNotEqual(state, sym, Adjusted);
+ // Is 'V' the smallest possible value, or out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ ComparisonType.apply(Min);
- // If the path is still feasible then as a consequence we know that
+ if (V < Min) {
+ // sym cannot be any value less than 'V'. This path is infeasible.
+ return NULL;
+ } else if (V == Min) {
+ // If the path is feasible then as a consequence we know that
// 'sym+Adjustment == V' because there are no smaller values.
// Add this constraint.
- return isFeasible ? AddEQ(state, sym, Adjusted) : NULL;
+ return assumeSymEQ(state, sym, V, Adjustment);
+ }
+
+ // Reject a path if the value of sym is a constant X and !(X+Adj >= V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_LE, V);
+ return isFeasible ? state : NULL;
}
return state;
@@ -256,8 +333,10 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state,
ProgramStateRef BasicConstraintManager::AddEQ(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) {
- // Create a new state with the old binding replaced.
- return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V));
+ // Now that we have an actual value, we can throw out the NE-set.
+ // Create a new state with the old bindings replaced.
+ state = state->remove<ConstNotEq>(sym);
+ return state->set<ConstEq>(sym, &getBasicVals().getValue(V));
}
ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state,
@@ -269,7 +348,7 @@ ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state,
ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet();
// Now add V to the NE set.
- S = ISetFactory.add(S, &state->getBasicVals().getValue(V));
+ S = ISetFactory.add(S, &getBasicVals().getValue(V));
// Create a new state with the old binding replaced.
return state->set<ConstNotEq>(sym, S);
@@ -289,7 +368,7 @@ bool BasicConstraintManager::isNotEqual(ProgramStateRef state,
const ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym);
// See if V is present in the NE-set.
- return T ? T->contains(&state->getBasicVals().getValue(V)) : false;
+ return T ? T->contains(&getBasicVals().getValue(V)) : false;
}
bool BasicConstraintManager::isEqual(ProgramStateRef state,
diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index fe96700..20c7361 100644
--- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -13,6 +13,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index a264212..7ba2fa7 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -48,6 +48,10 @@ static inline const Stmt *GetStmt(const ProgramPoint &P) {
return SP->getStmt();
else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P))
return BE->getSrc()->getTerminator();
+ else if (const CallEnter *CE = dyn_cast<CallEnter>(&P))
+ return CE->getCallExpr();
+ else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P))
+ return CEE->getCalleeContext()->getCallSite();
return 0;
}
@@ -427,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
- if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
+ if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
PathDiagnosticCallPiece *C =
PathDiagnosticCallPiece::construct(N, *CE, SMgr);
PD.getActivePath().push_front(C);
@@ -437,21 +441,23 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
}
if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) {
+ // Flush all locations, and pop the active path.
+ bool VisitedEntireCall = PD.isWithinCall();
PD.popActivePath();
- // The current active path should never be empty. Either we
- // just added a bunch of stuff to the top-level path, or
- // we have a previous CallExit. If the front of the active
- // path is not a PathDiagnosticCallPiece, it means that the
+
+ // Either we just added a bunch of stuff to the top-level path, or
+ // we have a previous CallExitEnd. If the former, it means that the
// path terminated within a function call. We must then take the
// current contents of the active path and place it within
// a new PathDiagnosticCallPiece.
- assert(!PD.getActivePath().empty());
- PathDiagnosticCallPiece *C =
- dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
- if (!C) {
+ PathDiagnosticCallPiece *C;
+ if (VisitedEntireCall) {
+ C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
+ } else {
const Decl *Caller = CE->getLocationContext()->getDecl();
C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
}
+
C->setCallee(*CE, SMgr);
if (!CallStack.empty()) {
assert(CallStack.back().first == C);
@@ -864,6 +870,7 @@ public:
void rawAddEdge(PathDiagnosticLocation NewLoc);
void addContext(const Stmt *S);
+ void addContext(const PathDiagnosticLocation &L);
void addExtendedContext(const Stmt *S);
};
} // end anonymous namespace
@@ -1031,7 +1038,10 @@ void EdgeBuilder::addContext(const Stmt *S) {
return;
PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC);
+ addContext(L);
+}
+void EdgeBuilder::addContext(const PathDiagnosticLocation &L) {
while (!CLocs.empty()) {
const PathDiagnosticLocation &TopContextLoc = CLocs.back();
@@ -1051,6 +1061,78 @@ void EdgeBuilder::addContext(const Stmt *S) {
CLocs.push_back(L);
}
+// Cone-of-influence: support the reverse propagation of "interesting" symbols
+// and values by tracing interesting calculations backwards through evaluated
+// expressions along a path. This is probably overly complicated, but the idea
+// is that if an expression computed an "interesting" value, the child
+// expressions are are also likely to be "interesting" as well (which then
+// propagates to the values they in turn compute). This reverse propagation
+// is needed to track interesting correlations across function call boundaries,
+// where formal arguments bind to actual arguments, etc. This is also needed
+// because the constraint solver sometimes simplifies certain symbolic values
+// into constants when appropriate, and this complicates reasoning about
+// interesting values.
+typedef llvm::DenseSet<const Expr *> InterestingExprs;
+
+static void reversePropagateIntererstingSymbols(BugReport &R,
+ InterestingExprs &IE,
+ const ProgramState *State,
+ const Expr *Ex,
+ const LocationContext *LCtx) {
+ SVal V = State->getSVal(Ex, LCtx);
+ if (!(R.isInteresting(V) || IE.count(Ex)))
+ return;
+
+ switch (Ex->getStmtClass()) {
+ default:
+ if (!isa<CastExpr>(Ex))
+ break;
+ // Fall through.
+ case Stmt::BinaryOperatorClass:
+ case Stmt::UnaryOperatorClass: {
+ for (Stmt::const_child_iterator CI = Ex->child_begin(),
+ CE = Ex->child_end();
+ CI != CE; ++CI) {
+ if (const Expr *child = dyn_cast_or_null<Expr>(*CI)) {
+ IE.insert(child);
+ SVal ChildV = State->getSVal(child, LCtx);
+ R.markInteresting(ChildV);
+ }
+ break;
+ }
+ }
+ }
+
+ R.markInteresting(V);
+}
+
+static void reversePropagateInterestingSymbols(BugReport &R,
+ InterestingExprs &IE,
+ const ProgramState *State,
+ const LocationContext *CalleeCtx,
+ const LocationContext *CallerCtx)
+{
+ // FIXME: Handle non-CallExpr-based CallEvents.
+ const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame();
+ const Stmt *CallSite = Callee->getCallSite();
+ if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
+ FunctionDecl::param_const_iterator PI = FD->param_begin(),
+ PE = FD->param_end();
+ CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
+ for (; AI != AE && PI != PE; ++AI, ++PI) {
+ if (const Expr *ArgE = *AI) {
+ if (const ParmVarDecl *PD = *PI) {
+ Loc LV = State->getLValue(PD, CalleeCtx);
+ if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV)))
+ IE.insert(ArgE);
+ }
+ }
+ }
+ }
+ }
+}
+
static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PathDiagnosticBuilder &PDB,
const ExplodedNode *N,
@@ -1058,6 +1140,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
EdgeBuilder EB(PD, PDB);
const SourceManager& SM = PDB.getSourceManager();
StackDiagVector CallStack;
+ InterestingExprs IE;
const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
while (NextNode) {
@@ -1066,16 +1149,27 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
do {
- if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
- const StackFrameContext *LCtx =
- CE->getLocationContext()->getCurrentStackFrame();
- PathDiagnosticLocation Loc(LCtx->getCallSite(),
- PDB.getSourceManager(),
- LCtx);
- EB.addEdge(Loc, true);
- EB.flushLocations();
+ if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
+ if (const Expr *Ex = PS->getStmtAs<Expr>())
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().getPtr(), Ex,
+ N->getLocationContext());
+ }
+
+ if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
+ const Stmt *S = CE->getCalleeContext()->getCallSite();
+ if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().getPtr(), Ex,
+ N->getLocationContext());
+ }
+
PathDiagnosticCallPiece *C =
PathDiagnosticCallPiece::construct(N, *CE, SM);
+
+ EB.addEdge(C->callReturn, true);
+ EB.flushLocations();
+
PD.getActivePath().push_front(C);
PD.pushActivePath(&C->path);
CallStack.push_back(StackDiagPair(C, N));
@@ -1092,26 +1186,26 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
EB.addEdge(pos);
// Flush all locations, and pop the active path.
+ bool VisitedEntireCall = PD.isWithinCall();
EB.flushLocations();
PD.popActivePath();
- assert(!PD.getActivePath().empty());
PDB.LC = N->getLocationContext();
- // The current active path should never be empty. Either we
- // just added a bunch of stuff to the top-level path, or
- // we have a previous CallExit. If the front of the active
- // path is not a PathDiagnosticCallPiece, it means that the
+ // Either we just added a bunch of stuff to the top-level path, or
+ // we have a previous CallExitEnd. If the former, it means that the
// path terminated within a function call. We must then take the
// current contents of the active path and place it within
// a new PathDiagnosticCallPiece.
- PathDiagnosticCallPiece *C =
- dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
- if (!C) {
- const Decl * Caller = CE->getLocationContext()->getDecl();
+ PathDiagnosticCallPiece *C;
+ if (VisitedEntireCall) {
+ C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
+ } else {
+ const Decl *Caller = CE->getLocationContext()->getDecl();
C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
}
+
C->setCallee(*CE, SM);
- EB.addContext(CE->getCallExpr());
+ EB.addContext(C->getLocation());
if (!CallStack.empty()) {
assert(CallStack.back().first == C);
@@ -1127,7 +1221,19 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PDB.LC = N->getLocationContext();
// Block edges.
- if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ // Does this represent entering a call? If so, look at propagating
+ // interesting symbols across call boundaries.
+ if (NextNode) {
+ const LocationContext *CallerCtx = NextNode->getLocationContext();
+ const LocationContext *CalleeCtx = PDB.LC;
+ if (CallerCtx != CalleeCtx) {
+ reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
+ N->getState().getPtr(),
+ CalleeCtx, CallerCtx);
+ }
+ }
+
const CFGBlock &Blk = *BE->getSrc();
const Stmt *Term = Blk.getTerminator();
@@ -1430,9 +1536,12 @@ void BugReporter::FlushReports() {
I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I)
const_cast<BugType*>(*I)->FlushReports(*this);
- typedef llvm::FoldingSet<BugReportEquivClass> SetTy;
- for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){
- BugReportEquivClass& EQ = *EI;
+ // We need to flush reports in deterministic order to ensure the order
+ // of the reports is consistent between runs.
+ typedef std::vector<BugReportEquivClass *> ContVecTy;
+ for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end();
+ EI != EE; ++EI){
+ BugReportEquivClass& EQ = **EI;
FlushReport(EQ);
}
@@ -1768,9 +1877,11 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
} while(finalReportConfigToken != originalReportConfigToken);
// Finally, prune the diagnostic path of uninteresting stuff.
- bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces());
- assert(hasSomethingInteresting);
- (void) hasSomethingInteresting;
+ if (R->shouldPrunePath()) {
+ bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces());
+ assert(hasSomethingInteresting);
+ (void) hasSomethingInteresting;
+ }
}
void BugReporter::Register(BugType *BT) {
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 6532486..46aa9e2 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -34,15 +34,23 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) {
// a[0], p->f, *p
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
- if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
- if (U->getOpcode() == UO_Deref)
- return U->getSubExpr()->IgnoreParenCasts();
- }
- else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
- return ME->getBase()->IgnoreParenCasts();
- }
- else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
- return AE->getBase();
+ while (true) {
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) {
+ assert(B->isAssignmentOp());
+ S = B->getLHS()->IgnoreParenCasts();
+ continue;
+ }
+ else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
+ if (U->getOpcode() == UO_Deref)
+ return U->getSubExpr()->IgnoreParenCasts();
+ }
+ else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
+ return ME->getBase()->IgnoreParenCasts();
+ }
+ else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
+ return AE->getBase();
+ }
+ break;
}
return NULL;
@@ -55,14 +63,6 @@ const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) {
return NULL;
}
-const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) {
- // Callee is checked as a PreVisit to the CallExpr.
- const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
- if (const CallExpr *CE = dyn_cast<CallExpr>(S))
- return CE->getCallee();
- return NULL;
-}
-
const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) {
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S))
@@ -119,10 +119,16 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
return NULL;
if (!StoreSite) {
- const ExplodedNode *Node = N, *Last = NULL;
+ // Make sure the region is actually bound to value V here.
+ // This is necessary because the region may not actually be live at the
+ // report's error node.
+ if (N->getState()->getSVal(R) != V)
+ return NULL;
- for ( ; Node ; Node = Node->getFirstPred()) {
+ const ExplodedNode *Node = N, *Last = N;
+ // Now look for the store of V.
+ for ( ; Node ; Node = Node->getFirstPred()) {
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
if (const PostStmt *P = Node->getLocationAs<PostStmt>())
if (const DeclStmt *DS = P->getStmtAs<DeclStmt>())
@@ -137,9 +143,11 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
// looking for store sites.
if (Node->getState()->getSVal(R) != V)
break;
+
+ Last = Node;
}
- if (!Node || !Last) {
+ if (!Node) {
satisfied = true;
return NULL;
}
@@ -189,6 +197,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
os << "declared without an initial value";
}
}
+ else {
+ os << "initialized here";
+ }
}
}
@@ -215,7 +226,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
<< " is assigned to ";
}
else
- return NULL;
+ os << "Value assigned to ";
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
os << '\'' << *VR->getDecl() << '\'';
@@ -285,12 +296,11 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
return NULL;
}
-BugReporterVisitor *
-bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
- const Stmt *S,
- BugReport *report) {
+void bugreporter::addTrackNullOrUndefValueVisitor(const ExplodedNode *N,
+ const Stmt *S,
+ BugReport *report) {
if (!S || !N)
- return 0;
+ return;
ProgramStateManager &StateMgr = N->getState()->getStateManager();
@@ -306,7 +316,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
}
if (!N)
- return 0;
+ return;
ProgramStateRef state = N->getState();
@@ -320,10 +330,18 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
// What did we load?
- SVal V = state->getSVal(loc::MemRegionVal(R));
+ SVal V = state->getRawSVal(loc::MemRegionVal(R));
report->markInteresting(R);
report->markInteresting(V);
- return new FindLastStoreBRVisitor(V, R);
+
+ if (V.getAsLocSymbol()) {
+ BugReporterVisitor *ConstraintTracker
+ = new TrackConstraintBRVisitor(cast<loc::MemRegionVal>(V), false);
+ report->addVisitor(ConstraintTracker);
+ }
+
+ report->addVisitor(new FindLastStoreBRVisitor(V, R));
+ return;
}
}
}
@@ -343,11 +361,10 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
if (R) {
report->markInteresting(R);
- return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false);
+ report->addVisitor(new TrackConstraintBRVisitor(loc::MemRegionVal(R),
+ false));
}
}
-
- return 0;
}
BugReporterVisitor *
@@ -389,7 +406,7 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
// The receiver was nil, and hence the method was skipped.
// Register a BugReporterVisitor to issue a message telling us how
// the receiver was null.
- BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver, &BR));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Receiver, &BR);
// Issue a message saying that the method was skipped.
PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
N->getLocationContext());
@@ -699,6 +716,9 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString,
BugReporterContext &BRC,
BugReport &report,
const ExplodedNode *N) {
+ // FIXME: If there's already a constraint tracker for this variable,
+ // we shouldn't emit anything here (c.f. the double note in
+ // test/Analysis/inlining/path-notes.c)
SmallString<256> buf;
llvm::raw_svector_ostream Out(buf);
Out << "Assuming " << LhsString << " is ";
diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt
index 1ea25bd..b16b233d 100644
--- a/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -1,14 +1,14 @@
set(LLVM_LINK_COMPONENTS support)
-set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite)
-
add_clang_library(clangStaticAnalyzerCore
AnalysisManager.cpp
+ APSIntType.cpp
BasicConstraintManager.cpp
BasicValueFactory.cpp
BlockCounter.cpp
BugReporter.cpp
BugReporterVisitors.cpp
+ CallEvent.cpp
Checker.cpp
CheckerContext.cpp
CheckerHelpers.cpp
@@ -25,7 +25,6 @@ add_clang_library(clangStaticAnalyzerCore
FunctionSummary.cpp
HTMLDiagnostics.cpp
MemRegion.cpp
- ObjCMessage.cpp
PathDiagnostic.cpp
PlistDiagnostics.cpp
ProgramState.cpp
@@ -41,5 +40,19 @@ add_clang_library(clangStaticAnalyzerCore
TextPathDiagnostics.cpp
)
-add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes
- ClangStmtNodes)
+add_dependencies(clangStaticAnalyzerCore
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangStaticAnalyzerCore
+ clangBasic
+ clangLex
+ clangAST
+ clangFrontend
+ clangRewrite
+ )
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
new file mode 100644
index 0000000..e3f4c61
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -0,0 +1,856 @@
+//===- Calls.cpp - Wrapper for all function and method calls ------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines CallEvent and its subclasses, which represent path-
+/// sensitive instances of different kinds of function and method calls
+/// (C, C++, and Objective-C).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/AST/ParentMap.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang;
+using namespace ento;
+
+QualType CallEvent::getResultType() const {
+ QualType ResultTy = getDeclaredResultType();
+
+ if (ResultTy.isNull())
+ ResultTy = getOriginExpr()->getType();
+
+ return ResultTy;
+}
+
+static bool isCallbackArg(SVal V, QualType T) {
+ // If the parameter is 0, it's harmless.
+ if (V.isZeroConstant())
+ return false;
+
+ // If a parameter is a block or a callback, assume it can modify pointer.
+ if (T->isBlockPointerType() ||
+ T->isFunctionPointerType() ||
+ T->isObjCSelType())
+ return true;
+
+ // Check if a callback is passed inside a struct (for both, struct passed by
+ // reference and by value). Dig just one level into the struct for now.
+
+ if (isa<PointerType>(T) || isa<ReferenceType>(T))
+ T = T->getPointeeType();
+
+ if (const RecordType *RT = T->getAsStructureType()) {
+ const RecordDecl *RD = RT->getDecl();
+ for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
+ I != E; ++I) {
+ QualType FieldT = I->getType();
+ if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CallEvent::hasNonZeroCallbackArg() const {
+ unsigned NumOfArgs = getNumArgs();
+
+ // If calling using a function pointer, assume the function does not
+ // have a callback. TODO: We could check the types of the arguments here.
+ if (!getDecl())
+ return false;
+
+ unsigned Idx = 0;
+ for (CallEvent::param_type_iterator I = param_type_begin(),
+ E = param_type_end();
+ I != E && Idx < NumOfArgs; ++I, ++Idx) {
+ if (NumOfArgs <= Idx)
+ break;
+
+ if (isCallbackArg(getArgSVal(Idx), *I))
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Returns true if a type is a pointer-to-const or reference-to-const
+/// with no further indirection.
+static bool isPointerToConst(QualType Ty) {
+ QualType PointeeTy = Ty->getPointeeType();
+ if (PointeeTy == QualType())
+ return false;
+ if (!PointeeTy.isConstQualified())
+ return false;
+ if (PointeeTy->isAnyPointerType())
+ return false;
+ return true;
+}
+
+// Try to retrieve the function declaration and find the function parameter
+// types which are pointers/references to a non-pointer const.
+// We will not invalidate the corresponding argument regions.
+static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
+ const CallEvent &Call) {
+ unsigned Idx = 0;
+ for (CallEvent::param_type_iterator I = Call.param_type_begin(),
+ E = Call.param_type_end();
+ I != E; ++I, ++Idx) {
+ if (isPointerToConst(*I))
+ PreserveArgs.insert(Idx);
+ }
+}
+
+ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
+ ProgramStateRef Orig) const {
+ ProgramStateRef Result = (Orig ? Orig : getState());
+
+ SmallVector<const MemRegion *, 8> RegionsToInvalidate;
+ getExtraInvalidatedRegions(RegionsToInvalidate);
+
+ // Indexes of arguments whose values will be preserved by the call.
+ llvm::SmallSet<unsigned, 1> PreserveArgs;
+ if (!argumentsMayEscape())
+ findPtrToConstParams(PreserveArgs, *this);
+
+ for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) {
+ if (PreserveArgs.count(Idx))
+ continue;
+
+ SVal V = getArgSVal(Idx);
+
+ // If we are passing a location wrapped as an integer, unwrap it and
+ // invalidate the values referred by the location.
+ if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
+ V = Wrapped->getLoc();
+ else if (!isa<Loc>(V))
+ continue;
+
+ if (const MemRegion *R = V.getAsRegion()) {
+ // Invalidate the value of the variable passed by reference.
+
+ // Are we dealing with an ElementRegion? If the element type is
+ // a basic integer type (e.g., char, int) and the underlying region
+ // is a variable region then strip off the ElementRegion.
+ // FIXME: We really need to think about this for the general case
+ // as sometimes we are reasoning about arrays and other times
+ // about (char*), etc., is just a form of passing raw bytes.
+ // e.g., void *p = alloca(); foo((char*)p);
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ // Checking for 'integral type' is probably too promiscuous, but
+ // we'll leave it in for now until we have a systematic way of
+ // handling all of these cases. Eventually we need to come up
+ // with an interface to StoreManager so that this logic can be
+ // appropriately delegated to the respective StoreManagers while
+ // still allowing us to do checker-specific logic (e.g.,
+ // invalidating reference counts), probably via callbacks.
+ if (ER->getElementType()->isIntegralOrEnumerationType()) {
+ const MemRegion *superReg = ER->getSuperRegion();
+ if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
+ isa<ObjCIvarRegion>(superReg))
+ R = cast<TypedRegion>(superReg);
+ }
+ // FIXME: What about layers of ElementRegions?
+ }
+
+ // Mark this region for invalidation. We batch invalidate regions
+ // below for efficiency.
+ RegionsToInvalidate.push_back(R);
+ }
+ }
+
+ // Invalidate designated regions using the batch invalidation API.
+ // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
+ // global variables.
+ return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(),
+ BlockCount, getLocationContext(),
+ /*Symbols=*/0, this);
+}
+
+ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
+ const ProgramPointTag *Tag) const {
+ if (const Expr *E = getOriginExpr()) {
+ if (IsPreVisit)
+ return PreStmt(E, getLocationContext(), Tag);
+ return PostStmt(E, getLocationContext(), Tag);
+ }
+
+ const Decl *D = getDecl();
+ assert(D && "Cannot get a program point without a statement or decl");
+
+ SourceLocation Loc = getSourceRange().getBegin();
+ if (IsPreVisit)
+ return PreImplicitCall(D, Loc, getLocationContext(), Tag);
+ return PostImplicitCall(D, Loc, getLocationContext(), Tag);
+}
+
+SVal CallEvent::getArgSVal(unsigned Index) const {
+ const Expr *ArgE = getArgExpr(Index);
+ if (!ArgE)
+ return UnknownVal();
+ return getSVal(ArgE);
+}
+
+SourceRange CallEvent::getArgSourceRange(unsigned Index) const {
+ const Expr *ArgE = getArgExpr(Index);
+ if (!ArgE)
+ return SourceRange();
+ return ArgE->getSourceRange();
+}
+
+void CallEvent::dump(raw_ostream &Out) const {
+ ASTContext &Ctx = getState()->getStateManager().getContext();
+ if (const Expr *E = getOriginExpr()) {
+ E->printPretty(Out, Ctx, 0, Ctx.getPrintingPolicy());
+ Out << "\n";
+ return;
+ }
+
+ if (const Decl *D = getDecl()) {
+ Out << "Call to ";
+ D->print(Out, Ctx.getPrintingPolicy());
+ return;
+ }
+
+ // FIXME: a string representation of the kind would be nice.
+ Out << "Unknown call (type " << getKind() << ")";
+}
+
+
+bool CallEvent::mayBeInlined(const Stmt *S) {
+ // FIXME: Kill this.
+ return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S)
+ || isa<CXXConstructExpr>(S);
+}
+
+static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
+ CallEvent::BindingsTy &Bindings,
+ SValBuilder &SVB,
+ const CallEvent &Call,
+ CallEvent::param_iterator I,
+ CallEvent::param_iterator E) {
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+
+ unsigned Idx = 0;
+ for (; I != E; ++I, ++Idx) {
+ const ParmVarDecl *ParamDecl = *I;
+ assert(ParamDecl && "Formal parameter has no decl?");
+
+ SVal ArgVal = Call.getArgSVal(Idx);
+ if (!ArgVal.isUnknown()) {
+ Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+ Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
+ }
+ }
+
+ // FIXME: Variadic arguments are not handled at all right now.
+}
+
+
+CallEvent::param_iterator AnyFunctionCall::param_begin() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_begin();
+}
+
+CallEvent::param_iterator AnyFunctionCall::param_end() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_end();
+}
+
+void AnyFunctionCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const FunctionDecl *D = cast<FunctionDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->param_begin(), D->param_end());
+}
+
+QualType AnyFunctionCall::getDeclaredResultType() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return QualType();
+
+ return D->getResultType();
+}
+
+bool AnyFunctionCall::argumentsMayEscape() const {
+ if (hasNonZeroCallbackArg())
+ return true;
+
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return true;
+
+ const IdentifierInfo *II = D->getIdentifier();
+ if (!II)
+ return true;
+
+ // This set of "escaping" APIs is
+
+ // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
+ // value into thread local storage. The value can later be retrieved with
+ // 'void *ptheread_getspecific(pthread_key)'. So even thought the
+ // parameter is 'const void *', the region escapes through the call.
+ if (II->isStr("pthread_setspecific"))
+ return true;
+
+ // - xpc_connection_set_context stores a value which can be retrieved later
+ // with xpc_connection_get_context.
+ if (II->isStr("xpc_connection_set_context"))
+ return true;
+
+ // - funopen - sets a buffer for future IO calls.
+ if (II->isStr("funopen"))
+ return true;
+
+ StringRef FName = II->getName();
+
+ // - CoreFoundation functions that end with "NoCopy" can free a passed-in
+ // buffer even if it is const.
+ if (FName.endswith("NoCopy"))
+ return true;
+
+ // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove.
+ if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ return true;
+
+ // - Many CF containers allow objects to escape through custom
+ // allocators/deallocators upon container construction. (PR12101)
+ if (FName.startswith("CF") || FName.startswith("CG")) {
+ return StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "WithData") != StringRef::npos ||
+ StrInStrNoCase(FName, "AppendValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetAttribute") != StringRef::npos;
+ }
+
+ return false;
+}
+
+
+const FunctionDecl *SimpleCall::getDecl() const {
+ const FunctionDecl *D = getOriginExpr()->getDirectCallee();
+ if (D)
+ return D;
+
+ return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl();
+}
+
+
+const FunctionDecl *CXXInstanceCall::getDecl() const {
+ const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr());
+ if (!CE)
+ return AnyFunctionCall::getDecl();
+
+ const FunctionDecl *D = CE->getDirectCallee();
+ if (D)
+ return D;
+
+ return getSVal(CE->getCallee()).getAsFunctionDecl();
+}
+
+void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ if (const MemRegion *R = getCXXThisVal().getAsRegion())
+ Regions.push_back(R);
+}
+
+static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){
+ const MemRegion *R = ThisVal.getAsRegion();
+ if (!R)
+ return 0;
+
+ const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R->StripCasts());
+ if (!TR)
+ return 0;
+
+ const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl();
+ if (!RD)
+ return 0;
+
+ const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD);
+ const FunctionDecl *Definition;
+ if (!Result->hasBody(Definition))
+ return 0;
+
+ return cast<CXXMethodDecl>(Definition);
+}
+
+
+RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
+ const Decl *D = getDecl();
+ if (!D)
+ return RuntimeDefinition();
+
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
+ if (!MD->isVirtual())
+ return AnyFunctionCall::getRuntimeDefinition();
+
+ // If the method is virtual, see if we can find the actual implementation
+ // based on context-sensitivity.
+ // FIXME: Virtual method calls behave differently when an object is being
+ // constructed or destructed. It's not as simple as "no devirtualization"
+ // because a /partially/ constructed object can be referred to through a
+ // base pointer. We'll eventually want to use DynamicTypeInfo here.
+ if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal()))
+ return RuntimeDefinition(Devirtualized);
+
+ return RuntimeDefinition();
+}
+
+void CXXInstanceCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
+
+ // Handle the binding of 'this' in the new stack frame.
+ // We need to make sure we have the proper layering of CXXBaseObjectRegions.
+ SVal ThisVal = getCXXThisVal();
+ if (!ThisVal.isUnknown()) {
+ ProgramStateManager &StateMgr = getState()->getStateManager();
+ SValBuilder &SVB = StateMgr.getSValBuilder();
+
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
+
+ if (const MemRegion *ThisReg = ThisVal.getAsRegion()) {
+ ASTContext &Ctx = SVB.getContext();
+ const CXXRecordDecl *Class = MD->getParent();
+ QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class));
+
+ // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
+ bool Failed;
+ ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed);
+ assert(!Failed && "Calling an incorrectly devirtualized method");
+
+ // If we couldn't build the correct cast, just strip off all casts.
+ if (ThisVal.isUnknown())
+ ThisVal = loc::MemRegionVal(ThisReg->StripCasts());
+ }
+
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ }
+}
+
+
+
+const Expr *CXXMemberCall::getCXXThisExpr() const {
+ return getOriginExpr()->getImplicitObjectArgument();
+}
+
+
+const Expr *CXXMemberOperatorCall::getCXXThisExpr() const {
+ return getOriginExpr()->getArg(0);
+}
+
+
+const BlockDataRegion *BlockCall::getBlockRegion() const {
+ const Expr *Callee = getOriginExpr()->getCallee();
+ const MemRegion *DataReg = getSVal(Callee).getAsRegion();
+
+ return dyn_cast_or_null<BlockDataRegion>(DataReg);
+}
+
+CallEvent::param_iterator BlockCall::param_begin() const {
+ const BlockDecl *D = getBlockDecl();
+ if (!D)
+ return 0;
+ return D->param_begin();
+}
+
+CallEvent::param_iterator BlockCall::param_end() const {
+ const BlockDecl *D = getBlockDecl();
+ if (!D)
+ return 0;
+ return D->param_end();
+}
+
+void BlockCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ // FIXME: This also needs to invalidate captured globals.
+ if (const MemRegion *R = getBlockRegion())
+ Regions.push_back(R);
+}
+
+void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const BlockDecl *D = cast<BlockDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->param_begin(), D->param_end());
+}
+
+
+QualType BlockCall::getDeclaredResultType() const {
+ const BlockDataRegion *BR = getBlockRegion();
+ if (!BR)
+ return QualType();
+ QualType BlockTy = BR->getCodeRegion()->getLocationType();
+ return cast<FunctionType>(BlockTy->getPointeeType())->getResultType();
+}
+
+
+SVal CXXConstructorCall::getCXXThisVal() const {
+ if (Data)
+ return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
+ return UnknownVal();
+}
+
+void CXXConstructorCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ if (Data)
+ Regions.push_back(static_cast<const MemRegion *>(Data));
+}
+
+void CXXConstructorCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
+
+ SVal ThisVal = getCXXThisVal();
+ if (!ThisVal.isUnknown()) {
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ }
+}
+
+
+
+SVal CXXDestructorCall::getCXXThisVal() const {
+ if (Data)
+ return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
+ return UnknownVal();
+}
+
+
+CallEvent::param_iterator ObjCMethodCall::param_begin() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_begin();
+}
+
+CallEvent::param_iterator ObjCMethodCall::param_end() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_end();
+}
+
+void
+ObjCMethodCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ if (const MemRegion *R = getReceiverSVal().getAsRegion())
+ Regions.push_back(R);
+}
+
+QualType ObjCMethodCall::getDeclaredResultType() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return QualType();
+
+ return D->getResultType();
+}
+
+SVal ObjCMethodCall::getReceiverSVal() const {
+ // FIXME: Is this the best way to handle class receivers?
+ if (!isInstanceMessage())
+ return UnknownVal();
+
+ if (const Expr *RecE = getOriginExpr()->getInstanceReceiver())
+ return getSVal(RecE);
+
+ // An instance message with no expression means we are sending to super.
+ // In this case the object reference is the same as 'self'.
+ const LocationContext *LCtx = getLocationContext();
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ assert(SelfDecl && "No message receiver Expr, but not in an ObjC method");
+ return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
+}
+
+SourceRange ObjCMethodCall::getSourceRange() const {
+ switch (getMessageKind()) {
+ case OCM_Message:
+ return getOriginExpr()->getSourceRange();
+ case OCM_PropertyAccess:
+ case OCM_Subscript:
+ return getContainingPseudoObjectExpr()->getSourceRange();
+ }
+ llvm_unreachable("unknown message kind");
+}
+
+typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy;
+
+const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
+ assert(Data != 0 && "Lazy lookup not yet performed.");
+ assert(getMessageKind() != OCM_Message && "Explicit message send.");
+ return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer();
+}
+
+ObjCMessageKind ObjCMethodCall::getMessageKind() const {
+ if (Data == 0) {
+ ParentMap &PM = getLocationContext()->getParentMap();
+ const Stmt *S = PM.getParent(getOriginExpr());
+ if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) {
+ const Expr *Syntactic = POE->getSyntacticForm();
+
+ // This handles the funny case of assigning to the result of a getter.
+ // This can happen if the getter returns a non-const reference.
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic))
+ Syntactic = BO->getLHS();
+
+ ObjCMessageKind K;
+ switch (Syntactic->getStmtClass()) {
+ case Stmt::ObjCPropertyRefExprClass:
+ K = OCM_PropertyAccess;
+ break;
+ case Stmt::ObjCSubscriptRefExprClass:
+ K = OCM_Subscript;
+ break;
+ default:
+ // FIXME: Can this ever happen?
+ K = OCM_Message;
+ break;
+ }
+
+ if (K != OCM_Message) {
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(POE, K).getOpaqueValue();
+ assert(getMessageKind() == K);
+ return K;
+ }
+ }
+
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(0, 1).getOpaqueValue();
+ assert(getMessageKind() == OCM_Message);
+ return OCM_Message;
+ }
+
+ ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data);
+ if (!Info.getPointer())
+ return OCM_Message;
+ return static_cast<ObjCMessageKind>(Info.getInt());
+}
+
+
+bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
+ Selector Sel) const {
+ assert(IDecl);
+ const SourceManager &SM =
+ getState()->getStateManager().getContext().getSourceManager();
+
+ // If the class interface is declared inside the main file, assume it is not
+ // subcassed.
+ // TODO: It could actually be subclassed if the subclass is private as well.
+ // This is probably very rare.
+ SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
+ if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc))
+ return false;
+
+
+ // We assume that if the method is public (declared outside of main file) or
+ // has a parent which publicly declares the method, the method could be
+ // overridden in a subclass.
+
+ // Find the first declaration in the class hierarchy that declares
+ // the selector.
+ ObjCMethodDecl *D = 0;
+ while (true) {
+ D = IDecl->lookupMethod(Sel, true);
+
+ // Cannot find a public definition.
+ if (!D)
+ return false;
+
+ // If outside the main file,
+ if (D->getLocation().isValid() && !SM.isFromMainFile(D->getLocation()))
+ return true;
+
+ if (D->isOverriding()) {
+ // Search in the superclass on the next iteration.
+ IDecl = D->getClassInterface();
+ if (!IDecl)
+ return false;
+
+ IDecl = IDecl->getSuperClass();
+ if (!IDecl)
+ return false;
+
+ continue;
+ }
+
+ return false;
+ };
+
+ llvm_unreachable("The while loop should always terminate.");
+}
+
+RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
+ const ObjCMessageExpr *E = getOriginExpr();
+ assert(E);
+ Selector Sel = E->getSelector();
+
+ if (E->isInstanceMessage()) {
+
+ // Find the the receiver type.
+ const ObjCObjectPointerType *ReceiverT = 0;
+ bool CanBeSubClassed = false;
+ QualType SupersType = E->getSuperType();
+ const MemRegion *Receiver = 0;
+
+ if (!SupersType.isNull()) {
+ // Super always means the type of immediate predecessor to the method
+ // where the call occurs.
+ ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+ } else {
+ Receiver = getReceiverSVal().getAsRegion();
+ if (!Receiver)
+ return RuntimeDefinition();
+
+ DynamicTypeInfo DTI = getState()->getDynamicTypeInfo(Receiver);
+ QualType DynType = DTI.getType();
+ CanBeSubClassed = DTI.canBeASubClass();
+ ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType);
+
+ if (ReceiverT && CanBeSubClassed)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
+ if (!canBeOverridenInSubclass(IDecl, Sel))
+ CanBeSubClassed = false;
+ }
+
+ // Lookup the method implementation.
+ if (ReceiverT)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
+ const ObjCMethodDecl *MD = IDecl->lookupPrivateMethod(Sel);
+ if (CanBeSubClassed)
+ return RuntimeDefinition(MD, Receiver);
+ else
+ return RuntimeDefinition(MD, 0);
+ }
+
+ } else {
+ // This is a class method.
+ // If we have type info for the receiver class, we are calling via
+ // class name.
+ if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) {
+ // Find/Return the method implementation.
+ return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel));
+ }
+ }
+
+ return RuntimeDefinition();
+}
+
+void ObjCMethodCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const ObjCMethodDecl *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->param_begin(), D->param_end());
+
+ SVal SelfVal = getReceiverSVal();
+ if (!SelfVal.isUnknown()) {
+ const VarDecl *SelfD = CalleeCtx->getAnalysisDeclContext()->getSelfDecl();
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+ Loc SelfLoc = SVB.makeLoc(MRMgr.getVarRegion(SelfD, CalleeCtx));
+ Bindings.push_back(std::make_pair(SelfLoc, SelfVal));
+ }
+}
+
+CallEventRef<>
+CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE))
+ return create<CXXMemberCall>(MCE, State, LCtx);
+
+ if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
+ if (MD->isInstance())
+ return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
+
+ } else if (CE->getCallee()->getType()->isBlockPointerType()) {
+ return create<BlockCall>(CE, State, LCtx);
+ }
+
+ // Otherwise, it's a normal function call, static member function call, or
+ // something we can't reason about.
+ return create<FunctionCall>(CE, State, LCtx);
+}
+
+
+CallEventRef<>
+CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
+ ProgramStateRef State) {
+ const LocationContext *ParentCtx = CalleeCtx->getParent();
+ const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame();
+ assert(CallerCtx && "This should not be used for top-level stack frames");
+
+ const Stmt *CallSite = CalleeCtx->getCallSite();
+
+ if (CallSite) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite))
+ return getSimpleCall(CE, State, CallerCtx);
+
+ switch (CallSite->getStmtClass()) {
+ case Stmt::CXXConstructExprClass: {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
+ ThisVal.getAsRegion(), State, CallerCtx);
+ }
+ case Stmt::CXXNewExprClass:
+ return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx);
+ case Stmt::ObjCMessageExprClass:
+ return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite),
+ State, CallerCtx);
+ default:
+ llvm_unreachable("This is not an inlineable statement.");
+ }
+ }
+
+ // Fall back to the CFG. The only thing we haven't handled yet is
+ // destructors, though this could change in the future.
+ const CFGBlock *B = CalleeCtx->getCallSiteBlock();
+ CFGElement E = (*B)[CalleeCtx->getIndex()];
+ assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs");
+ assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet");
+
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
+ Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx);
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ const Stmt *Trigger;
+ if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E))
+ Trigger = AutoDtor->getTriggerStmt();
+ else
+ Trigger = Dtor->getBody();
+
+ return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
+ State, CallerCtx);
+}
+
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 0bcc343..c786655 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -14,7 +14,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/AST/DeclBase.h"
@@ -25,6 +25,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const {
return !StmtCheckers.empty() ||
!PreObjCMessageCheckers.empty() ||
!PostObjCMessageCheckers.empty() ||
+ !PreCallCheckers.empty() ||
+ !PostCallCheckers.empty() ||
!LocationCheckers.empty() ||
!BindCheckers.empty() ||
!EndAnalysisCheckers.empty() ||
@@ -138,7 +140,7 @@ namespace {
const CheckersTy &Checkers;
const Stmt *S;
ExprEngine &Eng;
- bool wasInlined;
+ bool WasInlined;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
@@ -146,7 +148,7 @@ namespace {
CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
const Stmt *s, ExprEngine &eng, bool wasInlined = false)
: IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
- wasInlined(wasInlined) {}
+ WasInlined(wasInlined) {}
void runChecker(CheckerManager::CheckStmtFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -155,7 +157,7 @@ namespace {
ProgramPoint::PostStmtKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Bldr, Eng, Pred, L, wasInlined);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
checkFn(S, C);
}
};
@@ -167,38 +169,35 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
const ExplodedNodeSet &Src,
const Stmt *S,
ExprEngine &Eng,
- bool wasInlined) {
+ bool WasInlined) {
CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
- S, Eng, wasInlined);
+ S, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
namespace {
struct CheckObjCMessageContext {
typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy;
- bool IsPreVisit;
+ bool IsPreVisit, WasInlined;
const CheckersTy &Checkers;
- const ObjCMessage &Msg;
+ const ObjCMethodCall &Msg;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers,
- const ObjCMessage &msg, ExprEngine &eng)
- : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
+ const ObjCMethodCall &msg, ExprEngine &eng,
+ bool wasInlined)
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Msg(msg), Eng(eng) { }
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
- ProgramPoint::PostStmtKind;
- const ProgramPoint &L =
- ProgramPoint::getProgramPoint(Msg.getMessageExpr(),
- K, Pred->getLocationContext(),
- checkFn.Checker);
- CheckerContext C(Bldr, Eng, Pred, L);
+ const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
- checkFn(Msg, C);
+ checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
}
};
}
@@ -207,12 +206,56 @@ namespace {
void CheckerManager::runCheckersForObjCMessage(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const ObjCMessage &msg,
- ExprEngine &Eng) {
+ const ObjCMethodCall &msg,
+ ExprEngine &Eng,
+ bool WasInlined) {
CheckObjCMessageContext C(isPreVisit,
isPreVisit ? PreObjCMessageCheckers
: PostObjCMessageCheckers,
- msg, Eng);
+ msg, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+ // FIXME: This has all the same signatures as CheckObjCMessageContext.
+ // Is there a way we can merge the two?
+ struct CheckCallContext {
+ typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy;
+ bool IsPreVisit, WasInlined;
+ const CheckersTy &Checkers;
+ const CallEvent &Call;
+ ExprEngine &Eng;
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
+ const CallEvent &call, ExprEngine &eng,
+ bool wasInlined)
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Call(call), Eng(eng) { }
+
+ void runChecker(CheckerManager::CheckCallFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+
+ checkFn(*Call.cloneWithState(Pred->getState()), C);
+ }
+ };
+}
+
+/// \brief Run checkers for visiting an abstract call event.
+void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ CheckCallContext C(isPreVisit,
+ isPreVisit ? PreCallCheckers
+ : PostCallCheckers,
+ Call, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -381,21 +424,25 @@ namespace {
SymbolReaper &SR;
const Stmt *S;
ExprEngine &Eng;
+ ProgramPoint::Kind ProgarmPointKind;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
- const Stmt *s, ExprEngine &eng)
- : Checkers(checkers), SR(sr), S(s), Eng(eng) { }
+ const Stmt *s, ExprEngine &eng,
+ ProgramPoint::Kind K)
+ : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { }
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind,
Pred->getLocationContext(), checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
+ // Note, do not pass the statement to the checkers without letting them
+ // differentiate if we ran remove dead bindings before or after the
+ // statement.
checkFn(SR, C);
}
};
@@ -406,8 +453,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper,
const Stmt *S,
- ExprEngine &Eng) {
- CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng);
+ ExprEngine &Eng,
+ ProgramPoint::Kind K) {
+ CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -426,7 +474,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) {
+ const CallEvent *Call) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
@@ -456,16 +504,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
/// Only one checker will evaluate the call.
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const CallExpr *CE,
- ExprEngine &Eng,
- GraphExpander *defaultEval) {
- if (EvalCallCheckers.empty() &&
- InlineCallCheckers.empty() &&
- defaultEval == 0) {
- Dst.insert(Src);
- return;
- }
-
+ const CallEvent &Call,
+ ExprEngine &Eng) {
+ const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
for (ExplodedNodeSet::iterator
NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) {
@@ -529,10 +570,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
if (!anyEvaluated) {
- if (defaultEval)
- defaultEval->expandGraph(Dst, Pred);
- else
- Dst.insert(Pred);
+ NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
+ Eng.defaultEvalCall(B, Pred, Call);
}
}
}
@@ -590,6 +629,13 @@ void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
PostObjCMessageCheckers.push_back(checkfn);
}
+void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
+ PreCallCheckers.push_back(checkfn);
+}
+void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) {
+ PostCallCheckers.push_back(checkfn);
+}
+
void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
LocationCheckers.push_back(checkfn);
}
@@ -673,6 +719,3 @@ CheckerManager::~CheckerManager() {
for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i)
CheckerDtors[i]();
}
-
-// Anchor for the vtable.
-GraphExpander::~GraphExpander() { }
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index ca662c7..1f13742 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -26,6 +26,8 @@
using namespace clang;
using namespace ento;
+STATISTIC(NumSteps,
+ "The # of steps executed.");
STATISTIC(NumReachedMaxSteps,
"The # of times we reached the max number of steps.");
STATISTIC(NumPathsExplored,
@@ -74,7 +76,7 @@ public:
}
virtual void enqueue(const WorkListUnit& U) {
- Queue.push_front(U);
+ Queue.push_back(U);
}
virtual WorkListUnit dequeue() {
@@ -207,6 +209,8 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
--Steps;
}
+ NumSteps++;
+
const WorkListUnit& WU = WList->dequeue();
// Set the current block counter.
@@ -248,7 +252,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
}
- case ProgramPoint::CallExitKind:
+ case ProgramPoint::CallExitBeginKind:
SubEng.processCallExit(Pred);
break;
@@ -261,7 +265,9 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
}
default:
assert(isa<PostStmt>(Loc) ||
- isa<PostInitializer>(Loc));
+ isa<PostInitializer>(Loc) ||
+ isa<PostImplicitCall>(Loc) ||
+ isa<CallExitEnd>(Loc));
HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred);
break;
}
@@ -502,7 +508,8 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
}
// Do not create extra nodes. Move to the next CFG element.
- if (isa<PostInitializer>(N->getLocation())) {
+ if (isa<PostInitializer>(N->getLocation()) ||
+ isa<PostImplicitCall>(N->getLocation())) {
WList->enqueue(N, Block, Idx+1);
return;
}
@@ -531,14 +538,13 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
WList->enqueue(Succ, Block, Idx+1);
}
-ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) {
- // Create a CallExit node and enqueue it.
+ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) {
+ // Create a CallExitBegin node and enqueue it.
const StackFrameContext *LocCtx
= cast<StackFrameContext>(N->getLocationContext());
- const Stmt *CE = LocCtx->getCallSite();
- // Use the the callee location context.
- CallExit Loc(CE, LocCtx);
+ // Use the callee location context.
+ CallExitBegin Loc(LocCtx);
bool isNew;
ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew);
@@ -565,12 +571,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set,
void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) {
for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) {
ExplodedNode *N = *I;
- // If we are in an inlined call, generate CallExit node.
+ // If we are in an inlined call, generate CallExitBegin node.
if (N->getLocationContext()->getParent()) {
- N = generateCallExitNode(N);
+ N = generateCallExitBeginNode(N);
if (N)
WList->enqueue(N);
} else {
+ // TODO: We should run remove dead bindings here.
G->addEndOfPath(N);
NumPathsExplored++;
}
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index b5ea3db..52644f7 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -71,6 +71,11 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
else
return svalBuilder.makeBoolVal(cast<CXXBoolLiteralExpr>(E));
}
+ case Stmt::CXXScalarValueInitExprClass:
+ case Stmt::ImplicitValueInitExprClass: {
+ QualType Ty = cast<Expr>(E)->getType();
+ return svalBuilder.makeZeroVal(Ty);
+ }
case Stmt::IntegerLiteralClass: {
// In C++, this expression may have been bound to a temporary object.
SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx));
@@ -91,8 +96,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::CXXBindTemporaryExprClass:
E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
continue;
- case Stmt::ObjCPropertyRefExprClass:
- return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E));
+ case Stmt::SubstNonTypeTemplateParmExprClass:
+ E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement();
+ continue;
case Stmt::ObjCStringLiteralClass: {
MemRegionManager &MRMgr = svalBuilder.getRegionManager();
const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E);
@@ -227,13 +233,6 @@ EnvironmentManager::removeDeadBindings(Environment Env,
RSScaner.scan(X);
continue;
}
-
- // Otherwise the expression is dead with a couple exceptions.
- // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the
- // beginning of itself, but we need its UndefinedVal to determine its
- // SVal.
- if (X.isUndef() && cast<UndefinedVal>(X).getData())
- EBMapRef = EBMapRef.add(BlkExpr, X);
}
// Go through he deferred locations and add them to the new environment if
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 0dcbe1f..b79f3f5 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -13,12 +13,14 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/ParentMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
#include <vector>
using namespace clang;
@@ -57,7 +59,7 @@ ExplodedGraph::~ExplodedGraph() {}
//===----------------------------------------------------------------------===//
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
- // Reclaimn all nodes that match *all* the following criteria:
+ // Reclaim all nodes that match *all* the following criteria:
//
// (1) 1 predecessor (that has one successor)
// (2) 1 successor (that has one predecessor)
@@ -67,6 +69,9 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// (6) The 'GDM' is the same as the predecessor.
// (7) The LocationContext is the same as the predecessor.
// (8) The PostStmt is for a non-consumed Stmt or Expr.
+ // (9) The successor is not a CallExpr StmtPoint (so that we would be able to
+ // find it when retrying a call with no inlining).
+ // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
// Conditions 1 and 2.
if (node->pred_size() != 1 || node->succ_size() != 1)
@@ -82,8 +87,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Condition 3.
ProgramPoint progPoint = node->getLocation();
- if (!isa<PostStmt>(progPoint) ||
- (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint)))
+ if (!isa<PostStmt>(progPoint))
return false;
// Condition 4.
@@ -108,7 +112,13 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
return false;
}
- return true;
+ // Condition 9.
+ const ProgramPoint SuccLoc = succ->getLocation();
+ if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc))
+ if (CallEvent::mayBeInlined(SP->getStmt()))
+ return false;
+
+ return true;
}
void ExplodedGraph::collectNode(ExplodedNode *node) {
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1fd9068..b0435fb 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -18,13 +18,12 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtCXX.h"
-#include "clang/AST/DeclCXX.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/PrettyStackTrace.h"
@@ -42,8 +41,6 @@ using llvm::APSInt;
STATISTIC(NumRemoveDeadBindings,
"The # of times RemoveDeadBindings is called");
-STATISTIC(NumRemoveDeadBindingsSkipped,
- "The # of times RemoveDeadBindings is skipped");
STATISTIC(NumMaxBlockCountReached,
"The # of aborted paths due to reaching the maximum block count in "
"a top level function");
@@ -54,15 +51,6 @@ STATISTIC(NumTimesRetriedWithoutInlining,
"The # of times we re-evaluated a call without inlining");
//===----------------------------------------------------------------------===//
-// Utility functions.
-//===----------------------------------------------------------------------===//
-
-static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) {
- IdentifierInfo* II = &Ctx.Idents.get(name);
- return Ctx.Selectors.getSelector(0, &II);
-}
-
-//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -163,7 +151,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
// analyzing an "open" program.
const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
if (SFC->getParent() == 0) {
- loc::MemRegionVal L(getCXXThisRegion(MD, SFC));
+ loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
SVal V = state->getSVal(L);
if (const Loc *LV = dyn_cast<Loc>(&V)) {
state = state->assume(*LV, true);
@@ -196,7 +184,7 @@ ExprEngine::processRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) {
+ const CallEvent *Call) {
return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
Explicits, Regions, Call);
}
@@ -231,6 +219,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred);
return;
}
+ currentBuilderContext = 0;
}
static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
@@ -251,7 +240,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return true;
// Run before processing a call.
- if (isa<CallExpr>(S.getStmt()))
+ if (CallEvent::mayBeInlined(S.getStmt()))
return true;
// Is this an expression that is consumed by another expression? If so,
@@ -260,62 +249,47 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return !PM.isConsumedExpr(cast<Expr>(S.getStmt()));
}
-void ExprEngine::ProcessStmt(const CFGStmt S,
- ExplodedNode *Pred) {
- // Reclaim any unnecessary nodes in the ExplodedGraph.
- G.reclaimRecentlyAllocatedNodes();
-
- currentStmt = S.getStmt();
- PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
- currentStmt->getLocStart(),
- "Error evaluating statement");
-
- EntryNode = Pred;
-
- ProgramStateRef EntryState = EntryNode->getState();
- CleanedState = EntryState;
-
- // Create the cleaned state.
- const LocationContext *LC = EntryNode->getLocationContext();
- SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
-
- if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) {
- NumRemoveDeadBindings++;
- getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
-
- const StackFrameContext *SFC = LC->getCurrentStackFrame();
-
- // Create a state in which dead bindings are removed from the environment
- // and the store. TODO: The function should just return new env and store,
- // not a new state.
- CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
- } else {
- NumRemoveDeadBindingsSkipped++;
- }
+void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
+ const Stmt *ReferenceStmt,
+ const LocationContext *LC,
+ const Stmt *DiagnosticStmt,
+ ProgramPoint::Kind K) {
+ assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
+ ReferenceStmt == 0) && "PreStmt is not generally supported by "
+ "the SymbolReaper yet");
+ NumRemoveDeadBindings++;
+ CleanedState = Pred->getState();
+ SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager());
+
+ getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
+
+ // Create a state in which dead bindings are removed from the environment
+ // and the store. TODO: The function should just return new env and store,
+ // not a new state.
+ const StackFrameContext *SFC = LC->getCurrentStackFrame();
+ CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
// Process any special transfer function for dead symbols.
- ExplodedNodeSet Tmp;
// A tag to track convenience transitions, which can be removed at cleanup.
static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
-
if (!SymReaper.hasDeadSymbols()) {
// Generate a CleanedNode that has the environment and store cleaned
// up. Since no symbols are dead, we can optimize and not clean out
// the constraint manager.
- StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
- Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag);
+ StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext);
+ Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K);
} else {
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
- getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode,
- SymReaper, currentStmt, *this);
+ getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper,
+ DiagnosticStmt, *this, K);
// For each node in CheckedSet, generate CleanedNodes that have the
// environment, the store, and the constraints cleaned up but have the
// user-supplied states as the predecessors.
- StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext);
+ StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext);
for (ExplodedNodeSet::const_iterator
I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
ProgramStateRef CheckerState = (*I)->getState();
@@ -324,10 +298,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
SymReaper);
- assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) &&
+ assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Environment as a part of "
"checkDeadSymbols processing.");
- assert(StateMgr.haveEqualStores(CheckerState, EntryState) &&
+ assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Store as a part of "
"checkDeadSymbols processing.");
@@ -335,13 +309,35 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
- Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag,
- ProgramPoint::PostPurgeDeadSymbolsKind);
+ Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false,
+ &cleanupTag, K);
}
}
+}
+
+void ExprEngine::ProcessStmt(const CFGStmt S,
+ ExplodedNode *Pred) {
+ // Reclaim any unnecessary nodes in the ExplodedGraph.
+ G.reclaimRecentlyAllocatedNodes();
+
+ currentStmt = S.getStmt();
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ currentStmt->getLocStart(),
+ "Error evaluating statement");
+ // Remove dead bindings and symbols.
+ EntryNode = Pred;
+ ExplodedNodeSet CleanedStates;
+ if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){
+ removeDead(EntryNode, CleanedStates, currentStmt,
+ Pred->getLocationContext(), currentStmt);
+ } else
+ CleanedStates.Add(EntryNode);
+
+ // Visit the statement.
ExplodedNodeSet Dst;
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ for (ExplodedNodeSet::iterator I = CleanedStates.begin(),
+ E = CleanedStates.end(); I != E; ++I) {
ExplodedNodeSet DstI;
// Visit the statement.
Visit(currentStmt, *I, DstI);
@@ -360,49 +356,49 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
void ExprEngine::ProcessInitializer(const CFGInitializer Init,
ExplodedNode *Pred) {
ExplodedNodeSet Dst;
+ NodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+
+ ProgramStateRef State = Pred->getState();
- // We don't set EntryNode and currentStmt. And we don't clean up state.
const CXXCtorInitializer *BMI = Init.getInitializer();
+
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ BMI->getSourceLocation(),
+ "Error evaluating initializer");
+
+ // We don't set EntryNode and currentStmt. And we don't clean up state.
const StackFrameContext *stackFrame =
cast<StackFrameContext>(Pred->getLocationContext());
const CXXConstructorDecl *decl =
cast<CXXConstructorDecl>(stackFrame->getDecl());
- const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame);
-
- SVal thisVal = Pred->getState()->getSVal(thisReg);
+ SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame));
+ // Evaluate the initializer, if necessary
if (BMI->isAnyMemberInitializer()) {
- // Evaluate the initializer.
-
- StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
- ProgramStateRef state = Pred->getState();
-
- const FieldDecl *FD = BMI->getAnyMember();
-
- SVal FieldLoc = state->getLValue(FD, thisVal);
- SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext());
- state = state->bindLoc(FieldLoc, InitVal);
+ // Constructors build the object directly in the field,
+ // but non-objects must be copied in from the initializer.
+ if (!isa<CXXConstructExpr>(BMI->getInit())) {
+ SVal FieldLoc;
+ if (BMI->isIndirectMemberInitializer())
+ FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal);
+ else
+ FieldLoc = State->getLValue(BMI->getMember(), thisVal);
- // Use a custom node building process.
- PostInitializer PP(BMI, stackFrame);
- // Builder automatically add the generated node to the deferred set,
- // which are processed in the builder's dtor.
- Bldr.generateNode(PP, Pred, state);
+ SVal InitVal = State->getSVal(BMI->getInit(), stackFrame);
+ State = State->bindLoc(FieldLoc, InitVal);
+ }
} else {
- assert(BMI->isBaseInitializer());
-
- // Get the base class declaration.
- const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit());
-
- // Create the base object region.
- SVal baseVal =
- getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType());
- const MemRegion *baseReg = baseVal.getAsRegion();
- assert(baseReg);
-
- VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst);
+ assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
+ // We already did all the work when visiting the CXXConstructExpr.
}
+ // Construct a PostInitializer node whether the state changed or not,
+ // so that the diagnostics don't get confused.
+ PostInitializer PP(BMI, stackFrame);
+ // Builder automatically add the generated node to the deferred set,
+ // which are processed in the builder's dtor.
+ Bldr.generateNode(PP, State, Pred);
+
// Enqueue the new nodes onto the work list.
Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx);
}
@@ -442,27 +438,51 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
if (const ReferenceType *refType = varType->getAs<ReferenceType>())
varType = refType->getPointeeType();
- const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl();
- assert(recordDecl && "get CXXRecordDecl fail");
- const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor();
-
Loc dest = state->getLValue(varDecl, Pred->getLocationContext());
- VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(),
+ VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(),
Dtor.getTriggerStmt(), Pred, Dst);
}
void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor,
+ LCtx->getCurrentStackFrame());
+ SVal ThisVal = Pred->getState()->getSVal(ThisPtr);
+
+ // Create the base object region.
+ QualType BaseTy = D.getBaseSpecifier()->getType();
+ SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
+
+ VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(),
+ CurDtor->getBody(), Pred, Dst);
+}
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const FieldDecl *Member = D.getFieldDecl();
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ Loc ThisVal = getSValBuilder().getCXXThis(CurDtor,
+ LCtx->getCurrentStackFrame());
+ SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal)));
+
+ VisitCXXDestructor(Member->getType(),
+ cast<loc::MemRegionVal>(FieldVal).getRegion(),
+ CurDtor->getBody(), Pred, Dst);
+}
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {}
-void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
+void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet &DstTop) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
S->getLocStart(),
@@ -490,7 +510,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXTypeidExprClass:
case Stmt::CXXUuidofExprClass:
case Stmt::CXXUnresolvedConstructExprClass:
- case Stmt::CXXScalarValueInitExprClass:
case Stmt::DependentScopeDeclRefExprClass:
case Stmt::UnaryTypeTraitExprClass:
case Stmt::BinaryTypeTraitExprClass:
@@ -514,7 +533,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// We don't handle default arguments either yet, but we can fake it
// for now by just skipping them.
- case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXDefaultArgExprClass:
break;
@@ -544,6 +562,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
+ case Stmt::ObjCSubscriptRefExprClass:
+ case Stmt::ObjCPropertyRefExprClass:
+ llvm_unreachable("These are handled by PseudoObjectExpr");
+
case Stmt::GNUNullExprClass: {
// GNU __null is a pointer-width integer, not an actual pointer.
ProgramStateRef state = Pred->getState();
@@ -559,23 +581,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
- // FIXME.
- case Stmt::ObjCSubscriptRefExprClass:
- break;
-
- case Stmt::ObjCPropertyRefExprClass:
- // Implicitly handled by Environment::getSVal().
- break;
-
- case Stmt::ImplicitValueInitExprClass: {
- ProgramStateRef state = Pred->getState();
- QualType ty = cast<ImplicitValueInitExpr>(S)->getType();
- SVal val = svalBuilder.makeZeroVal(ty);
- Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(),
- val));
- break;
- }
-
case Stmt::ExprWithCleanupsClass:
// Handled due to fully linearised CFG.
break;
@@ -592,7 +597,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ObjCIsaExprClass:
case Stmt::ObjCProtocolExprClass:
case Stmt::ObjCSelectorExprClass:
- case Expr::ObjCNumericLiteralClass:
case Stmt::ParenListExprClass:
case Stmt::PredefinedExprClass:
case Stmt::ShuffleVectorExprClass:
@@ -613,6 +617,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::AddrLabelExprClass:
case Stmt::IntegerLiteralClass:
case Stmt::CharacterLiteralClass:
+ case Stmt::ImplicitValueInitExprClass:
+ case Stmt::CXXScalarValueInitExprClass:
case Stmt::CXXBoolLiteralExprClass:
case Stmt::ObjCBoolLiteralExprClass:
case Stmt::FloatingLiteralClass:
@@ -620,6 +626,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::StringLiteralClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::CXXBindTemporaryExprClass:
+ case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXNullPtrLiteralExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet preVisit;
@@ -630,22 +637,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
case Expr::ObjCArrayLiteralClass:
- case Expr::ObjCDictionaryLiteralClass: {
+ case Expr::ObjCDictionaryLiteralClass:
+ // FIXME: explicitly model with a region and the actual contents
+ // of the container. For now, conjure a symbol.
+ case Expr::ObjCBoxedExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet preVisit;
getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
- // FIXME: explicitly model with a region and the actual contents
- // of the container. For now, conjure a symbol.
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext);
+ const Expr *Ex = cast<Expr>(S);
+ QualType resultType = Ex->getType();
+
for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
it != et; ++it) {
ExplodedNode *N = *it;
- const Expr *Ex = cast<Expr>(S);
- QualType resultType = Ex->getType();
const LocationContext *LCtx = N->getLocationContext();
SVal result =
svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType,
@@ -671,6 +680,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
+ case Stmt::MSAsmStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
case Stmt::BlockExprClass:
Bldr.takeNodes(Pred);
VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst);
@@ -727,12 +742,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
case Stmt::CXXTemporaryObjectExprClass:
- case Stmt::CXXConstructExprClass: {
- const CXXConstructExpr *C = cast<CXXConstructExpr>(S);
- // For block-level CXXConstructExpr, we don't have a destination region.
- // Let VisitCXXConstructExpr() create one.
+ case Stmt::CXXConstructExprClass: {
Bldr.takeNodes(Pred);
- VisitCXXConstructExpr(C, 0, Pred, Dst);
+ VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
}
@@ -868,33 +880,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
- case Stmt::ObjCMessageExprClass: {
+ case Stmt::ObjCMessageExprClass:
Bldr.takeNodes(Pred);
- // Is this a property access?
- const ParentMap &PM = Pred->getLocationContext()->getParentMap();
- const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S);
- bool evaluated = false;
-
- if (const PseudoObjectExpr *PO =
- dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) {
- const Expr *syntactic = PO->getSyntacticForm();
- if (const ObjCPropertyRefExpr *PR =
- dyn_cast<ObjCPropertyRefExpr>(syntactic)) {
- bool isSetter = ME->getNumArgs() > 0;
- VisitObjCMessage(ObjCMessage(ME, PR, isSetter), Pred, Dst);
- evaluated = true;
- }
- else if (isa<BinaryOperator>(syntactic)) {
- VisitObjCMessage(ObjCMessage(ME, 0, true), Pred, Dst);
- }
- }
-
- if (!evaluated)
- VisitObjCMessage(ME, Pred, Dst);
-
+ VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
- }
case Stmt::ObjCAtThrowStmtClass: {
// FIXME: This is not complete. We basically treat @throw as
@@ -982,6 +972,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame();
assert(CalleeSF && CallerSF);
ExplodedNode *BeforeProcessingCall = 0;
+ const Stmt *CE = CalleeSF->getCallSite();
// Find the first node before we started processing the call expression.
while (N) {
@@ -993,11 +984,15 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
if (L.getLocationContext()->getCurrentStackFrame() != CallerSF)
continue;
// We reached the caller. Find the node right before we started
- // processing the CallExpr.
- if (isa<PostPurgeDeadSymbols>(L))
+ // processing the call.
+ if (L.isPurgeKind())
+ continue;
+ if (isa<PreImplicitCall>(&L))
+ continue;
+ if (isa<CallEnter>(&L))
continue;
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L))
- if (SP->getStmt() == CalleeSF->getCallSite())
+ if (SP->getStmt() == CE)
continue;
break;
}
@@ -1008,7 +1003,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
// TODO: Clean up the unneeded nodes.
// Build an Epsilon node from which we will restart the analyzes.
- const Stmt *CE = CalleeSF->getCallSite();
+ // Note that CE is permitted to be NULL!
ProgramPoint NewNodeLoc =
EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
// Add the special flag to GDM to signal retrying with no inlining.
@@ -1073,63 +1068,6 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
// Branch processing.
//===----------------------------------------------------------------------===//
-ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state,
- const Stmt *Terminator,
- const LocationContext *LCtx,
- bool branchTaken) {
-
- switch (Terminator->getStmtClass()) {
- default:
- return state;
-
- case Stmt::BinaryOperatorClass: { // '&&' and '||'
-
- const BinaryOperator* B = cast<BinaryOperator>(Terminator);
- BinaryOperator::Opcode Op = B->getOpcode();
-
- assert (Op == BO_LAnd || Op == BO_LOr);
-
- // For &&, if we take the true branch, then the value of the whole
- // expression is that of the RHS expression.
- //
- // For ||, if we take the false branch, then the value of the whole
- // expression is that of the RHS expression.
-
- const Expr *Ex = (Op == BO_LAnd && branchTaken) ||
- (Op == BO_LOr && !branchTaken)
- ? B->getRHS() : B->getLHS();
-
- return state->BindExpr(B, LCtx, UndefinedVal(Ex));
- }
-
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass: { // ?:
- const AbstractConditionalOperator* C
- = cast<AbstractConditionalOperator>(Terminator);
-
- // For ?, if branchTaken == true then the value is either the LHS or
- // the condition itself. (GNU extension).
-
- const Expr *Ex;
-
- if (branchTaken)
- Ex = C->getTrueExpr();
- else
- Ex = C->getFalseExpr();
-
- return state->BindExpr(C, LCtx, UndefinedVal(Ex));
- }
-
- case Stmt::ChooseExprClass: { // ?:
-
- const ChooseExpr *C = cast<ChooseExpr>(Terminator);
-
- const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS();
- return state->BindExpr(C, LCtx, UndefinedVal(Ex));
- }
- }
-}
-
/// RecoverCastedSymbol - A helper function for ProcessBranch that is used
/// to try to recover some path-sensitivity for casts of symbolic
/// integers that promote their values (which are currently not tracked well).
@@ -1172,6 +1110,45 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
return state->getSVal(Ex, LCtx);
}
+static const Stmt *ResolveCondition(const Stmt *Condition,
+ const CFGBlock *B) {
+ if (const Expr *Ex = dyn_cast<Expr>(Condition))
+ Condition = Ex->IgnoreParens();
+
+ const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition);
+ if (!BO || !BO->isLogicalOp())
+ return Condition;
+
+ // For logical operations, we still have the case where some branches
+ // use the traditional "merge" approach and others sink the branch
+ // directly into the basic blocks representing the logical operation.
+ // We need to distinguish between those two cases here.
+
+ // The invariants are still shifting, but it is possible that the
+ // last element in a CFGBlock is not a CFGStmt. Look for the last
+ // CFGStmt as the value of the condition.
+ CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
+ for (; I != E; ++I) {
+ CFGElement Elem = *I;
+ CFGStmt *CS = dyn_cast<CFGStmt>(&Elem);
+ if (!CS)
+ continue;
+ if (CS->getStmt() != Condition)
+ break;
+ return Condition;
+ }
+
+ assert(I != E);
+
+ while (Condition) {
+ BO = dyn_cast<BinaryOperator>(Condition);
+ if (!BO || !BO->isLogicalOp())
+ return Condition;
+ Condition = BO->getRHS()->IgnoreParens();
+ }
+ llvm_unreachable("could not resolve condition");
+}
+
void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
NodeBuilderContext& BldCtx,
ExplodedNode *Pred,
@@ -1188,6 +1165,12 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
return;
}
+
+ // Resolve the condition in the precense of nested '||' and '&&'.
+ if (const Expr *Ex = dyn_cast<Expr>(Condition))
+ Condition = Ex->IgnoreParens();
+
+ Condition = ResolveCondition(Condition, BldCtx.getBlock());
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Condition->getLocStart(),
"Error evaluating branch");
@@ -1230,14 +1213,10 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
}
}
- const LocationContext *LCtx = PredI->getLocationContext();
-
// If the condition is still unknown, give up.
if (X.isUnknownOrUndef()) {
- builder.generateNode(MarkBranch(PrevState, Term, LCtx, true),
- true, PredI);
- builder.generateNode(MarkBranch(PrevState, Term, LCtx, false),
- false, PredI);
+ builder.generateNode(PrevState, true, PredI);
+ builder.generateNode(PrevState, false, PredI);
continue;
}
@@ -1246,8 +1225,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
// Process the true branch.
if (builder.isFeasible(true)) {
if (ProgramStateRef state = PrevState->assume(V, true))
- builder.generateNode(MarkBranch(state, Term, LCtx, true),
- true, PredI);
+ builder.generateNode(state, true, PredI);
else
builder.markInfeasible(true);
}
@@ -1255,8 +1233,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
// Process the false branch.
if (builder.isFeasible(false)) {
if (ProgramStateRef state = PrevState->assume(V, false))
- builder.generateNode(MarkBranch(state, Term, LCtx, false),
- false, PredI);
+ builder.generateNode(state, false, PredI);
else
builder.markInfeasible(false);
}
@@ -1433,7 +1410,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
const LocationContext *LCtx = Pred->getLocationContext();
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
- assert(Ex->isLValue());
+ assert(Ex->isGLValue());
SVal V = state->getLValue(VD, Pred->getLocationContext());
// For references, the 'lvalue' is the pointer address stored in the
@@ -1450,7 +1427,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
return;
}
if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) {
- assert(!Ex->isLValue());
+ assert(!Ex->isGLValue());
SVal V = svalBuilder.makeIntVal(ED->getInitVal());
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
return;
@@ -1493,7 +1470,7 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
SVal V = state->getLValue(A->getType(),
state->getSVal(Idx, LCtx),
state->getSVal(Base, LCtx));
- assert(A->isLValue());
+ assert(A->isGLValue());
Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V),
false, 0, ProgramPoint::PostLValueKind);
}
@@ -1506,14 +1483,26 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext);
ExplodedNodeSet Dst;
Decl *member = M->getMemberDecl();
+
if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
- assert(M->isLValue());
+ assert(M->isGLValue());
Bldr.takeNodes(Pred);
VisitCommonDeclRefExpr(M, VD, Pred, Dst);
Bldr.addNodes(Dst);
return;
}
-
+
+ // Handle C++ method calls.
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(member)) {
+ Bldr.takeNodes(Pred);
+ SVal MDVal = svalBuilder.getFunctionPointer(MD);
+ ProgramStateRef state =
+ Pred->getState()->BindExpr(M, Pred->getLocationContext(), MDVal);
+ Bldr.generateNode(M, Pred, state);
+ return;
+ }
+
+
FieldDecl *field = dyn_cast<FieldDecl>(member);
if (!field) // FIXME: skipping member expressions for non-fields
return;
@@ -1538,10 +1527,17 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// For all other cases, compute an lvalue.
SVal L = state->getLValue(field, baseExprVal);
- if (M->isLValue())
+ if (M->isGLValue()) {
+ if (field->getType()->isReferenceType()) {
+ if (const MemRegion *R = L.getAsRegion())
+ L = state->getSVal(R);
+ else
+ L = UnknownVal();
+ }
+
Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0,
ProgramPoint::PostLValueKind);
- else {
+ } else {
Bldr.takeNodes(Pred);
evalLoad(Dst, M, M, Pred, state, L);
Bldr.addNodes(Dst);
@@ -1591,9 +1587,9 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
/// evalStore - Handle the semantics of a store via an assignment.
/// @param Dst The node set to store generated state nodes
-/// @param AssignE The assignment expression if the store happens in an
+/// @param AssignE The assignment expression if the store happens in an
/// assignment.
-/// @param LocatioinE The location expression that is stored to.
+/// @param LocationE The location expression that is stored to.
/// @param state The current simulation state
/// @param location The location to store the value
/// @param Val The value to be stored
@@ -1606,10 +1602,6 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
// ProgramPoint if it is non-NULL, and LocationE otherwise.
const Expr *StoreE = AssignE ? AssignE : LocationE;
- if (isa<loc::ObjCPropRef>(location)) {
- assert(false);
- }
-
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false);
@@ -1634,7 +1626,6 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
QualType LoadTy)
{
assert(!isa<NonLoc>(location) && "location cannot be a NonLoc.");
- assert(!isa<loc::ObjCPropRef>(location));
// Are we loading from a region? This actually results in two loads; one
// to fetch the address of the referenced value and one to fetch the
@@ -1814,6 +1805,12 @@ void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred,
Bldr.generateNode(A, Pred, state);
}
+void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ Bldr.generateNode(A, Pred, Pred->getState());
+}
+
//===----------------------------------------------------------------------===//
// Visualization.
//===----------------------------------------------------------------------===//
@@ -1852,6 +1849,16 @@ struct DOTGraphTraits<ExplodedNode*> :
return "";
}
+ static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) {
+ if (SLoc.isFileID()) {
+ Out << "\\lline="
+ << GraphPrintSourceManager->getExpansionLineNumber(SLoc)
+ << " col="
+ << GraphPrintSourceManager->getExpansionColumnNumber(SLoc)
+ << "\\l";
+ }
+ }
+
static std::string getNodeLabel(const ExplodedNode *N, void*){
std::string sbuf;
@@ -1861,10 +1868,17 @@ struct DOTGraphTraits<ExplodedNode*> :
ProgramPoint Loc = N->getLocation();
switch (Loc.getKind()) {
- case ProgramPoint::BlockEntranceKind:
+ case ProgramPoint::BlockEntranceKind: {
Out << "Block Entrance: B"
<< cast<BlockEntrance>(Loc).getBlock()->getBlockID();
+ if (const NamedDecl *ND =
+ dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) {
+ Out << " (";
+ ND->printName(Out);
+ Out << ")";
+ }
break;
+ }
case ProgramPoint::BlockExitKind:
assert (false);
@@ -1874,30 +1888,54 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "CallEnter";
break;
- case ProgramPoint::CallExitKind:
- Out << "CallExit";
+ case ProgramPoint::CallExitBeginKind:
+ Out << "CallExitBegin";
+ break;
+
+ case ProgramPoint::CallExitEndKind:
+ Out << "CallExitEnd";
+ break;
+
+ case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
+ Out << "PostStmtPurgeDeadSymbols";
+ break;
+
+ case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
+ Out << "PreStmtPurgeDeadSymbols";
break;
case ProgramPoint::EpsilonKind:
Out << "Epsilon Point";
break;
+ case ProgramPoint::PreImplicitCallKind: {
+ ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc);
+ Out << "PreCall: ";
+
+ // FIXME: Get proper printing options.
+ PC->getDecl()->print(Out, LangOptions());
+ printLocation(Out, PC->getLocation());
+ break;
+ }
+
+ case ProgramPoint::PostImplicitCallKind: {
+ ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc);
+ Out << "PostCall: ";
+
+ // FIXME: Get proper printing options.
+ PC->getDecl()->print(Out, LangOptions());
+ printLocation(Out, PC->getLocation());
+ break;
+ }
+
default: {
if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) {
const Stmt *S = L->getStmt();
- SourceLocation SLoc = S->getLocStart();
Out << S->getStmtClassName() << ' ' << (void*) S << ' ';
LangOptions LO; // FIXME.
S->printPretty(Out, 0, PrintingPolicy(LO));
-
- if (SLoc.isFileID()) {
- Out << "\\lline="
- << GraphPrintSourceManager->getExpansionLineNumber(SLoc)
- << " col="
- << GraphPrintSourceManager->getExpansionColumnNumber(SLoc)
- << "\\l";
- }
+ printLocation(Out, S->getLocStart());
if (isa<PreStmt>(Loc))
Out << "\\lPreStmt\\l;";
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 93e598a..46cba81 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -50,7 +50,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
}
// Simulate the effects of a "store": bind the value of the RHS
// to the L-Value represented by the LHS.
- SVal ExprVal = B->isLValue() ? LeftV : RightV;
+ SVal ExprVal = B->isGLValue() ? LeftV : RightV;
evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal),
LeftV, RightV);
continue;
@@ -58,6 +58,26 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
if (!B->isAssignmentOp()) {
StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext);
+
+ if (B->isAdditiveOp()) {
+ // If one of the operands is a location, conjure a symbol for the other
+ // one (offset) if it's unknown so that memory arithmetic always
+ // results in an ElementRegion.
+ // TODO: This can be removed after we enable history tracking with
+ // SymSymExpr.
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ if (isa<Loc>(LeftV) &&
+ RHS->getType()->isIntegerType() && RightV.isUnknown()) {
+ RightV = svalBuilder.getConjuredSymbolVal(RHS, LCtx,
+ RHS->getType(), Count);
+ }
+ if (isa<Loc>(RightV) &&
+ LHS->getType()->isIntegerType() && LeftV.isUnknown()) {
+ LeftV = svalBuilder.getConjuredSymbolVal(LHS, LCtx,
+ LHS->getType(), Count);
+ }
+ }
+
// Process non-assignments except commas or short-circuited
// logical expressions (LAnd and LOr).
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
@@ -145,7 +165,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
// In C++, assignment and compound assignment operators return an
// lvalue.
- if (B->isLValue())
+ if (B->isGLValue())
state = state->BindExpr(B, LCtx, location);
else
state = state->BindExpr(B, LCtx, Result);
@@ -162,14 +182,35 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
CanQualType T = getContext().getCanonicalType(BE->getType());
+
+ // Get the value of the block itself.
SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
Pred->getLocationContext());
+ ProgramStateRef State = Pred->getState();
+
+ // If we created a new MemRegion for the block, we should explicitly bind
+ // the captured variables.
+ if (const BlockDataRegion *BDR =
+ dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
+
+ BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
+ E = BDR->referenced_vars_end();
+
+ for (; I != E; ++I) {
+ const MemRegion *capturedR = I.getCapturedRegion();
+ const MemRegion *originalR = I.getOriginalRegion();
+ if (capturedR != originalR) {
+ SVal originalV = State->getSVal(loc::MemRegionVal(originalR));
+ State = State->bindLoc(loc::MemRegionVal(capturedR), originalV);
+ }
+ }
+ }
+
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
Bldr.generateNode(BE, Pred,
- Pred->getState()->BindExpr(BE, Pred->getLocationContext(),
- V),
+ State->BindExpr(BE, Pred->getLocationContext(), V),
false, 0,
ProgramPoint::PostLValueKind);
@@ -238,7 +279,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_Dependent:
case CK_ArrayToPointerDecay:
case CK_BitCast:
- case CK_LValueBitCast:
case CK_IntegralCast:
case CK_NullToPointer:
case CK_IntegralToPointer:
@@ -278,7 +318,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
SVal val = state->getSVal(Ex, LCtx);
- val = getStoreManager().evalDerivedToBase(val, T);
+ val = getStoreManager().evalDerivedToBase(val, CastE);
state = state->BindExpr(CastE, LCtx, val);
Bldr.generateNode(CastE, Pred, state);
continue;
@@ -291,7 +331,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
// Compute the type of the result.
QualType resultType = CastE->getType();
- if (CastE->isLValue())
+ if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
bool Failed = false;
@@ -337,10 +377,11 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_UserDefinedConversion:
case CK_ConstructorConversion:
case CK_VectorSplat:
- case CK_MemberPointerToBoolean: {
+ case CK_MemberPointerToBoolean:
+ case CK_LValueBitCast: {
// Recover some path-sensitivty by conjuring a new value.
QualType resultType = CastE->getType();
- if (CastE->isLValue())
+ if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
const LocationContext *LCtx = Pred->getLocationContext();
SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx,
@@ -366,8 +407,16 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
SVal ILV = state->getSVal(ILE, Pred->getLocationContext());
const LocationContext *LC = Pred->getLocationContext();
state = state->bindCompoundLiteral(CL, LC, ILV);
-
- if (CL->isLValue())
+
+ // Compound literal expressions are a GNU extension in C++.
+ // Unlike in C, where CLs are lvalues, in C++ CLs are prvalues,
+ // and like temporary objects created by the functional notation T()
+ // CLs are destroyed at the end of the containing full-expression.
+ // HOWEVER, an rvalue of array type is not something the analyzer can
+ // reason about, since we expect all regions to be wrapped in Locs.
+ // So we treat array CLs as lvalues as well, knowing that they will decay
+ // to pointers as soon as they are used.
+ if (CL->isGLValue() || CL->getType()->isArrayType())
B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC)));
else
B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
@@ -404,31 +453,39 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
const LocationContext *LC = N->getLocationContext();
if (const Expr *InitEx = VD->getInit()) {
- SVal InitVal = state->getSVal(InitEx, Pred->getLocationContext());
-
- // We bound the temp obj region to the CXXConstructExpr. Now recover
- // the lazy compound value when the variable is not a reference.
- if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() &&
- !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){
- InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion());
- assert(isa<nonloc::LazyCompoundVal>(InitVal));
- }
-
- // Recover some path-sensitivity if a scalar value evaluated to
- // UnknownVal.
- if (InitVal.isUnknown()) {
- QualType Ty = InitEx->getType();
- if (InitEx->isLValue()) {
- Ty = getContext().getPointerType(Ty);
- }
-
- InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty,
- currentBuilderContext->getCurrentBlockCount());
+ SVal InitVal = state->getSVal(InitEx, LC);
+
+ if (InitVal == state->getLValue(VD, LC) ||
+ (VD->getType()->isArrayType() &&
+ isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) {
+ // We constructed the object directly in the variable.
+ // No need to bind anything.
+ B.generateNode(DS, N, state);
+ } else {
+ // We bound the temp obj region to the CXXConstructExpr. Now recover
+ // the lazy compound value when the variable is not a reference.
+ if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() &&
+ !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){
+ InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion());
+ assert(isa<nonloc::LazyCompoundVal>(InitVal));
+ }
+
+ // Recover some path-sensitivity if a scalar value evaluated to
+ // UnknownVal.
+ if (InitVal.isUnknown()) {
+ QualType Ty = InitEx->getType();
+ if (InitEx->isGLValue()) {
+ Ty = getContext().getPointerType(Ty);
+ }
+
+ InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty,
+ currentBuilderContext->getCurrentBlockCount());
+ }
+ B.takeNodes(N);
+ ExplodedNodeSet Dst2;
+ evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true);
+ B.addNodes(Dst2);
}
- B.takeNodes(N);
- ExplodedNodeSet Dst2;
- evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true);
- B.addNodes(Dst2);
}
else {
B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC)));
@@ -443,48 +500,44 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
ProgramStateRef state = Pred->getState();
- const LocationContext *LCtx = Pred->getLocationContext();
- SVal X = state->getSVal(B, LCtx);
- assert(X.isUndef());
-
- const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData();
- assert(Ex);
-
- if (Ex == B->getRHS()) {
- X = state->getSVal(Ex, LCtx);
-
- // Handle undefined values.
- if (X.isUndef()) {
- Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
- return;
- }
-
- DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X);
-
- // We took the RHS. Because the value of the '&&' or '||' expression must
- // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
- // or 1. Alternatively, we could take a lazy approach, and calculate this
- // value later when necessary. We don't have the machinery in place for
- // this right now, and since most logical expressions are used for branches,
- // the payoff is not likely to be large. Instead, we do eager evaluation.
- if (ProgramStateRef newState = state->assume(XD, true))
- Bldr.generateNode(B, Pred,
- newState->BindExpr(B, LCtx,
- svalBuilder.makeIntVal(1U, B->getType())));
-
- if (ProgramStateRef newState = state->assume(XD, false))
- Bldr.generateNode(B, Pred,
- newState->BindExpr(B, LCtx,
- svalBuilder.makeIntVal(0U, B->getType())));
+
+ ExplodedNode *N = Pred;
+ while (!isa<BlockEntrance>(N->getLocation())) {
+ ProgramPoint P = N->getLocation();
+ assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P));
+ (void) P;
+ assert(N->pred_size() == 1);
+ N = *N->pred_begin();
+ }
+ assert(N->pred_size() == 1);
+ N = *N->pred_begin();
+ BlockEdge BE = cast<BlockEdge>(N->getLocation());
+ SVal X;
+
+ // Determine the value of the expression by introspecting how we
+ // got this location in the CFG. This requires looking at the previous
+ // block we were in and what kind of control-flow transfer was involved.
+ const CFGBlock *SrcBlock = BE.getSrc();
+ // The only terminator (if there is one) that makes sense is a logical op.
+ CFGTerminator T = SrcBlock->getTerminator();
+ if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) {
+ (void) Term;
+ assert(Term->isLogicalOp());
+ assert(SrcBlock->succ_size() == 2);
+ // Did we take the true or false branch?
+ unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0;
+ X = svalBuilder.makeIntVal(constant, B->getType());
}
else {
- // We took the LHS expression. Depending on whether we are '&&' or
- // '||' we know what the value of the expression is via properties of
- // the short-circuiting.
- X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
- B->getType());
- Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
+ // If there is no terminator, by construction the last statement
+ // in SrcBlock is the value of the enclosing expression.
+ assert(!SrcBlock->empty());
+ CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin());
+ const Stmt *S = Elem.getStmt();
+ X = N->getState()->getSVal(S, Pred->getLocationContext());
}
+
+ Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X));
}
void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
@@ -519,16 +572,17 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
svalBuilder.makeCompoundVal(T, vals)));
return;
}
-
- if (Loc::isLocType(T) || T->isIntegerType()) {
- assert(IE->getNumInits() == 1);
- const Expr *initEx = IE->getInit(0);
- B.generateNode(IE, Pred, state->BindExpr(IE, LCtx,
- state->getSVal(initEx, LCtx)));
- return;
- }
-
- llvm_unreachable("unprocessed InitListExpr type");
+
+ // Handle scalars: int{5} and int{}.
+ assert(NumInitElements <= 1);
+
+ SVal V;
+ if (NumInitElements == 0)
+ V = getSValBuilder().makeZeroVal(T);
+ else
+ V = state->getSVal(IE->getInit(0), LCtx);
+
+ B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
}
void ExprEngine::VisitGuardedExpr(const Expr *Ex,
@@ -537,17 +591,41 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
StmtNodeBuilder B(Pred, Dst, *currentBuilderContext);
-
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
- SVal X = state->getSVal(Ex, LCtx);
- assert (X.isUndef());
- const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
- assert(SE);
- X = state->getSVal(SE, LCtx);
-
- // Make sure that we invalidate the previous binding.
- B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true));
+ const CFGBlock *SrcBlock = 0;
+
+ for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) {
+ ProgramPoint PP = N->getLocation();
+ if (isa<PreStmtPurgeDeadSymbols>(PP) || isa<BlockEntrance>(PP)) {
+ assert(N->pred_size() == 1);
+ continue;
+ }
+ SrcBlock = cast<BlockEdge>(&PP)->getSrc();
+ break;
+ }
+
+ // Find the last expression in the predecessor block. That is the
+ // expression that is used for the value of the ternary expression.
+ bool hasValue = false;
+ SVal V;
+
+ for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(),
+ E = SrcBlock->rend(); I != E; ++I) {
+ CFGElement CE = *I;
+ if (CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) {
+ const Expr *ValEx = cast<Expr>(CS->getStmt());
+ hasValue = true;
+ V = state->getSVal(ValEx, LCtx);
+ break;
+ }
+ }
+
+ assert(hasValue);
+ (void) hasValue;
+
+ // Generate a new node with the binding from the appropriate path.
+ B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true));
}
void ExprEngine::
@@ -648,7 +726,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
}
case UO_Plus:
- assert(!U->isLValue());
+ assert(!U->isGLValue());
// FALL-THROUGH.
case UO_Deref:
case UO_AddrOf:
@@ -671,7 +749,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
case UO_LNot:
case UO_Minus:
case UO_Not: {
- assert (!U->isLValue());
+ assert (!U->isGLValue());
const Expr *Ex = U->getSubExpr()->IgnoreParens();
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
@@ -796,7 +874,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
// Since the lvalue-to-rvalue conversion is explicit in the AST,
// we bind an l-value if the operator is prefix and an lvalue (in C++).
- if (U->isLValue())
+ if (U->isGLValue())
state = state->BindExpr(U, LCtx, loc);
else
state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index a14a491..44a860f 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -14,26 +14,14 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/Basic/PrettyStackTrace.h"
using namespace clang;
using namespace ento;
-const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D,
- const StackFrameContext *SFC) {
- const Type *T = D->getTypeForDecl();
- QualType PT = getContext().getPointerType(QualType(T, 0));
- return svalBuilder.getRegionManager().getCXXThisRegion(PT, SFC);
-}
-
-const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl,
- const StackFrameContext *frameCtx) {
- return svalBuilder.getRegionManager().
- getCXXThisRegion(decl->getThisType(getContext()), frameCtx);
-}
-
void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
@@ -53,208 +41,220 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R)));
}
-void ExprEngine::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- VisitCXXConstructExpr(expr, 0, Pred, Dst);
-}
-
-void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
- const MemRegion *Dest,
+void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ const MemRegion *Target = 0;
+
+ switch (CE->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete: {
+ // See if we're constructing an existing region by looking at the next
+ // element in the CFG.
+ const CFGBlock *B = currentBuilderContext->getBlock();
+ if (currentStmtIdx + 1 < B->size()) {
+ CFGElement Next = (*B)[currentStmtIdx+1];
+
+ // Is this a constructor for a local variable?
+ if (const CFGStmt *StmtElem = dyn_cast<CFGStmt>(&Next)) {
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) {
+ if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) {
+ if (Var->getInit()->IgnoreImplicit() == CE) {
+ QualType Ty = Var->getType();
+ if (const ArrayType *AT = getContext().getAsArrayType(Ty)) {
+ // FIXME: Handle arrays, which run the same constructor for
+ // every element. This workaround will just run the first
+ // constructor (which should still invalidate the entire array).
+ SVal Base = State->getLValue(Var, LCtx);
+ Target = State->getLValue(AT->getElementType(),
+ getSValBuilder().makeZeroArrayIndex(),
+ Base).getAsRegion();
+ } else {
+ Target = State->getLValue(Var, LCtx).getAsRegion();
+ }
+ }
+ }
+ }
+ }
+
+ // Is this a constructor for a member?
+ if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) {
+ const CXXCtorInitializer *Init = InitElem->getInitializer();
+ assert(Init->isAnyMemberInitializer());
+
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+ LCtx->getCurrentStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ if (Init->isIndirectMemberInitializer()) {
+ SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal);
+ Target = Field.getAsRegion();
+ } else {
+ SVal Field = State->getLValue(Init->getMember(), ThisVal);
+ Target = Field.getAsRegion();
+ }
+ }
-#if 0
- const CXXConstructorDecl *CD = E->getConstructor();
- assert(CD);
-#endif
-
-#if 0
- if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall()))
- // FIXME: invalidate the object.
- return;
-#endif
-
-#if 0
- // Is the constructor elidable?
- if (E->isElidable()) {
- destNodes.Add(Pred);
- return;
- }
-#endif
-
- // Perform the previsit of the constructor.
- ExplodedNodeSet SrcNodes;
- SrcNodes.Add(Pred);
- ExplodedNodeSet TmpNodes;
- getCheckerManager().runCheckersForPreStmt(TmpNodes, SrcNodes, E, *this);
-
- // Evaluate the constructor. Currently we don't now allow checker-specific
- // implementations of specific constructors (as we do with ordinary
- // function calls. We can re-evaluate this in the future.
-
-#if 0
- // Inlining currently isn't fully implemented.
-
- if (AMgr.shouldInlineCall()) {
- if (!Dest)
- Dest =
- svalBuilder.getRegionManager().getCXXTempObjectRegion(E,
- Pred->getLocationContext());
-
- // The callee stack frame context used to create the 'this'
- // parameter region.
- const StackFrameContext *SFC =
- AMgr.getStackFrame(CD, Pred->getLocationContext(),
- E, currentBuilderContext->getBlock(),
- currentStmtIdx);
-
- // Create the 'this' region.
- const CXXThisRegion *ThisR =
- getCXXThisRegion(E->getConstructor()->getParent(), SFC);
-
- CallEnter Loc(E, SFC, Pred->getLocationContext());
-
- StmtNodeBuilder Bldr(SrcNodes, TmpNodes, *currentBuilderContext);
- for (ExplodedNodeSet::iterator NI = SrcNodes.begin(),
- NE = SrcNodes.end(); NI != NE; ++NI) {
- ProgramStateRef state = (*NI)->getState();
- // Setup 'this' region, so that the ctor is evaluated on the object pointed
- // by 'Dest'.
- state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
- Bldr.generateNode(Loc, *NI, state);
+ // FIXME: This will eventually need to handle new-expressions as well.
}
+
+ // If we couldn't find an existing region to construct into, we'll just
+ // generate a symbolic region, which is fine.
+
+ break;
}
-#endif
-
- // Default semantics: invalidate all regions passed as arguments.
- ExplodedNodeSet destCall;
- {
- StmtNodeBuilder Bldr(TmpNodes, destCall, *currentBuilderContext);
- for (ExplodedNodeSet::iterator i = TmpNodes.begin(), e = TmpNodes.end();
- i != e; ++i)
- {
- ExplodedNode *Pred = *i;
- const LocationContext *LC = Pred->getLocationContext();
- ProgramStateRef state = Pred->getState();
-
- state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC);
- Bldr.generateNode(E, Pred, state);
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ case CXXConstructExpr::CK_Delegating: {
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+ LCtx->getCurrentStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
+ Target = ThisVal.getAsRegion();
+ } else {
+ // Cast to the base type.
+ QualType BaseTy = CE->getType();
+ SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
+ Target = BaseVal.getAsRegion();
}
+ break;
+ }
}
- // Do the post visit.
- getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this);
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXConstructorCall> Call =
+ CEMgr.getCXXConstructorCall(CE, Target, State, LCtx);
+
+ ExplodedNodeSet DstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit,
+ *Call, *this);
+
+ ExplodedNodeSet DstInvalidated;
+ StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ defaultEvalCall(Bldr, *I, *Call);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated,
+ *Call, *this);
+ getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
}
-void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
- const MemRegion *Dest,
- const Stmt *S,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
- if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall()))
- return;
+void ExprEngine::VisitCXXDestructor(QualType ObjectType,
+ const MemRegion *Dest,
+ const Stmt *S,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) {
+ ObjectType = AT->getElementType();
+ Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(),
+ loc::MemRegionVal(Dest)).getAsRegion();
+ }
- // Create the context for 'this' region.
- const StackFrameContext *SFC =
- AnalysisDeclContexts.getContext(DD)->
- getStackFrame(Pred->getLocationContext(), S,
- currentBuilderContext->getBlock(), currentStmtIdx);
+ const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
+ assert(RecordDecl && "Only CXXRecordDecls should have destructors");
+ const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
- const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC);
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXDestructorCall> Call =
+ CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx);
- CallEnter PP(S, SFC, Pred->getLocationContext());
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ Call->getSourceRange().getBegin(),
+ "Error evaluating destructor");
- ProgramStateRef state = Pred->getState();
- state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
- Bldr.generateNode(PP, Pred, state);
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
+ *Call, *this);
+
+ ExplodedNodeSet DstInvalidated;
+ StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ defaultEvalCall(Bldr, *I, *Call);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
+ *Call, *this);
}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ // FIXME: Much of this should eventually migrate to CXXAllocatorCall.
+ // Also, we need to decide how allocators actually work -- they're not
+ // really part of the CXXNewExpr because they happen BEFORE the
+ // CXXConstructExpr subexpression. See PR12014 for some discussion.
StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
unsigned blockCount = currentBuilderContext->getCurrentBlockCount();
const LocationContext *LCtx = Pred->getLocationContext();
DefinedOrUnknownSVal symVal =
- svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount);
- const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion();
- QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
- const ElementRegion *EleReg =
- getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount);
+ ProgramStateRef State = Pred->getState();
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXAllocatorCall> Call =
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+
+ // Invalidate placement args.
+ // FIXME: Once we figure out how we want allocators to work,
+ // we should be using the usual pre-/(default-)eval-/post-call checks here.
+ State = Call->invalidateRegions(blockCount);
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- ProgramStateRef state = Pred->getState();
- state = state->BindExpr(CNE, Pred->getLocationContext(),
+ const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion();
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ const ElementRegion *EleReg =
+ getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ State = State->BindExpr(CNE, Pred->getLocationContext(),
loc::MemRegionVal(EleReg));
- Bldr.generateNode(CNE, Pred, state);
+ Bldr.generateNode(CNE, Pred, State);
return;
}
- // FIXME: Update for AST changes.
-#if 0
- // Evaluate constructor arguments.
- const FunctionProtoType *FnType = NULL;
- const CXXConstructorDecl *CD = CNE->getConstructor();
- if (CD)
- FnType = CD->getType()->getAs<FunctionProtoType>();
- ExplodedNodeSet argsEvaluated;
- Bldr.takeNodes(Pred);
- evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(),
- FnType, Pred, argsEvaluated);
- Bldr.addNodes(argsEvaluated);
-
- // Initialize the object region and bind the 'new' expression.
- for (ExplodedNodeSet::iterator I = argsEvaluated.begin(),
- E = argsEvaluated.end(); I != E; ++I) {
-
- ProgramStateRef state = (*I)->getState();
-
- // Accumulate list of regions that are invalidated.
- // FIXME: Eventually we should unify the logic for constructor
- // processing in one place.
- SmallVector<const MemRegion*, 10> regionsToInvalidate;
- for (CXXNewExpr::const_arg_iterator
- ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end();
- ai != ae; ++ai)
- {
- SVal val = state->getSVal(*ai, (*I)->getLocationContext());
- if (const MemRegion *region = val.getAsRegion())
- regionsToInvalidate.push_back(region);
- }
+ // FIXME: Once we have proper support for CXXConstructExprs inside
+ // CXXNewExpr, we need to make sure that the constructed object is not
+ // immediately invalidated here. (The placement call should happen before
+ // the constructor call anyway.)
+ FunctionDecl *FD = CNE->getOperatorNew();
+ if (FD && FD->isReservedGlobalPlacementOperator()) {
+ // Non-array placement new should always return the placement location.
+ SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
+ State = State->BindExpr(CNE, LCtx, PlacementLoc);
+ } else {
+ State = State->BindExpr(CNE, LCtx, symVal);
+ }
- if (ObjTy->isRecordType()) {
- regionsToInvalidate.push_back(EleReg);
- // Invalidate the regions.
- // TODO: Pass the call to new information as the last argument, to limit
- // the globals which will get invalidated.
- state = state->invalidateRegions(regionsToInvalidate,
- CNE, blockCount, 0, 0);
-
- } else {
- // Invalidate the regions.
- // TODO: Pass the call to new information as the last argument, to limit
- // the globals which will get invalidated.
- state = state->invalidateRegions(regionsToInvalidate,
- CNE, blockCount, 0, 0);
-
- if (CNE->hasInitializer()) {
- SVal V = state->getSVal(*CNE->constructor_arg_begin(),
- (*I)->getLocationContext());
- state = state->bindLoc(loc::MemRegionVal(EleReg), V);
- } else {
- // Explicitly set to undefined, because currently we retrieve symbolic
- // value from symbolic region.
- state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal());
- }
+ // If the type is not a record, we won't have a CXXConstructExpr as an
+ // initializer. Copy the value over.
+ if (const Expr *Init = CNE->getInitializer()) {
+ if (!isa<CXXConstructExpr>(Init)) {
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ (void)ObjTy;
+ assert(!ObjTy->isRecordType());
+ SVal Location = State->getSVal(CNE, LCtx);
+ if (isa<Loc>(Location))
+ State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx));
}
- state = state->BindExpr(CNE, (*I)->getLocationContext(),
- loc::MemRegionVal(EleReg));
- Bldr.generateNode(CNE, *I, state);
}
-#endif
+
+ Bldr.generateNode(CNE, Pred, State);
}
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index b9f4e15..8ee6723 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -11,16 +11,25 @@
//
//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "ExprEngine"
+
+#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/AST/DeclCXX.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/Support/SaveAndRestore.h"
+#define CXX_INLINING_ENABLED 1
+
using namespace clang;
using namespace ento;
+STATISTIC(NumOfDynamicDispatchPathSplits,
+ "The # of times we split the path due to imprecise dynamic dispatch info");
+
void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// Get the entry block in the CFG of the callee.
const StackFrameContext *calleeCtx = CE.getCalleeContext();
@@ -37,11 +46,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// Construct an edge representing the starting location in the callee.
BlockEdge Loc(Entry, Succ, calleeCtx);
- // Construct a new state which contains the mapping from actual to
- // formal arguments.
- const LocationContext *callerCtx = Pred->getLocationContext();
- ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx,
- calleeCtx);
+ ProgramStateRef state = Pred->getState();
// Construct a new node and add it to the worklist.
bool isNew;
@@ -51,71 +56,178 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
Engine.getWorkList()->enqueue(Node);
}
-static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) {
+// Find the last statement on the path to the exploded node and the
+// corresponding Block.
+static std::pair<const Stmt*,
+ const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
+ const Stmt *S = 0;
+ const StackFrameContext *SF =
+ Node->getLocation().getLocationContext()->getCurrentStackFrame();
+
+ // Back up through the ExplodedGraph until we reach a statement node.
while (Node) {
const ProgramPoint &PP = Node->getLocation();
- // Skip any BlockEdges.
- if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) {
- assert(Node->pred_size() == 1);
- Node = *Node->pred_begin();
- continue;
- }
+
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
- const Stmt *S = SP->getStmt();
- return dyn_cast<ReturnStmt>(S);
+ S = SP->getStmt();
+ break;
+ } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) {
+ S = CEE->getCalleeContext()->getCallSite();
+ if (S)
+ break;
+ // If we have an implicit call, we'll probably end up with a
+ // StmtPoint inside the callee, which is acceptable.
+ // (It's possible a function ONLY contains implicit calls -- such as an
+ // implicitly-generated destructor -- so we shouldn't just skip back to
+ // the CallEnter node and keep going.)
+ } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) {
+ // If we reached the CallEnter for this function, it has no statements.
+ if (CE->getCalleeContext() == SF)
+ break;
}
- break;
+
+ Node = *Node->pred_begin();
}
- return 0;
+
+ const CFGBlock *Blk = 0;
+ if (S) {
+ // Now, get the enclosing basic block.
+ while (Node && Node->pred_size() >=1 ) {
+ const ProgramPoint &PP = Node->getLocation();
+ if (isa<BlockEdge>(PP) &&
+ (PP.getLocationContext()->getCurrentStackFrame() == SF)) {
+ BlockEdge &EPP = cast<BlockEdge>(PP);
+ Blk = EPP.getDst();
+ break;
+ }
+ Node = *Node->pred_begin();
+ }
+ }
+
+ return std::pair<const Stmt*, const CFGBlock*>(S, Blk);
}
-void ExprEngine::processCallExit(ExplodedNode *Pred) {
- ProgramStateRef state = Pred->getState();
- const StackFrameContext *calleeCtx =
- Pred->getLocationContext()->getCurrentStackFrame();
- const LocationContext *callerCtx = calleeCtx->getParent();
- const Stmt *CE = calleeCtx->getCallSite();
+/// The call exit is simulated with a sequence of nodes, which occur between
+/// CallExitBegin and CallExitEnd. The following operations occur between the
+/// two program points:
+/// 1. CallExitBegin (triggers the start of call exit sequence)
+/// 2. Bind the return value
+/// 3. Run Remove dead bindings to clean up the dead symbols from the callee.
+/// 4. CallExitEnd (switch to the caller context)
+/// 5. PostStmt<CallExpr>
+void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
+ // Step 1 CEBNode was generated before the call.
+
+ const StackFrameContext *calleeCtx =
+ CEBNode->getLocationContext()->getCurrentStackFrame();
+
+ // The parent context might not be a stack frame, so make sure we
+ // look up the first enclosing stack frame.
+ const StackFrameContext *callerCtx =
+ calleeCtx->getParent()->getCurrentStackFrame();
+ const Stmt *CE = calleeCtx->getCallSite();
+ ProgramStateRef state = CEBNode->getState();
+ // Find the last statement in the function and the corresponding basic block.
+ const Stmt *LastSt = 0;
+ const CFGBlock *Blk = 0;
+ llvm::tie(LastSt, Blk) = getLastStmt(CEBNode);
+
+ // Step 2: generate node with bound return value: CEBNode -> BindedRetNode.
+
// If the callee returns an expression, bind its value to CallExpr.
- if (const ReturnStmt *RS = getReturnStmt(Pred)) {
- const LocationContext *LCtx = Pred->getLocationContext();
- SVal V = state->getSVal(RS, LCtx);
- state = state->BindExpr(CE, callerCtx, V);
+ if (CE) {
+ if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
+ const LocationContext *LCtx = CEBNode->getLocationContext();
+ SVal V = state->getSVal(RS, LCtx);
+ state = state->BindExpr(CE, callerCtx, V);
+ }
+
+ // Bind the constructed object value to CXXConstructExpr.
+ if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
+ loc::MemRegionVal This =
+ svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
+ SVal ThisV = state->getSVal(This);
+
+ // Always bind the region to the CXXConstructExpr.
+ state = state->BindExpr(CCE, callerCtx, ThisV);
+ }
}
-
- // Bind the constructed object value to CXXConstructExpr.
- if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
- const CXXThisRegion *ThisR =
- getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
-
- SVal ThisV = state->getSVal(ThisR);
- // Always bind the region to the CXXConstructExpr.
- state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV);
+
+ // Step 3: BindedRetNode -> CleanedNodes
+ // If we can find a statement and a block in the inlined function, run remove
+ // dead bindings before returning from the call. This is important to ensure
+ // that we report the issues such as leaks in the stack contexts in which
+ // they occurred.
+ ExplodedNodeSet CleanedNodes;
+ if (LastSt && Blk) {
+ static SimpleProgramPointTag retValBind("ExprEngine : Bind Return Value");
+ PostStmt Loc(LastSt, calleeCtx, &retValBind);
+ bool isNew;
+ ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew);
+ BindedRetNode->addPredecessor(CEBNode, G);
+ if (!isNew)
+ return;
+
+ NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
+ currentBuilderContext = &Ctx;
+ // Here, we call the Symbol Reaper with 0 statement and caller location
+ // context, telling it to clean up everything in the callee's context
+ // (and it's children). We use LastStmt as a diagnostic statement, which
+ // which the PreStmtPurge Dead point will be associated.
+ removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt,
+ ProgramPoint::PostStmtPurgeDeadSymbolsKind);
+ currentBuilderContext = 0;
+ } else {
+ CleanedNodes.Add(CEBNode);
}
-
- static SimpleProgramPointTag returnTag("ExprEngine : Call Return");
- PostStmt Loc(CE, callerCtx, &returnTag);
- bool isNew;
- ExplodedNode *N = G.getNode(Loc, state, false, &isNew);
- N->addPredecessor(Pred, G);
- if (!isNew)
- return;
-
- // Perform the post-condition check of the CallExpr.
- ExplodedNodeSet Dst;
- NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N);
- SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
- &Ctx);
- SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
-
- getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this,
- /* wasInlined */ true);
-
- // Enqueue the next element in the block.
- for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) {
- Engine.getWorkList()->enqueue(*I,
- calleeCtx->getCallSiteBlock(),
- calleeCtx->getIndex()+1);
+
+ for (ExplodedNodeSet::iterator I = CleanedNodes.begin(),
+ E = CleanedNodes.end(); I != E; ++I) {
+
+ // Step 4: Generate the CallExit and leave the callee's context.
+ // CleanedNodes -> CEENode
+ CallExitEnd Loc(calleeCtx, callerCtx);
+ bool isNew;
+ ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState();
+ ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew);
+ CEENode->addPredecessor(*I, G);
+ if (!isNew)
+ return;
+
+ // Step 5: Perform the post-condition check of the CallExpr and enqueue the
+ // result onto the work list.
+ // CEENode -> Dst -> WorkList
+ NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
+ SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
+ &Ctx);
+ SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call,
+ *this, true);
+
+ ExplodedNodeSet Dst;
+ if (isa<ObjCMethodCall>(Call)) {
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall,
+ cast<ObjCMethodCall>(*Call),
+ *this, true);
+ } else if (CE) {
+ getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
+ *this, true);
+ } else {
+ Dst.insert(DstPostCall);
+ }
+
+ // Enqueue the next element in the block.
+ for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
+ PSI != PSE; ++PSI) {
+ Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
+ calleeCtx->getIndex()+1);
+ }
}
}
@@ -130,8 +242,8 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) {
}
// Determine if we should inline the call.
-bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) {
- AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD);
+bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
const CFG *CalleeCFG = CalleeADC->getCFG();
// It is possible that the CFG cannot be constructed.
@@ -143,258 +255,185 @@ bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) {
== AMgr.InlineMaxStackDepth)
return false;
- if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD))
+ if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D))
return false;
if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
return false;
- return true;
-}
+ // Do not inline variadic calls (for now).
+ if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
+ if (BD->isVariadic())
+ return false;
+ }
+ else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (FD->isVariadic())
+ return false;
+ }
-// For now, skip inlining variadic functions.
-// We also don't inline blocks.
-static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) {
- if (!E->getAnalysisManager().shouldInlineCall())
+ // It is possible that the live variables analysis cannot be
+ // run. If so, bail out.
+ if (!CalleeADC->getAnalysis<RelaxedLiveVariables>())
return false;
- QualType callee = CE->getCallee()->getType();
- const FunctionProtoType *FT = 0;
- if (const PointerType *PT = callee->getAs<PointerType>())
- FT = dyn_cast<FunctionProtoType>(PT->getPointeeType());
- else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) {
- // FIXME: inline blocks.
- // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType());
- (void) BT;
- return false;
- }
- // If we have no prototype, assume the function is okay.
- if (!FT)
- return true;
- // Skip inlining of variadic functions.
- return !FT->isVariadic();
+ return true;
}
-bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
- const CallExpr *CE,
- ExplodedNode *Pred) {
- if (!shouldInlineCallExpr(CE, this))
- return false;
-
- ProgramStateRef state = Pred->getState();
- const Expr *Callee = CE->getCallee();
- const FunctionDecl *FD =
- state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl();
- if (!FD || !FD->hasBody(FD))
- return false;
-
- switch (CE->getStmtClass()) {
- default:
- // FIXME: Handle C++.
- break;
- case Stmt::CallExprClass: {
- if (!shouldInlineDecl(FD, Pred))
+/// The GDM component containing the dynamic dispatch bifurcation info. When
+/// the exact type of the receiver is not known, we want to explore both paths -
+/// one on which we do inline it and the other one on which we don't. This is
+/// done to ensure we do not drop coverage.
+/// This is the map from the receiver region to a bool, specifying either we
+/// consider this region's information precise or not along the given path.
+namespace clang {
+namespace ento {
+enum DynamicDispatchMode { DynamicDispatchModeInlined = 1,
+ DynamicDispatchModeConservative };
+
+struct DynamicDispatchBifurcationMap {};
+typedef llvm::ImmutableMap<const MemRegion*,
+ unsigned int> DynamicDispatchBifur;
+template<> struct ProgramStateTrait<DynamicDispatchBifurcationMap>
+ : public ProgramStatePartialTrait<DynamicDispatchBifur> {
+ static void *GDMIndex() { static int index; return &index; }
+};
+
+}}
+
+bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred,
+ ProgramStateRef State) {
+ assert(D);
+
+ const LocationContext *CurLC = Pred->getLocationContext();
+ const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
+ const LocationContext *ParentOfCallee = 0;
+
+ // FIXME: Refactor this check into a hypothetical CallEvent::canInline.
+ switch (Call.getKind()) {
+ case CE_Function:
+ break;
+ case CE_CXXMember:
+ case CE_CXXMemberOperator:
+ if (!CXX_INLINING_ENABLED)
+ return false;
+ break;
+ case CE_CXXConstructor: {
+ if (!CXX_INLINING_ENABLED)
+ return false;
+
+ // Only inline constructors and destructors if we built the CFGs for them
+ // properly.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
+ !ADC->getCFGBuildOptions().AddInitializers)
+ return false;
+
+ const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
+
+ // FIXME: We don't handle constructors or destructors for arrays properly.
+ const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion();
+ if (Target && isa<ElementRegion>(Target))
+ return false;
+
+ // FIXME: This is a hack. We don't handle temporary destructors
+ // right now, so we shouldn't inline their constructors.
+ const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
+ if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
+ if (!Target || !isa<DeclRegion>(Target))
return false;
- // Construct a new stack frame for the callee.
- AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD);
- const StackFrameContext *CallerSFC =
- Pred->getLocationContext()->getCurrentStackFrame();
- const StackFrameContext *CalleeSFC =
- CalleeADC->getStackFrame(CallerSFC, CE,
- currentBuilderContext->getBlock(),
- currentStmtIdx);
-
- CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext());
- bool isNew;
- if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) {
- N->addPredecessor(Pred, G);
- if (isNew)
- Engine.getWorkList()->enqueue(N);
- }
- return true;
- }
+ break;
}
- return false;
-}
+ case CE_CXXDestructor: {
+ if (!CXX_INLINING_ENABLED)
+ return false;
-static bool isPointerToConst(const ParmVarDecl *ParamDecl) {
- QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType();
- if (PointeeTy != QualType() && PointeeTy.isConstQualified() &&
- !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) {
- return true;
- }
- return false;
-}
+ // Only inline constructors and destructors if we built the CFGs for them
+ // properly.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
+ !ADC->getCFGBuildOptions().AddInitializers)
+ return false;
-// Try to retrieve the function declaration and find the function parameter
-// types which are pointers/references to a non-pointer const.
-// We do not invalidate the corresponding argument regions.
-static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
- const CallOrObjCMessage &Call) {
- const Decl *CallDecl = Call.getDecl();
- if (!CallDecl)
- return;
+ const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call);
- if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) {
- const IdentifierInfo *II = FDecl->getIdentifier();
-
- // List the cases, where the region should be invalidated even if the
- // argument is const.
- if (II) {
- StringRef FName = II->getName();
- // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
- // value into thread local storage. The value can later be retrieved with
- // 'void *ptheread_getspecific(pthread_key)'. So even thought the
- // parameter is 'const void *', the region escapes through the call.
- // - funopen - sets a buffer for future IO calls.
- // - ObjC functions that end with "NoCopy" can free memory, of the passed
- // in buffer.
- // - Many CF containers allow objects to escape through custom
- // allocators/deallocators upon container construction.
- // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
- // be deallocated by NSMapRemove.
- if (FName == "pthread_setspecific" ||
- FName == "funopen" ||
- FName.endswith("NoCopy") ||
- (FName.startswith("NS") &&
- (FName.find("Insert") != StringRef::npos)) ||
- Call.isCFCGAllowingEscape(FName))
- return;
- }
+ // FIXME: We don't handle constructors or destructors for arrays properly.
+ const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion();
+ if (Target && isa<ElementRegion>(Target))
+ return false;
- for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) {
- if (FDecl && Idx < FDecl->getNumParams()) {
- if (isPointerToConst(FDecl->getParamDecl(Idx)))
- PreserveArgs.insert(Idx);
- }
- }
- return;
+ break;
}
+ case CE_CXXAllocator:
+ if (!CXX_INLINING_ENABLED)
+ return false;
- if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) {
- assert(MDecl->param_size() <= Call.getNumArgs());
- unsigned Idx = 0;
- for (clang::ObjCMethodDecl::param_const_iterator
- I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) {
- if (isPointerToConst(*I))
- PreserveArgs.insert(Idx);
- }
- return;
+ // Do not inline allocators until we model deallocators.
+ // This is unfortunate, but basically necessary for smart pointers and such.
+ return false;
+ case CE_Block: {
+ const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion();
+ assert(BR && "If we have the block definition we should have its region");
+ AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D);
+ ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC,
+ cast<BlockDecl>(D),
+ BR);
+ break;
}
-}
-
-ProgramStateRef
-ExprEngine::invalidateArguments(ProgramStateRef State,
- const CallOrObjCMessage &Call,
- const LocationContext *LC) {
- SmallVector<const MemRegion *, 8> RegionsToInvalidate;
-
- if (Call.isObjCMessage()) {
- // Invalidate all instance variables of the receiver of an ObjC message.
- // FIXME: We should be able to do better with inter-procedural analysis.
- if (const MemRegion *MR = Call.getInstanceMessageReceiver(LC).getAsRegion())
- RegionsToInvalidate.push_back(MR);
-
- } else if (Call.isCXXCall()) {
- // Invalidate all instance variables for the callee of a C++ method call.
- // FIXME: We should be able to do better with inter-procedural analysis.
- // FIXME: We can probably do better for const versus non-const methods.
- if (const MemRegion *Callee = Call.getCXXCallee().getAsRegion())
- RegionsToInvalidate.push_back(Callee);
-
- } else if (Call.isFunctionCall()) {
- // Block calls invalidate all captured-by-reference values.
- SVal CalleeVal = Call.getFunctionCallee();
- if (const MemRegion *Callee = CalleeVal.getAsRegion()) {
- if (isa<BlockDataRegion>(Callee))
- RegionsToInvalidate.push_back(Callee);
- }
+ case CE_ObjCMessage:
+ if (!(getAnalysisManager().IPAMode == DynamicDispatch ||
+ getAnalysisManager().IPAMode == DynamicDispatchBifurcate))
+ return false;
+ break;
}
- // Indexes of arguments whose values will be preserved by the call.
- llvm::SmallSet<unsigned, 1> PreserveArgs;
- findPtrToConstParams(PreserveArgs, Call);
-
- for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) {
- if (PreserveArgs.count(idx))
- continue;
-
- SVal V = Call.getArgSVal(idx);
-
- // If we are passing a location wrapped as an integer, unwrap it and
- // invalidate the values referred by the location.
- if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
- V = Wrapped->getLoc();
- else if (!isa<Loc>(V))
- continue;
-
- if (const MemRegion *R = V.getAsRegion()) {
- // Invalidate the value of the variable passed by reference.
-
- // Are we dealing with an ElementRegion? If the element type is
- // a basic integer type (e.g., char, int) and the underlying region
- // is a variable region then strip off the ElementRegion.
- // FIXME: We really need to think about this for the general case
- // as sometimes we are reasoning about arrays and other times
- // about (char*), etc., is just a form of passing raw bytes.
- // e.g., void *p = alloca(); foo((char*)p);
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- // Checking for 'integral type' is probably too promiscuous, but
- // we'll leave it in for now until we have a systematic way of
- // handling all of these cases. Eventually we need to come up
- // with an interface to StoreManager so that this logic can be
- // appropriately delegated to the respective StoreManagers while
- // still allowing us to do checker-specific logic (e.g.,
- // invalidating reference counts), probably via callbacks.
- if (ER->getElementType()->isIntegralOrEnumerationType()) {
- const MemRegion *superReg = ER->getSuperRegion();
- if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
- isa<ObjCIvarRegion>(superReg))
- R = cast<TypedRegion>(superReg);
- }
- // FIXME: What about layers of ElementRegions?
- }
-
- // Mark this region for invalidation. We batch invalidate regions
- // below for efficiency.
- RegionsToInvalidate.push_back(R);
- } else {
- // Nuke all other arguments passed by reference.
- // FIXME: is this necessary or correct? This handles the non-Region
- // cases. Is it ever valid to store to these?
- State = State->unbindLoc(cast<Loc>(V));
- }
- }
+ if (!shouldInlineDecl(D, Pred))
+ return false;
+
+ if (!ParentOfCallee)
+ ParentOfCallee = CallerSFC;
+
+ // This may be NULL, but that's fine.
+ const Expr *CallE = Call.getOriginExpr();
+
+ // Construct a new stack frame for the callee.
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
+ const StackFrameContext *CalleeSFC =
+ CalleeADC->getStackFrame(ParentOfCallee, CallE,
+ currentBuilderContext->getBlock(),
+ currentStmtIdx);
+
+ CallEnter Loc(CallE, CalleeSFC, CurLC);
- // Invalidate designated regions using the batch invalidation API.
+ // Construct a new state which contains the mapping from actual to
+ // formal arguments.
+ State = State->enterStackFrame(Call, CalleeSFC);
- // FIXME: We can have collisions on the conjured symbol if the
- // expression *I also creates conjured symbols. We probably want
- // to identify conjured symbols by an expression pair: the enclosing
- // expression (the context) and the expression itself. This should
- // disambiguate conjured symbols.
- unsigned Count = currentBuilderContext->getCurrentBlockCount();
- StoreManager::InvalidatedSymbols IS;
+ bool isNew;
+ if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) {
+ N->addPredecessor(Pred, G);
+ if (isNew)
+ Engine.getWorkList()->enqueue(N);
+ }
- // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
- // global variables.
- return State->invalidateRegions(RegionsToInvalidate,
- Call.getOriginExpr(), Count, LC,
- &IS, &Call);
+ // If we decided to inline the call, the successor has been manually
+ // added onto the work list so remove it from the node builder.
+ Bldr.takeNodes(Pred);
+ return true;
}
-static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N,
- const CallExpr *CE) {
- void *ReplayState = N->getState()->get<ReplayWithoutInlining>();
+static ProgramStateRef getInlineFailedState(ProgramStateRef State,
+ const Stmt *CallE) {
+ void *ReplayState = State->get<ReplayWithoutInlining>();
if (!ReplayState)
return 0;
- const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState);
- if (CE == ReplayCE) {
- return N->getState()->remove<ReplayWithoutInlining>();
- }
- return 0;
+
+ assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call.");
+ (void)CallE;
+
+ return State->remove<ReplayWithoutInlining>();
}
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
@@ -402,74 +441,175 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
// Perform the previsit of the CallExpr.
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
-
- // Now evaluate the call itself.
- class DefaultEval : public GraphExpander {
- ExprEngine &Eng;
- const CallExpr *CE;
- public:
-
- DefaultEval(ExprEngine &eng, const CallExpr *ce)
- : Eng(eng), CE(ce) {}
- virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
-
- ProgramStateRef state = getReplayWithoutInliningState(Pred, CE);
-
- // First, try to inline the call.
- if (state == 0 && Eng.InlineCall(Dst, CE, Pred))
- return;
- // First handle the return value.
- StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext);
+ // Get the call in its initial state. We use this as a template to perform
+ // all the checks.
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> CallTemplate
+ = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
- // Get the callee.
- const Expr *Callee = CE->getCallee()->IgnoreParens();
- if (state == 0)
- state = Pred->getState();
- SVal L = state->getSVal(Callee, Pred->getLocationContext());
+ // Evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call.
+ ExplodedNodeSet dstCallEvaluated;
+ for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
+ I != E; ++I) {
+ evalCall(dstCallEvaluated, *I, *CallTemplate);
+ }
+
+ // Finally, perform the post-condition check of the CallExpr and store
+ // the created nodes in 'Dst'.
+ // Note that if the call was inlined, dstCallEvaluated will be empty.
+ // The post-CallExpr check will occur in processCallExit.
+ getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
+ *this);
+}
- // Figure out the result type. We do this dance to handle references.
- QualType ResultTy;
- if (const FunctionDecl *FD = L.getAsFunctionDecl())
- ResultTy = FD->getResultType();
- else
- ResultTy = CE->getType();
+void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ const CallEvent &Call) {
+ // WARNING: At this time, the state attached to 'Call' may be older than the
+ // state in 'Pred'. This is a minor optimization since CheckerManager will
+ // use an updated CallEvent instance when calling checkers, but if 'Call' is
+ // ever used directly in this function all callers should be updated to pass
+ // the most recent state. (It is probably not worth doing the work here since
+ // for some callers this will not be necessary.)
- if (CE->isLValue())
- ResultTy = Eng.getContext().getPointerType(ResultTy);
+ // Run any pre-call checks using the generic call interface.
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this);
- // Conjure a symbol value to use as the result.
- SValBuilder &SVB = Eng.getSValBuilder();
- unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount();
- const LocationContext *LCtx = Pred->getLocationContext();
- SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
+ // Actually evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call, and get a callback at
+ // defaultEvalCall if all of them fail.
+ ExplodedNodeSet dstCallEvaluated;
+ getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
+ Call, *this);
- // Generate a new state with the return value set.
- state = state->BindExpr(CE, LCtx, RetVal);
+ // Finally, run any post-call checks.
+ getCheckerManager().runCheckersForPostCall(Dst, dstCallEvaluated,
+ Call, *this);
+}
- // Invalidate the arguments.
- state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx),
- LCtx);
+ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
+ const LocationContext *LCtx,
+ ProgramStateRef State) {
+ const Expr *E = Call.getOriginExpr();
+ if (!E)
+ return State;
- // And make the result node.
- Bldr.generateNode(CE, Pred, state);
+ // Some method families have known return values.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
+ switch (Msg->getMethodFamily()) {
+ default:
+ break;
+ case OMF_autorelease:
+ case OMF_retain:
+ case OMF_self: {
+ // These methods return their receivers.
+ return State->BindExpr(E, LCtx, Msg->getReceiverSVal());
}
- };
-
- // Finally, evaluate the function call. We try each of the checkers
- // to see if the can evaluate the function call.
- ExplodedNodeSet dstCallEvaluated;
- DefaultEval defEval(*this, CE);
- getCheckerManager().runCheckersForEvalCall(dstCallEvaluated,
- dstPreVisit,
- CE, *this, &defEval);
-
- // Finally, perform the post-condition check of the CallExpr and store
- // the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
- *this);
+ }
+ } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){
+ return State->BindExpr(E, LCtx, C->getCXXThisVal());
+ }
+
+ // Conjure a symbol if the return value is unknown.
+ QualType ResultTy = Call.getResultType();
+ SValBuilder &SVB = getSValBuilder();
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ SVal R = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count);
+ return State->BindExpr(E, LCtx, R);
+}
+
+// Conservatively evaluate call by invalidating regions and binding
+// a conjured return value.
+void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
+ ExplodedNode *Pred, ProgramStateRef State) {
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ State = Call.invalidateRegions(Count, State);
+ State = bindReturnValue(Call, Pred->getLocationContext(), State);
+
+ // And make the result node.
+ Bldr.generateNode(Call.getProgramPoint(), State, Pred);
+}
+
+void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
+ const CallEvent &CallTemplate) {
+ // Make sure we have the most recent state attached to the call.
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<> Call = CallTemplate.cloneWithState(State);
+
+ if (!getAnalysisManager().shouldInlineCall()) {
+ conservativeEvalCall(*Call, Bldr, Pred, State);
+ return;
+ }
+ // Try to inline the call.
+ // The origin expression here is just used as a kind of checksum;
+ // this should still be safe even for CallEvents that don't come from exprs.
+ const Expr *E = Call->getOriginExpr();
+ ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
+
+ if (InlinedFailedState) {
+ // If we already tried once and failed, make sure we don't retry later.
+ State = InlinedFailedState;
+ } else {
+ RuntimeDefinition RD = Call->getRuntimeDefinition();
+ const Decl *D = RD.getDecl();
+ if (D) {
+ // Explore with and without inlining the call.
+ if (RD.mayHaveOtherDefinitions() &&
+ getAnalysisManager().IPAMode == DynamicDispatchBifurcate) {
+ BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred);
+ return;
+ }
+ // We are not bifurcating and we do have a Decl, so just inline.
+ if (inlineCall(*Call, D, Bldr, Pred, State))
+ return;
+ }
+ }
+
+ // If we can't inline it, handle the return value and invalidate the regions.
+ conservativeEvalCall(*Call, Bldr, Pred, State);
}
+void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
+ const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ assert(BifurReg);
+
+ // Check if we've performed the split already - note, we only want
+ // to split the path once per memory region.
+ ProgramStateRef State = Pred->getState();
+ const unsigned int *BState =
+ State->get<DynamicDispatchBifurcationMap>(BifurReg);
+ if (BState) {
+ // If we are on "inline path", keep inlining if possible.
+ if (*BState == DynamicDispatchModeInlined)
+ if (inlineCall(Call, D, Bldr, Pred, State))
+ return;
+ // If inline failed, or we are on the path where we assume we
+ // don't have enough info about the receiver to inline, conjure the
+ // return value and invalidate the regions.
+ conservativeEvalCall(Call, Bldr, Pred, State);
+ return;
+ }
+
+ // If we got here, this is the first time we process a message to this
+ // region, so split the path.
+ ProgramStateRef IState =
+ State->set<DynamicDispatchBifurcationMap>(BifurReg,
+ DynamicDispatchModeInlined);
+ inlineCall(Call, D, Bldr, Pred, IState);
+
+ ProgramStateRef NoIState =
+ State->set<DynamicDispatchBifurcationMap>(BifurReg,
+ DynamicDispatchModeConservative);
+ conservativeEvalCall(Call, Bldr, Pred, NoIState);
+
+ NumOfDynamicDispatchPathSplits++;
+ return;
+}
+
+
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index c8ad70a..e3bc498 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -13,8 +13,8 @@
#include "clang/AST/StmtObjC.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
using namespace clang;
using namespace ento;
@@ -74,7 +74,6 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
const Stmt *elem = S->getElement();
ProgramStateRef state = Pred->getState();
SVal elementV;
- StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
@@ -86,10 +85,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
}
ExplodedNodeSet dstLocation;
- Bldr.takeNodes(Pred);
evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false);
- Bldr.addNodes(dstLocation);
-
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
+
for (ExplodedNodeSet::iterator NI = dstLocation.begin(),
NE = dstLocation.end(); NI!=NE; ++NI) {
Pred = *NI;
@@ -126,148 +126,135 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
Bldr.generateNode(S, Pred, hasElems);
Bldr.generateNode(S, Pred, noElems);
}
+
+ // Finally, run any custom checkers.
+ // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+}
+
+static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) {
+ if (!Class)
+ return false;
+ if (Class->getIdentifier() == II)
+ return true;
+ return isSubclass(Class->getSuperClass(), II);
}
-void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
+void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
-
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Msg =
+ CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+
// Handle the previsits checks.
ExplodedNodeSet dstPrevisit;
- getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
- msg, *this);
-
+ getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
+ *Msg, *this);
+ ExplodedNodeSet dstGenericPrevisit;
+ getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
+ *Msg, *this);
+
// Proceed with evaluate the message expression.
ExplodedNodeSet dstEval;
- StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext);
+ StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currentBuilderContext);
- for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(),
- DE = dstPrevisit.end(); DI != DE; ++DI) {
-
+ for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
+ DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
- bool RaisesException = false;
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
- if (const Expr *Receiver = msg.getInstanceReceiver()) {
- ProgramStateRef state = Pred->getState();
- SVal recVal = state->getSVal(Receiver, Pred->getLocationContext());
+ if (UpdatedMsg->isInstanceMessage()) {
+ SVal recVal = UpdatedMsg->getReceiverSVal();
if (!recVal.isUndef()) {
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
ProgramStateRef notNilState, nilState;
- llvm::tie(notNilState, nilState) = state->assume(receiverVal);
+ llvm::tie(notNilState, nilState) = State->assume(receiverVal);
// There are three cases: can be nil or non-nil, must be nil, must be
// non-nil. We ignore must be nil, and merge the rest two into non-nil.
+ // FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
+ // Revisit once we have lazier constraints.
if (nilState && !notNilState) {
continue;
}
// Check if the "raise" message was sent.
assert(notNilState);
- if (msg.getSelector() == RaiseSel)
- RaisesException = true;
+ if (Msg->getSelector() == RaiseSel) {
+ // If we raise an exception, for now treat it as a sink.
+ // Eventually we will want to handle exceptions properly.
+ Bldr.generateNode(currentStmt, Pred, State, true);
+ continue;
+ }
- // If we raise an exception, for now treat it as a sink.
- // Eventually we will want to handle exceptions properly.
- // Dispatch to plug-in transfer function.
- evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException);
- }
- }
- else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
- IdentifierInfo* ClsName = Iface->getIdentifier();
- Selector S = msg.getSelector();
-
- // Check for special instance methods.
- if (!NSExceptionII) {
- ASTContext &Ctx = getContext();
- NSExceptionII = &Ctx.Idents.get("NSException");
+ // Generate a transition to non-Nil state.
+ if (notNilState != State)
+ Pred = Bldr.generateNode(currentStmt, Pred, notNilState);
}
-
- if (ClsName == NSExceptionII) {
- enum { NUM_RAISE_SELECTORS = 2 };
-
- // Lazily create a cache of the selectors.
- if (!NSExceptionInstanceRaiseSelectors) {
+ } else {
+ // Check for special class methods.
+ if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) {
+ if (!NSExceptionII) {
ASTContext &Ctx = getContext();
- NSExceptionInstanceRaiseSelectors =
- new Selector[NUM_RAISE_SELECTORS];
- SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
- unsigned idx = 0;
-
- // raise:format:
- II.push_back(&Ctx.Idents.get("raise"));
- II.push_back(&Ctx.Idents.get("format"));
- NSExceptionInstanceRaiseSelectors[idx++] =
- Ctx.Selectors.getSelector(II.size(), &II[0]);
-
- // raise:format::arguments:
- II.push_back(&Ctx.Idents.get("arguments"));
- NSExceptionInstanceRaiseSelectors[idx++] =
- Ctx.Selectors.getSelector(II.size(), &II[0]);
+ NSExceptionII = &Ctx.Idents.get("NSException");
}
- for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i)
- if (S == NSExceptionInstanceRaiseSelectors[i]) {
- RaisesException = true;
- break;
+ if (isSubclass(Iface, NSExceptionII)) {
+ enum { NUM_RAISE_SELECTORS = 2 };
+
+ // Lazily create a cache of the selectors.
+ if (!NSExceptionInstanceRaiseSelectors) {
+ ASTContext &Ctx = getContext();
+ NSExceptionInstanceRaiseSelectors =
+ new Selector[NUM_RAISE_SELECTORS];
+ SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
+ unsigned idx = 0;
+
+ // raise:format:
+ II.push_back(&Ctx.Idents.get("raise"));
+ II.push_back(&Ctx.Idents.get("format"));
+ NSExceptionInstanceRaiseSelectors[idx++] =
+ Ctx.Selectors.getSelector(II.size(), &II[0]);
+
+ // raise:format:arguments:
+ II.push_back(&Ctx.Idents.get("arguments"));
+ NSExceptionInstanceRaiseSelectors[idx++] =
+ Ctx.Selectors.getSelector(II.size(), &II[0]);
}
+
+ Selector S = Msg->getSelector();
+ bool RaisesException = false;
+ for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) {
+ if (S == NSExceptionInstanceRaiseSelectors[i]) {
+ RaisesException = true;
+ break;
+ }
+ }
+ if (RaisesException) {
+ // If we raise an exception, for now treat it as a sink.
+ // Eventually we will want to handle exceptions properly.
+ Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
+ continue;
+ }
+
+ }
}
-
- // If we raise an exception, for now treat it as a sink.
- // Eventually we will want to handle exceptions properly.
- // Dispatch to plug-in transfer function.
- evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException);
}
+
+ // Evaluate the call.
+ defaultEvalCall(Bldr, Pred, *UpdatedMsg);
}
+ ExplodedNodeSet dstPostvisit;
+ getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval,
+ *Msg, *this);
+
// Finally, perform the post-condition check of the ObjCMessageExpr and store
// the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
+ *Msg, *this);
}
-
-void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
- const ObjCMessage &msg,
- ExplodedNode *Pred,
- ProgramStateRef state,
- bool GenSink) {
- // First handle the return value.
- SVal ReturnValue = UnknownVal();
-
- // Some method families have known return values.
- switch (msg.getMethodFamily()) {
- default:
- break;
- case OMF_autorelease:
- case OMF_retain:
- case OMF_self: {
- // These methods return their receivers.
- const Expr *ReceiverE = msg.getInstanceReceiver();
- if (ReceiverE)
- ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext());
- break;
- }
- }
-
- // If we failed to figure out the return value, use a conjured value instead.
- if (ReturnValue.isUnknown()) {
- SValBuilder &SVB = getSValBuilder();
- QualType ResultTy = msg.getResultType(getContext());
- unsigned Count = currentBuilderContext->getCurrentBlockCount();
- const Expr *CurrentE = cast<Expr>(currentStmt);
- const LocationContext *LCtx = Pred->getLocationContext();
- ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count);
- }
-
- // Bind the return value.
- const LocationContext *LCtx = Pred->getLocationContext();
- state = state->BindExpr(currentStmt, LCtx, ReturnValue);
-
- // Invalidate the arguments (and the receiver)
- state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx);
-
- // And create the new node.
- Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink);
- assert(Bldr.hasGeneratedNodes());
-}
-
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 629f1ea..0152e32 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -95,37 +95,6 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
}
}
-static void flattenPath(PathPieces &primaryPath, PathPieces &currentPath,
- const PathPieces &oldPath) {
- for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end();
- it != et; ++it ) {
- PathDiagnosticPiece *piece = it->getPtr();
- if (const PathDiagnosticCallPiece *call =
- dyn_cast<PathDiagnosticCallPiece>(piece)) {
- IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter =
- call->getCallEnterEvent();
- if (callEnter)
- currentPath.push_back(callEnter);
- flattenPath(primaryPath, primaryPath, call->path);
- IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
- call->getCallExitEvent();
- if (callExit)
- currentPath.push_back(callExit);
- continue;
- }
- if (PathDiagnosticMacroPiece *macro =
- dyn_cast<PathDiagnosticMacroPiece>(piece)) {
- currentPath.push_back(piece);
- PathPieces newPath;
- flattenPath(primaryPath, newPath, macro->subPieces);
- macro->subPieces = newPath;
- continue;
- }
-
- currentPath.push_back(piece);
- }
-}
-
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
SmallVectorImpl<std::string> *FilesMade) {
@@ -152,8 +121,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
return;
// First flatten out the entire path to make it easier to use.
- PathPieces path;
- flattenPath(path, path, D.path);
+ PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false);
// The path as already been prechecked that all parts of the path are
// from the same file and that it is non-empty.
@@ -428,6 +396,15 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
os << "<div class=\"PathIndex";
if (Kind) os << " PathIndex" << Kind;
os << "\">" << num << "</div>";
+
+ if (num > 1) {
+ os << "</td><td><div class=\"PathNav\"><a href=\"#Path"
+ << (num - 1)
+ << "\" title=\"Previous event ("
+ << (num - 1)
+ << ")\">&#x2190;</a></div></td>";
+ }
+
os << "</td><td>";
}
@@ -441,9 +418,10 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc();
assert(L.isFileID());
StringRef BufferInfo = L.getBufferData();
- const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data();
- Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(),
- MacroName, BufferInfo.end());
+ std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc();
+ const char* MacroName = LocInfo.second + BufferInfo.data();
+ Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),
+ BufferInfo.begin(), MacroName, BufferInfo.end());
Token TheTok;
rawLexer.LexFromRawLexer(TheTok);
@@ -453,8 +431,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
os << "':\n";
- if (max > 1)
- os << "</td></tr></table>";
+ if (max > 1) {
+ os << "</td>";
+ if (num < max) {
+ os << "<td><div class=\"PathNav\"><a href=\"#";
+ if (num == max - 1)
+ os << "EndPath";
+ else
+ os << "Path" << (num + 1);
+ os << "\" title=\"Next event ("
+ << (num + 1)
+ << ")\">&#x2192;</a></div></td>";
+ }
+
+ os << "</tr></table>";
+ }
// Within a macro piece. Write out each event.
ProcessMacroPiece(os, *MP, 0);
@@ -462,8 +453,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
else {
os << html::EscapeText(P.getString());
- if (max > 1)
- os << "</td></tr></table>";
+ if (max > 1) {
+ os << "</td>";
+ if (num < max) {
+ os << "<td><div class=\"PathNav\"><a href=\"#";
+ if (num == max - 1)
+ os << "EndPath";
+ else
+ os << "Path" << (num + 1);
+ os << "\" title=\"Next event ("
+ << (num + 1)
+ << ")\">&#x2192;</a></div></td>";
+ }
+
+ os << "</tr></table>";
+ }
}
os << "</div></td></tr>";
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index ed94c79..62e602a 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -179,7 +179,7 @@ const StackFrameContext *VarRegion::getStackFrame() const {
// Region extents.
//===----------------------------------------------------------------------===//
-DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const {
+DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const {
ASTContext &Ctx = svalBuilder.getContext();
QualType T = getDesugaredValueType(Ctx);
@@ -470,7 +470,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
}
void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
- os << "base " << decl->getName();
+ os << "base{" << superRegion << ',' << decl->getName() << '}';
}
void CXXThisRegion::dumpToStream(raw_ostream &os) const {
@@ -518,10 +518,6 @@ void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "StaticGlobalsMemSpace{" << CR << '}';
}
-void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
- os << "NonStaticGlobalSpaceRegion";
-}
-
void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "GlobalInternalSpaceRegion";
}
@@ -534,17 +530,45 @@ void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "GlobalImmutableSpaceRegion";
}
-void MemRegion::dumpPretty(raw_ostream &os) const {
+void HeapSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "HeapSpaceRegion";
+}
+
+void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "UnknownSpaceRegion";
+}
+
+void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StackArgumentsSpaceRegion";
+}
+
+void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StackLocalsSpaceRegion";
+}
+
+bool MemRegion::canPrintPretty() const {
+ return false;
+}
+
+void MemRegion::printPretty(raw_ostream &os) const {
return;
}
-void VarRegion::dumpPretty(raw_ostream &os) const {
+bool VarRegion::canPrintPretty() const {
+ return true;
+}
+
+void VarRegion::printPretty(raw_ostream &os) const {
os << getDecl()->getName();
}
-void FieldRegion::dumpPretty(raw_ostream &os) const {
- superRegion->dumpPretty(os);
- os << "->" << getDecl();
+bool FieldRegion::canPrintPretty() const {
+ return superRegion->canPrintPretty();
+}
+
+void FieldRegion::printPretty(raw_ostream &os) const {
+ superRegion->printPretty(os);
+ os << "." << getDecl()->getName();
}
//===----------------------------------------------------------------------===//
@@ -643,6 +667,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){
return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion());
}
+/// Look through a chain of LocationContexts to either find the
+/// StackFrameContext that matches a DeclContext, or find a VarRegion
+/// for a variable captured by a block.
+static llvm::PointerUnion<const StackFrameContext *, const VarRegion *>
+getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
+ const DeclContext *DC,
+ const VarDecl *VD) {
+ while (LC) {
+ if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) {
+ if (cast<DeclContext>(SFC->getDecl()) == DC)
+ return SFC;
+ }
+ if (const BlockInvocationContext *BC =
+ dyn_cast<BlockInvocationContext>(LC)) {
+ const BlockDataRegion *BR =
+ static_cast<const BlockDataRegion*>(BC->getContextData());
+ // FIXME: This can be made more efficient.
+ for (BlockDataRegion::referenced_vars_iterator
+ I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end(); I != E; ++I) {
+ if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion()))
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
+ }
+ }
+
+ LC = LC->getParent();
+ }
+ return (const StackFrameContext*)0;
+}
+
const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
const MemRegion *sReg = 0;
@@ -675,7 +730,13 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
// FIXME: Once we implement scope handling, we will need to properly lookup
// 'D' to the proper LocationContext.
const DeclContext *DC = D->getDeclContext();
- const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC);
+ llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V =
+ getStackOrCaptureRegionForDeclContext(LC, DC, D);
+
+ if (V.is<const VarRegion*>())
+ return V.get<const VarRegion*>();
+
+ const StackFrameContext *STC = V.get<const StackFrameContext*>();
if (!STC)
sReg = getUnknownRegion();
@@ -800,6 +861,10 @@ const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
return getSubRegion<SymbolicRegion>(sym, getUnknownRegion());
}
+const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) {
+ return getSubRegion<SymbolicRegion>(Sym, getHeapRegion());
+}
+
const FieldRegion*
MemRegionManager::getFieldRegion(const FieldDecl *d,
const MemRegion* superRegion){
@@ -823,6 +888,37 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E,
const CXXBaseObjectRegion *
MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl,
const MemRegion *superRegion) {
+ // Check that the base class is actually a direct base of this region.
+ if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(superRegion)) {
+ if (const CXXRecordDecl *Class = TVR->getValueType()->getAsCXXRecordDecl()){
+ if (Class->isVirtuallyDerivedFrom(decl)) {
+ // Virtual base regions should not be layered, since the layout rules
+ // are different.
+ while (const CXXBaseObjectRegion *Base =
+ dyn_cast<CXXBaseObjectRegion>(superRegion)) {
+ superRegion = Base->getSuperRegion();
+ }
+ assert(superRegion && !isa<MemSpaceRegion>(superRegion));
+
+ } else {
+ // Non-virtual bases should always be direct bases.
+#ifndef NDEBUG
+ bool FoundBase = false;
+ for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(),
+ E = Class->bases_end();
+ I != E; ++I) {
+ if (I->getType()->getAsCXXRecordDecl() == decl) {
+ FoundBase = true;
+ break;
+ }
+ }
+
+ assert(FoundBase && "Not a direct base class of this region");
+#endif
+ }
+ }
+ }
+
return getSubRegion<CXXBaseObjectRegion>(decl, superRegion);
}
@@ -898,24 +994,26 @@ const MemRegion *MemRegion::getBaseRegion() const {
// View handling.
//===----------------------------------------------------------------------===//
-const MemRegion *MemRegion::StripCasts() const {
+const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
const MemRegion *R = this;
while (true) {
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- // FIXME: generalize. Essentially we want to strip away ElementRegions
- // that were layered on a symbolic region because of casts. We only
- // want to strip away ElementRegions, however, where the index is 0.
- SVal index = ER->getIndex();
- if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) {
- if (CI->getValue().getSExtValue() == 0) {
- R = ER->getSuperRegion();
- continue;
- }
- }
+ switch (R->getKind()) {
+ case ElementRegionKind: {
+ const ElementRegion *ER = cast<ElementRegion>(R);
+ if (!ER->getIndex().isZeroConstant())
+ return R;
+ R = ER->getSuperRegion();
+ break;
+ }
+ case CXXBaseObjectRegionKind:
+ if (!StripBaseCasts)
+ return R;
+ R = cast<CXXBaseObjectRegion>(R)->getSuperRegion();
+ break;
+ default:
+ return R;
}
- break;
}
- return R;
}
// FIXME: Merge with the implementation of the same method in Store.cpp
@@ -973,12 +1071,14 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const {
RegionOffset MemRegion::getAsOffset() const {
const MemRegion *R = this;
+ const MemRegion *SymbolicOffsetBase = 0;
int64_t Offset = 0;
while (1) {
switch (R->getKind()) {
default:
- return RegionOffset(0);
+ return RegionOffset(R, RegionOffset::Symbolic);
+
case SymbolicRegionKind:
case AllocaRegionKind:
case CompoundLiteralRegionKind:
@@ -987,31 +1087,95 @@ RegionOffset MemRegion::getAsOffset() const {
case VarRegionKind:
case CXXTempObjectRegionKind:
goto Finish;
+
+ case ObjCIvarRegionKind:
+ // This is a little strange, but it's a compromise between
+ // ObjCIvarRegions having unknown compile-time offsets (when using the
+ // non-fragile runtime) and yet still being distinct, non-overlapping
+ // regions. Thus we treat them as "like" base regions for the purposes
+ // of computing offsets.
+ goto Finish;
+
+ case CXXBaseObjectRegionKind: {
+ const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R);
+ R = BOR->getSuperRegion();
+
+ QualType Ty;
+ if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) {
+ Ty = TVR->getDesugaredValueType(getContext());
+ } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+ // If our base region is symbolic, we don't know what type it really is.
+ // Pretend the type of the symbol is the true dynamic type.
+ // (This will at least be self-consistent for the life of the symbol.)
+ Ty = SR->getSymbol()->getType(getContext())->getPointeeType();
+ }
+
+ const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
+ if (!Child) {
+ // We cannot compute the offset of the base class.
+ SymbolicOffsetBase = R;
+ }
+
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
+ const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child);
+
+ CharUnits BaseOffset;
+ const CXXRecordDecl *Base = BOR->getDecl();
+ if (Child->isVirtuallyDerivedFrom(Base))
+ BaseOffset = Layout.getVBaseClassOffset(Base);
+ else
+ BaseOffset = Layout.getBaseClassOffset(Base);
+
+ // The base offset is in chars, not in bits.
+ Offset += BaseOffset.getQuantity() * getContext().getCharWidth();
+ break;
+ }
case ElementRegionKind: {
const ElementRegion *ER = cast<ElementRegion>(R);
- QualType EleTy = ER->getValueType();
+ R = ER->getSuperRegion();
- if (!IsCompleteType(getContext(), EleTy))
- return RegionOffset(0);
+ QualType EleTy = ER->getValueType();
+ if (!IsCompleteType(getContext(), EleTy)) {
+ // We cannot compute the offset of the base class.
+ SymbolicOffsetBase = R;
+ continue;
+ }
SVal Index = ER->getIndex();
if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) {
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
int64_t i = CI->getValue().getSExtValue();
- CharUnits Size = getContext().getTypeSizeInChars(EleTy);
- Offset += i * Size.getQuantity() * 8;
+ // This type size is in bits.
+ Offset += i * getContext().getTypeSize(EleTy);
} else {
// We cannot compute offset for non-concrete index.
- return RegionOffset(0);
+ SymbolicOffsetBase = R;
}
- R = ER->getSuperRegion();
break;
}
case FieldRegionKind: {
const FieldRegion *FR = cast<FieldRegion>(R);
+ R = FR->getSuperRegion();
+
const RecordDecl *RD = FR->getDecl()->getParent();
- if (!RD->isCompleteDefinition())
+ if (!RD->isCompleteDefinition()) {
// We cannot compute offset for incomplete type.
- return RegionOffset(0);
+ SymbolicOffsetBase = R;
+ }
+
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
// Get the field number.
unsigned idx = 0;
for (RecordDecl::field_iterator FI = RD->field_begin(),
@@ -1022,13 +1186,14 @@ RegionOffset MemRegion::getAsOffset() const {
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
// This is offset in bits.
Offset += Layout.getFieldOffset(idx);
- R = FR->getSuperRegion();
break;
}
}
}
Finish:
+ if (SymbolicOffsetBase)
+ return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic);
return RegionOffset(R, Offset);
}
@@ -1056,26 +1221,37 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
typedef BumpVector<const MemRegion*> VarVec;
VarVec *BV = (VarVec*) A.Allocate<VarVec>();
new (BV) VarVec(BC, E - I);
+ VarVec *BVOriginal = (VarVec*) A.Allocate<VarVec>();
+ new (BVOriginal) VarVec(BC, E - I);
for ( ; I != E; ++I) {
const VarDecl *VD = *I;
const VarRegion *VR = 0;
+ const VarRegion *OriginalVR = 0;
- if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage())
+ if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) {
VR = MemMgr.getVarRegion(VD, this);
+ OriginalVR = MemMgr.getVarRegion(VD, LC);
+ }
else {
- if (LC)
+ if (LC) {
VR = MemMgr.getVarRegion(VD, LC);
+ OriginalVR = VR;
+ }
else {
VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ OriginalVR = MemMgr.getVarRegion(VD, LC);
}
}
assert(VR);
+ assert(OriginalVR);
BV->push_back(VR, BC);
+ BVOriginal->push_back(OriginalVR, BC);
}
ReferencedVars = BV;
+ OriginalVars = BVOriginal;
}
BlockDataRegion::referenced_vars_iterator
@@ -1085,8 +1261,14 @@ BlockDataRegion::referenced_vars_begin() const {
BumpVector<const MemRegion*> *Vec =
static_cast<BumpVector<const MemRegion*>*>(ReferencedVars);
- return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ?
- NULL : Vec->begin());
+ if (Vec == (void*) 0x1)
+ return BlockDataRegion::referenced_vars_iterator(0, 0);
+
+ BumpVector<const MemRegion*> *VecOriginal =
+ static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
+
+ return BlockDataRegion::referenced_vars_iterator(Vec->begin(),
+ VecOriginal->begin());
}
BlockDataRegion::referenced_vars_iterator
@@ -1096,6 +1278,12 @@ BlockDataRegion::referenced_vars_end() const {
BumpVector<const MemRegion*> *Vec =
static_cast<BumpVector<const MemRegion*>*>(ReferencedVars);
- return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ?
- NULL : Vec->end());
+ if (Vec == (void*) 0x1)
+ return BlockDataRegion::referenced_vars_iterator(0, 0);
+
+ BumpVector<const MemRegion*> *VecOriginal =
+ static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
+
+ return BlockDataRegion::referenced_vars_iterator(Vec->end(),
+ VecOriginal->end());
}
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
deleted file mode 100644
index 65cdcd9..0000000
--- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines ObjCMessage which serves as a common wrapper for ObjC
-// message expressions or implicit messages for loading/storing ObjC properties.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
-#include "clang/AST/DeclCXX.h"
-
-using namespace clang;
-using namespace ento;
-
-QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
- QualType resultTy;
- bool isLVal = false;
-
- if (isObjCMessage()) {
- resultTy = Msg.getResultType(ctx);
- } else if (const CXXConstructExpr *Ctor =
- CallE.dyn_cast<const CXXConstructExpr *>()) {
- resultTy = Ctor->getType();
- } else {
- const CallExpr *FunctionCall = CallE.get<const CallExpr *>();
-
- isLVal = FunctionCall->isLValue();
- const Expr *Callee = FunctionCall->getCallee();
- if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl())
- resultTy = FD->getResultType();
- else
- resultTy = FunctionCall->getType();
- }
-
- if (isLVal)
- resultTy = ctx.getPointerType(resultTy);
-
- return resultTy;
-}
-
-SVal CallOrObjCMessage::getFunctionCallee() const {
- assert(isFunctionCall());
- assert(!isCXXCall());
- const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens();
- return State->getSVal(Fun, LCtx);
-}
-
-SVal CallOrObjCMessage::getCXXCallee() const {
- assert(isCXXCall());
- const CallExpr *ActualCall = CallE.get<const CallExpr *>();
- const Expr *callee =
- cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument();
-
- // FIXME: Will eventually need to cope with member pointers. This is
- // a limitation in getImplicitObjectArgument().
- if (!callee)
- return UnknownVal();
-
- return State->getSVal(callee, LCtx);
-}
-
-SVal
-CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const {
- assert(isObjCMessage());
- return Msg.getInstanceReceiverSVal(State, LC);
-}
-
-const Decl *CallOrObjCMessage::getDecl() const {
- if (isCXXCall()) {
- const CXXMemberCallExpr *CE =
- cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>());
- assert(CE);
- return CE->getMethodDecl();
- } else if (isObjCMessage()) {
- return Msg.getMethodDecl();
- } else if (isFunctionCall()) {
- // In case of a C style call, use the path sensitive information to find
- // the function declaration.
- SVal CalleeVal = getFunctionCallee();
- return CalleeVal.getAsFunctionDecl();
- }
- return 0;
-}
-
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 01dd965..7d52aac 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -16,6 +16,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtCXX.h"
@@ -58,6 +59,48 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
PathPieces::~PathPieces() {}
+
+void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
+ bool ShouldFlattenMacros) const {
+ for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) {
+ PathDiagnosticPiece *Piece = I->getPtr();
+
+ switch (Piece->getKind()) {
+ case PathDiagnosticPiece::Call: {
+ PathDiagnosticCallPiece *Call = cast<PathDiagnosticCallPiece>(Piece);
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> CallEnter =
+ Call->getCallEnterEvent();
+ if (CallEnter)
+ Current.push_back(CallEnter);
+ Call->path.flattenTo(Primary, Primary, ShouldFlattenMacros);
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
+ Call->getCallExitEvent();
+ if (callExit)
+ Current.push_back(callExit);
+ break;
+ }
+ case PathDiagnosticPiece::Macro: {
+ PathDiagnosticMacroPiece *Macro = cast<PathDiagnosticMacroPiece>(Piece);
+ if (ShouldFlattenMacros) {
+ Macro->subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros);
+ } else {
+ Current.push_back(Piece);
+ PathPieces NewPath;
+ Macro->subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros);
+ // FIXME: This probably shouldn't mutate the original path piece.
+ Macro->subPieces = NewPath;
+ }
+ break;
+ }
+ case PathDiagnosticPiece::Event:
+ case PathDiagnosticPiece::ControlFlow:
+ Current.push_back(Piece);
+ break;
+ }
+ }
+}
+
+
PathDiagnostic::~PathDiagnostic() {}
PathDiagnostic::PathDiagnostic(const Decl *declWithIssue,
@@ -147,23 +190,14 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) {
if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
// Keep the PathDiagnostic with the shorter path.
+ // Note, the enclosing routine is called in deterministic order, so the
+ // results will be consistent between runs (no reason to break ties if the
+ // size is the same).
const unsigned orig_size = orig->full_size();
const unsigned new_size = D->full_size();
-
- if (orig_size <= new_size) {
- bool shouldKeepOriginal = true;
- if (orig_size == new_size) {
- // Here we break ties in a fairly arbitrary, but deterministic, way.
- llvm::FoldingSetNodeID fullProfile, fullProfileOrig;
- D->FullProfile(fullProfile);
- orig->FullProfile(fullProfileOrig);
- if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash())
- shouldKeepOriginal = false;
- }
+ if (orig_size <= new_size)
+ return;
- if (shouldKeepOriginal)
- return;
- }
Diags.RemoveNode(orig);
delete orig;
}
@@ -242,30 +276,86 @@ PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) {
//===----------------------------------------------------------------------===//
static SourceLocation getValidSourceLocation(const Stmt* S,
- LocationOrAnalysisDeclContext LAC) {
- SourceLocation L = S->getLocStart();
+ LocationOrAnalysisDeclContext LAC,
+ bool UseEnd = false) {
+ SourceLocation L = UseEnd ? S->getLocEnd() : S->getLocStart();
assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
"be passed to PathDiagnosticLocation upon creation.");
// S might be a temporary statement that does not have a location in the
- // source code, so find an enclosing statement and use it's location.
+ // source code, so find an enclosing statement and use its location.
if (!L.isValid()) {
- ParentMap *PM = 0;
+ AnalysisDeclContext *ADC;
if (LAC.is<const LocationContext*>())
- PM = &LAC.get<const LocationContext*>()->getParentMap();
+ ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext();
else
- PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
+ ADC = LAC.get<AnalysisDeclContext*>();
+
+ ParentMap &PM = ADC->getParentMap();
+
+ const Stmt *Parent = S;
+ do {
+ Parent = PM.getParent(Parent);
+
+ // In rare cases, we have implicit top-level expressions,
+ // such as arguments for implicit member initializers.
+ // In this case, fall back to the start of the body (even if we were
+ // asked for the statement end location).
+ if (!Parent) {
+ const Stmt *Body = ADC->getBody();
+ if (Body)
+ L = Body->getLocStart();
+ else
+ L = ADC->getDecl()->getLocEnd();
+ break;
+ }
- while (!L.isValid()) {
- S = PM->getParent(S);
- L = S->getLocStart();
- }
+ L = UseEnd ? Parent->getLocEnd() : Parent->getLocStart();
+ } while (!L.isValid());
}
return L;
}
+static PathDiagnosticLocation
+getLocationForCaller(const StackFrameContext *SFC,
+ const LocationContext *CallerCtx,
+ const SourceManager &SM) {
+ const CFGBlock &Block = *SFC->getCallSiteBlock();
+ CFGElement Source = Block[SFC->getIndex()];
+
+ switch (Source.getKind()) {
+ case CFGElement::Invalid:
+ llvm_unreachable("Invalid CFGElement");
+ case CFGElement::Statement:
+ return PathDiagnosticLocation(cast<CFGStmt>(Source).getStmt(),
+ SM, CallerCtx);
+ case CFGElement::Initializer: {
+ const CFGInitializer &Init = cast<CFGInitializer>(Source);
+ return PathDiagnosticLocation(Init.getInitializer()->getInit(),
+ SM, CallerCtx);
+ }
+ case CFGElement::AutomaticObjectDtor: {
+ const CFGAutomaticObjDtor &Dtor = cast<CFGAutomaticObjDtor>(Source);
+ return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),
+ SM, CallerCtx);
+ }
+ case CFGElement::BaseDtor:
+ case CFGElement::MemberDtor: {
+ const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();
+ if (const Stmt *CallerBody = CallerInfo->getBody())
+ return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);
+ return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
+ }
+ case CFGElement::TemporaryDtor:
+ llvm_unreachable("not yet implemented!");
+ }
+
+ llvm_unreachable("Unknown CFGElement kind");
+}
+
+
PathDiagnosticLocation
PathDiagnosticLocation::createBegin(const Decl *D,
const SourceManager &SM) {
@@ -280,6 +370,17 @@ PathDiagnosticLocation
SM, SingleLocK);
}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createEnd(const Stmt *S,
+ const SourceManager &SM,
+ LocationOrAnalysisDeclContext LAC) {
+ if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S))
+ return createEndBrace(CS, SM);
+ return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),
+ SM, SingleLocK);
+}
+
PathDiagnosticLocation
PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
const SourceManager &SM) {
@@ -339,6 +440,19 @@ PathDiagnosticLocation
else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
S = PS->getStmt();
}
+ else if (const PostImplicitCall *PIE = dyn_cast<PostImplicitCall>(&P)) {
+ return PathDiagnosticLocation(PIE->getLocation(), SMng);
+ }
+ else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) {
+ return getLocationForCaller(CE->getCalleeContext(),
+ CE->getLocationContext(),
+ SMng);
+ }
+ else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) {
+ return getLocationForCaller(CEE->getCalleeContext(),
+ CEE->getLocationContext(),
+ SMng);
+ }
return PathDiagnosticLocation(S, SMng, P.getLocationContext());
}
@@ -495,25 +609,14 @@ PathDiagnosticLocation PathDiagnostic::getLocation() const {
// Manipulation of PathDiagnosticCallPieces.
//===----------------------------------------------------------------------===//
-static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N,
- const SourceManager &SM) {
- while (N) {
- ProgramPoint PP = N->getLocation();
- if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP))
- return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext());
- if (N->pred_empty())
- break;
- N = *N->pred_begin();
- }
- return PathDiagnosticLocation();
-}
-
PathDiagnosticCallPiece *
PathDiagnosticCallPiece::construct(const ExplodedNode *N,
- const CallExit &CE,
+ const CallExitEnd &CE,
const SourceManager &SM) {
- const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
- PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
+ const Decl *caller = CE.getLocationContext()->getDecl();
+ PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(),
+ CE.getLocationContext(),
+ SM);
return new PathDiagnosticCallPiece(caller, pos);
}
@@ -528,11 +631,11 @@ PathDiagnosticCallPiece::construct(PathPieces &path,
void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
const SourceManager &SM) {
- const Decl *D = CE.getCalleeContext()->getDecl();
- Callee = D;
- callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM,
- CE.getLocationContext());
- callEnterWithin = PathDiagnosticLocation::createBegin(D, SM);
+ const StackFrameContext *CalleeCtx = CE.getCalleeContext();
+ Callee = CalleeCtx->getDecl();
+
+ callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM);
+ callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM);
}
IntrusiveRefCntPtr<PathDiagnosticEventPiece>
@@ -667,16 +770,15 @@ StackHintGenerator::~StackHintGenerator() {}
std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
ProgramPoint P = N->getLocation();
- const CallExit *CExit = dyn_cast<CallExit>(&P);
- assert(CExit && "Stack Hints should be constructed at CallExit points.");
+ const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P);
+ assert(CExit && "Stack Hints should be constructed at CallExitEnd points.");
- const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
+ // FIXME: Use CallEvent to abstract this over all calls.
+ const Stmt *CallSite = CExit->getCalleeContext()->getCallSite();
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite);
if (!CE)
return "";
- // Get the successor node to make sure the return statement is evaluated and
- // CE is set to the result value.
- N = *N->succ_begin();
if (!N)
return getMessageForSymbolNotFound();
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 323cede..58a4bba 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -31,7 +31,6 @@ namespace {
const std::string OutputFile;
const LangOptions &LangOpts;
OwningPtr<PathDiagnosticConsumer> SubPD;
- bool flushed;
const bool SupportsCrossFileDiagnostics;
public:
PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts,
@@ -61,7 +60,7 @@ PlistDiagnostics::PlistDiagnostics(const std::string& output,
const LangOptions &LO,
bool supportsMultipleFiles,
PathDiagnosticConsumer *subPD)
- : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false),
+ : OutputFile(output), LangOpts(LO), SubPD(subPD),
SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
PathDiagnosticConsumer*
@@ -183,10 +182,18 @@ static void ReportControlFlow(raw_ostream &o,
I!=E; ++I) {
Indent(o, indent) << "<dict>\n";
++indent;
+
+ // Make the ranges of the start and end point self-consistent with adjacent edges
+ // by forcing to use only the beginning of the range. This simplifies the layout
+ // logic for clients.
Indent(o, indent) << "<key>start</key>\n";
- EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1);
+ SourceLocation StartEdge = I->getStart().asRange().getBegin();
+ EmitRange(o, SM, LangOpts, SourceRange(StartEdge, StartEdge), FM, indent+1);
+
Indent(o, indent) << "<key>end</key>\n";
- EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1);
+ SourceLocation EndEdge = I->getEnd().asRange().getBegin();
+ EmitRange(o, SM, LangOpts, SourceRange(EndEdge, EndEdge), FM, indent+1);
+
--indent;
Indent(o, indent) << "</dict>\n";
}
@@ -382,6 +389,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
if (const PathDiagnosticCallPiece *call =
dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece>
+ callEnterWithin = call->getCallEnterWithinCallerEvent();
+ if (callEnterWithin)
+ AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation());
+
WorkList.push_back(&call->path);
}
else if (const PathDiagnosticMacroPiece *macro =
@@ -476,6 +488,17 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <key>issue_context</key>";
EmitString(o, declName) << '\n';
}
+
+ // Output the bug hash for issue unique-ing. Currently, it's just an
+ // offset from the beginning of the function.
+ if (const Stmt *Body = DeclWithIssue->getBody()) {
+ FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()),
+ *SM);
+ FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM);
+ o << " <key>issue_hash</key><integer>"
+ << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber()
+ << "</integer>\n";
+ }
}
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index b9cfa27..dc988cc 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/CFG.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
@@ -70,6 +71,19 @@ ProgramState::~ProgramState() {
stateMgr->getStoreManager().decrementReferenceCount(store);
}
+ProgramStateManager::ProgramStateManager(ASTContext &Ctx,
+ StoreManagerCreator CreateSMgr,
+ ConstraintManagerCreator CreateCMgr,
+ llvm::BumpPtrAllocator &alloc,
+ SubEngine &SubEng)
+ : Eng(&SubEng), EnvMgr(alloc), GDMFactory(alloc),
+ svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
+ CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
+ StoreMgr.reset((*CreateSMgr)(*this));
+ ConstraintMgr.reset((*CreateCMgr)(*this, SubEng));
+}
+
+
ProgramStateManager::~ProgramStateManager() {
for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end();
I!=E; ++I)
@@ -157,7 +171,7 @@ ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols *IS,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
if (!IS) {
StoreManager::InvalidatedSymbols invalidated;
return invalidateRegionsImpl(Regions, E, Count, LCtx,
@@ -171,7 +185,7 @@ ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
ProgramStateManager &Mgr = getStateManager();
SubEngine* Eng = Mgr.getOwningEngine();
@@ -203,11 +217,11 @@ ProgramStateRef ProgramState::unbindLoc(Loc LV) const {
}
ProgramStateRef
-ProgramState::enterStackFrame(const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx) const {
- const StoreRef &new_store =
- getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx);
- return makeWithStore(new_store);
+ProgramState::enterStackFrame(const CallEvent &Call,
+ const StackFrameContext *CalleeCtx) const {
+ const StoreRef &NewStore =
+ getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx);
+ return makeWithStore(NewStore);
}
SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
@@ -485,8 +499,6 @@ ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key)
return getPersistentState(NewState);
}
-void ScanReachableSymbols::anchor() { }
-
bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I)
if (!scan(*I))
@@ -530,6 +542,9 @@ bool ScanReachableSymbols::scan(SVal val) {
if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val))
return scan(X->getRegion());
+ if (nonloc::LazyCompoundVal *X = dyn_cast<nonloc::LazyCompoundVal>(&val))
+ return scan(X->getRegion());
+
if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val))
return scan(X->getLoc());
@@ -564,20 +579,30 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
return false;
// If this is a subregion, also visit the parent regions.
- if (const SubRegion *SR = dyn_cast<SubRegion>(R))
- if (!scan(SR->getSuperRegion()))
+ if (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
+ const MemRegion *Super = SR->getSuperRegion();
+ if (!scan(Super))
return false;
- // Now look at the binding to this region (if any).
- if (!scan(state->getSValAsScalarOrLoc(R)))
- return false;
+ // When we reach the topmost region, scan all symbols in it.
+ if (isa<MemSpaceRegion>(Super)) {
+ StoreManager &StoreMgr = state->getStateManager().getStoreManager();
+ if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this))
+ return false;
+ }
+ }
- // Now look at the subregions.
- if (!SRM.get())
- SRM.reset(state->getStateManager().getStoreManager().
- getSubRegionMap(state->getStore()));
+ // Regions captured by a block are also implicitly reachable.
+ if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) {
+ BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
+ E = BDR->referenced_vars_end();
+ for ( ; I != E; ++I) {
+ if (!scan(I.getCapturedRegion()))
+ return false;
+ }
+ }
- return SRM->iterSubRegions(R, *this);
+ return true;
}
bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const {
@@ -707,3 +732,39 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
return Tainted;
}
+
+/// The GDM component containing the dynamic type info. This is a map from a
+/// symbol to it's most likely type.
+namespace clang {
+namespace ento {
+typedef llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo> DynamicTypeMap;
+template<> struct ProgramStateTrait<DynamicTypeMap>
+ : public ProgramStatePartialTrait<DynamicTypeMap> {
+ static void *GDMIndex() { static int index; return &index; }
+};
+}}
+
+DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const {
+ // Look up the dynamic type in the GDM.
+ const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg);
+ if (GDMType)
+ return *GDMType;
+
+ // Otherwise, fall back to what we know about the region.
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(Reg))
+ return DynamicTypeInfo(TR->getValueType());
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) {
+ SymbolRef Sym = SR->getSymbol();
+ return DynamicTypeInfo(Sym->getType(getStateManager().getContext()));
+ }
+
+ return DynamicTypeInfo();
+}
+
+ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg,
+ DynamicTypeInfo NewTy) const {
+ ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy);
+ assert(NewState);
+ return NewState;
+}
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 98eb958..550404a 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/Debug.h"
@@ -143,6 +144,92 @@ private:
}
}
+ const llvm::APSInt &getMinValue() const {
+ assert(!isEmpty());
+ return ranges.begin()->From();
+ }
+
+ bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+ // This function has nine cases, the cartesian product of range-testing
+ // both the upper and lower bounds against the symbol's type.
+ // Each case requires a different pinning operation.
+ // The function returns false if the described range is entirely outside
+ // the range of values for the associated symbol.
+ APSIntType Type(getMinValue());
+ APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower);
+ APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper);
+
+ switch (LowerTest) {
+ case APSIntType::RTR_Below:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower < Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range starts below what's possible but ends within it. Pin.
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range spans all possible values for the symbol. Pin.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Within:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps around, but all lower values are not possible.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range may or may not wrap around, but both limits are valid.
+ Type.apply(Lower);
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range starts within what's possible but ends above it. Pin.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Above:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps but is outside the symbol's set of possible values.
+ return false;
+ case APSIntType::RTR_Within:
+ // The range starts above what's possible but ends within it (wrap).
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower < Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ }
+
+ return true;
+ }
+
public:
// Returns a set containing the values in the receiving set, intersected with
// the closed range [Lower, Upper]. Unlike the Range type, this range uses
@@ -152,8 +239,10 @@ public:
// intersection with the two ranges [Min, Upper] and [Lower, Max],
// or, alternatively, /removing/ all integers between Upper and Lower.
RangeSet Intersect(BasicValueFactory &BV, Factory &F,
- const llvm::APSInt &Lower,
- const llvm::APSInt &Upper) const {
+ llvm::APSInt Lower, llvm::APSInt Upper) const {
+ if (!pin(Lower, Upper))
+ return F.getEmptySet();
+
PrimRangeSet newRanges = F.getEmptySet();
PrimRangeSet::iterator i = begin(), e = end();
@@ -166,6 +255,7 @@ public:
IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e);
IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e);
}
+
return newRanges;
}
@@ -206,8 +296,8 @@ namespace {
class RangeConstraintManager : public SimpleConstraintManager{
RangeSet GetRange(ProgramStateRef state, SymbolRef sym);
public:
- RangeConstraintManager(SubEngine &subengine)
- : SimpleConstraintManager(subengine) {}
+ RangeConstraintManager(SubEngine &subengine, BasicValueFactory &BVF)
+ : SimpleConstraintManager(subengine, BVF) {}
ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
@@ -252,9 +342,9 @@ private:
} // end anonymous namespace
-ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&,
- SubEngine &subeng) {
- return new RangeConstraintManager(subeng);
+ConstraintManager *
+ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine &Eng) {
+ return new RangeConstraintManager(Eng, StMgr.getBasicVals());
}
const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St,
@@ -288,8 +378,8 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) {
// Lazily generate a new RangeSet representing all possible values for the
// given symbol type.
- QualType T = state->getSymbolManager().getType(sym);
- BasicValueFactory& BV = state->getBasicVals();
+ BasicValueFactory &BV = getBasicVals();
+ QualType T = sym->getType(BV.getContext());
return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T));
}
@@ -306,117 +396,154 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) {
// UINT_MAX, 0, 1, and 2.
ProgramStateRef
-RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- llvm::APSInt Lower = Int-Adjustment;
+RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within)
+ return St;
+
+ llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment;
llvm::APSInt Upper = Lower;
--Lower;
++Upper;
// [Int-Adjustment+1, Int-Adjustment-1]
// Notice that the lower bound is greater than the upper bound.
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Upper, Lower);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
+RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within)
+ return NULL;
+
// [Int-Adjustment, Int-Adjustment]
- BasicValueFactory &BV = state->getBasicVals();
- llvm::APSInt AdjInt = Int-Adjustment;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, AdjInt, AdjInt);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment;
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Min = BV.getMinValue(T);
+RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return NULL;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return St;
+ }
// Special case for Int == Min. This is always false.
- if (Int == Min)
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (ComparisonVal == Min)
return NULL;
llvm::APSInt Lower = Min-Adjustment;
- llvm::APSInt Upper = Int-Adjustment;
+ llvm::APSInt Upper = ComparisonVal-Adjustment;
--Upper;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Max = BV.getMaxValue(T);
+RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return St;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return NULL;
+ }
// Special case for Int == Max. This is always false.
- if (Int == Max)
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (ComparisonVal == Max)
return NULL;
- llvm::APSInt Lower = Int-Adjustment;
+ llvm::APSInt Lower = ComparisonVal-Adjustment;
llvm::APSInt Upper = Max-Adjustment;
++Lower;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Min = BV.getMinValue(T);
+RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return St;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return NULL;
+ }
// Special case for Int == Min. This is always feasible.
- if (Int == Min)
- return state;
-
- const llvm::APSInt &Max = BV.getMaxValue(T);
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (ComparisonVal == Min)
+ return St;
- llvm::APSInt Lower = Int-Adjustment;
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ llvm::APSInt Lower = ComparisonVal-Adjustment;
llvm::APSInt Upper = Max-Adjustment;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Max = BV.getMaxValue(T);
+RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return NULL;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return St;
+ }
// Special case for Int == Max. This is always feasible.
- if (Int == Max)
- return state;
-
- const llvm::APSInt &Min = BV.getMinValue(T);
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (ComparisonVal == Max)
+ return St;
+ llvm::APSInt Min = AdjustmentType.getMinValue();
llvm::APSInt Lower = Min-Adjustment;
- llvm::APSInt Upper = Int-Adjustment;
+ llvm::APSInt Upper = ComparisonVal-Adjustment;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
//===------------------------------------------------------------------------===
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index cc3ea8c3..bc4e4bb 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -17,10 +17,11 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/CXXInheritance.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Basic/TargetInfo.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -40,23 +41,49 @@ using llvm::Optional;
namespace {
class BindingKey {
public:
- enum Kind { Direct = 0x0, Default = 0x1 };
+ enum Kind { Default = 0x0, Direct = 0x1 };
private:
- llvm ::PointerIntPair<const MemRegion*, 1> P;
- uint64_t Offset;
+ enum { Symbolic = 0x2 };
+ llvm::PointerIntPair<const MemRegion *, 2> P;
+ uint64_t Data;
+
+ explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k)
+ : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) {
+ assert(r && Base && "Must have known regions.");
+ assert(getConcreteOffsetRegion() == Base && "Failed to store base region");
+ }
explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
- : P(r, (unsigned) k), Offset(offset) {}
+ : P(r, k), Data(offset) {
+ assert(r && "Must have known regions.");
+ assert(getOffset() == offset && "Failed to store offset");
+ assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r)) && "Not a base");
+ }
public:
- bool isDirect() const { return P.getInt() == Direct; }
+ bool isDirect() const { return P.getInt() & Direct; }
+ bool hasSymbolicOffset() const { return P.getInt() & Symbolic; }
const MemRegion *getRegion() const { return P.getPointer(); }
- uint64_t getOffset() const { return Offset; }
+ uint64_t getOffset() const {
+ assert(!hasSymbolicOffset());
+ return Data;
+ }
+
+ const MemRegion *getConcreteOffsetRegion() const {
+ assert(hasSymbolicOffset());
+ return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data));
+ }
+
+ const MemRegion *getBaseRegion() const {
+ if (hasSymbolicOffset())
+ return getConcreteOffsetRegion()->getBaseRegion();
+ return getRegion()->getBaseRegion();
+ }
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddPointer(P.getOpaqueValue());
- ID.AddInteger(Offset);
+ ID.AddInteger(Data);
}
static BindingKey Make(const MemRegion *R, Kind k);
@@ -66,48 +93,48 @@ public:
return true;
if (P.getOpaqueValue() > X.P.getOpaqueValue())
return false;
- return Offset < X.Offset;
+ return Data < X.Data;
}
bool operator==(const BindingKey &X) const {
return P.getOpaqueValue() == X.P.getOpaqueValue() &&
- Offset == X.Offset;
+ Data == X.Data;
}
- bool isValid() const {
- return getRegion() != NULL;
- }
+ LLVM_ATTRIBUTE_USED void dump() const;
};
} // end anonymous namespace
BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- const RegionRawOffset &O = ER->getAsArrayOffset();
-
- // FIXME: There are some ElementRegions for which we cannot compute
- // raw offsets yet, including regions with symbolic offsets. These will be
- // ignored by the store.
- return BindingKey(O.getRegion(), O.getOffset().getQuantity(), k);
- }
+ const RegionOffset &RO = R->getAsOffset();
+ if (RO.hasSymbolicOffset())
+ return BindingKey(R, RO.getRegion(), k);
- return BindingKey(R, 0, k);
+ return BindingKey(RO.getRegion(), RO.getOffset(), k);
}
namespace llvm {
static inline
raw_ostream &operator<<(raw_ostream &os, BindingKey K) {
- os << '(' << K.getRegion() << ',' << K.getOffset()
- << ',' << (K.isDirect() ? "direct" : "default")
+ os << '(' << K.getRegion();
+ if (!K.hasSymbolicOffset())
+ os << ',' << K.getOffset();
+ os << ',' << (K.isDirect() ? "direct" : "default")
<< ')';
return os;
}
} // end llvm namespace
+void BindingKey::dump() const {
+ llvm::errs() << *this;
+}
+
//===----------------------------------------------------------------------===//
// Actual Store type.
//===----------------------------------------------------------------------===//
-typedef llvm::ImmutableMap<BindingKey, SVal> RegionBindings;
+typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings;
+typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> RegionBindings;
//===----------------------------------------------------------------------===//
// Fine-grained control of RegionStoreManager.
@@ -138,75 +165,15 @@ public:
namespace {
-class RegionStoreSubRegionMap : public SubRegionMap {
-public:
- typedef llvm::ImmutableSet<const MemRegion*> Set;
- typedef llvm::DenseMap<const MemRegion*, Set> Map;
-private:
- Set::Factory F;
- Map M;
-public:
- bool add(const MemRegion* Parent, const MemRegion* SubRegion) {
- Map::iterator I = M.find(Parent);
-
- if (I == M.end()) {
- M.insert(std::make_pair(Parent, F.add(F.getEmptySet(), SubRegion)));
- return true;
- }
-
- I->second = F.add(I->second, SubRegion);
- return false;
- }
-
- void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R);
-
- ~RegionStoreSubRegionMap() {}
-
- const Set *getSubRegions(const MemRegion *Parent) const {
- Map::const_iterator I = M.find(Parent);
- return I == M.end() ? NULL : &I->second;
- }
-
- bool iterSubRegions(const MemRegion* Parent, Visitor& V) const {
- Map::const_iterator I = M.find(Parent);
-
- if (I == M.end())
- return true;
-
- Set S = I->second;
- for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) {
- if (!V.Visit(Parent, *SI))
- return false;
- }
-
- return true;
- }
-};
-
-void
-RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL,
- const SubRegion *R) {
- const MemRegion *superR = R->getSuperRegion();
- if (add(superR, R))
- if (const SubRegion *sr = dyn_cast<SubRegion>(superR))
- WL.push_back(sr);
-}
-
class RegionStoreManager : public StoreManager {
const RegionStoreFeatures Features;
RegionBindings::Factory RBFactory;
+ ClusterBindings::Factory CBFactory;
public:
RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f)
- : StoreManager(mgr),
- Features(f),
- RBFactory(mgr.getAllocator()) {}
-
- SubRegionMap *getSubRegionMap(Store store) {
- return getRegionStoreSubRegionMap(store);
- }
-
- RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store);
+ : StoreManager(mgr), Features(f),
+ RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {}
Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R);
/// getDefaultBinding - Returns an SVal* representing an optional default
@@ -257,13 +224,15 @@ public:
const Expr *E, unsigned Count,
const LocationContext *LCtx,
InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call,
+ const CallEvent *Call,
InvalidatedRegions *Invalidated);
+ bool scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Callbacks);
+
public: // Made public for helper classes.
- void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R,
- RegionStoreSubRegionMap &M);
+ RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R);
RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V);
@@ -282,6 +251,8 @@ public: // Made public for helper classes.
BindingKey::Default);
}
+ RegionBindings removeCluster(RegionBindings B, const MemRegion *R);
+
public: // Part of public interface to class.
StoreRef Bind(Store store, Loc LV, SVal V);
@@ -307,10 +278,14 @@ public: // Part of public interface to class.
/// BindStruct - Bind a compound value to a structure.
StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V);
+ /// BindVector - Bind a compound value to a vector.
+ StoreRef BindVector(Store store, const TypedValueRegion* R, SVal V);
+
StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V);
- /// KillStruct - Set the entire struct to unknown.
- StoreRef KillStruct(Store store, const TypedRegion* R, SVal DefaultVal);
+ /// Clears out all bindings in the given region and assigns a new value
+ /// as a Default binding.
+ StoreRef BindAggregate(Store store, const TypedRegion *R, SVal DefaultVal);
StoreRef Remove(Store store, Loc LV);
@@ -377,10 +352,8 @@ public: // Part of public interface to class.
/// Get the state and region whose binding this region R corresponds to.
std::pair<Store, const MemRegion*>
GetLazyBinding(RegionBindings B, const MemRegion *R,
- const MemRegion *originalRegion);
-
- StoreRef CopyLazyBindings(nonloc::LazyCompoundVal V, Store store,
- const TypedRegion *R);
+ const MemRegion *originalRegion,
+ bool includeSuffix = false);
//===------------------------------------------------------------------===//
// State pruning.
@@ -390,11 +363,7 @@ public: // Part of public interface to class.
/// It returns a new Store with these values removed.
StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
SymbolReaper& SymReaper);
-
- StoreRef enterStackFrame(ProgramStateRef state,
- const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx);
-
+
//===------------------------------------------------------------------===//
// Region "extents".
//===------------------------------------------------------------------===//
@@ -416,14 +385,18 @@ public: // Part of public interface to class.
void iterBindings(Store store, BindingsHandler& f) {
RegionBindings B = GetRegionBindings(store);
- for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
- const BindingKey &K = I.getKey();
- if (!K.isDirect())
- continue;
- if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) {
- // FIXME: Possibly incorporate the offset?
- if (!f.HandleBinding(*this, store, R, I.getData()))
- return;
+ for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ const BindingKey &K = CI.getKey();
+ if (!K.isDirect())
+ continue;
+ if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) {
+ // FIXME: Possibly incorporate the offset?
+ if (!f.HandleBinding(*this, store, R, CI.getData()))
+ return;
+ }
}
}
}
@@ -448,28 +421,6 @@ ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
}
-RegionStoreSubRegionMap*
-RegionStoreManager::getRegionStoreSubRegionMap(Store store) {
- RegionBindings B = GetRegionBindings(store);
- RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap();
-
- SmallVector<const SubRegion*, 10> WL;
-
- for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I)
- if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion()))
- M->process(WL, R);
-
- // We also need to record in the subregion map "intermediate" regions that
- // don't have direct bindings but are super regions of those that do.
- while (!WL.empty()) {
- const SubRegion *R = WL.back();
- WL.pop_back();
- M->process(WL, R);
- }
-
- return M;
-}
-
//===----------------------------------------------------------------------===//
// Region Cluster analysis.
//===----------------------------------------------------------------------===//
@@ -478,14 +429,11 @@ namespace {
template <typename DERIVED>
class ClusterAnalysis {
protected:
- typedef BumpVector<BindingKey> RegionCluster;
- typedef llvm::DenseMap<const MemRegion *, RegionCluster *> ClusterMap;
- llvm::DenseMap<const RegionCluster*, unsigned> Visited;
- typedef SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10>
- WorkList;
-
- BumpVectorContext BVC;
- ClusterMap ClusterM;
+ typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap;
+ typedef SmallVector<const MemRegion *, 10> WorkList;
+
+ llvm::SmallPtrSet<const ClusterBindings *, 16> Visited;
+
WorkList WL;
RegionStoreManager &RM;
@@ -496,6 +444,10 @@ protected:
const bool includeGlobals;
+ const ClusterBindings *getCluster(const MemRegion *R) {
+ return B.lookup(R);
+ }
+
public:
ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr,
RegionBindings b, const bool includeGlobals)
@@ -505,59 +457,36 @@ public:
RegionBindings getRegionBindings() const { return B; }
- RegionCluster &AddToCluster(BindingKey K) {
- const MemRegion *R = K.getRegion();
- const MemRegion *baseR = R->getBaseRegion();
- RegionCluster &C = getCluster(baseR);
- C.push_back(K, BVC);
- static_cast<DERIVED*>(this)->VisitAddedToCluster(baseR, C);
- return C;
- }
-
bool isVisited(const MemRegion *R) {
- return (bool) Visited[&getCluster(R->getBaseRegion())];
- }
-
- RegionCluster& getCluster(const MemRegion *R) {
- RegionCluster *&CRef = ClusterM[R];
- if (!CRef) {
- void *Mem = BVC.getAllocator().template Allocate<RegionCluster>();
- CRef = new (Mem) RegionCluster(BVC, 10);
- }
- return *CRef;
+ return Visited.count(getCluster(R));
}
void GenerateClusters() {
- // Scan the entire set of bindings and make the region clusters.
+ // Scan the entire set of bindings and record the region clusters.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){
- RegionCluster &C = AddToCluster(RI.getKey());
- if (const MemRegion *R = RI.getData().getAsRegion()) {
- // Generate a cluster, but don't add the region to the cluster
- // if there aren't any bindings.
- getCluster(R->getBaseRegion());
- }
- if (includeGlobals) {
- const MemRegion *R = RI.getKey().getRegion();
- if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()))
- AddToWorkList(R, C);
- }
+ const MemRegion *Base = RI.getKey();
+
+ const ClusterBindings &Cluster = RI.getData();
+ assert(!Cluster.isEmpty() && "Empty clusters should be removed");
+ static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster);
+
+ if (includeGlobals)
+ if (isa<NonStaticGlobalSpaceRegion>(Base->getMemorySpace()))
+ AddToWorkList(Base, &Cluster);
}
}
- bool AddToWorkList(const MemRegion *R, RegionCluster &C) {
- if (unsigned &visited = Visited[&C])
- return false;
- else
- visited = 1;
+ bool AddToWorkList(const MemRegion *R, const ClusterBindings *C) {
+ if (C) {
+ if (Visited.count(C))
+ return false;
+ Visited.insert(C);
+ }
- WL.push_back(std::make_pair(R, &C));
+ WL.push_back(R);
return true;
}
- bool AddToWorkList(BindingKey K) {
- return AddToWorkList(K.getRegion());
- }
-
bool AddToWorkList(const MemRegion *R) {
const MemRegion *baseR = R->getBaseRegion();
return AddToWorkList(baseR, getCluster(baseR));
@@ -565,22 +494,20 @@ public:
void RunWorkList() {
while (!WL.empty()) {
- const MemRegion *baseR;
- RegionCluster *C;
- llvm::tie(baseR, C) = WL.back();
- WL.pop_back();
+ const MemRegion *baseR = WL.pop_back_val();
- // First visit the cluster.
- static_cast<DERIVED*>(this)->VisitCluster(baseR, C->begin(), C->end());
+ // First visit the cluster.
+ if (const ClusterBindings *Cluster = getCluster(baseR))
+ static_cast<DERIVED*>(this)->VisitCluster(baseR, *Cluster);
- // Next, visit the base region.
+ // Next, visit the base region.
static_cast<DERIVED*>(this)->VisitBaseRegion(baseR);
}
}
public:
- void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C) {}
- void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E) {}
+ void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {}
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings &C) {}
void VisitBaseRegion(const MemRegion *baseR) {}
};
}
@@ -589,16 +516,99 @@ public:
// Binding invalidation.
//===----------------------------------------------------------------------===//
-void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B,
- const MemRegion *R,
- RegionStoreSubRegionMap &M) {
+bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Callbacks) {
+ assert(R == R->getBaseRegion() && "Should only be called for base regions");
+ RegionBindings B = GetRegionBindings(S);
+ const ClusterBindings *Cluster = B.lookup(R);
+
+ if (!Cluster)
+ return true;
- if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R))
- for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end();
- I != E; ++I)
- RemoveSubRegionBindings(B, *I, M);
+ for (ClusterBindings::iterator RI = Cluster->begin(), RE = Cluster->end();
+ RI != RE; ++RI) {
+ if (!Callbacks.scan(RI.getData()))
+ return false;
+ }
- B = removeBinding(B, R);
+ return true;
+}
+
+RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B,
+ const SubRegion *R) {
+ BindingKey SRKey = BindingKey::Make(R, BindingKey::Default);
+ const MemRegion *ClusterHead = SRKey.getBaseRegion();
+ if (R == ClusterHead) {
+ // We can remove an entire cluster's bindings all in one go.
+ return RBFactory.remove(B, R);
+ }
+
+ if (SRKey.hasSymbolicOffset()) {
+ const SubRegion *Base = cast<SubRegion>(SRKey.getConcreteOffsetRegion());
+ B = removeSubRegionBindings(B, Base);
+ return addBinding(B, Base, BindingKey::Default, UnknownVal());
+ }
+
+ // This assumes the region being invalidated is char-aligned. This isn't
+ // true for bitfields, but since bitfields have no subregions they shouldn't
+ // be using this function anyway.
+ uint64_t Length = UINT64_MAX;
+
+ SVal Extent = R->getExtent(svalBuilder);
+ if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) {
+ const llvm::APSInt &ExtentInt = ExtentCI->getValue();
+ assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
+ // Extents are in bytes but region offsets are in bits. Be careful!
+ Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth();
+ }
+
+ const ClusterBindings *Cluster = B.lookup(ClusterHead);
+ if (!Cluster)
+ return B;
+
+ ClusterBindings Result = *Cluster;
+
+ // It is safe to iterate over the bindings as they are being changed
+ // because they are in an ImmutableMap.
+ for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end();
+ I != E; ++I) {
+ BindingKey NextKey = I.getKey();
+ if (NextKey.getRegion() == SRKey.getRegion()) {
+ if (NextKey.getOffset() > SRKey.getOffset() &&
+ NextKey.getOffset() - SRKey.getOffset() < Length) {
+ // Case 1: The next binding is inside the region we're invalidating.
+ // Remove it.
+ Result = CBFactory.remove(Result, NextKey);
+ } else if (NextKey.getOffset() == SRKey.getOffset()) {
+ // Case 2: The next binding is at the same offset as the region we're
+ // invalidating. In this case, we need to leave default bindings alone,
+ // since they may be providing a default value for a regions beyond what
+ // we're invalidating.
+ // FIXME: This is probably incorrect; consider invalidating an outer
+ // struct whose first field is bound to a LazyCompoundVal.
+ if (NextKey.isDirect())
+ Result = CBFactory.remove(Result, NextKey);
+ }
+ } else if (NextKey.hasSymbolicOffset()) {
+ const MemRegion *Base = NextKey.getConcreteOffsetRegion();
+ if (R->isSubRegionOf(Base)) {
+ // Case 3: The next key is symbolic and we just changed something within
+ // its concrete region. We don't know if the binding is still valid, so
+ // we'll be conservative and remove it.
+ if (NextKey.isDirect())
+ Result = CBFactory.remove(Result, NextKey);
+ } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) {
+ // Case 4: The next key is symbolic, but we changed a known
+ // super-region. In this case the binding is certainly no longer valid.
+ if (R == Base || BaseSR->isSubRegionOf(R))
+ Result = CBFactory.remove(Result, NextKey);
+ }
+ }
+ }
+
+ if (Result.isEmpty())
+ return RBFactory.remove(B, ClusterHead);
+ return RBFactory.add(B, ClusterHead, Result);
}
namespace {
@@ -621,7 +631,7 @@ public:
: ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals),
Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {}
- void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E);
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings &C);
void VisitBaseRegion(const MemRegion *baseR);
private:
@@ -646,26 +656,31 @@ void invalidateRegionsWorker::VisitBinding(SVal V) {
const MemRegion *LazyR = LCS->getRegion();
RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore());
+ // FIXME: This should not have to walk all bindings in the old store.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){
- const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion());
- if (baseR && baseR->isSubRegionOf(LazyR))
- VisitBinding(RI.getData());
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ BindingKey K = CI.getKey();
+ if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) {
+ if (BaseR == LazyR)
+ VisitBinding(CI.getData());
+ else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR))
+ VisitBinding(CI.getData());
+ }
+ }
}
return;
}
}
-void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
- BindingKey *I, BindingKey *E) {
- for ( ; I != E; ++I) {
- // Get the old binding. Is it a region? If so, add it to the worklist.
- const BindingKey &K = *I;
- if (const SVal *V = RM.lookup(B, K))
- VisitBinding(*V);
+void invalidateRegionsWorker::VisitCluster(const MemRegion *BaseR,
+ const ClusterBindings &C) {
+ for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I)
+ VisitBinding(I.getData());
- B = RM.removeBinding(B, K);
- }
+ B = RM.removeCluster(B, BaseR);
}
void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
@@ -681,8 +696,22 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
BI != BE; ++BI) {
const VarRegion *VR = *BI;
const VarDecl *VD = VR->getDecl();
- if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage())
+ if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) {
AddToWorkList(VR);
+ }
+ else if (Loc::isLocType(VR->getValueType())) {
+ // Map the current bindings to a Store to retrieve the value
+ // of the binding. If that binding itself is a region, we should
+ // invalidate that region. This is because a block may capture
+ // a pointer value, but the thing pointed by that pointer may
+ // get invalidated.
+ Store store = B.getRootWithoutRetain();
+ SVal V = RM.getBinding(store, loc::MemRegionVal(VR));
+ if (const Loc *L = dyn_cast<Loc>(&V)) {
+ if (const MemRegion *LR = L->getAsRegion())
+ AddToWorkList(LR);
+ }
+ }
}
return;
}
@@ -771,7 +800,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store,
const Expr *Ex, unsigned Count,
const LocationContext *LCtx,
InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call,
+ const CallEvent *Call,
InvalidatedRegions *Invalidated) {
invalidateRegionsWorker W(*this, StateMgr,
RegionStoreManager::GetRegionBindings(store),
@@ -868,10 +897,22 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) {
return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx));
}
+// This mirrors Type::getCXXRecordDeclForPointerType(), but there doesn't
+// appear to be another need for this in the rest of the codebase.
+static const CXXRecordDecl *GetCXXRecordDeclForReferenceType(QualType Ty) {
+ if (const ReferenceType *RT = Ty->getAs<ReferenceType>())
+ if (const RecordType *RCT = RT->getPointeeType()->getAs<RecordType>())
+ return dyn_cast<CXXRecordDecl>(RCT->getDecl());
+ return 0;
+}
+
SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) {
const CXXRecordDecl *baseDecl;
+
if (baseType->isPointerType())
baseDecl = baseType->getCXXRecordDeclForPointerType();
+ else if (baseType->isReferenceType())
+ baseDecl = GetCXXRecordDeclForReferenceType(baseType);
else
baseDecl = baseType->getAsCXXRecordDecl();
@@ -894,7 +935,7 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType,
loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base);
if (!baseRegVal)
return UnknownVal();
- const MemRegion *BaseRegion = baseRegVal->stripCasts();
+ const MemRegion *BaseRegion = baseRegVal->stripCasts(/*StripBases=*/false);
// Assume the derived class is a pointer or a reference to a CXX record.
derivedType = derivedType->getPointeeType();
@@ -917,23 +958,20 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType,
if (SRDecl == DerivedDecl)
return loc::MemRegionVal(TSR);
- // If the region type is a subclass of the derived type.
- if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) {
- // This occurs in two cases.
- // 1) We are processing an upcast.
- // 2) We are processing a downcast but we jumped directly from the
- // ancestor to a child of the cast value, so conjure the
- // appropriate region to represent value (the intermediate node).
- return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl,
- BaseRegion));
- }
-
- // If super region is not a parent of derived class, the cast definitely
- // fails.
- if (!derivedType->isVoidType() &&
- DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) {
- Failed = true;
- return UnknownVal();
+ if (!derivedType->isVoidType()) {
+ // Static upcasts are marked as DerivedToBase casts by Sema, so this will
+ // only happen when multiple or virtual inheritance is involved.
+ CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+ if (SRDecl->isDerivedFrom(DerivedDecl, Paths)) {
+ SVal Result = loc::MemRegionVal(TSR);
+ const CXXBasePath &Path = *Paths.begin();
+ for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end();
+ I != E; ++I) {
+ Result = evalDerivedToBase(Result, I->Base->getType());
+ }
+ return Result;
+ }
}
if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR))
@@ -1036,8 +1074,12 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) {
if (RTy->isUnionType())
return UnknownVal();
- if (RTy->isArrayType())
- return getBindingForArray(store, R);
+ if (RTy->isArrayType()) {
+ if (RTy->isConstantArrayType())
+ return getBindingForArray(store, R);
+ else
+ return UnknownVal();
+ }
// FIXME: handle Vector types.
if (RTy->isVectorType())
@@ -1099,7 +1141,8 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) {
std::pair<Store, const MemRegion *>
RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
- const MemRegion *originalRegion) {
+ const MemRegion *originalRegion,
+ bool includeSuffix) {
if (originalRegion != R) {
if (Optional<SVal> OV = getDefaultBinding(B, R)) {
@@ -1121,9 +1164,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
const std::pair<Store, const MemRegion *> &X =
GetLazyBinding(B, FR->getSuperRegion(), originalRegion);
- if (X.second)
- return std::make_pair(X.first,
- MRMgr.getFieldRegionWithSuper(FR, X.second));
+ if (X.second) {
+ if (includeSuffix)
+ return std::make_pair(X.first,
+ MRMgr.getFieldRegionWithSuper(FR, X.second));
+ return X;
+ }
+
}
// C++ base object region is another kind of region that we should blast
// through to look for lazy compound value. It is like a field region.
@@ -1132,9 +1179,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
const std::pair<Store, const MemRegion *> &X =
GetLazyBinding(B, baseReg->getSuperRegion(), originalRegion);
- if (X.second)
- return std::make_pair(X.first,
- MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, X.second));
+ if (X.second) {
+ if (includeSuffix)
+ return std::make_pair(X.first,
+ MRMgr.getCXXBaseObjectRegionWithSuper(baseReg,
+ X.second));
+ return X;
+ }
}
// The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is
@@ -1143,7 +1194,11 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
}
SVal RegionStoreManager::getBindingForElement(Store store,
- const ElementRegion* R) {
+ const ElementRegion* R) {
+ // We do not currently model bindings of the CompoundLiteralregion.
+ if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
+ return UnknownVal();
+
// Check if the region has a binding.
RegionBindings B = GetRegionBindings(store);
if (const Optional<SVal> &V = getDirectBinding(B, R))
@@ -1274,7 +1329,16 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store,
// At this point we have already checked in either getBindingForElement or
// getBindingForField if 'R' has a direct binding.
RegionBindings B = GetRegionBindings(store);
+
+ // Lazy binding?
+ Store lazyBindingStore = NULL;
+ const MemRegion *lazyBindingRegion = NULL;
+ llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R,
+ true);
+ if (lazyBindingRegion)
+ return getLazyBinding(lazyBindingRegion, lazyBindingStore);
+
// Record whether or not we see a symbolic index. That can completely
// be out of scope of our lookup.
bool hasSymbolicIndex = false;
@@ -1299,14 +1363,6 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store,
break;
}
- // Lazy binding?
- Store lazyBindingStore = NULL;
- const MemRegion *lazyBindingRegion = NULL;
- llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R);
-
- if (lazyBindingRegion)
- return getLazyBinding(lazyBindingRegion, lazyBindingStore);
-
if (R->hasStackNonParametersStorage()) {
if (isa<ElementRegion>(R)) {
// Currently we don't reason specially about Clang-style vectors. Check
@@ -1410,15 +1466,49 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) {
return svalBuilder.getRegionValueSymbolVal(R);
}
+static bool mayHaveLazyBinding(QualType Ty) {
+ return Ty->isArrayType() || Ty->isStructureOrClassType();
+}
+
SVal RegionStoreManager::getBindingForStruct(Store store,
const TypedValueRegion* R) {
- assert(R->getValueType()->isStructureOrClassType());
+ const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl();
+ if (RD->field_empty())
+ return UnknownVal();
+
+ // If we already have a lazy binding, don't create a new one,
+ // unless the first field might have a lazy binding of its own.
+ // (Right now we can't tell the difference.)
+ QualType FirstFieldType = RD->field_begin()->getType();
+ if (!mayHaveLazyBinding(FirstFieldType)) {
+ RegionBindings B = GetRegionBindings(store);
+ BindingKey K = BindingKey::Make(R, BindingKey::Default);
+ if (const nonloc::LazyCompoundVal *V =
+ dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
+ return *V;
+ }
+ }
+
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
-SVal RegionStoreManager::getBindingForArray(Store store,
+SVal RegionStoreManager::getBindingForArray(Store store,
const TypedValueRegion * R) {
- assert(Ctx.getAsConstantArrayType(R->getValueType()));
+ const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType());
+ assert(Ty && "Only constant array types can have compound bindings.");
+
+ // If we already have a lazy binding, don't create a new one,
+ // unless the first element might have a lazy binding of its own.
+ // (Right now we can't tell the difference.)
+ if (!mayHaveLazyBinding(Ty->getElementType())) {
+ RegionBindings B = GetRegionBindings(store);
+ BindingKey K = BindingKey::Make(R, BindingKey::Default);
+ if (const nonloc::LazyCompoundVal *V =
+ dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
+ return *V;
+ }
+ }
+
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
@@ -1426,16 +1516,23 @@ bool RegionStoreManager::includedInBindings(Store store,
const MemRegion *region) const {
RegionBindings B = GetRegionBindings(store);
region = region->getBaseRegion();
-
- for (RegionBindings::iterator it = B.begin(), ei = B.end(); it != ei; ++it) {
- const BindingKey &K = it.getKey();
- if (region == K.getRegion())
- return true;
- const SVal &D = it.getData();
- if (const MemRegion *r = D.getAsRegion())
- if (r == region)
- return true;
+
+ // Quick path: if the base is the head of a cluster, the region is live.
+ if (B.lookup(region))
+ return true;
+
+ // Slow path: if the region is the VALUE of any binding, it is live.
+ for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) {
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ const SVal &D = CI.getData();
+ if (const MemRegion *R = D.getAsRegion())
+ if (R->getBaseRegion() == region)
+ return true;
+ }
}
+
return false;
}
@@ -1461,24 +1558,15 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) {
const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion();
// Check if the region is a struct region.
- if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R))
- if (TR->getValueType()->isStructureOrClassType())
+ if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) {
+ QualType Ty = TR->getValueType();
+ if (Ty->isStructureOrClassType())
return BindStruct(store, TR, V);
-
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- if (ER->getIndex().isZeroConstant()) {
- if (const TypedValueRegion *superR =
- dyn_cast<TypedValueRegion>(ER->getSuperRegion())) {
- QualType superTy = superR->getValueType();
- // For now, just invalidate the fields of the struct/union/class.
- // This is for test rdar_test_7185607 in misc-ps-region-store.m.
- // FIXME: Precisely handle the fields of the record.
- if (superTy->isStructureOrClassType())
- return KillStruct(store, superR, UnknownVal());
- }
- }
+ if (Ty->isVectorType())
+ return BindVector(store, TR, V);
}
- else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
// Binding directly to a symbolic region should be treated as binding
// to element 0.
QualType T = SR->getSymbol()->getType(Ctx);
@@ -1492,10 +1580,13 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) {
R = GetElementZeroRegion(SR, T);
}
+ // Clear out bindings that may overlap with this binding.
+
// Perform the binding.
RegionBindings B = GetRegionBindings(store);
- return StoreRef(addBinding(B, R, BindingKey::Direct,
- V).getRootWithoutRetain(), *this);
+ B = removeSubRegionBindings(B, cast<SubRegion>(R));
+ BindingKey Key = BindingKey::Make(R, BindingKey::Direct);
+ return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this);
}
StoreRef RegionStoreManager::BindDecl(Store store, const VarRegion *VR,
@@ -1566,12 +1657,12 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R,
nonloc::LazyCompoundVal LCV =
cast<nonloc::LazyCompoundVal>(svalBuilder.
makeLazyCompoundVal(StoreRef(store, *this), S));
- return CopyLazyBindings(LCV, store, R);
+ return BindAggregate(store, R, LCV);
}
// Handle lazy compound values.
- if (nonloc::LazyCompoundVal *LCV = dyn_cast<nonloc::LazyCompoundVal>(&Init))
- return CopyLazyBindings(*LCV, store, R);
+ if (isa<nonloc::LazyCompoundVal>(Init))
+ return BindAggregate(store, R, Init);
// Remaining case: explicit compound values.
@@ -1607,6 +1698,46 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R,
return newStore;
}
+StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R,
+ SVal V) {
+ QualType T = R->getValueType();
+ assert(T->isVectorType());
+ const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs.
+
+ // Handle lazy compound values and symbolic values.
+ if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V))
+ return BindAggregate(store, R, V);
+
+ // We may get non-CompoundVal accidentally due to imprecise cast logic or
+ // that we are binding symbolic struct value. Kill the field values, and if
+ // the value is symbolic go and bind it as a "default" binding.
+ if (!isa<nonloc::CompoundVal>(V)) {
+ return BindAggregate(store, R, UnknownVal());
+ }
+
+ QualType ElemType = VT->getElementType();
+ nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V);
+ nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
+ unsigned index = 0, numElements = VT->getNumElements();
+ StoreRef newStore(store, *this);
+
+ for ( ; index != numElements ; ++index) {
+ if (VI == VE)
+ break;
+
+ NonLoc Idx = svalBuilder.makeArrayIndex(index);
+ const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx);
+
+ if (ElemType->isArrayType())
+ newStore = BindArray(newStore.getStore(), ER, *VI);
+ else if (ElemType->isStructureOrClassType())
+ newStore = BindStruct(newStore.getStore(), ER, *VI);
+ else
+ newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI);
+ }
+ return newStore;
+}
+
StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
SVal V) {
@@ -1622,17 +1753,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
if (!RD->isCompleteDefinition())
return StoreRef(store, *this);
- // Handle lazy compound values.
- if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V))
- return CopyLazyBindings(*LCV, store, R);
+ // Handle lazy compound values and symbolic values.
+ if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V))
+ return BindAggregate(store, R, V);
// We may get non-CompoundVal accidentally due to imprecise cast logic or
// that we are binding symbolic struct value. Kill the field values, and if
// the value is symbolic go and bind it as a "default" binding.
- if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) {
- SVal SV = isa<nonloc::SymbolVal>(V) ? V : UnknownVal();
- return KillStruct(store, R, SV);
- }
+ if (V.isUnknown() || !isa<nonloc::CompoundVal>(V))
+ return BindAggregate(store, R, UnknownVal());
nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V);
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
@@ -1646,10 +1775,10 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
break;
// Skip any unnamed bitfields to stay in sync with the initializers.
- if ((*FI)->isUnnamedBitfield())
+ if (FI->isUnnamedBitfield())
continue;
- QualType FTy = (*FI)->getType();
+ QualType FTy = FI->getType();
const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R);
if (FTy->isArrayType())
@@ -1671,58 +1800,16 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
return newStore;
}
-StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R,
- SVal DefaultVal) {
- BindingKey key = BindingKey::Make(R, BindingKey::Default);
-
- // The BindingKey may be "invalid" if we cannot handle the region binding
- // explicitly. One example is something like array[index], where index
- // is a symbolic value. In such cases, we want to invalidate the entire
- // array, as the index assignment could have been to any element. In
- // the case of nested symbolic indices, we need to march up the region
- // hierarchy untile we reach a region whose binding we can reason about.
- const SubRegion *subReg = R;
-
- while (!key.isValid()) {
- if (const SubRegion *tmp = dyn_cast<SubRegion>(subReg->getSuperRegion())) {
- subReg = tmp;
- key = BindingKey::Make(tmp, BindingKey::Default);
- }
- else
- break;
- }
-
- // Remove the old bindings, using 'subReg' as the root of all regions
- // we will invalidate.
+StoreRef RegionStoreManager::BindAggregate(Store store, const TypedRegion *R,
+ SVal Val) {
+ // Remove the old bindings, using 'R' as the root of all regions
+ // we will invalidate. Then add the new binding.
RegionBindings B = GetRegionBindings(store);
- OwningPtr<RegionStoreSubRegionMap>
- SubRegions(getRegionStoreSubRegionMap(store));
- RemoveSubRegionBindings(B, subReg, *SubRegions);
- // Set the default value of the struct region to "unknown".
- if (!key.isValid())
- return StoreRef(B.getRootWithoutRetain(), *this);
-
- return StoreRef(addBinding(B, key, DefaultVal).getRootWithoutRetain(), *this);
-}
-
-StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
- Store store,
- const TypedRegion *R) {
-
- // Nuke the old bindings stemming from R.
- RegionBindings B = GetRegionBindings(store);
+ B = removeSubRegionBindings(B, R);
+ B = addBinding(B, R, BindingKey::Default, Val);
- OwningPtr<RegionStoreSubRegionMap>
- SubRegions(getRegionStoreSubRegionMap(store));
-
- // B and DVM are updated after the call to RemoveSubRegionBindings.
- RemoveSubRegionBindings(B, R, *SubRegions.get());
-
- // Now copy the bindings. This amounts to just binding 'V' to 'R'. This
- // results in a zero-copy algorithm.
- return StoreRef(addBinding(B, R, BindingKey::Default,
- V).getRootWithoutRetain(), *this);
+ return StoreRef(B.getRootWithoutRetain(), *this);
}
//===----------------------------------------------------------------------===//
@@ -1732,9 +1819,14 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K,
SVal V) {
- if (!K.isValid())
- return B;
- return RBFactory.add(B, K, V);
+ const MemRegion *Base = K.getBaseRegion();
+
+ const ClusterBindings *ExistingCluster = B.lookup(Base);
+ ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster
+ : CBFactory.getEmptyMap());
+
+ ClusterBindings NewCluster = CBFactory.add(Cluster, K, V);
+ return RBFactory.add(B, Base, NewCluster);
}
RegionBindings RegionStoreManager::addBinding(RegionBindings B,
@@ -1744,9 +1836,11 @@ RegionBindings RegionStoreManager::addBinding(RegionBindings B,
}
const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) {
- if (!K.isValid())
- return NULL;
- return B.lookup(K);
+ const ClusterBindings *Cluster = B.lookup(K.getBaseRegion());
+ if (!Cluster)
+ return 0;
+
+ return Cluster->lookup(K);
}
const SVal *RegionStoreManager::lookup(RegionBindings B,
@@ -1757,9 +1851,15 @@ const SVal *RegionStoreManager::lookup(RegionBindings B,
RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
BindingKey K) {
- if (!K.isValid())
+ const MemRegion *Base = K.getBaseRegion();
+ const ClusterBindings *Cluster = B.lookup(Base);
+ if (!Cluster)
return B;
- return RBFactory.remove(B, K);
+
+ ClusterBindings NewCluster = CBFactory.remove(*Cluster, K);
+ if (NewCluster.isEmpty())
+ return RBFactory.remove(B, Base);
+ return RBFactory.add(B, Base, NewCluster);
}
RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
@@ -1768,6 +1868,11 @@ RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
return removeBinding(B, BindingKey::Make(R, k));
}
+RegionBindings RegionStoreManager::removeCluster(RegionBindings B,
+ const MemRegion *Base) {
+ return RBFactory.remove(B, Base);
+}
+
//===----------------------------------------------------------------------===//
// State pruning.
//===----------------------------------------------------------------------===//
@@ -1789,8 +1894,8 @@ public:
SymReaper(symReaper), CurrentLCtx(LCtx) {}
// Called by ClusterAnalysis.
- void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C);
- void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E);
+ void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C);
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings &C);
void VisitBindingKey(BindingKey K);
bool UpdatePostponed();
@@ -1799,18 +1904,18 @@ public:
}
void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
- RegionCluster &C) {
+ const ClusterBindings &C) {
if (const VarRegion *VR = dyn_cast<VarRegion>(baseR)) {
if (SymReaper.isLive(VR))
- AddToWorkList(baseR, C);
+ AddToWorkList(baseR, &C);
return;
}
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) {
if (SymReaper.isLive(SR->getSymbol()))
- AddToWorkList(SR, C);
+ AddToWorkList(SR, &C);
else
Postponed.push_back(SR);
@@ -1818,7 +1923,7 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
}
if (isa<NonStaticGlobalSpaceRegion>(baseR)) {
- AddToWorkList(baseR, C);
+ AddToWorkList(baseR, &C);
return;
}
@@ -1828,34 +1933,57 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
cast<StackArgumentsSpaceRegion>(TR->getSuperRegion());
const StackFrameContext *RegCtx = StackReg->getStackFrame();
if (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx))
- AddToWorkList(TR, C);
+ AddToWorkList(TR, &C);
}
}
void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR,
- BindingKey *I, BindingKey *E) {
- for ( ; I != E; ++I)
- VisitBindingKey(*I);
+ const ClusterBindings &C) {
+ for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) {
+ VisitBindingKey(I.getKey());
+ VisitBinding(I.getData());
+ }
}
void removeDeadBindingsWorker::VisitBinding(SVal V) {
// Is it a LazyCompoundVal? All referenced regions are live as well.
if (const nonloc::LazyCompoundVal *LCS =
- dyn_cast<nonloc::LazyCompoundVal>(&V)) {
+ dyn_cast<nonloc::LazyCompoundVal>(&V)) {
const MemRegion *LazyR = LCS->getRegion();
RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore());
+
+ // FIXME: This should not have to walk all bindings in the old store.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){
- const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion());
- if (baseR && baseR->isSubRegionOf(LazyR))
- VisitBinding(RI.getData());
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ BindingKey K = CI.getKey();
+ if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) {
+ if (BaseR == LazyR)
+ VisitBinding(CI.getData());
+ else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR))
+ VisitBinding(CI.getData());
+ }
+ }
}
+
return;
}
// If V is a region, then add it to the worklist.
- if (const MemRegion *R = V.getAsRegion())
+ if (const MemRegion *R = V.getAsRegion()) {
AddToWorkList(R);
+
+ // All regions captured by a block are also live.
+ if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
+ BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end();
+ for ( ; I != E; ++I)
+ AddToWorkList(I.getCapturedRegion());
+ }
+ }
+
// Update the set of live symbols.
for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end();
@@ -1874,26 +2002,7 @@ void removeDeadBindingsWorker::VisitBindingKey(BindingKey K) {
// should continue to track that symbol.
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R))
SymReaper.markLive(SymR->getSymbol());
-
- // For BlockDataRegions, enqueue the VarRegions for variables marked
- // with __block (passed-by-reference).
- // via BlockDeclRefExprs.
- if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) {
- for (BlockDataRegion::referenced_vars_iterator
- RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end();
- RI != RE; ++RI) {
- if ((*RI)->getDecl()->getAttr<BlocksAttr>())
- AddToWorkList(*RI);
- }
-
- // No possible data bindings on a BlockDataRegion.
- return;
- }
}
-
- // Visit the data binding for K.
- if (const SVal *V = RM.lookup(B, K))
- VisitBinding(*V);
}
bool removeDeadBindingsWorker::UpdatePostponed() {
@@ -1933,68 +2042,32 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
// as live. We now remove all the regions that are dead from the store
// as well as update DSymbols with the set symbols that are now dead.
for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- const BindingKey &K = I.getKey();
+ const MemRegion *Base = I.getKey();
// If the cluster has been visited, we know the region has been marked.
- if (W.isVisited(K.getRegion()))
+ if (W.isVisited(Base))
continue;
// Remove the dead entry.
- B = removeBinding(B, K);
+ B = removeCluster(B, Base);
- // Mark all non-live symbols that this binding references as dead.
- if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(K.getRegion()))
+ if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base))
SymReaper.maybeDead(SymR->getSymbol());
- SVal X = I.getData();
- SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
- for (; SI != SE; ++SI)
- SymReaper.maybeDead(*SI);
+ // Mark all non-live symbols that this binding references as dead.
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ SVal X = CI.getData();
+ SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
+ for (; SI != SE; ++SI)
+ SymReaper.maybeDead(*SI);
+ }
}
return StoreRef(B.getRootWithoutRetain(), *this);
}
-
-StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
- const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx)
-{
- FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl());
- FunctionDecl::param_const_iterator PI = FD->param_begin(),
- PE = FD->param_end();
- StoreRef store = StoreRef(state->getStore(), *this);
-
- if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) {
- CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
-
- // Copy the arg expression value to the arg variables. We check that
- // PI != PE because the actual number of arguments may be different than
- // the function declaration.
- for (; AI != AE && PI != PE; ++AI, ++PI) {
- SVal ArgVal = state->getSVal(*AI, callerCtx);
- store = Bind(store.getStore(),
- svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
- ArgVal);
- }
- } else if (const CXXConstructExpr *CE =
- dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) {
- CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(),
- AE = CE->arg_end();
-
- // Copy the arg expression value to the arg variables.
- for (; AI != AE; ++AI, ++PI) {
- SVal ArgVal = state->getSVal(*AI, callerCtx);
- store = Bind(store.getStore(),
- svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
- ArgVal);
- }
- } else
- assert(isa<CXXDestructorDecl>(calleeCtx->getDecl()));
-
- return store;
-}
-
//===----------------------------------------------------------------------===//
// Utility methods.
//===----------------------------------------------------------------------===//
@@ -2002,8 +2075,16 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
void RegionStoreManager::print(Store store, raw_ostream &OS,
const char* nl, const char *sep) {
RegionBindings B = GetRegionBindings(store);
- OS << "Store (direct and default bindings):" << nl;
+ OS << "Store (direct and default bindings), "
+ << (void*) B.getRootWithoutRetain()
+ << " :" << nl;
- for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I)
- OS << ' ' << I.getKey() << " : " << I.getData() << nl;
+ for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ OS << ' ' << CI.getKey() << " : " << CI.getData() << nl;
+ }
+ OS << nl;
+ }
}
diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 9e97f5e..d1936cd 100644
--- a/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
@@ -61,7 +62,6 @@ NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs,
NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
const SymExpr *rhs, QualType type) {
assert(lhs && rhs);
- assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true);
assert(!Loc::isLocType(type));
return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
}
@@ -149,6 +149,18 @@ SValBuilder::getConjuredSymbolVal(const Stmt *stmt,
return nonloc::SymbolVal(sym);
}
+DefinedOrUnknownSVal
+SValBuilder::getConjuredHeapSymbolVal(const Expr *E,
+ const LocationContext *LCtx,
+ unsigned VisitCount) {
+ QualType T = E->getType();
+ assert(Loc::isLocType(T));
+ assert(SymbolManager::canSymbolicate(T));
+
+ SymbolRef sym = SymMgr.getConjuredSymbol(E, LCtx, T, VisitCount);
+ return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym));
+}
+
DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag,
const MemRegion *region,
const Expr *expr, QualType type,
@@ -193,31 +205,48 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
return loc::MemRegionVal(BD);
}
+/// Return a memory region for the 'this' object reference.
+loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D,
+ const StackFrameContext *SFC) {
+ return loc::MemRegionVal(getRegionManager().
+ getCXXThisRegion(D->getThisType(getContext()), SFC));
+}
+
+/// Return a memory region for the 'this' object reference.
+loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
+ const StackFrameContext *SFC) {
+ const Type *T = D->getTypeForDecl();
+ QualType PT = getContext().getPointerType(QualType(T, 0));
+ return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC));
+}
+
//===----------------------------------------------------------------------===//
-SVal SValBuilder::makeGenericVal(ProgramStateRef State,
- BinaryOperator::Opcode Op,
- NonLoc LHS, NonLoc RHS,
- QualType ResultTy) {
- // If operands are tainted, create a symbol to ensure that we propagate taint.
- if (State->isTainted(RHS) || State->isTainted(LHS)) {
- const SymExpr *symLHS;
- const SymExpr *symRHS;
-
- if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) {
- symLHS = LHS.getAsSymExpr();
+SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,
+ BinaryOperator::Opcode Op,
+ NonLoc LHS, NonLoc RHS,
+ QualType ResultTy) {
+ if (!State->isTainted(RHS) && !State->isTainted(LHS))
+ return UnknownVal();
+
+ const SymExpr *symLHS = LHS.getAsSymExpr();
+ const SymExpr *symRHS = RHS.getAsSymExpr();
+ // TODO: When the Max Complexity is reached, we should conjure a symbol
+ // instead of generating an Unknown value and propagate the taint info to it.
+ const unsigned MaxComp = 10000; // 100000 28X
+
+ if (symLHS && symRHS &&
+ (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp)
+ return makeNonLoc(symLHS, Op, symRHS, ResultTy);
+
+ if (symLHS && symLHS->computeComplexity() < MaxComp)
+ if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS))
return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy);
- }
- if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) {
- symRHS = RHS.getAsSymExpr();
+ if (symRHS && symRHS->computeComplexity() < MaxComp)
+ if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS))
return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy);
- }
- symLHS = LHS.getAsSymExpr();
- symRHS = RHS.getAsSymExpr();
- return makeNonLoc(symLHS, Op, symRHS, ResultTy);
- }
return UnknownVal();
}
@@ -324,7 +353,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
// Are we casting from an array to a pointer? If so just pass on
// the decayed value.
- if (castTy->isPointerType())
+ if (castTy->isPointerType() || castTy->isReferenceType())
return val;
// Are we casting from an array to an integer? If so, cast the decayed
@@ -340,9 +369,12 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
// Check for casts from a region to a specific type.
if (const MemRegion *R = val.getAsRegion()) {
+ // Handle other casts of locations to integers.
+ if (castTy->isIntegerType())
+ return evalCastFromLoc(loc::MemRegionVal(R), castTy);
+
// FIXME: We should handle the case where we strip off view layers to get
// to a desugared type.
-
if (!Loc::isLocType(castTy)) {
// FIXME: There can be gross cases where one casts the result of a function
// (that returns a pointer) to some other value that happens to fit
diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp
index b94aff4..8437f50 100644
--- a/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/lib/StaticAnalyzer/Core/SVals.cpp
@@ -133,9 +133,9 @@ const MemRegion *SVal::getAsRegion() const {
return 0;
}
-const MemRegion *loc::MemRegionVal::stripCasts() const {
+const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const {
const MemRegion *R = getRegion();
- return R ? R->StripCasts() : NULL;
+ return R ? R->StripCasts(StripBaseCasts) : NULL;
}
const void *nonloc::LazyCompoundVal::getStore() const {
@@ -309,22 +309,6 @@ void Loc::dumpToStream(raw_ostream &os) const {
case loc::MemRegionKind:
os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString();
break;
- case loc::ObjCPropRefKind: {
- const ObjCPropertyRefExpr *E = cast<loc::ObjCPropRef>(this)->getPropRefExpr();
- os << "objc-prop{";
- if (E->isSuperReceiver())
- os << "super.";
- else if (E->getBase())
- os << "<base>.";
-
- if (E->isImplicitProperty())
- os << E->getImplicitPropertyGetter()->getSelector().getAsString();
- else
- os << E->getExplicitProperty()->getName();
-
- os << "}";
- break;
- }
default:
llvm_unreachable("Pretty-printing not implemented for this Loc.");
}
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index a76a2da..5568f1c 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -71,9 +72,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond,
ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
Loc Cond, bool Assumption) {
-
- BasicValueFactory &BasicVals = state->getBasicVals();
-
switch (Cond.getSubKind()) {
default:
assert (false && "'Assume' not implemented for this Loc.");
@@ -88,7 +86,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
while (SubR) {
// FIXME: now we only find the first symbolic region.
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR)) {
- const llvm::APSInt &zero = BasicVals.getZeroWithPtrWidth();
+ const llvm::APSInt &zero = getBasicVals().getZeroWithPtrWidth();
if (Assumption)
return assumeSymNE(state, SymR->getSymbol(), zero, zero);
else
@@ -134,12 +132,17 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
}
-ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol(
- ProgramStateRef State,
- SymbolRef Sym,
- bool Assumption) {
- QualType T = State->getSymbolManager().getType(Sym);
- const llvm::APSInt &zero = State->getBasicVals().getValue(0, T);
+ProgramStateRef
+SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State,
+ SymbolRef Sym, bool Assumption) {
+ BasicValueFactory &BVF = getBasicVals();
+ QualType T = Sym->getType(BVF.getContext());
+
+ // None of the constraint solvers currently support non-integer types.
+ if (!T->isIntegerType())
+ return State;
+
+ const llvm::APSInt &zero = BVF.getValue(0, T);
if (Assumption)
return assumeSymNE(State, Sym, zero, zero);
else
@@ -158,8 +161,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
return assumeAuxForSymbol(state, sym, Assumption);
}
- BasicValueFactory &BasicVals = state->getBasicVals();
- SymbolManager &SymMgr = state->getSymbolManager();
+ BasicValueFactory &BasicVals = getBasicVals();
switch (Cond.getSubKind()) {
default:
@@ -184,7 +186,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
BinaryOperator::Opcode op = SE->getOpcode();
// Implicitly compare non-comparison expressions to 0.
if (!BinaryOperator::isComparisonOp(op)) {
- QualType T = SymMgr.getType(SE);
+ QualType T = SE->getType(BasicVals.getContext());
const llvm::APSInt &zero = BasicVals.getValue(0, T);
op = (Assumption ? BO_NE : BO_EQ);
return assumeSymRel(state, SE, op, zero);
@@ -209,33 +211,20 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
} // end switch
}
-static llvm::APSInt computeAdjustment(const SymExpr *LHS,
- SymbolRef &Sym) {
- llvm::APSInt DefaultAdjustment;
- DefaultAdjustment = 0;
-
- // First check if the LHS is a simple symbol reference.
- if (isa<SymbolData>(LHS))
- return DefaultAdjustment;
-
- // Next, see if it's a "($sym+constant1)" expression.
- const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS);
-
- // We cannot simplify "($sym1+$sym2)".
- if (!SE)
- return DefaultAdjustment;
-
- // Get the constant out of the expression "($sym+constant1)" or
- // "<expr>+constant1".
- Sym = SE->getLHS();
- switch (SE->getOpcode()) {
- case BO_Add:
- return SE->getRHS();
- case BO_Sub:
- return -SE->getRHS();
- default:
- // We cannot simplify non-additive operators.
- return DefaultAdjustment;
+static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) {
+ // Is it a "($sym+constant1)" expression?
+ if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) {
+ BinaryOperator::Opcode Op = SE->getOpcode();
+ if (Op == BO_Add || Op == BO_Sub) {
+ Sym = SE->getLHS();
+ Adjustment = APSIntType(Adjustment).convert(SE->getRHS());
+
+ // Don't forget to negate the adjustment if it's being subtracted.
+ // This should happen /after/ promotion, in case the value being
+ // subtracted is, say, CHAR_MIN, and the promoted type is 'int'.
+ if (Op == BO_Sub)
+ Adjustment = -Adjustment;
+ }
}
}
@@ -246,6 +235,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state,
assert(BinaryOperator::isComparisonOp(op) &&
"Non-comparison ops should be rewritten as comparisons to zero.");
+ BasicValueFactory &BVF = getBasicVals();
+ ASTContext &Ctx = BVF.getContext();
+
+ // Get the type used for calculating wraparound.
+ APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType(Ctx));
+
// We only handle simple comparisons of the form "$sym == constant"
// or "($sym+constant1) == constant2".
// The adjustment is "constant1" in the above expression. It's used to
@@ -254,28 +249,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state,
// in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to
// the subclasses of SimpleConstraintManager to handle the adjustment.
SymbolRef Sym = LHS;
- llvm::APSInt Adjustment = computeAdjustment(LHS, Sym);
-
- // FIXME: This next section is a hack. It silently converts the integers to
- // be of the same type as the symbol, which is not always correct. Really the
- // comparisons should be performed using the Int's type, then mapped back to
- // the symbol's range of values.
- ProgramStateManager &StateMgr = state->getStateManager();
- ASTContext &Ctx = StateMgr.getContext();
-
- QualType T = Sym->getType(Ctx);
- assert(T->isIntegerType() || Loc::isLocType(T));
- unsigned bitwidth = Ctx.getTypeSize(T);
- bool isSymUnsigned
- = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T);
-
- // Convert the adjustment.
- Adjustment.setIsUnsigned(isSymUnsigned);
- Adjustment = Adjustment.extOrTrunc(bitwidth);
-
- // Convert the right-hand side integer.
- llvm::APSInt ConvertedInt(Int, isSymUnsigned);
- ConvertedInt = ConvertedInt.extOrTrunc(bitwidth);
+ llvm::APSInt Adjustment = WraparoundType.getZeroValue();
+ computeAdjustment(Sym, Adjustment);
+
+ // Convert the right-hand side integer as necessary.
+ APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int));
+ llvm::APSInt ConvertedInt = ComparisonType.convert(Int);
switch (op) {
default:
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
index e082d9d..088d70c 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
@@ -23,8 +23,10 @@ namespace ento {
class SimpleConstraintManager : public ConstraintManager {
SubEngine &SU;
+ BasicValueFactory &BVF;
public:
- SimpleConstraintManager(SubEngine &subengine) : SU(subengine) {}
+ SimpleConstraintManager(SubEngine &subengine, BasicValueFactory &BV)
+ : SU(subengine), BVF(BV) {}
virtual ~SimpleConstraintManager();
//===------------------------------------------------------------------===//
@@ -79,6 +81,8 @@ protected:
// Internal implementation.
//===------------------------------------------------------------------===//
+ BasicValueFactory &getBasicVals() const { return BVF; }
+
bool canReasonAbout(SVal X) const;
ProgramStateRef assumeAux(ProgramStateRef state,
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index d0558f1..ad58a07 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -106,9 +107,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
return UnknownVal();
llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue();
- i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() ||
- Loc::isLocType(castTy));
- i = i.extOrTrunc(Context.getTypeSize(castTy));
+ BasicVals.getAPSIntType(castTy).apply(i);
if (isLocType)
return makeIntLocVal(i);
@@ -139,9 +138,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) {
return makeLocAsInteger(val, BitWidth);
llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue();
- i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() ||
- Loc::isLocType(castTy));
- i = i.extOrTrunc(BitWidth);
+ BasicVals.getAPSIntType(castTy).apply(i);
return makeIntVal(i);
}
@@ -272,14 +269,40 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy);
// If we reach this point, the expression cannot be simplified.
- // Make a SymbolVal for the entire expression.
- return makeNonLoc(LHS, op, RHS, resultTy);
+ // Make a SymbolVal for the entire expression, after converting the RHS.
+ const llvm::APSInt *ConvertedRHS = &RHS;
+ if (BinaryOperator::isComparisonOp(op)) {
+ // We're looking for a type big enough to compare the symbolic value
+ // with the given constant.
+ // FIXME: This is an approximation of Sema::UsualArithmeticConversions.
+ ASTContext &Ctx = getContext();
+ QualType SymbolType = LHS->getType(Ctx);
+ uint64_t ValWidth = RHS.getBitWidth();
+ uint64_t TypeWidth = Ctx.getTypeSize(SymbolType);
+
+ if (ValWidth < TypeWidth) {
+ // If the value is too small, extend it.
+ ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
+ } else if (ValWidth == TypeWidth) {
+ // If the value is signed but the symbol is unsigned, do the comparison
+ // in unsigned space. [C99 6.3.1.8]
+ // (For the opposite case, the value is already unsigned.)
+ if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType())
+ ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
+ }
+ } else
+ ConvertedRHS = &BasicVals.Convert(resultTy, RHS);
+
+ return makeNonLoc(LHS, op, *ConvertedRHS, resultTy);
}
SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs,
QualType resultTy) {
+ NonLoc InputLHS = lhs;
+ NonLoc InputRHS = rhs;
+
// Handle trivial case where left-side and right-side are the same.
if (lhs == rhs)
switch (op) {
@@ -304,7 +327,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
while (1) {
switch (lhs.getSubKind()) {
default:
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ return makeSymExprValNN(state, op, lhs, rhs, resultTy);
case nonloc::LocAsIntegerKind: {
Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc();
switch (rhs.getSubKind()) {
@@ -315,8 +338,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
case nonloc::ConcreteIntKind: {
// Transform the integer into a location and compare.
llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue();
- i.setIsUnsigned(true);
- i = i.extOrTrunc(Context.getTypeSize(Context.VoidPtrTy));
+ BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i);
return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy);
}
default:
@@ -327,86 +349,78 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
return makeTruthVal(true, resultTy);
default:
// This case also handles pointer arithmetic.
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
}
}
case nonloc::ConcreteIntKind: {
- const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs);
-
- // Is the RHS a symbol we can simplify?
- // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case.
- if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) {
- SymbolRef RSym = srhs->getSymbol();
- if (RSym->getType(Context)->isIntegerType()) {
- if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
- // The symbol evaluates to a constant.
- const llvm::APSInt *rhs_I;
- if (BinaryOperator::isRelationalOp(op))
- rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant);
- else
- rhs_I = &BasicVals.Convert(resultTy, *Constant);
-
- rhs = nonloc::ConcreteInt(*rhs_I);
- }
+ llvm::APSInt LHSValue = cast<nonloc::ConcreteInt>(lhs).getValue();
+
+ // If we're dealing with two known constants, just perform the operation.
+ if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) {
+ llvm::APSInt RHSValue = *KnownRHSValue;
+ if (BinaryOperator::isComparisonOp(op)) {
+ // We're looking for a type big enough to compare the two values.
+ // FIXME: This is not correct. char + short will result in a promotion
+ // to int. Unfortunately we have lost types by this point.
+ APSIntType CompareType = std::max(APSIntType(LHSValue),
+ APSIntType(RHSValue));
+ CompareType.apply(LHSValue);
+ CompareType.apply(RHSValue);
+ } else if (!BinaryOperator::isShiftOp(op)) {
+ APSIntType IntType = BasicVals.getAPSIntType(resultTy);
+ IntType.apply(LHSValue);
+ IntType.apply(RHSValue);
}
- }
- if (isa<nonloc::ConcreteInt>(rhs)) {
- return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs));
- } else {
- const llvm::APSInt& lhsValue = lhsInt.getValue();
-
- // Swap the left and right sides and flip the operator if doing so
- // allows us to better reason about the expression (this is a form
- // of expression canonicalization).
- // While we're at it, catch some special cases for non-commutative ops.
- NonLoc tmp = rhs;
- rhs = lhs;
- lhs = tmp;
+ const llvm::APSInt *Result =
+ BasicVals.evalAPSInt(op, LHSValue, RHSValue);
+ if (!Result)
+ return UndefinedVal();
- switch (op) {
- case BO_LT:
- case BO_GT:
- case BO_LE:
- case BO_GE:
- op = ReverseComparison(op);
- continue;
- case BO_EQ:
- case BO_NE:
- case BO_Add:
- case BO_Mul:
- case BO_And:
- case BO_Xor:
- case BO_Or:
- continue;
- case BO_Shr:
- if (lhsValue.isAllOnesValue() && lhsValue.isSigned())
- // At this point lhs and rhs have been swapped.
- return rhs;
- // FALL-THROUGH
- case BO_Shl:
- if (lhsValue == 0)
- // At this point lhs and rhs have been swapped.
- return rhs;
- return makeGenericVal(state, op, rhs, lhs, resultTy);
- default:
- return makeGenericVal(state, op, rhs, lhs, resultTy);
- }
+ return nonloc::ConcreteInt(*Result);
+ }
+
+ // Swap the left and right sides and flip the operator if doing so
+ // allows us to better reason about the expression (this is a form
+ // of expression canonicalization).
+ // While we're at it, catch some special cases for non-commutative ops.
+ switch (op) {
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ op = ReverseComparison(op);
+ // FALL-THROUGH
+ case BO_EQ:
+ case BO_NE:
+ case BO_Add:
+ case BO_Mul:
+ case BO_And:
+ case BO_Xor:
+ case BO_Or:
+ std::swap(lhs, rhs);
+ continue;
+ case BO_Shr:
+ // (~0)>>a
+ if (LHSValue.isAllOnesValue() && LHSValue.isSigned())
+ return evalCastFromNonLoc(lhs, resultTy);
+ // FALL-THROUGH
+ case BO_Shl:
+ // 0<<a and 0>>a
+ if (LHSValue == 0)
+ return evalCastFromNonLoc(lhs, resultTy);
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
+ default:
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
}
case nonloc::SymbolValKind: {
- nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs);
+ // We only handle LHS as simple symbols or SymIntExprs.
+ SymbolRef Sym = cast<nonloc::SymbolVal>(lhs).getSymbol();
// LHS is a symbolic expression.
- if (selhs->isExpression()) {
-
- // Only handle LHS of the form "$sym op constant", at least for now.
- const SymIntExpr *symIntExpr =
- dyn_cast<SymIntExpr>(selhs->getSymbol());
-
- if (!symIntExpr)
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) {
// Is this a logical not? (!x is represented as x == 0.)
if (op == BO_EQ && rhs.isZeroConstant()) {
@@ -452,95 +466,57 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
}
// For now, only handle expressions whose RHS is a constant.
- const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs);
- if (!rhsInt)
- return makeGenericVal(state, op, lhs, rhs, resultTy);
-
- // If both the LHS and the current expression are additive,
- // fold their constants.
- if (BinaryOperator::isAdditiveOp(op)) {
- BinaryOperator::Opcode lop = symIntExpr->getOpcode();
- if (BinaryOperator::isAdditiveOp(lop)) {
- // resultTy may not be the best type to convert to, but it's
- // probably the best choice in expressions with mixed type
- // (such as x+1U+2LL). The rules for implicit conversions should
- // choose a reasonable type to preserve the expression, and will
- // at least match how the value is going to be used.
- const llvm::APSInt &first =
- BasicVals.Convert(resultTy, symIntExpr->getRHS());
- const llvm::APSInt &second =
- BasicVals.Convert(resultTy, rhsInt->getValue());
- const llvm::APSInt *newRHS;
- if (lop == op)
- newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
- else
- newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
- return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy);
- }
- }
-
- // Otherwise, make a SymbolVal out of the expression.
- return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy);
-
- // LHS is a simple symbol (not a symbolic expression).
- } else {
- nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs);
- SymbolRef Sym = slhs->getSymbol();
- QualType lhsType = Sym->getType(Context);
-
- // The conversion type is usually the result type, but not in the case
- // of relational expressions.
- QualType conversionType = resultTy;
- if (BinaryOperator::isRelationalOp(op))
- conversionType = lhsType;
-
- // Does the symbol simplify to a constant? If so, "fold" the constant
- // by setting 'lhs' to a ConcreteInt and try again.
- if (lhsType->isIntegerType())
- if (const llvm::APSInt *Constant = state->getSymVal(Sym)) {
- // The symbol evaluates to a constant. If necessary, promote the
- // folded constant (LHS) to the result type.
- const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType,
- *Constant);
- lhs = nonloc::ConcreteInt(lhs_I);
-
- // Also promote the RHS (if necessary).
-
- // For shifts, it is not necessary to promote the RHS.
- if (BinaryOperator::isShiftOp(op))
+ if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) {
+ // If both the LHS and the current expression are additive,
+ // fold their constants and try again.
+ if (BinaryOperator::isAdditiveOp(op)) {
+ BinaryOperator::Opcode lop = symIntExpr->getOpcode();
+ if (BinaryOperator::isAdditiveOp(lop)) {
+ // Convert the two constants to a common type, then combine them.
+
+ // resultTy may not be the best type to convert to, but it's
+ // probably the best choice in expressions with mixed type
+ // (such as x+1U+2LL). The rules for implicit conversions should
+ // choose a reasonable type to preserve the expression, and will
+ // at least match how the value is going to be used.
+ APSIntType IntType = BasicVals.getAPSIntType(resultTy);
+ const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS());
+ const llvm::APSInt &second = IntType.convert(*RHSValue);
+
+ const llvm::APSInt *newRHS;
+ if (lop == op)
+ newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
+ else
+ newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
+
+ assert(newRHS && "Invalid operation despite common type!");
+ rhs = nonloc::ConcreteInt(*newRHS);
+ lhs = nonloc::SymbolVal(symIntExpr->getLHS());
+ op = lop;
continue;
-
- // Other operators: do an implicit conversion. This shouldn't be
- // necessary once we support truncation/extension of symbolic values.
- if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){
- rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType,
- rhs_I->getValue()));
}
-
- continue;
}
- // Is the RHS a symbol we can simplify?
- if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) {
- SymbolRef RSym = srhs->getSymbol();
- if (RSym->getType(Context)->isIntegerType()) {
- if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
- // The symbol evaluates to a constant.
- const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType,
- *Constant);
- rhs = nonloc::ConcreteInt(rhs_I);
- }
- }
+ // Otherwise, make a SymIntExpr out of the expression.
+ return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy);
}
- if (isa<nonloc::ConcreteInt>(rhs)) {
- return MakeSymIntVal(slhs->getSymbol(), op,
- cast<nonloc::ConcreteInt>(rhs).getValue(),
- resultTy);
+
+ } else if (isa<SymbolData>(Sym)) {
+ // Does the symbol simplify to a constant? If so, "fold" the constant
+ // by setting 'lhs' to a ConcreteInt and try again.
+ if (const llvm::APSInt *Constant = state->getSymVal(Sym)) {
+ lhs = nonloc::ConcreteInt(*Constant);
+ continue;
}
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ // Is the RHS a constant?
+ if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs))
+ return MakeSymIntVal(Sym, op, *RHSValue, resultTy);
}
+
+ // Give up -- this is not a symbolic expression we can handle.
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
}
}
@@ -697,11 +673,18 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// regions, though.
return UnknownVal();
- // If both values wrap regions, see if they're from different base regions.
+ const MemSpaceRegion *LeftMS = LeftMR->getMemorySpace();
+ const MemSpaceRegion *RightMS = RightMR->getMemorySpace();
+ const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion();
const MemRegion *LeftBase = LeftMR->getBaseRegion();
const MemRegion *RightBase = RightMR->getBaseRegion();
- if (LeftBase != RightBase &&
- !isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) {
+
+ // If the two regions are from different known memory spaces they cannot be
+ // equal. Also, assume that no symbolic region (whose memory space is
+ // unknown) is on the stack.
+ if (LeftMS != RightMS &&
+ ((LeftMS != UnknownMS && RightMS != UnknownMS) ||
+ (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) {
switch (op) {
default:
return UnknownVal();
@@ -712,24 +695,20 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
}
}
- // The two regions are from the same base region. See if they're both a
- // type of region we know how to compare.
- const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace();
- const MemSpaceRegion *RightMS = RightBase->getMemorySpace();
-
- // Heuristic: assume that no symbolic region (whose memory space is
- // unknown) is on the stack.
- // FIXME: we should be able to be more precise once we can do better
- // aliasing constraints for symbolic regions, but this is a reasonable,
- // albeit unsound, assumption that holds most of the time.
- if (isa<StackSpaceRegion>(LeftMS) ^ isa<StackSpaceRegion>(RightMS)) {
+ // If both values wrap regions, see if they're from different base regions.
+ // Note, heap base symbolic regions are assumed to not alias with
+ // each other; for example, we assume that malloc returns different address
+ // on each invocation.
+ if (LeftBase != RightBase &&
+ ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) ||
+ (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){
switch (op) {
- default:
- break;
- case BO_EQ:
- return makeTruthVal(false, resultTy);
- case BO_NE:
- return makeTruthVal(true, resultTy);
+ default:
+ return UnknownVal();
+ case BO_EQ:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ return makeTruthVal(true, resultTy);
}
}
@@ -885,6 +864,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
return evalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy);
}
}
+ return UnknownVal();
}
// We are dealing with pointer arithmetic.
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 11748ae..3af60a1 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclObjC.h"
@@ -23,10 +24,21 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr)
: svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
-StoreRef StoreManager::enterStackFrame(ProgramStateRef state,
- const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx) {
- return StoreRef(state->getStore(), *this);
+StoreRef StoreManager::enterStackFrame(Store OldStore,
+ const CallEvent &Call,
+ const StackFrameContext *LCtx) {
+ StoreRef Store = StoreRef(OldStore, *this);
+
+ SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings;
+ Call.getInitialStackFrameContents(LCtx, InitialBindings);
+
+ for (CallEvent::BindingsTy::iterator I = InitialBindings.begin(),
+ E = InitialBindings.end();
+ I != E; ++I) {
+ Store = Bind(Store.getStore(), I->first, I->second);
+ }
+
+ return Store;
}
const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base,
@@ -210,6 +222,17 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
llvm_unreachable("unreachable");
}
+SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) {
+ // Walk through the cast path to create nested CXXBaseRegions.
+ SVal Result = Derived;
+ for (CastExpr::path_const_iterator I = Cast->path_begin(),
+ E = Cast->path_end();
+ I != E; ++I) {
+ Result = evalDerivedToBase(Result, (*I)->getType());
+ }
+ return Result;
+}
+
/// CastRetrievedVal - Used by subclasses of StoreManager to implement
/// implicit casts that arise from loads from regions that are reinterpreted
@@ -357,6 +380,3 @@ bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr,
return true;
}
-
-void SubRegionMap::anchor() { }
-void SubRegionMap::Visitor::anchor() { }
diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp
index adefb58..0bc192d 100644
--- a/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -164,6 +164,13 @@ void SymExpr::symbol_iterator::expand() {
llvm_unreachable("unhandled expansion case");
}
+unsigned SymExpr::computeComplexity() const {
+ unsigned R = 0;
+ for (symbol_iterator I = symbol_begin(), E = symbol_end(); I != E; ++I)
+ R++;
+ return R;
+}
+
const SymbolRegionValue*
SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
llvm::FoldingSetNodeID profile;
@@ -501,6 +508,9 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
return false;
return true;
}
+ // If no statement is provided, everything is this and parent contexts is live.
+ if (!Loc)
+ return true;
return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
}
@@ -510,6 +520,10 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
if (VarContext == CurrentContext) {
+ // If no statemetnt is provided, everything is live.
+ if (!Loc)
+ return true;
+
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;
diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
index fe912df..e5b8553 100644
--- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
@@ -42,6 +42,7 @@ public:
bool supportsLogicalOpControlFlow() const { return true; }
bool supportsAllBlockEdges() const { return true; }
virtual bool useVerboseDescription() const { return true; }
+ virtual bool supportsCrossFileDiagnostics() const { return true; }
};
} // end anonymous namespace
@@ -58,7 +59,9 @@ void TextPathDiagnostics::FlushDiagnosticsImpl(
for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
et = Diags.end(); it != et; ++it) {
const PathDiagnostic *D = *it;
- for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
+
+ PathPieces FlatPath = D->path.flatten(/*ShouldFlattenMacros=*/true);
+ for (PathPieces::const_iterator I = FlatPath.begin(), E = FlatPath.end();
I != E; ++I) {
unsigned diagID =
Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note,
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 008f744..fcdaaea 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CallGraph.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
@@ -57,6 +58,7 @@ STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level).");
STATISTIC(NumBlocksInAnalyzedFunctions,
"The # of basic blocks in the analyzed functions.");
STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
+STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
//===----------------------------------------------------------------------===//
// Special PathDiagnosticConsumers.
@@ -102,7 +104,7 @@ public:
/// The local declaration to all declarations ratio might be very small when
/// working with a PCH file.
SetOfDecls LocalTUDecls;
-
+
// PD is owned by AnalysisManager.
PathDiagnosticConsumer *PD;
@@ -212,7 +214,6 @@ public:
Opts.AnalysisPurgeOpt, Opts.EagerlyAssume,
Opts.TrimGraph,
Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
- Opts.CFGAddInitializers,
Opts.EagerlyTrimEGraph,
Opts.IPAMode,
Opts.InlineMaxStackDepth,
@@ -230,7 +231,7 @@ public:
/// \brief Build the call graph for all the top level decls of this TU and
/// use it to define the order in which the functions should be visited.
- void HandleDeclsGallGraph();
+ void HandleDeclsGallGraph(const unsigned LocalTUDeclsSize);
/// \brief Run analyzes(syntax or path sensitive) on the given function.
/// \param Mode - determines if we are requesting syntax only or path
@@ -246,6 +247,7 @@ public:
SetOfConstDecls *VisitedCallees);
/// Visitors for the RecursiveASTVisitor.
+ bool shouldWalkTypesOfTypeLocs() const { return false; }
/// Handle callbacks for arbitrary Decls.
bool VisitDecl(Decl *D) {
@@ -306,18 +308,22 @@ void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
if (isa<ObjCMethodDecl>(*I))
continue;
- LocalTUDecls.insert(*I);
+ LocalTUDecls.push_back(*I);
}
}
-void AnalysisConsumer::HandleDeclsGallGraph() {
+void AnalysisConsumer::HandleDeclsGallGraph(const unsigned LocalTUDeclsSize) {
// Otherwise, use the Callgraph to derive the order.
// Build the Call Graph.
CallGraph CG;
+
// Add all the top level declarations to the graph.
- for (SetOfDecls::iterator I = LocalTUDecls.begin(),
- E = LocalTUDecls.end(); I != E; ++I)
- CG.addToCallGraph(*I);
+ // Note: CallGraph can trigger deserialization of more items from a pch
+ // (though HandleInterestingDecl); triggering additions to LocalTUDecls.
+ // We rely on random access to add the initially processed Decls to CG.
+ for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
+ CG.addToCallGraph(LocalTUDecls[i]);
+ }
// Find the top level nodes - children of root + the unreachable (parentless)
// nodes.
@@ -338,11 +344,11 @@ void AnalysisConsumer::HandleDeclsGallGraph() {
// translation unit. This step is very important for performance. It ensures
// that we analyze the root functions before the externally available
// subroutines.
- std::queue<CallGraphNode*> BFSQueue;
+ std::deque<CallGraphNode*> BFSQueue;
for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator
TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend();
TI != TE; ++TI)
- BFSQueue.push(*TI);
+ BFSQueue.push_back(*TI);
// BFS over all of the functions, while skipping the ones inlined into
// the previously processed functions. Use external Visited set, which is
@@ -350,7 +356,14 @@ void AnalysisConsumer::HandleDeclsGallGraph() {
SmallPtrSet<CallGraphNode*,24> Visited;
while(!BFSQueue.empty()) {
CallGraphNode *N = BFSQueue.front();
- BFSQueue.pop();
+ BFSQueue.pop_front();
+
+ // Push the children into the queue.
+ for (CallGraphNode::const_iterator CI = N->begin(),
+ CE = N->end(); CI != CE; ++CI) {
+ if (!Visited.count(*CI))
+ BFSQueue.push_back(*CI);
+ }
// Skip the functions which have been processed already or previously
// inlined.
@@ -365,19 +378,13 @@ void AnalysisConsumer::HandleDeclsGallGraph() {
(Mgr->InliningMode == All ? 0 : &VisitedCallees));
// Add the visited callees to the global visited set.
- for (SetOfConstDecls::const_iterator I = VisitedCallees.begin(),
- E = VisitedCallees.end(); I != E; ++I){
+ for (SetOfConstDecls::iterator I = VisitedCallees.begin(),
+ E = VisitedCallees.end(); I != E; ++I) {
CallGraphNode *VN = CG.getNode(*I);
if (VN)
Visited.insert(VN);
}
Visited.insert(N);
-
- // Push the children into the queue.
- for (CallGraphNode::const_iterator CI = N->begin(),
- CE = N->end(); CI != CE; ++CI) {
- BFSQueue.push(*CI);
- }
}
}
@@ -402,12 +409,18 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
RecVisitorBR = &BR;
// Process all the top level declarations.
- for (SetOfDecls::iterator I = LocalTUDecls.begin(),
- E = LocalTUDecls.end(); I != E; ++I)
- TraverseDecl(*I);
+ //
+ // Note: TraverseDecl may modify LocalTUDecls, but only by appending more
+ // entries. Thus we don't use an iterator, but rely on LocalTUDecls
+ // random access. By doing so, we automatically compensate for iterators
+ // possibly being invalidated, although this is a bit slower.
+ const unsigned LocalTUDeclsSize = LocalTUDecls.size();
+ for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
+ TraverseDecl(LocalTUDecls[i]);
+ }
if (Mgr->shouldInlineCall())
- HandleDeclsGallGraph();
+ HandleDeclsGallGraph(LocalTUDeclsSize);
// After all decls handled, run checkers on the entire TranslationUnit.
checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
@@ -475,6 +488,12 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
return;
DisplayFunction(D, Mode);
+ CFG *DeclCFG = Mgr->getCFG(D);
+ if (DeclCFG) {
+ unsigned CFGSize = DeclCFG->size();
+ MaxCFGSize = MaxCFGSize < CFGSize ? CFGSize : MaxCFGSize;
+ }
+
// Clear the AnalysisManager of old AnalysisDeclContexts.
Mgr->ClearContexts();
@@ -510,6 +529,10 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
if (!Mgr->getCFG(D))
return;
+ // See if the LiveVariables analysis scales.
+ if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
+ return;
+
ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries);
// Set the graph auditor.
@@ -520,7 +543,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
}
// Execute the worklist algorithm.
- Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0),
+ Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
Mgr->getMaxNodes());
// Release the auditor (if any) so that it doesn't monitor the graph
diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt
index bbcb085..06d1485 100644
--- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt
@@ -1,8 +1,5 @@
set(LLVM_NO_RTTI 1)
-set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite
- clangStaticAnalyzerCheckers)
-
include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../Checkers )
add_clang_library(clangStaticAnalyzerFrontend
@@ -16,6 +13,18 @@ add_dependencies(clangStaticAnalyzerFrontend
clangStaticAnalyzerCore
ClangAttrClasses
ClangAttrList
+ ClangCommentNodes
ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangDiagnosticFrontend
ClangStmtNodes
)
+
+target_link_libraries(clangStaticAnalyzerFrontend
+ clangBasic
+ clangLex
+ clangAST
+ clangFrontend
+ clangRewrite
+ clangStaticAnalyzerCheckers
+ )
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
index c06da0d..0229aed 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -118,7 +118,7 @@ CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts,
for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) {
if (checkerOpts[i].isUnclaimed())
- diags.Report(diag::warn_unknown_analyzer_checker)
+ diags.Report(diag::err_unknown_analyzer_checker)
<< checkerOpts[i].getName();
}
diff --git a/lib/Tooling/ArgumentsAdjusters.cpp b/lib/Tooling/ArgumentsAdjusters.cpp
new file mode 100644
index 0000000..31dd4659
--- /dev/null
+++ b/lib/Tooling/ArgumentsAdjusters.cpp
@@ -0,0 +1,34 @@
+//===--- ArgumentsAdjusters.cpp - Command line arguments adjuster ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains definitions of classes which implement ArgumentsAdjuster
+// interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ArgumentsAdjusters.h"
+
+namespace clang {
+namespace tooling {
+
+void ArgumentsAdjuster::anchor() {
+}
+
+/// Add -fsyntax-only option to the commnand line arguments.
+CommandLineArguments
+ClangSyntaxOnlyAdjuster::Adjust(const CommandLineArguments &Args) {
+ CommandLineArguments AdjustedArgs = Args;
+ // FIXME: Remove options that generate output.
+ AdjustedArgs.push_back("-fsyntax-only");
+ return AdjustedArgs;
+}
+
+} // end namespace tooling
+} // end namespace clang
+
diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt
index b84b211..49d3101 100644
--- a/lib/Tooling/CMakeLists.txt
+++ b/lib/Tooling/CMakeLists.txt
@@ -1,7 +1,27 @@
set(LLVM_LINK_COMPONENTS support)
-SET(LLVM_USED_LIBS clangBasic clangFrontend clangAST)
add_clang_library(clangTooling
+ ArgumentsAdjusters.cpp
+ CommandLineClangTool.cpp
CompilationDatabase.cpp
+ Refactoring.cpp
+ RefactoringCallbacks.cpp
Tooling.cpp
)
+
+add_dependencies(clangTooling
+ ClangAttrClasses
+ ClangAttrList
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangDiagnosticFrontend
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangTooling
+ clangBasic
+ clangFrontend
+ clangAST
+ clangASTMatchers
+ clangRewrite
+ )
diff --git a/lib/Tooling/CommandLineClangTool.cpp b/lib/Tooling/CommandLineClangTool.cpp
new file mode 100644
index 0000000..8da2a33
--- /dev/null
+++ b/lib/Tooling/CommandLineClangTool.cpp
@@ -0,0 +1,80 @@
+//===--- CommandLineClangTool.cpp - command-line clang tools driver -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the CommandLineClangTool class used to run clang
+// tools as separate command-line applications with a consistent common
+// interface for handling compilation database and input files.
+//
+// It provides a common subset of command-line options, common algorithm
+// for locating a compilation database and source files, and help messages
+// for the basic command-line interface.
+//
+// It creates a CompilationDatabase, initializes a ClangTool and runs a
+// user-specified FrontendAction over all TUs in which the given files are
+// compiled.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CommandLineClangTool.h"
+#include "clang/Tooling/Tooling.h"
+
+using namespace clang::tooling;
+using namespace llvm;
+
+static const char *MoreHelpText =
+ "\n"
+ "-p <build-path> is used to read a compile command database.\n"
+ "\n"
+ "\tFor example, it can be a CMake build directory in which a file named\n"
+ "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n"
+ "\tCMake option to get this output). When no build path is specified,\n"
+ "\tclang-check will attempt to locate it automatically using all parent\n"
+ "\tpaths of the first input file. See:\n"
+ "\thttp://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
+ "\texample of setting up Clang Tooling on a source tree.\n"
+ "\n"
+ "<source0> ... specify the paths of source files. These paths are looked\n"
+ "\tup in the compile command database. If the path of a file is absolute,\n"
+ "\tit needs to point into CMake's source tree. If the path is relative,\n"
+ "\tthe current working directory needs to be in the CMake source tree and\n"
+ "\tthe file must be in a subdirectory of the current working directory.\n"
+ "\t\"./\" prefixes in the relative files will be automatically removed,\n"
+ "\tbut the rest of a relative path must be a suffix of a path in the\n"
+ "\tcompile command database.\n"
+ "\n";
+
+CommandLineClangTool::CommandLineClangTool() :
+ BuildPath("p", cl::desc("Build path"), cl::Optional),
+ SourcePaths(cl::Positional, cl::desc("<source0> [... <sourceN>]"),
+ cl::OneOrMore),
+ MoreHelp(MoreHelpText) {
+}
+
+void CommandLineClangTool::initialize(int argc, const char **argv) {
+ Compilations.reset(FixedCompilationDatabase::loadFromCommandLine(argc, argv));
+ cl::ParseCommandLineOptions(argc, argv);
+ if (!Compilations) {
+ std::string ErrorMessage;
+ if (!BuildPath.empty()) {
+ Compilations.reset(CompilationDatabase::autoDetectFromDirectory(
+ BuildPath, ErrorMessage));
+ } else {
+ Compilations.reset(CompilationDatabase::autoDetectFromSource(
+ SourcePaths[0], ErrorMessage));
+ }
+ if (!Compilations)
+ llvm::report_fatal_error(ErrorMessage);
+ }
+}
+
+int CommandLineClangTool::run(FrontendActionFactory *ActionFactory) {
+ ClangTool Tool(*Compilations, SourcePaths);
+ return Tool.run(ActionFactory);
+}
diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp
index dd9ccc0..3139cc2 100644
--- a/lib/Tooling/CompilationDatabase.cpp
+++ b/lib/Tooling/CompilationDatabase.cpp
@@ -12,11 +12,16 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/system_error.h"
+#ifdef USE_CUSTOM_COMPILATION_DATABASE
+#include "CustomCompilationDatabase.h"
+#endif
+
namespace clang {
namespace tooling {
@@ -121,6 +126,52 @@ CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
return Database.take();
}
+static CompilationDatabase *
+findCompilationDatabaseFromDirectory(StringRef Directory) {
+#ifdef USE_CUSTOM_COMPILATION_DATABASE
+ if (CompilationDatabase *DB =
+ ::clang::tooling::findCompilationDatabaseForDirectory(Directory))
+ return DB;
+#endif
+ while (!Directory.empty()) {
+ std::string LoadErrorMessage;
+
+ if (CompilationDatabase *DB =
+ CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
+ return DB;
+
+ Directory = llvm::sys::path::parent_path(Directory);
+ }
+ return NULL;
+}
+
+CompilationDatabase *
+CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
+ std::string &ErrorMessage) {
+ llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+
+ CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory);
+
+ if (!DB)
+ ErrorMessage = ("Could not auto-detect compilation database for file \"" +
+ SourceFile + "\"").str();
+ return DB;
+}
+
+CompilationDatabase *
+CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
+ std::string &ErrorMessage) {
+ llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
+
+ CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath);
+
+ if (!DB)
+ ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
+ SourceDir + "\"").str();
+ return DB;
+}
+
FixedCompilationDatabase *
FixedCompilationDatabase::loadFromCommandLine(int &Argc,
const char **Argv,
@@ -148,6 +199,11 @@ FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
return Result;
}
+std::vector<std::string>
+FixedCompilationDatabase::getAllFiles() const {
+ return std::vector<std::string>();
+}
+
JSONCompilationDatabase *
JSONCompilationDatabase::loadFromFile(StringRef FilePath,
std::string &ErrorMessage) {
@@ -179,8 +235,10 @@ JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
std::vector<CompileCommand>
JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
+ llvm::SmallString<128> NativeFilePath;
+ llvm::sys::path::native(FilePath, NativeFilePath);
llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
- CommandsRefI = IndexByFile.find(FilePath);
+ CommandsRefI = IndexByFile.find(NativeFilePath);
if (CommandsRefI == IndexByFile.end())
return std::vector<CompileCommand>();
const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue();
@@ -196,6 +254,21 @@ JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
return Commands;
}
+std::vector<std::string>
+JSONCompilationDatabase::getAllFiles() const {
+ std::vector<std::string> Result;
+
+ llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
+ CommandsRefI = IndexByFile.begin();
+ const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
+ CommandsRefEnd = IndexByFile.end();
+ for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
+ Result.push_back(CommandsRefI->first().str());
+ }
+
+ return Result;
+}
+
bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
llvm::yaml::document_iterator I = YAMLStream.begin();
if (I == YAMLStream.end()) {
@@ -222,10 +295,9 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
ErrorMessage = "Expected object.";
return false;
}
- llvm::yaml::ScalarNode *Directory;
- llvm::yaml::ScalarNode *Command;
- llvm::SmallString<8> FileStorage;
- llvm::StringRef File;
+ llvm::yaml::ScalarNode *Directory = NULL;
+ llvm::yaml::ScalarNode *Command = NULL;
+ llvm::yaml::ScalarNode *File = NULL;
for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
KVE = Object->end();
KVI != KVE; ++KVI) {
@@ -242,20 +314,39 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
}
llvm::yaml::ScalarNode *KeyString =
llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
+ if (KeyString == NULL) {
+ ErrorMessage = "Expected strings as key.";
+ return false;
+ }
llvm::SmallString<8> KeyStorage;
if (KeyString->getValue(KeyStorage) == "directory") {
Directory = ValueString;
} else if (KeyString->getValue(KeyStorage) == "command") {
Command = ValueString;
} else if (KeyString->getValue(KeyStorage) == "file") {
- File = ValueString->getValue(FileStorage);
+ File = ValueString;
} else {
ErrorMessage = ("Unknown key: \"" +
KeyString->getRawValue() + "\"").str();
return false;
}
}
- IndexByFile[File].push_back(
+ if (!File) {
+ ErrorMessage = "Missing key: \"file\".";
+ return false;
+ }
+ if (!Command) {
+ ErrorMessage = "Missing key: \"command\".";
+ return false;
+ }
+ if (!Directory) {
+ ErrorMessage = "Missing key: \"directory\".";
+ return false;
+ }
+ llvm::SmallString<8> FileStorage;
+ llvm::SmallString<128> NativeFilePath;
+ llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath);
+ IndexByFile[NativeFilePath].push_back(
CompileCommandRef(Directory, Command));
}
return true;
@@ -263,4 +354,3 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
} // end namespace tooling
} // end namespace clang
-
diff --git a/lib/Tooling/CustomCompilationDatabase.h b/lib/Tooling/CustomCompilationDatabase.h
new file mode 100644
index 0000000..b375f8d
--- /dev/null
+++ b/lib/Tooling/CustomCompilationDatabase.h
@@ -0,0 +1,42 @@
+//===--- CustomCompilationDatabase.h --------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains a hook to supply a custom \c CompilationDatabase
+// implementation.
+//
+// The mechanism can be used by IDEs or non-public code bases to integrate with
+// their build system. Currently we support statically linking in an
+// implementation of \c findCompilationDatabaseForDirectory and enabling it
+// with -DUSE_CUSTOM_COMPILATION_DATABASE when compiling the Tooling library.
+//
+// FIXME: The strategy forward is to provide a plugin system that can load
+// custom compilation databases and make enabling that a build option.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H
+#define LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tooling {
+class CompilationDatabase;
+
+/// \brief Returns a CompilationDatabase for the given \c Directory.
+///
+/// \c Directory can be any directory within a project. This methods will
+/// then try to find compilation database files in \c Directory or any of its
+/// parents. If a compilation database cannot be found or loaded, returns NULL.
+clang::tooling::CompilationDatabase *findCompilationDatabaseForDirectory(
+ llvm::StringRef Directory);
+
+} // namespace tooling
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H
diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp
new file mode 100644
index 0000000..6284353
--- /dev/null
+++ b/lib/Tooling/Refactoring.cpp
@@ -0,0 +1,186 @@
+//===--- Refactoring.cpp - Framework for clang refactoring tools ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements tools to support refactorings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/Support/raw_os_ostream.h"
+
+namespace clang {
+namespace tooling {
+
+static const char * const InvalidLocation = "";
+
+Replacement::Replacement()
+ : FilePath(InvalidLocation), Offset(0), Length(0) {}
+
+Replacement::Replacement(llvm::StringRef FilePath, unsigned Offset,
+ unsigned Length, llvm::StringRef ReplacementText)
+ : FilePath(FilePath), Offset(Offset),
+ Length(Length), ReplacementText(ReplacementText) {}
+
+Replacement::Replacement(SourceManager &Sources, SourceLocation Start,
+ unsigned Length, llvm::StringRef ReplacementText) {
+ setFromSourceLocation(Sources, Start, Length, ReplacementText);
+}
+
+Replacement::Replacement(SourceManager &Sources, const CharSourceRange &Range,
+ llvm::StringRef ReplacementText) {
+ setFromSourceRange(Sources, Range, ReplacementText);
+}
+
+bool Replacement::isApplicable() const {
+ return FilePath != InvalidLocation;
+}
+
+bool Replacement::apply(Rewriter &Rewrite) const {
+ SourceManager &SM = Rewrite.getSourceMgr();
+ const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
+ if (Entry == NULL)
+ return false;
+ FileID ID;
+ // FIXME: Use SM.translateFile directly.
+ SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
+ ID = Location.isValid() ?
+ SM.getFileID(Location) :
+ SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
+ // FIXME: We cannot check whether Offset + Length is in the file, as
+ // the remapping API is not public in the RewriteBuffer.
+ const SourceLocation Start =
+ SM.getLocForStartOfFile(ID).
+ getLocWithOffset(Offset);
+ // ReplaceText returns false on success.
+ // ReplaceText only fails if the source location is not a file location, in
+ // which case we already returned false earlier.
+ bool RewriteSucceeded = !Rewrite.ReplaceText(Start, Length, ReplacementText);
+ assert(RewriteSucceeded);
+ return RewriteSucceeded;
+}
+
+std::string Replacement::toString() const {
+ std::string result;
+ llvm::raw_string_ostream stream(result);
+ stream << FilePath << ": " << Offset << ":+" << Length
+ << ":\"" << ReplacementText << "\"";
+ return result;
+}
+
+bool Replacement::Less::operator()(const Replacement &R1,
+ const Replacement &R2) const {
+ if (R1.FilePath != R2.FilePath) return R1.FilePath < R2.FilePath;
+ if (R1.Offset != R2.Offset) return R1.Offset < R2.Offset;
+ if (R1.Length != R2.Length) return R1.Length < R2.Length;
+ return R1.ReplacementText < R2.ReplacementText;
+}
+
+void Replacement::setFromSourceLocation(SourceManager &Sources,
+ SourceLocation Start, unsigned Length,
+ llvm::StringRef ReplacementText) {
+ const std::pair<FileID, unsigned> DecomposedLocation =
+ Sources.getDecomposedLoc(Start);
+ const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
+ this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation;
+ this->Offset = DecomposedLocation.second;
+ this->Length = Length;
+ this->ReplacementText = ReplacementText;
+}
+
+// FIXME: This should go into the Lexer, but we need to figure out how
+// to handle ranges for refactoring in general first - there is no obvious
+// good way how to integrate this into the Lexer yet.
+static int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) {
+ SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
+ SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
+ std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
+ std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
+ if (Start.first != End.first) return -1;
+ if (Range.isTokenRange())
+ End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
+ LangOptions());
+ return End.second - Start.second;
+}
+
+void Replacement::setFromSourceRange(SourceManager &Sources,
+ const CharSourceRange &Range,
+ llvm::StringRef ReplacementText) {
+ setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
+ getRangeSize(Sources, Range), ReplacementText);
+}
+
+bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) {
+ bool Result = true;
+ for (Replacements::const_iterator I = Replaces.begin(),
+ E = Replaces.end();
+ I != E; ++I) {
+ if (I->isApplicable()) {
+ Result = I->apply(Rewrite) && Result;
+ } else {
+ Result = false;
+ }
+ }
+ return Result;
+}
+
+bool saveRewrittenFiles(Rewriter &Rewrite) {
+ for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
+ E = Rewrite.buffer_end();
+ I != E; ++I) {
+ // FIXME: This code is copied from the FixItRewriter.cpp - I think it should
+ // go into directly into Rewriter (there we also have the Diagnostics to
+ // handle the error cases better).
+ const FileEntry *Entry =
+ Rewrite.getSourceMgr().getFileEntryForID(I->first);
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream FileStream(
+ Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary);
+ if (!ErrorInfo.empty())
+ return false;
+ I->second.write(FileStream);
+ FileStream.flush();
+ }
+ return true;
+}
+
+RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
+ ArrayRef<std::string> SourcePaths)
+ : Tool(Compilations, SourcePaths) {}
+
+Replacements &RefactoringTool::getReplacements() { return Replace; }
+
+int RefactoringTool::run(FrontendActionFactory *ActionFactory) {
+ int Result = Tool.run(ActionFactory);
+ LangOptions DefaultLangOptions;
+ DiagnosticOptions DefaultDiagnosticOptions;
+ TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(),
+ DefaultDiagnosticOptions);
+ DiagnosticsEngine Diagnostics(
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
+ &DiagnosticPrinter, false);
+ SourceManager Sources(Diagnostics, Tool.getFiles());
+ Rewriter Rewrite(Sources, DefaultLangOptions);
+ if (!applyAllReplacements(Replace, Rewrite)) {
+ llvm::errs() << "Skipped some replacements.\n";
+ }
+ if (!saveRewrittenFiles(Rewrite)) {
+ llvm::errs() << "Could not save rewritten files.\n";
+ return 1;
+ }
+ return Result;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/RefactoringCallbacks.cpp b/lib/Tooling/RefactoringCallbacks.cpp
new file mode 100644
index 0000000..4de125e
--- /dev/null
+++ b/lib/Tooling/RefactoringCallbacks.cpp
@@ -0,0 +1,81 @@
+//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
+
+namespace clang {
+namespace tooling {
+
+RefactoringCallback::RefactoringCallback() {}
+tooling::Replacements &RefactoringCallback::getReplacements() {
+ return Replace;
+}
+
+static Replacement replaceStmtWithText(SourceManager &Sources,
+ const Stmt &From,
+ StringRef Text) {
+ return tooling::Replacement(Sources, CharSourceRange::getTokenRange(
+ From.getSourceRange()), Text);
+}
+static Replacement replaceStmtWithStmt(SourceManager &Sources,
+ const Stmt &From,
+ const Stmt &To) {
+ return replaceStmtWithText(Sources, From, Lexer::getSourceText(
+ CharSourceRange::getTokenRange(To.getSourceRange()),
+ Sources, LangOptions()));
+}
+
+ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
+ : FromId(FromId), ToText(ToText) {}
+
+void ReplaceStmtWithText::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
+ Replace.insert(tooling::Replacement(
+ *Result.SourceManager,
+ CharSourceRange::getTokenRange(FromMatch->getSourceRange()),
+ ToText));
+ }
+}
+
+ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
+ : FromId(FromId), ToId(ToId) {}
+
+void ReplaceStmtWithStmt::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
+ const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
+ if (FromMatch && ToMatch)
+ Replace.insert(replaceStmtWithStmt(
+ *Result.SourceManager, *FromMatch, *ToMatch));
+}
+
+ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
+ bool PickTrueBranch)
+ : Id(Id), PickTrueBranch(PickTrueBranch) {}
+
+void ReplaceIfStmtWithItsBody::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
+ const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
+ if (Body) {
+ Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
+ } else if (!PickTrueBranch) {
+ // If we want to use the 'else'-branch, but it doesn't exist, delete
+ // the whole 'if'.
+ Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, ""));
+ }
+ }
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp
index fa2374f..e93e0c9 100644
--- a/lib/Tooling/Tooling.cpp
+++ b/lib/Tooling/Tooling.cpp
@@ -12,13 +12,13 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Tooling/ArgumentsAdjusters.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/ADT/STLExtras.h"
@@ -26,6 +26,13 @@
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"
+// For chdir, see the comment in ClangTool::run for more information.
+#ifdef _WIN32
+# include <direct.h>
+#else
+# include <unistd.h>
+#endif
+
namespace clang {
namespace tooling {
@@ -40,8 +47,8 @@ static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics,
const char *BinaryName) {
const std::string DefaultOutputName = "a.out";
clang::driver::Driver *CompilerDriver = new clang::driver::Driver(
- BinaryName, llvm::sys::getDefaultTargetTriple(),
- DefaultOutputName, false, *Diagnostics);
+ BinaryName, llvm::sys::getDefaultTargetTriple(),
+ DefaultOutputName, false, *Diagnostics);
CompilerDriver->setTitle("clang_based_tool");
return CompilerDriver;
}
@@ -108,29 +115,26 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code,
return Invocation.run();
}
-/// \brief Returns the absolute path of 'File', by prepending it with
-/// 'BaseDirectory' if 'File' is not absolute.
-///
-/// Otherwise returns 'File'.
-/// If 'File' starts with "./", the returned path will not contain the "./".
-/// Otherwise, the returned path will contain the literal path-concatenation of
-/// 'BaseDirectory' and 'File'.
-///
-/// \param File Either an absolute or relative path.
-/// \param BaseDirectory An absolute path.
-static std::string getAbsolutePath(
- StringRef File, StringRef BaseDirectory) {
- assert(llvm::sys::path::is_absolute(BaseDirectory));
+std::string getAbsolutePath(StringRef File) {
+ llvm::SmallString<1024> BaseDirectory;
+ if (const char *PWD = ::getenv("PWD"))
+ BaseDirectory = PWD;
+ else
+ llvm::sys::fs::current_path(BaseDirectory);
+ SmallString<1024> PathStorage;
if (llvm::sys::path::is_absolute(File)) {
- return File;
+ llvm::sys::path::native(File, PathStorage);
+ return PathStorage.str();
}
StringRef RelativePath(File);
+ // FIXME: Should '.\\' be accepted on Win32?
if (RelativePath.startswith("./")) {
RelativePath = RelativePath.substr(strlen("./"));
}
llvm::SmallString<1024> AbsolutePath(BaseDirectory);
llvm::sys::path::append(AbsolutePath, RelativePath);
- return AbsolutePath.str();
+ llvm::sys::path::native(Twine(AbsolutePath), PathStorage);
+ return PathStorage.str();
}
ToolInvocation::ToolInvocation(
@@ -140,7 +144,9 @@ ToolInvocation::ToolInvocation(
}
void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
- MappedFileContents[FilePath] = Content;
+ SmallString<1024> PathStorage;
+ llvm::sys::path::native(FilePath, PathStorage);
+ MappedFileContents[PathStorage] = Content;
}
bool ToolInvocation::run() {
@@ -167,20 +173,15 @@ bool ToolInvocation::run() {
}
llvm::OwningPtr<clang::CompilerInvocation> Invocation(
newInvocation(&Diagnostics, *CC1Args));
- return runInvocation(BinaryName, Compilation.get(),
- Invocation.take(), *CC1Args, ToolAction.take());
+ return runInvocation(BinaryName, Compilation.get(), Invocation.take(),
+ *CC1Args);
}
-// Exists solely for the purpose of lookup of the resource path.
-static int StaticSymbol;
-
bool ToolInvocation::runInvocation(
const char *BinaryName,
clang::driver::Compilation *Compilation,
clang::CompilerInvocation *Invocation,
- const clang::driver::ArgStringList &CC1Args,
- clang::FrontendAction *ToolAction) {
- llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction);
+ const clang::driver::ArgStringList &CC1Args) {
// Show the invocation, with -v.
if (Invocation->getHeaderSearchOpts().Verbose) {
llvm::errs() << "clang Invocation:\n";
@@ -194,6 +195,11 @@ bool ToolInvocation::runInvocation(
Compiler.setFileManager(Files);
// FIXME: What about LangOpts?
+ // ToolAction can have lifetime requirements for Compiler or its members, and
+ // we need to ensure it's deleted earlier than Compiler. So we pass it to an
+ // OwningPtr declared after the Compiler variable.
+ llvm::OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take());
+
// Create the compilers actual diagnostics engine.
Compiler.createDiagnostics(CC1Args.size(),
const_cast<char**>(CC1Args.data()));
@@ -203,18 +209,10 @@ bool ToolInvocation::runInvocation(
Compiler.createSourceManager(*Files);
addFileMappingsTo(Compiler.getSourceManager());
- // Infer the builtin include path if unspecified.
- if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes &&
- Compiler.getHeaderSearchOpts().ResourceDir.empty()) {
- // This just needs to be some symbol in the binary.
- void *const SymbolAddr = &StaticSymbol;
- Compiler.getHeaderSearchOpts().ResourceDir =
- clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr);
- }
-
- const bool Success = Compiler.ExecuteAction(*ToolAction);
+ const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
Compiler.resetAndLeakFileManager();
+ Files->clearStatCaches();
return Success;
}
@@ -228,35 +226,23 @@ void ToolInvocation::addFileMappingsTo(SourceManager &Sources) {
// FIXME: figure out what '0' stands for.
const FileEntry *FromFile = Files->getVirtualFile(
It->getKey(), Input->getBufferSize(), 0);
- // FIXME: figure out memory management ('true').
- Sources.overrideFileContents(FromFile, Input, true);
+ Sources.overrideFileContents(FromFile, Input);
}
}
ClangTool::ClangTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths)
- : Files((FileSystemOptions())) {
- llvm::SmallString<1024> BaseDirectory;
- if (const char *PWD = ::getenv("PWD"))
- BaseDirectory = PWD;
- else
- llvm::sys::fs::current_path(BaseDirectory);
+ : Files((FileSystemOptions())),
+ ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) {
for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) {
- llvm::SmallString<1024> File(getAbsolutePath(
- SourcePaths[I], BaseDirectory));
+ llvm::SmallString<1024> File(getAbsolutePath(SourcePaths[I]));
- std::vector<CompileCommand> CompileCommands =
+ std::vector<CompileCommand> CompileCommandsForFile =
Compilations.getCompileCommands(File.str());
- if (!CompileCommands.empty()) {
- for (int I = 0, E = CompileCommands.size(); I != E; ++I) {
- CompileCommand &Command = CompileCommands[I];
- if (!Command.Directory.empty()) {
- // FIXME: What should happen if CommandLine includes -working-directory
- // as well?
- Command.CommandLine.push_back(
- "-working-directory=" + Command.Directory);
- }
- CommandLines.push_back(std::make_pair(File.str(), Command.CommandLine));
+ if (!CompileCommandsForFile.empty()) {
+ for (int I = 0, E = CompileCommandsForFile.size(); I != E; ++I) {
+ CompileCommands.push_back(std::make_pair(File.str(),
+ CompileCommandsForFile[I]));
}
} else {
// FIXME: There are two use cases here: doing a fuzzy
@@ -273,11 +259,39 @@ void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
MappedFileContents.push_back(std::make_pair(FilePath, Content));
}
+void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) {
+ ArgsAdjuster.reset(Adjuster);
+}
+
int ClangTool::run(FrontendActionFactory *ActionFactory) {
+ // Exists solely for the purpose of lookup of the resource path.
+ // This just needs to be some symbol in the binary.
+ static int StaticSymbol;
+ // The driver detects the builtin header path based on the path of the
+ // executable.
+ // FIXME: On linux, GetMainExecutable is independent of the value of the
+ // first argument, thus allowing ClangTool and runToolOnCode to just
+ // pass in made-up names here. Make sure this works on other platforms.
+ std::string MainExecutable =
+ llvm::sys::Path::GetMainExecutable("clang_tool", &StaticSymbol).str();
+
bool ProcessingFailed = false;
- for (unsigned I = 0; I < CommandLines.size(); ++I) {
- std::string File = CommandLines[I].first;
- std::vector<std::string> &CommandLine = CommandLines[I].second;
+ for (unsigned I = 0; I < CompileCommands.size(); ++I) {
+ std::string File = CompileCommands[I].first;
+ // FIXME: chdir is thread hostile; on the other hand, creating the same
+ // behavior as chdir is complex: chdir resolves the path once, thus
+ // guaranteeing that all subsequent relative path operations work
+ // on the same path the original chdir resulted in. This makes a difference
+ // for example on network filesystems, where symlinks might be switched
+ // during runtime of the tool. Fixing this depends on having a file system
+ // abstraction that allows openat() style interactions.
+ if (chdir(CompileCommands[I].second.Directory.c_str()))
+ llvm::report_fatal_error("Cannot chdir into \"" +
+ CompileCommands[I].second.Directory + "\n!");
+ std::vector<std::string> CommandLine =
+ ArgsAdjuster->Adjust(CompileCommands[I].second.CommandLine);
+ assert(!CommandLine.empty());
+ CommandLine[0] = MainExecutable;
llvm::outs() << "Processing: " << File << ".\n";
ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files);
for (int I = 0, E = MappedFileContents.size(); I != E; ++I) {
diff --git a/runtime/compiler-rt/Makefile b/runtime/compiler-rt/Makefile
index 534168b..f133591 100644
--- a/runtime/compiler-rt/Makefile
+++ b/runtime/compiler-rt/Makefile
@@ -74,8 +74,8 @@ RuntimeDirs :=
ifeq ($(OS),Darwin)
RuntimeDirs += darwin
RuntimeLibrary.darwin.Configs := \
- eprintf 10.4 osx ios cc_kext \
- asan_osx profile_osx profile_ios
+ eprintf 10.4 osx cc_kext \
+ asan_osx profile_osx
endif
# On Linux, include a library which has all the runtime functions.
@@ -90,7 +90,7 @@ RuntimeLibrary.linux.Configs += \
endif
ifeq ($(ARCH),x86_64)
RuntimeLibrary.linux.Configs += \
- full-x86_64 profile-x86_64 asan-x86_64
+ full-x86_64 profile-x86_64 asan-x86_64 tsan-x86_64
endif
endif
diff --git a/test/ARCMT/Common.h b/test/ARCMT/Common.h
index 16856ed..ed48949 100644
--- a/test/ARCMT/Common.h
+++ b/test/ARCMT/Common.h
@@ -6,6 +6,7 @@
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#define CF_CONSUMED __attribute__((cf_consumed))
+#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
#define NS_INLINE static __inline__ __attribute__((always_inline))
#define nil ((void*) 0)
@@ -21,7 +22,7 @@ typedef struct _NSZone NSZone;
typedef const void * CFTypeRef;
CFTypeRef CFRetain(CFTypeRef cf);
-id CFBridgingRelease(CFTypeRef CF_CONSUMED X);
+CFTypeRef CFMakeCollectable(CFTypeRef cf) NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
NS_INLINE NS_RETURNS_RETAINED id NSMakeCollectable(CFTypeRef CF_CONSUMED cf) NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
@@ -68,3 +69,36 @@ typedef const void* objc_objectptr_t;
extern __attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer);
extern __attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer);
extern objc_objectptr_t objc_unretainedPointer(id object);
+
+#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
+#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
+#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
+#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
+
+typedef id dispatch_object_t;
+typedef id xpc_object_t;
+
+void _dispatch_object_validate(dispatch_object_t object);
+void _xpc_object_validate(xpc_object_t object);
+
+#if __has_feature(objc_arc)
+
+NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
+ return (__bridge_retained CFTypeRef)X;
+}
+
+NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
+ return (__bridge_transfer id)X;
+}
+
+#else
+
+NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
+ return X ? CFRetain((CFTypeRef)X) : NULL;
+}
+
+NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
+ return [(id)CFMakeCollectable(X) autorelease];
+}
+
+#endif
diff --git a/test/ARCMT/autoreleases.m b/test/ARCMT/autoreleases.m
index 3acddb7..a131bc5 100644
--- a/test/ARCMT/autoreleases.m
+++ b/test/ARCMT/autoreleases.m
@@ -3,29 +3,21 @@
// RUN: diff %t %s.result
// DISABLE: mingw32
-typedef unsigned char BOOL;
+#include "Common.h"
-@interface NSObject {
- id isa;
-}
-+new;
-+alloc;
--init;
--autorelease;
-@end
-
-@interface NSAutoreleasePool : NSObject
-- drain;
-@end
-
@interface A : NSObject {
@package
id object;
}
@end
-@interface B : NSObject
+@interface B : NSObject {
+ id _prop;
+ xpc_object_t _xpc_prop;
+}
- (BOOL)containsSelf:(A*)a;
+@property (retain) id prop;
+@property (retain) xpc_object_t xpc_prop;
@end
@implementation A
@@ -35,6 +27,26 @@ typedef unsigned char BOOL;
- (BOOL)containsSelf:(A*)a {
return a->object == self;
}
+
+-(id) prop {
+ return _prop;
+}
+-(void) setProp:(id) newVal {
+ [_prop autorelease];
+ _prop = [newVal retain];
+}
+-(void) setProp2:(CFTypeRef) newVal {
+ [_prop autorelease];
+ _prop = (id)CFRetain(newVal);
+}
+
+-(id) xpc_prop {
+ return _xpc_prop;
+}
+-(void) setXpc_prop:(xpc_object_t) newVal {
+ [_xpc_prop autorelease];
+ _xpc_prop = xpc_retain(newVal);
+}
@end
void NSLog(id, ...);
@@ -47,3 +59,8 @@ int main (int argc, const char * argv[]) {
[pool drain];
return 0;
}
+
+void test(A *prevVal, A *newVal) {
+ [prevVal autorelease];
+ prevVal = [newVal retain];
+}
diff --git a/test/ARCMT/autoreleases.m.result b/test/ARCMT/autoreleases.m.result
index 49bc321..6593fc9 100644
--- a/test/ARCMT/autoreleases.m.result
+++ b/test/ARCMT/autoreleases.m.result
@@ -3,29 +3,21 @@
// RUN: diff %t %s.result
// DISABLE: mingw32
-typedef unsigned char BOOL;
+#include "Common.h"
-@interface NSObject {
- id isa;
-}
-+new;
-+alloc;
--init;
--autorelease;
-@end
-
-@interface NSAutoreleasePool : NSObject
-- drain;
-@end
-
@interface A : NSObject {
@package
id object;
}
@end
-@interface B : NSObject
+@interface B : NSObject {
+ id _prop;
+ xpc_object_t _xpc_prop;
+}
- (BOOL)containsSelf:(A*)a;
+@property (strong) id prop;
+@property (strong) xpc_object_t xpc_prop;
@end
@implementation A
@@ -35,6 +27,23 @@ typedef unsigned char BOOL;
- (BOOL)containsSelf:(A*)a {
return a->object == self;
}
+
+-(id) prop {
+ return _prop;
+}
+-(void) setProp:(id) newVal {
+ _prop = newVal;
+}
+-(void) setProp2:(CFTypeRef) newVal {
+ _prop = (id)CFBridgingRelease(CFRetain(newVal));
+}
+
+-(id) xpc_prop {
+ return _xpc_prop;
+}
+-(void) setXpc_prop:(xpc_object_t) newVal {
+ _xpc_prop = newVal;
+}
@end
void NSLog(id, ...);
@@ -47,3 +56,7 @@ int main (int argc, const char * argv[]) {
}
return 0;
}
+
+void test(A *prevVal, A *newVal) {
+ prevVal = newVal;
+}
diff --git a/test/ARCMT/checking.m b/test/ARCMT/checking.m
index cf71611..3ad911e 100644
--- a/test/ARCMT/checking.m
+++ b/test/ARCMT/checking.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 %s
+// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fblocks -Werror %s
// DISABLE: mingw32
#if __has_feature(objc_arc)
@@ -45,9 +45,9 @@ struct UnsafeS {
};
@interface A : NSObject
-- (id)retain;
-- (id)retainCount;
-- (id)autorelease;
+- (id)retain; // expected-note {{declaration has been explicitly marked unavailable here}}
+- (id)retainCount; // expected-note {{declaration has been explicitly marked unavailable here}}
+- (id)autorelease; // expected-note 2 {{declaration has been explicitly marked unavailable here}}
- (id)init;
- (oneway void)release;
- (void)dealloc;
@@ -79,7 +79,8 @@ void test1(A *a, BOOL b, struct UnsafeS *unsafeS) {
[a.delegate release]; // expected-error {{it is not safe to remove 'retain' message on the result of a 'delegate' message; the object that was passed to 'setDelegate:' may not be properly retained}} \
// expected-error {{ARC forbids explicit message send}}
[unsafeS->unsafeObj retain]; // expected-error {{it is not safe to remove 'retain' message on an __unsafe_unretained type}} \
- // expected-error {{ARC forbids explicit message send}}
+ // expected-error {{ARC forbids explicit message send}} \
+ // expected-error {{'retain' is unavailable}}
id foo = [unsafeS->unsafeObj retain]; // no warning.
[global_foo retain]; // expected-error {{it is not safe to remove 'retain' message on a global variable}} \
// expected-error {{ARC forbids explicit message send}}
@@ -87,10 +88,16 @@ void test1(A *a, BOOL b, struct UnsafeS *unsafeS) {
// expected-error {{ARC forbids explicit message send}}
[a dealloc];
[a retain];
- [a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}}
+ [a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}} \
+ // expected-error {{'retainCount' is unavailable}}
[a release];
[a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \
- // expected-error {{ARC forbids explicit message send}}
+ // expected-error {{ARC forbids explicit message send}} \
+ // expected-error {{'autorelease' is unavailable}}
+ [a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \
+ // expected-error {{ARC forbids explicit message send}} \
+ // expected-error {{'autorelease' is unavailable}}
+ a = 0;
CFStringRef cfstr;
NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \
@@ -135,7 +142,7 @@ void rdar8861761() {
- (void) noninit {
self = 0; // expected-error {{cannot assign to 'self' outside of a method in the init family}}
- for (id x in collection) { // expected-error {{use of undeclared identifier 'collection'}}
+ for (__strong id x in collection) { // expected-error {{use of undeclared identifier 'collection'}}
x = 0;
}
}
@@ -325,3 +332,13 @@ void rdar9504750(id p) {
self->x = [NSObject new]; // expected-error {{assigning retained object}}
}
@end
+
+@interface Test10 : NSObject
+@property (retain) id prop;
+-(void)foo;
+@end
+
+void test(Test10 *x) {
+ x.prop = ^{ [x foo]; }; // expected-warning {{likely to lead to a retain cycle}} \
+ // expected-note {{retained by the captured object}}
+}
diff --git a/test/ARCMT/cxx-rewrite.mm.result b/test/ARCMT/cxx-rewrite.mm.result
index a2dc9a5..7c944d5 100644
--- a/test/ARCMT/cxx-rewrite.mm.result
+++ b/test/ARCMT/cxx-rewrite.mm.result
@@ -16,7 +16,7 @@ struct foo {
[NSString string];
}
}
- ~foo(){ s; }
+ ~foo(){ }
private:
foo(foo const &);
foo &operator=(foo const &);
diff --git a/test/ARCMT/dispatch.m b/test/ARCMT/dispatch.m
index 75c4a83..58c7769 100644
--- a/test/ARCMT/dispatch.m
+++ b/test/ARCMT/dispatch.m
@@ -4,17 +4,6 @@
#include "Common.h"
-#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
-#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
-#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
-#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
-
-typedef id dispatch_object_t;
-typedef id xpc_object_t;
-
-void _dispatch_object_validate(dispatch_object_t object);
-void _xpc_object_validate(xpc_object_t object);
-
dispatch_object_t getme(void);
void func(dispatch_object_t o) {
diff --git a/test/ARCMT/dispatch.m.result b/test/ARCMT/dispatch.m.result
index e897672..55b6558 100644
--- a/test/ARCMT/dispatch.m.result
+++ b/test/ARCMT/dispatch.m.result
@@ -4,17 +4,6 @@
#include "Common.h"
-#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
-#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
-#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
-#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
-
-typedef id dispatch_object_t;
-typedef id xpc_object_t;
-
-void _dispatch_object_validate(dispatch_object_t object);
-void _xpc_object_validate(xpc_object_t object);
-
dispatch_object_t getme(void);
void func(dispatch_object_t o) {
diff --git a/test/ARCMT/nonobjc-to-objc-cast-2.m b/test/ARCMT/nonobjc-to-objc-cast-2.m
index 1ec0089..80d694e 100644
--- a/test/ARCMT/nonobjc-to-objc-cast-2.m
+++ b/test/ARCMT/nonobjc-to-objc-cast-2.m
@@ -3,16 +3,37 @@
#include "Common.h"
-@interface NSString : NSObject
--(id)string;
--(id)newString;
-@end
-
typedef const struct __CFString * CFStringRef;
typedef const void * CFTypeRef;
CFTypeRef CFBridgingRetain(id X);
id CFBridgingRelease(CFTypeRef);
+struct StrS {
+ CFStringRef sref_member;
+};
+
+@interface NSString : NSObject {
+ CFStringRef sref;
+ struct StrS *strS;
+}
+-(id)string;
+-(id)newString;
+@end
+
+@implementation NSString
+-(id)string {
+ if (0)
+ return sref;
+ else
+ return strS->sref_member;
+}
+-(id)newString {
+ return sref; // expected-error {{implicit conversion of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'id' requires a bridged cast}} \
+ // expected-note{{use __bridge to convert directly (no change in ownership)}} \
+ // expected-note{{use CFBridgingRelease call to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}}
+}
+@end
+
void f(BOOL b) {
CFStringRef cfstr;
NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \
diff --git a/test/ARCMT/nonobjc-to-objc-cast.m b/test/ARCMT/nonobjc-to-objc-cast.m
index fcdcd89..55b6655 100644
--- a/test/ARCMT/nonobjc-to-objc-cast.m
+++ b/test/ARCMT/nonobjc-to-objc-cast.m
@@ -5,11 +5,6 @@
#include "Common.h"
-@interface NSString : NSObject
--(id)string;
--(id)newString;
-@end
-
typedef const struct __CFString * CFStringRef;
extern const CFStringRef kUTTypePlainText;
extern const CFStringRef kUTTypeRTF;
@@ -21,6 +16,18 @@ extern const CFAllocatorRef kCFAllocatorDefault;
extern CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid);
+struct StrS {
+ CFStringRef sref_member;
+};
+
+@interface NSString : NSObject {
+ CFStringRef sref;
+ struct StrS *strS;
+}
+-(id)string;
+-(id)newString;
+@end
+
void f(BOOL b, id p) {
NSString *str = (NSString *)kUTTypePlainText;
str = b ? kUTTypeRTF : kUTTypePlainText;
@@ -41,6 +48,16 @@ void f(BOOL b, id p) {
}
@end
+@implementation NSString
+-(id)string {
+ if (0)
+ return sref;
+ else
+ return strS->sref_member;
+}
+-(id)newString { return 0; }
+@end
+
extern void consumeParam(CFStringRef CF_CONSUMED p);
void f2(NSString *s) {
diff --git a/test/ARCMT/nonobjc-to-objc-cast.m.result b/test/ARCMT/nonobjc-to-objc-cast.m.result
index b50a948..4f508f6 100644
--- a/test/ARCMT/nonobjc-to-objc-cast.m.result
+++ b/test/ARCMT/nonobjc-to-objc-cast.m.result
@@ -5,11 +5,6 @@
#include "Common.h"
-@interface NSString : NSObject
--(id)string;
--(id)newString;
-@end
-
typedef const struct __CFString * CFStringRef;
extern const CFStringRef kUTTypePlainText;
extern const CFStringRef kUTTypeRTF;
@@ -21,6 +16,18 @@ extern const CFAllocatorRef kCFAllocatorDefault;
extern CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid);
+struct StrS {
+ CFStringRef sref_member;
+};
+
+@interface NSString : NSObject {
+ CFStringRef sref;
+ struct StrS *strS;
+}
+-(id)string;
+-(id)newString;
+@end
+
void f(BOOL b, id p) {
NSString *str = (__bridge NSString *)kUTTypePlainText;
str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
@@ -28,9 +35,9 @@ void f(BOOL b, id p) {
str = (NSString *)p; // no change.
CFUUIDRef _uuid;
- NSString *_uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid);
- _uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid);
- _uuidString = (__bridge_transfer NSString *)(CFRetain(_uuid));
+ NSString *_uuidString = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
+ _uuidString = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
+ _uuidString = CFBridgingRelease(CFRetain(_uuid));
}
@implementation NSString (StrExt)
@@ -41,22 +48,32 @@ void f(BOOL b, id p) {
}
@end
+@implementation NSString
+-(id)string {
+ if (0)
+ return (__bridge id)(sref);
+ else
+ return (__bridge id)(strS->sref_member);
+}
+-(id)newString { return 0; }
+@end
+
extern void consumeParam(CFStringRef CF_CONSUMED p);
void f2(NSString *s) {
CFStringRef ref = (__bridge CFStringRef)([s string]);
ref = (__bridge CFStringRef)[s string];
ref = (__bridge CFStringRef)(s.string);
- ref = (__bridge_retained CFStringRef)([NSString new]);
- ref = (__bridge_retained CFStringRef)([s newString]);
- ref = (__bridge_retained CFStringRef)[NSString new];
- ref = (__bridge_retained CFStringRef)([[NSString alloc] init]);
- ref = (__bridge_retained CFStringRef)([s string]);
- ref = (__bridge_retained CFStringRef)[s string];
- ref = (__bridge_retained CFTypeRef)([s string]);
- ref = (__bridge_retained CFTypeRef)(s);
- ref = (__bridge_retained CFStringRef)(s);
-
- consumeParam((__bridge_retained CFStringRef)s);
- consumeParam((__bridge_retained CFStringRef)(s));
+ ref = CFBridgingRetain([NSString new]);
+ ref = CFBridgingRetain([s newString]);
+ ref = (CFStringRef)CFBridgingRetain([NSString new]);
+ ref = CFBridgingRetain([[NSString alloc] init]);
+ ref = CFBridgingRetain([s string]);
+ ref = (CFStringRef)CFBridgingRetain([s string]);
+ ref = CFBridgingRetain([s string]);
+ ref = CFBridgingRetain(s);
+ ref = CFBridgingRetain(s);
+
+ consumeParam((CFStringRef)CFBridgingRetain(s));
+ consumeParam(CFBridgingRetain(s));
}
diff --git a/test/ARCMT/objcmt-boxing.m b/test/ARCMT/objcmt-boxing.m
new file mode 100644
index 0000000..2ad65a1
--- /dev/null
+++ b/test/ARCMT/objcmt-boxing.m
@@ -0,0 +1,102 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++ -verify
+// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c++ %s.result
+
+#define YES __objc_yes
+#define NO __objc_no
+
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+#define INT_MIN (-__INT_MAX__ -1)
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@interface NSNumber : NSObject
+@end
+
+@interface NSNumber (NSNumberCreation)
+- (id)initWithChar:(char)value;
+- (id)initWithUnsignedChar:(unsigned char)value;
+- (id)initWithShort:(short)value;
+- (id)initWithUnsignedShort:(unsigned short)value;
+- (id)initWithInt:(int)value;
+- (id)initWithUnsignedInt:(unsigned int)value;
+- (id)initWithLong:(long)value;
+- (id)initWithUnsignedLong:(unsigned long)value;
+- (id)initWithLongLong:(long long)value;
+- (id)initWithUnsignedLongLong:(unsigned long long)value;
+- (id)initWithFloat:(float)value;
+- (id)initWithDouble:(double)value;
+- (id)initWithBool:(BOOL)value;
+- (id)initWithInteger:(NSInteger)value;
+- (id)initWithUnsignedInteger:(NSUInteger)value;
+
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
+@end
+
+enum {
+ NSASCIIStringEncoding = 1,
+ NSUTF8StringEncoding = 4,
+ NSUnicodeStringEncoding = 10
+};
+typedef NSUInteger NSStringEncoding;
+
+@interface NSString : NSObject
+@end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc;
++ (id)stringWithCString:(const char *)bytes;
+@end
+
+enum MyEnm {
+ ME_foo
+};
+
+void foo() {
+ [NSNumber numberWithInt:INT_MIN];
+ bool cppb;
+ [NSNumber numberWithBool:cppb];
+ MyEnm myenum;
+ [NSNumber numberWithInteger:myenum];
+ [NSNumber numberWithInteger:ME_foo];
+ [NSNumber numberWithDouble:cppb]; // expected-warning {{converting to boxing syntax requires casting 'bool' to 'double'}}
+}
+
+void boxString() {
+ NSString *s = [NSString stringWithUTF8String:"box"];
+ const char *cstr1;
+ char *cstr2;
+ s = [NSString stringWithUTF8String:cstr1];
+ s = [NSString stringWithUTF8String:cstr2];
+ s = [NSString stringWithCString:cstr1 encoding:NSASCIIStringEncoding];
+ s = [NSString stringWithCString:cstr1 encoding:NSUTF8StringEncoding];
+ s = [NSString stringWithCString:cstr1 encoding: NSUnicodeStringEncoding];
+ NSStringEncoding encode;
+ s = [NSString stringWithCString:cstr1 encoding:encode];
+ s = [NSString stringWithCString:cstr1];
+
+ static const char strarr[] = "coolbox";
+ s = [NSString stringWithUTF8String:strarr];
+}
diff --git a/test/ARCMT/objcmt-boxing.m.result b/test/ARCMT/objcmt-boxing.m.result
new file mode 100644
index 0000000..f101989
--- /dev/null
+++ b/test/ARCMT/objcmt-boxing.m.result
@@ -0,0 +1,102 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++ -verify
+// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c++ %s.result
+
+#define YES __objc_yes
+#define NO __objc_no
+
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+#define INT_MIN (-__INT_MAX__ -1)
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@interface NSNumber : NSObject
+@end
+
+@interface NSNumber (NSNumberCreation)
+- (id)initWithChar:(char)value;
+- (id)initWithUnsignedChar:(unsigned char)value;
+- (id)initWithShort:(short)value;
+- (id)initWithUnsignedShort:(unsigned short)value;
+- (id)initWithInt:(int)value;
+- (id)initWithUnsignedInt:(unsigned int)value;
+- (id)initWithLong:(long)value;
+- (id)initWithUnsignedLong:(unsigned long)value;
+- (id)initWithLongLong:(long long)value;
+- (id)initWithUnsignedLongLong:(unsigned long long)value;
+- (id)initWithFloat:(float)value;
+- (id)initWithDouble:(double)value;
+- (id)initWithBool:(BOOL)value;
+- (id)initWithInteger:(NSInteger)value;
+- (id)initWithUnsignedInteger:(NSUInteger)value;
+
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
+@end
+
+enum {
+ NSASCIIStringEncoding = 1,
+ NSUTF8StringEncoding = 4,
+ NSUnicodeStringEncoding = 10
+};
+typedef NSUInteger NSStringEncoding;
+
+@interface NSString : NSObject
+@end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc;
++ (id)stringWithCString:(const char *)bytes;
+@end
+
+enum MyEnm {
+ ME_foo
+};
+
+void foo() {
+ @INT_MIN;
+ bool cppb;
+ @(cppb);
+ MyEnm myenum;
+ @(myenum);
+ @(ME_foo);
+ [NSNumber numberWithDouble:cppb]; // expected-warning {{converting to boxing syntax requires casting 'bool' to 'double'}}
+}
+
+void boxString() {
+ NSString *s = @"box";
+ const char *cstr1;
+ char *cstr2;
+ s = @(cstr1);
+ s = @(cstr2);
+ s = @(cstr1);
+ s = @(cstr1);
+ s = [NSString stringWithCString:cstr1 encoding: NSUnicodeStringEncoding];
+ NSStringEncoding encode;
+ s = [NSString stringWithCString:cstr1 encoding:encode];
+ s = @(cstr1);
+
+ static const char strarr[] = "coolbox";
+ s = @(strarr);
+}
diff --git a/test/ARCMT/objcmt-numeric-literals.m b/test/ARCMT/objcmt-numeric-literals.m
index b86af4d..7f33dc2 100644
--- a/test/ARCMT/objcmt-numeric-literals.m
+++ b/test/ARCMT/objcmt-numeric-literals.m
@@ -1,6 +1,7 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++
// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c++ %s.result
#define YES __objc_yes
#define NO __objc_no
diff --git a/test/ARCMT/objcmt-numeric-literals.m.result b/test/ARCMT/objcmt-numeric-literals.m.result
index 1c4187a..bb7b515 100644
--- a/test/ARCMT/objcmt-numeric-literals.m.result
+++ b/test/ARCMT/objcmt-numeric-literals.m.result
@@ -1,6 +1,7 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++
// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c++ %s.result
#define YES __objc_yes
#define NO __objc_no
@@ -467,7 +468,7 @@ void foo() {
[NSNumber numberWithInteger:NO];
[NSNumber numberWithInteger:true];
[NSNumber numberWithInteger:false];
- [NSNumber numberWithInteger:VAL_INT];
+ @VAL_INT;
[NSNumber numberWithInteger:VAL_UINT];
[NSNumber numberWithUnsignedInteger:'a'];
@@ -497,5 +498,5 @@ void foo() {
[NSNumber numberWithUnsignedInteger:true];
[NSNumber numberWithUnsignedInteger:false];
[NSNumber numberWithUnsignedInteger:VAL_INT];
- [NSNumber numberWithUnsignedInteger:VAL_UINT];
+ @VAL_UINT;
}
diff --git a/test/ARCMT/objcmt-subscripting-literals-in-arc.m b/test/ARCMT/objcmt-subscripting-literals-in-arc.m
new file mode 100644
index 0000000..4d94162
--- /dev/null
+++ b/test/ARCMT/objcmt-subscripting-literals-in-arc.m
@@ -0,0 +1,106 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fobjc-arc -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11
+// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -fobjc-arc -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
+
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+typedef const struct __CFString * CFStringRef;
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@protocol NSCopying
+@end
+
+@interface NSString : NSObject
++ (id)stringWithString:(NSString *)string;
+- (id)initWithString:(NSString *)aString;
+@end
+
+@interface NSArray : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface NSArray (NSExtendedArray)
+- (id)objectAtIndexedSubscript:(unsigned)idx;
+@end
+
+@interface NSArray (NSArrayCreation)
++ (id)array;
++ (id)arrayWithObject:(id)anObject;
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
++ (id)arrayWithObjects:(id)firstObj, ...;
++ (id)arrayWithArray:(NSArray *)array;
+
+- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt;
+- (id)initWithObjects:(id)firstObj, ...;
+- (id)initWithArray:(NSArray *)array;
+@end
+
+@interface NSMutableArray : NSArray
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface NSMutableArray (NSExtendedMutableArray)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx;
+@end
+
+@interface NSDictionary : NSObject
+- (id)objectForKey:(id)aKey;
+@end
+
+@interface NSDictionary (NSExtendedDictionary)
+- (id)objectForKeyedSubscript:(id)key;
+@end
+
+@interface NSDictionary (NSDictionaryCreation)
++ (id)dictionary;
++ (id)dictionaryWithObject:(id)object forKey:(id)key;
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
++ (id)dictionaryWithDictionary:(NSDictionary *)dict;
++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+
+- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
+- (id)initWithObjectsAndKeys:(id)firstObject, ...;
+- (id)initWithDictionary:(NSDictionary *)otherDictionary;
+- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+@end
+
+@interface NSMutableDictionary : NSDictionary
+- (void)setObject:(id)anObject forKey:(id)aKey;
+@end
+
+@interface NSMutableDictionary (NSExtendedMutableDictionary)
+- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
+@end
+
+@interface NSNumber : NSObject
+@end
+
+@interface NSNumber (NSNumberCreation)
++ (NSNumber *)numberWithInt:(int)value;
+- (id)initWithInt:(int)value;
+@end
+
+@interface I {
+ NSArray *ivarArr;
+}
+@end
+@implementation I
+-(void) foo {
+ NSString *str;
+ NSArray *arr;
+ NSDictionary *dict;
+
+ arr = [NSArray arrayWithObjects:str, str, nil];
+ arr = [[NSArray alloc] initWithObjects:str, str, nil];
+ dict = [NSDictionary dictionaryWithObjectsAndKeys: @"value1", @"key1", @"value2", @"key2", nil];
+ dict = [[NSDictionary alloc] initWithObjectsAndKeys: @"value1", @"key1", @"value2", @"key2", nil];
+
+ NSNumber *n = [[NSNumber alloc] initWithInt:2];
+}
+@end
diff --git a/test/ARCMT/objcmt-subscripting-literals-in-arc.m.result b/test/ARCMT/objcmt-subscripting-literals-in-arc.m.result
new file mode 100644
index 0000000..6f7a723
--- /dev/null
+++ b/test/ARCMT/objcmt-subscripting-literals-in-arc.m.result
@@ -0,0 +1,106 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fobjc-arc -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11
+// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -fobjc-arc -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
+
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+typedef const struct __CFString * CFStringRef;
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@protocol NSCopying
+@end
+
+@interface NSString : NSObject
++ (id)stringWithString:(NSString *)string;
+- (id)initWithString:(NSString *)aString;
+@end
+
+@interface NSArray : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface NSArray (NSExtendedArray)
+- (id)objectAtIndexedSubscript:(unsigned)idx;
+@end
+
+@interface NSArray (NSArrayCreation)
++ (id)array;
++ (id)arrayWithObject:(id)anObject;
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
++ (id)arrayWithObjects:(id)firstObj, ...;
++ (id)arrayWithArray:(NSArray *)array;
+
+- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt;
+- (id)initWithObjects:(id)firstObj, ...;
+- (id)initWithArray:(NSArray *)array;
+@end
+
+@interface NSMutableArray : NSArray
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface NSMutableArray (NSExtendedMutableArray)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx;
+@end
+
+@interface NSDictionary : NSObject
+- (id)objectForKey:(id)aKey;
+@end
+
+@interface NSDictionary (NSExtendedDictionary)
+- (id)objectForKeyedSubscript:(id)key;
+@end
+
+@interface NSDictionary (NSDictionaryCreation)
++ (id)dictionary;
++ (id)dictionaryWithObject:(id)object forKey:(id)key;
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
++ (id)dictionaryWithDictionary:(NSDictionary *)dict;
++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+
+- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
+- (id)initWithObjectsAndKeys:(id)firstObject, ...;
+- (id)initWithDictionary:(NSDictionary *)otherDictionary;
+- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+@end
+
+@interface NSMutableDictionary : NSDictionary
+- (void)setObject:(id)anObject forKey:(id)aKey;
+@end
+
+@interface NSMutableDictionary (NSExtendedMutableDictionary)
+- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
+@end
+
+@interface NSNumber : NSObject
+@end
+
+@interface NSNumber (NSNumberCreation)
++ (NSNumber *)numberWithInt:(int)value;
+- (id)initWithInt:(int)value;
+@end
+
+@interface I {
+ NSArray *ivarArr;
+}
+@end
+@implementation I
+-(void) foo {
+ NSString *str;
+ NSArray *arr;
+ NSDictionary *dict;
+
+ arr = @[str, str];
+ arr = @[str, str];
+ dict = @{@"key1": @"value1", @"key2": @"value2"};
+ dict = @{@"key1": @"value1", @"key2": @"value2"};
+
+ NSNumber *n = @2;
+}
+@end
diff --git a/test/ARCMT/objcmt-subscripting-literals.m b/test/ARCMT/objcmt-subscripting-literals.m
index 3d26efe..0174fcf 100644
--- a/test/ARCMT/objcmt-subscripting-literals.m
+++ b/test/ARCMT/objcmt-subscripting-literals.m
@@ -1,14 +1,20 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11
// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
typedef signed char BOOL;
#define nil ((void*) 0)
+typedef const struct __CFString * CFStringRef;
+
@interface NSObject
+ (id)alloc;
@end
+@protocol NSCopying
+@end
+
@interface NSString : NSObject
+ (id)stringWithString:(NSString *)string;
- (id)initWithString:(NSString *)aString;
@@ -16,7 +22,10 @@ typedef signed char BOOL;
@interface NSArray : NSObject
- (id)objectAtIndex:(unsigned long)index;
-- (id)objectAtIndexedSubscript:(int)index;
+@end
+
+@interface NSArray (NSExtendedArray)
+- (id)objectAtIndexedSubscript:(unsigned)idx;
@end
@interface NSArray (NSArrayCreation)
@@ -29,16 +38,21 @@ typedef signed char BOOL;
- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt;
- (id)initWithObjects:(id)firstObj, ...;
- (id)initWithArray:(NSArray *)array;
-
-- (id)objectAtIndex:(unsigned long)index;
@end
@interface NSMutableArray : NSArray
- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
-- (void)setObject:(id)object atIndexedSubscript:(int)index;
+@end
+
+@interface NSMutableArray (NSExtendedMutableArray)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx;
@end
@interface NSDictionary : NSObject
+- (id)objectForKey:(id)aKey;
+@end
+
+@interface NSDictionary (NSExtendedDictionary)
- (id)objectForKeyedSubscript:(id)key;
@end
@@ -54,13 +68,14 @@ typedef signed char BOOL;
- (id)initWithObjectsAndKeys:(id)firstObject, ...;
- (id)initWithDictionary:(NSDictionary *)otherDictionary;
- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
-
-- (id)objectForKey:(id)aKey;
@end
@interface NSMutableDictionary : NSDictionary
- (void)setObject:(id)anObject forKey:(id)aKey;
-- (void)setObject:(id)object forKeyedSubscript:(id)key;
+@end
+
+@interface NSMutableDictionary (NSExtendedMutableDictionary)
+- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end
@interface NSNumber : NSObject
@@ -74,7 +89,9 @@ typedef signed char BOOL;
#define PAIR(x) @#x, [NSNumber numberWithInt:(x)]
#define TWO(x) ((x), (x))
-@interface I
+@interface I {
+ NSArray *ivarArr;
+}
@end
@implementation I
-(void) foo {
@@ -133,5 +150,74 @@ typedef signed char BOOL;
[mdict setObject:[dict objectForKey:@"key1"] forKey:[dict objectForKey:[NSArray arrayWithObject:@"arrkey"]]];
__strong NSArray **parr = 0;
o = [*parr objectAtIndex:2];
+ void *hd;
+ o = [(NSArray*)hd objectAtIndex:2];
+ o = [ivarArr objectAtIndex:2];
+}
+@end
+
+extern const CFStringRef globStr;
+
+void test1(NSString *str) {
+ NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: str, globStr, nil];
+ dict = [NSDictionary dictionaryWithObjectsAndKeys: globStr, str, nil];
+ dict = [NSDictionary dictionaryWithObject:str forKey:globStr];
+ dict = [NSDictionary dictionaryWithObject:globStr forKey:str];
+
+ NSArray *arr = [NSArray arrayWithObjects: globStr, globStr, nil];
+ arr = [NSArray arrayWithObjects: str, globStr, nil];
+ arr = [NSArray arrayWithObjects: globStr, str, nil];
+ arr = [NSArray arrayWithObject:globStr];
+}
+
+@interface Custom : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface Custom (Extended)
+- (id)objectAtIndexedSubscript:(unsigned)idx;
+@end
+
+@interface MutableCustom : Custom
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface MutableCustom (Extended)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx;
+@end
+
+@interface CustomUnavail : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface CustomUnavail (Extended)
+- (id)objectAtIndexedSubscript:(unsigned)idx __attribute__((unavailable));
+@end
+
+@interface MutableCustomUnavail : CustomUnavail
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface MutableCustomUnavail (Extended)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx __attribute__((unavailable));
+@end
+
+void test2() {
+ MutableCustom *mutc;
+ id o = [mutc objectAtIndex:4];
+ [mutc replaceObjectAtIndex:2 withObject:@"val"];
+
+ MutableCustomUnavail *mutcunaval;
+ o = [mutcunaval objectAtIndex:4];
+ [mutcunaval replaceObjectAtIndex:2 withObject:@"val"];
}
+
+@interface NSLocale : NSObject
++ (id)systemLocale;
++ (id)currentLocale;
+- (id)objectForKey:(id)key;
@end
+
+void test3(id key) {
+ id o = [[NSLocale currentLocale] objectForKey:key];
+}
diff --git a/test/ARCMT/objcmt-subscripting-literals.m.result b/test/ARCMT/objcmt-subscripting-literals.m.result
index 8ac6dcc..9975996 100644
--- a/test/ARCMT/objcmt-subscripting-literals.m.result
+++ b/test/ARCMT/objcmt-subscripting-literals.m.result
@@ -1,14 +1,20 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11
// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
typedef signed char BOOL;
#define nil ((void*) 0)
+typedef const struct __CFString * CFStringRef;
+
@interface NSObject
+ (id)alloc;
@end
+@protocol NSCopying
+@end
+
@interface NSString : NSObject
+ (id)stringWithString:(NSString *)string;
- (id)initWithString:(NSString *)aString;
@@ -16,7 +22,10 @@ typedef signed char BOOL;
@interface NSArray : NSObject
- (id)objectAtIndex:(unsigned long)index;
-- (id)objectAtIndexedSubscript:(int)index;
+@end
+
+@interface NSArray (NSExtendedArray)
+- (id)objectAtIndexedSubscript:(unsigned)idx;
@end
@interface NSArray (NSArrayCreation)
@@ -29,16 +38,21 @@ typedef signed char BOOL;
- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt;
- (id)initWithObjects:(id)firstObj, ...;
- (id)initWithArray:(NSArray *)array;
-
-- (id)objectAtIndex:(unsigned long)index;
@end
@interface NSMutableArray : NSArray
- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
-- (void)setObject:(id)object atIndexedSubscript:(int)index;
+@end
+
+@interface NSMutableArray (NSExtendedMutableArray)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx;
@end
@interface NSDictionary : NSObject
+- (id)objectForKey:(id)aKey;
+@end
+
+@interface NSDictionary (NSExtendedDictionary)
- (id)objectForKeyedSubscript:(id)key;
@end
@@ -54,13 +68,14 @@ typedef signed char BOOL;
- (id)initWithObjectsAndKeys:(id)firstObject, ...;
- (id)initWithDictionary:(NSDictionary *)otherDictionary;
- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
-
-- (id)objectForKey:(id)aKey;
@end
@interface NSMutableDictionary : NSDictionary
- (void)setObject:(id)anObject forKey:(id)aKey;
-- (void)setObject:(id)object forKeyedSubscript:(id)key;
+@end
+
+@interface NSMutableDictionary (NSExtendedMutableDictionary)
+- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end
@interface NSNumber : NSObject
@@ -74,7 +89,9 @@ typedef signed char BOOL;
#define PAIR(x) @#x, [NSNumber numberWithInt:(x)]
#define TWO(x) ((x), (x))
-@interface I
+@interface I {
+ NSArray *ivarArr;
+}
@end
@implementation I
-(void) foo {
@@ -133,5 +150,74 @@ typedef signed char BOOL;
mdict[dict[@[@"arrkey"]]] = dict[@"key1"];
__strong NSArray **parr = 0;
o = (*parr)[2];
+ void *hd;
+ o = ((NSArray*)hd)[2];
+ o = ivarArr[2];
+}
+@end
+
+extern const CFStringRef globStr;
+
+void test1(NSString *str) {
+ NSDictionary *dict = @{(id)globStr: str};
+ dict = @{str: (id)globStr};
+ dict = @{(id)globStr: str};
+ dict = @{str: (id)globStr};
+
+ NSArray *arr = @[(id)globStr, (id)globStr];
+ arr = @[str, (id)globStr];
+ arr = @[(id)globStr, str];
+ arr = @[(id)globStr];
+}
+
+@interface Custom : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface Custom (Extended)
+- (id)objectAtIndexedSubscript:(unsigned)idx;
+@end
+
+@interface MutableCustom : Custom
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface MutableCustom (Extended)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx;
+@end
+
+@interface CustomUnavail : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface CustomUnavail (Extended)
+- (id)objectAtIndexedSubscript:(unsigned)idx __attribute__((unavailable));
+@end
+
+@interface MutableCustomUnavail : CustomUnavail
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface MutableCustomUnavail (Extended)
+- (void)setObject:(id)obj atIndexedSubscript:(unsigned)idx __attribute__((unavailable));
+@end
+
+void test2() {
+ MutableCustom *mutc;
+ id o = mutc[4];
+ mutc[2] = @"val";
+
+ MutableCustomUnavail *mutcunaval;
+ o = [mutcunaval objectAtIndex:4];
+ [mutcunaval replaceObjectAtIndex:2 withObject:@"val"];
}
+
+@interface NSLocale : NSObject
++ (id)systemLocale;
++ (id)currentLocale;
+- (id)objectForKey:(id)key;
@end
+
+void test3(id key) {
+ id o = [[NSLocale currentLocale] objectForKey:key];
+}
diff --git a/test/ARCMT/objcmt-subscripting-unavailable.m b/test/ARCMT/objcmt-subscripting-unavailable.m
new file mode 100644
index 0000000..d72c362
--- /dev/null
+++ b/test/ARCMT/objcmt-subscripting-unavailable.m
@@ -0,0 +1,79 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11
+// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
+
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@interface NSArray : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface NSArray (NSArrayCreation)
++ (id)array;
++ (id)arrayWithObject:(id)anObject;
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
++ (id)arrayWithObjects:(id)firstObj, ...;
++ (id)arrayWithArray:(NSArray *)array;
+
+- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt;
+- (id)initWithObjects:(id)firstObj, ...;
+- (id)initWithArray:(NSArray *)array;
+@end
+
+@interface NSMutableArray : NSArray
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface NSDictionary : NSObject
+@end
+
+@interface NSDictionary (NSDictionaryCreation)
++ (id)dictionary;
++ (id)dictionaryWithObject:(id)object forKey:(id)key;
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
++ (id)dictionaryWithDictionary:(NSDictionary *)dict;
++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+
+- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
+- (id)initWithObjectsAndKeys:(id)firstObject, ...;
+- (id)initWithDictionary:(NSDictionary *)otherDictionary;
+- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+
+- (id)objectForKey:(id)aKey;
+@end
+
+@interface NSMutableDictionary : NSDictionary
+- (void)setObject:(id)anObject forKey:(id)aKey;
+@end
+
+@interface I
+@end
+@implementation I
+-(void) foo {
+ id str;
+ NSArray *arr;
+ NSDictionary *dict;
+
+ arr = [NSArray array];
+ arr = [NSArray arrayWithObject:str];
+ arr = [NSArray arrayWithObjects:str, str, nil];
+ dict = [NSDictionary dictionary];
+ dict = [NSDictionary dictionaryWithObject:arr forKey:str];
+
+ id o = [arr objectAtIndex:2];
+ o = [dict objectForKey:@"key"];
+ NSMutableArray *marr = 0;
+ NSMutableDictionary *mdict = 0;
+ [marr replaceObjectAtIndex:2 withObject:@"val"];
+ [mdict setObject:@"value" forKey:@"key"];
+ [marr replaceObjectAtIndex:2 withObject:[arr objectAtIndex:4]];
+ [mdict setObject:[dict objectForKey:@"key2"] forKey:@"key"];
+}
+@end
diff --git a/test/ARCMT/objcmt-subscripting-unavailable.m.result b/test/ARCMT/objcmt-subscripting-unavailable.m.result
new file mode 100644
index 0000000..bd74d55
--- /dev/null
+++ b/test/ARCMT/objcmt-subscripting-unavailable.m.result
@@ -0,0 +1,79 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11
+// RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
+
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@interface NSArray : NSObject
+- (id)objectAtIndex:(unsigned long)index;
+@end
+
+@interface NSArray (NSArrayCreation)
++ (id)array;
++ (id)arrayWithObject:(id)anObject;
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
++ (id)arrayWithObjects:(id)firstObj, ...;
++ (id)arrayWithArray:(NSArray *)array;
+
+- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt;
+- (id)initWithObjects:(id)firstObj, ...;
+- (id)initWithArray:(NSArray *)array;
+@end
+
+@interface NSMutableArray : NSArray
+- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject;
+@end
+
+@interface NSDictionary : NSObject
+@end
+
+@interface NSDictionary (NSDictionaryCreation)
++ (id)dictionary;
++ (id)dictionaryWithObject:(id)object forKey:(id)key;
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
++ (id)dictionaryWithDictionary:(NSDictionary *)dict;
++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+
+- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
+- (id)initWithObjectsAndKeys:(id)firstObject, ...;
+- (id)initWithDictionary:(NSDictionary *)otherDictionary;
+- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+
+- (id)objectForKey:(id)aKey;
+@end
+
+@interface NSMutableDictionary : NSDictionary
+- (void)setObject:(id)anObject forKey:(id)aKey;
+@end
+
+@interface I
+@end
+@implementation I
+-(void) foo {
+ id str;
+ NSArray *arr;
+ NSDictionary *dict;
+
+ arr = @[];
+ arr = @[str];
+ arr = @[str, str];
+ dict = @{};
+ dict = @{str: arr};
+
+ id o = [arr objectAtIndex:2];
+ o = [dict objectForKey:@"key"];
+ NSMutableArray *marr = 0;
+ NSMutableDictionary *mdict = 0;
+ [marr replaceObjectAtIndex:2 withObject:@"val"];
+ [mdict setObject:@"value" forKey:@"key"];
+ [marr replaceObjectAtIndex:2 withObject:[arr objectAtIndex:4]];
+ [mdict setObject:[dict objectForKey:@"key2"] forKey:@"key"];
+}
+@end
diff --git a/test/ARCMT/releases-driver.m b/test/ARCMT/releases-driver.m
index b75432a..7b1d2fb 100644
--- a/test/ARCMT/releases-driver.m
+++ b/test/ARCMT/releases-driver.m
@@ -53,9 +53,8 @@ void func(Foo *p) {
@end
@implementation Baz
-- dealloc {
+- (void) dealloc {
[_foo release];
- return 0;
}
@end
diff --git a/test/ARCMT/releases-driver.m.result b/test/ARCMT/releases-driver.m.result
index 70c0aec..4c864bd 100644
--- a/test/ARCMT/releases-driver.m.result
+++ b/test/ARCMT/releases-driver.m.result
@@ -49,9 +49,6 @@ void func(Foo *p) {
@end
@implementation Baz
-- dealloc {
- return 0;
-}
@end
#define RELEASE_MACRO(x) [x release]
diff --git a/test/ARCMT/releases.m b/test/ARCMT/releases.m
index 867fab9..5500895 100644
--- a/test/ARCMT/releases.m
+++ b/test/ARCMT/releases.m
@@ -58,9 +58,8 @@ void func(Foo *p) {
@end
@implementation Baz
-- dealloc {
+- (void) dealloc {
[_foo release];
- return 0;
}
@end
diff --git a/test/ARCMT/releases.m.result b/test/ARCMT/releases.m.result
index 556610a..473750e 100644
--- a/test/ARCMT/releases.m.result
+++ b/test/ARCMT/releases.m.result
@@ -54,9 +54,6 @@ void func(Foo *p) {
@end
@implementation Baz
-- dealloc {
- return 0;
-}
@end
void block_test(Foo *p) {
diff --git a/test/ARCMT/verify.m b/test/ARCMT/verify.m
new file mode 100644
index 0000000..9110fe6
--- /dev/null
+++ b/test/ARCMT/verify.m
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -arcmt-check -verify %s
+// RUN: %clang_cc1 -arcmt-check -verify %t.invalid 2>&1 | FileCheck %s
+
+#if 0
+// expected-error {{should be ignored}}
+#endif
+
+#error should not be ignored
+// expected-error@-1 {{should not be ignored}}
+
+// CHECK: error: 'error' diagnostics seen but not expected:
+// CHECK-NEXT: (frontend): error reading '{{.*}}verify.m.tmp.invalid'
+// CHECK-NEXT: 1 error generated.
diff --git a/test/ASTMerge/function.c b/test/ASTMerge/function.c
index f97ecee..320bca2 100644
--- a/test/ASTMerge/function.c
+++ b/test/ASTMerge/function.c
@@ -1,9 +1,15 @@
// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/function1.c
// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/function2.c
// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only -verify %s
// CHECK: function2.c:3:6: error: external function 'f1' declared with incompatible types in different translation units ('void (Int, double)' vs. 'void (int, float)')
// CHECK: function1.c:2:6: note: declared here with type 'void (int, float)'
// CHECK: function2.c:5:6: error: external function 'f3' declared with incompatible types in different translation units ('void (int)' vs. 'void (void)')
// CHECK: function1.c:4:6: note: declared here with type 'void (void)'
// CHECK: 2 errors generated
+
+// expected-error@3 {{external function 'f1' declared with incompatible types}}
+// expected-note@2 {{declared here}}
+// expected-error@5 {{external function 'f3' declared with incompatible types}}
+// expected-note@4 {{declared here}}
diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m
index 51a5912..589fcf5 100644
--- a/test/Analysis/MissingDealloc.m
+++ b/test/Analysis/MissingDealloc.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.osx.cocoa.Dealloc '-DIBOutlet=__attribute__((iboutlet))' %s -verify
+// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.osx.cocoa.Dealloc %s -verify
typedef signed char BOOL;
@protocol NSObject
- (BOOL)isEqual:(id)object;
@@ -53,10 +53,6 @@ typedef struct objc_selector *SEL;
//===------------------------------------------------------------------------===
// Don't warn about iVars that are IBOutlets.
-#ifndef IBOutlet
-#define IBOutlet
-#endif
-
@class NSWindow;
@interface HasOutlet : NSObject {
diff --git a/test/Analysis/NoReturn.m b/test/Analysis/NoReturn.m
index ea2efd0..1d948fa 100644
--- a/test/Analysis/NoReturn.m
+++ b/test/Analysis/NoReturn.m
@@ -76,3 +76,15 @@ int f3(int* x) {
return *x; // no-warning
}
+
+@interface CustomException : NSException
+@end
+
+int testCustomException(int *x) {
+ if (x != 0) return 0;
+
+ [CustomException raise:@"Blah" format:@"abc"];
+
+ return *x; // no-warning
+}
+
diff --git a/test/Analysis/PR12905.c b/test/Analysis/PR12905.c
new file mode 100644
index 0000000..8f678d1
--- /dev/null
+++ b/test/Analysis/PR12905.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core %s
+// PR12905
+
+void C(void);
+
+void t(void) {
+ C();
+}
diff --git a/test/Analysis/additive-folding-range-constraints.c b/test/Analysis/additive-folding-range-constraints.c
index 32e0cfe..7eb55ab 100644
--- a/test/Analysis/additive-folding-range-constraints.c
+++ b/test/Analysis/additive-folding-range-constraints.c
@@ -1,53 +1,37 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -verify -analyzer-constraints=range %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -analyzer-constraints=range %s
+
+void clang_analyzer_eval(int);
+
+#define UINT_MAX (~0U)
+#define INT_MAX (UINT_MAX & (UINT_MAX >> 1))
+#define INT_MIN (-INT_MAX - 1)
-// These are used to trigger warnings.
-typedef typeof(sizeof(int)) size_t;
-void *malloc(size_t);
-void free(void *);
-#define NULL ((void*)0)
-#define UINT_MAX (__INT_MAX__ *2U +1U)
// Each of these adjusted ranges has an adjustment small enough to split the
// solution range across an overflow boundary (Min for <, Max for >).
// This corresponds to one set of branches in RangeConstraintManager.
void smallAdjustmentGT (unsigned a) {
- char* b = NULL;
if (a+2 > 1)
- b = malloc(1);
- if (a == UINT_MAX-1 || a == UINT_MAX)
- return; // no-warning
- else if (a < UINT_MAX-1)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a < UINT_MAX-1); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a == UINT_MAX-1 || a == UINT_MAX); // expected-warning{{TRUE}}
}
void smallAdjustmentGE (unsigned a) {
- char* b = NULL;
if (a+2 >= 1)
- b = malloc(1);
- if (a == UINT_MAX-1)
- return; // no-warning
- else if (a < UINT_MAX-1 || a == UINT_MAX)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a < UINT_MAX-1 || a == UINT_MAX); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a == UINT_MAX-1); // expected-warning{{TRUE}}
}
void smallAdjustmentLT (unsigned a) {
- char* b = NULL;
if (a+1 < 2)
- b = malloc(1);
- if (a == 0 || a == UINT_MAX)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a == 0 || a == UINT_MAX); // expected-warning{{TRUE}}
}
void smallAdjustmentLE (unsigned a) {
- char* b = NULL;
if (a+1 <= 2)
- b = malloc(1);
- if (a == 0 || a == 1 || a == UINT_MAX)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a == 0 || a == 1 || a == UINT_MAX); // expected-warning{{TRUE}}
}
@@ -55,45 +39,134 @@ void smallAdjustmentLE (unsigned a) {
// comparison value over an overflow boundary (Min for <, Max for >).
// This corresponds to one set of branches in RangeConstraintManager.
void largeAdjustmentGT (unsigned a) {
- char* b = NULL;
if (a-2 > UINT_MAX-1)
- b = malloc(1);
- if (a == 1 || a == 0)
- free(b);
- else if (a > 1)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a != 1); // expected-warning{{TRUE}}
}
void largeAdjustmentGE (unsigned a) {
- char* b = NULL;
if (a-2 >= UINT_MAX-1)
- b = malloc(1);
- if (a > 1)
- return; // no-warning
- else if (a == 1 || a == 0)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a == 1 || a == 0); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a > 1); // expected-warning{{TRUE}}
}
void largeAdjustmentLT (unsigned a) {
- char* b = NULL;
if (a+2 < 1)
- b = malloc(1);
- if (a == UINT_MAX-1 || a == UINT_MAX)
- free(b);
- else if (a < UINT_MAX-1)
- return; // no-warning
- return; // no-warning
+ clang_analyzer_eval(a == UINT_MAX-1); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a != UINT_MAX-1); // expected-warning{{TRUE}}
}
void largeAdjustmentLE (unsigned a) {
- char* b = NULL;
if (a+2 <= 1)
- b = malloc(1);
- if (a < UINT_MAX-1)
- return; // no-warning
- else if (a == UINT_MAX-1 || a == UINT_MAX)
- free(b);
- return; // no-warning
+ clang_analyzer_eval(a == UINT_MAX-1 || a == UINT_MAX); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a < UINT_MAX-1); // expected-warning{{TRUE}}
+}
+
+
+// Test the nine cases in RangeConstraintManager's pinning logic.
+// For out-of-range tautologies, it may be the negation that actually
+// triggers the case in question.
+void mixedComparisons1(signed char a) {
+ // Case 1: The range is entirely below the symbol's range.
+ int min = INT_MIN;
+
+ clang_analyzer_eval((a - 2) >= (min + 5LL)); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a == 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
+}
+
+void mixedComparisons2(signed char a) {
+ // Case 2: Only the lower end of the range is outside.
+ clang_analyzer_eval((a - 5) < (-0x81LL)); // expected-warning{{UNKNOWN}}
+
+ if ((a - 5) < (-0x81LL)) {
+ clang_analyzer_eval(a == 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
+ }
+}
+
+void mixedComparisons3(signed char a) {
+ // Case 3: The entire symbol range is covered.
+ clang_analyzer_eval((a - 0x200) < -0x100LL); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a == 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
+}
+
+void mixedComparisons4(signed char a) {
+ // Case 4: The range wraps around, but the lower wrap is out-of-range.
+ clang_analyzer_eval((a - 5) > 0LL); // expected-warning{{UNKNOWN}}
+
+ if ((a - 5) > 0LL) {
+ clang_analyzer_eval(a == 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{FALSE}}
+ }
+}
+
+void mixedComparisons5(signed char a) {
+ // Case 5: The range is inside and may or may not wrap.
+ clang_analyzer_eval((a + 5) == 0LL); // expected-warning{{UNKNOWN}}
+
+ if ((a + 5) == 0LL) {
+ clang_analyzer_eval(a == 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{FALSE}}
+ } else {
+ clang_analyzer_eval(a == 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
+ }
+}
+
+void mixedComparisons6(signed char a) {
+ // Case 6: Only the upper end of the range is outside.
+ clang_analyzer_eval((a + 5) > 0x81LL); // expected-warning{{UNKNOWN}}
+
+ if ((a + 5) > 0x81LL) {
+ clang_analyzer_eval(a == 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{FALSE}}
+ }
+}
+
+void mixedComparisons7(signed char a) {
+ // Case 7: The range wraps around but is entirely outside the symbol's range.
+ int min = INT_MIN;
+
+ clang_analyzer_eval((a + 2) >= (min + 5LL)); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a == 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
+}
+
+void mixedComparisons8(signed char a) {
+ // Case 8: The range wraps, but the upper wrap is out of range.
+ clang_analyzer_eval((a + 5) < 0LL); // expected-warning{{UNKNOWN}}
+
+ if ((a + 5) < 0LL) {
+ clang_analyzer_eval(a == 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{FALSE}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
+ }
+}
+
+void mixedComparisons9(signed char a) {
+ // Case 9: The range is entirely above the symbol's range.
+ int max = INT_MAX;
+
+ clang_analyzer_eval((a + 2) <= (max - 5LL)); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a == 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == 0x7F); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == -0x80); // expected-warning{{UNKNOWN}}
}
diff --git a/test/Analysis/additive-folding.c b/test/Analysis/additive-folding.c
deleted file mode 100644
index beb08aa..0000000
--- a/test/Analysis/additive-folding.c
+++ /dev/null
@@ -1,203 +0,0 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode,unix.Malloc -verify -analyzer-constraints=basic %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode,unix.Malloc -verify -analyzer-constraints=range %s
-
-// These are used to trigger warnings.
-typedef typeof(sizeof(int)) size_t;
-void *malloc(size_t);
-void free(void *);
-#define NULL ((void*)0)
-#define UINT_MAX -1U
-
-//---------------
-// Plus/minus
-//---------------
-
-void separateExpressions (int a) {
- int b = a + 1;
- --b;
-
- char* buf = malloc(1);
- if (a != 0 && b == 0)
- return; // expected-warning{{never executed}}
- free(buf);
-}
-
-void oneLongExpression (int a) {
- // Expression canonicalization should still allow this to work, even though
- // the first term is on the left.
- int b = 15 + a + 15 - 10 - 20;
-
- char* buf = malloc(1);
- if (a != 0 && b == 0)
- return; // expected-warning{{never executed}}
- free(buf);
-}
-
-void mixedTypes (int a) {
- char* buf = malloc(1);
-
- // Different additive types should not cause crashes when constant-folding.
- // This is part of PR7406.
- int b = a + 1LL;
- if (a != 0 && (b-1) == 0) // not crash
- return; // expected-warning{{never executed}}
-
- int c = a + 1U;
- if (a != 0 && (c-1) == 0) // not crash
- return; // expected-warning{{never executed}}
-
- free(buf);
-}
-
-//---------------
-// Comparisons
-//---------------
-
-// Equality and inequality only
-void eq_ne (unsigned a) {
- char* b = NULL;
- if (a == UINT_MAX)
- b = malloc(1);
- if (a+1 != 0)
- return; // no-warning
- if (a-1 != UINT_MAX-1)
- return; // no-warning
- free(b);
-}
-
-void ne_eq (unsigned a) {
- char* b = NULL;
- if (a != UINT_MAX)
- b = malloc(1);
- if (a+1 == 0)
- return; // no-warning
- if (a-1 == UINT_MAX-1)
- return; // no-warning
- free(b);
-}
-
-// Mixed typed inequalities (part of PR7406)
-// These should not crash.
-void mixed_eq_ne (int a) {
- char* b = NULL;
- if (a == 1)
- b = malloc(1);
- if (a+1U != 2)
- return; // no-warning
- if (a-1U != 0)
- return; // expected-warning{{never executed}}
- free(b);
-}
-
-void mixed_ne_eq (int a) {
- char* b = NULL;
- if (a != 1)
- b = malloc(1);
- if (a+1U == 2)
- return; // no-warning
- if (a-1U == 0)
- return; // expected-warning{{never executed}}
- free(b);
-}
-
-
-// Simple order comparisons with no adjustment
-void baselineGT (unsigned a) {
- char* b = NULL;
- if (a > 0)
- b = malloc(1);
- if (a == 0)
- return; // no-warning
- free(b);
-}
-
-void baselineGE (unsigned a) {
- char* b = NULL;
- if (a >= UINT_MAX)
- b = malloc(1);
- if (a == UINT_MAX)
- free(b);
- return; // no-warning
-}
-
-void baselineLT (unsigned a) {
- char* b = NULL;
- if (a < UINT_MAX)
- b = malloc(1);
- if (a == UINT_MAX)
- return; // no-warning
- free(b);
-}
-
-void baselineLE (unsigned a) {
- char* b = NULL;
- if (a <= 0)
- b = malloc(1);
- if (a == 0)
- free(b);
- return; // no-warning
-}
-
-
-// Adjustment gives each of these an extra solution!
-void adjustedGT (unsigned a) {
- char* b = NULL;
- if (a-1 > UINT_MAX-1)
- b = malloc(1);
- return; // expected-warning{{leak}}
-}
-
-void adjustedGE (unsigned a) {
- char* b = NULL;
- if (a-1 >= UINT_MAX-1)
- b = malloc(1);
- if (a == UINT_MAX)
- free(b);
- return; // expected-warning{{leak}}
-}
-
-void adjustedLT (unsigned a) {
- char* b = NULL;
- if (a+1 < 1)
- b = malloc(1);
- return; // expected-warning{{leak}}
-}
-
-void adjustedLE (unsigned a) {
- char* b = NULL;
- if (a+1 <= 1)
- b = malloc(1);
- if (a == 0)
- free(b);
- return; // expected-warning{{leak}}
-}
-
-
-// Tautologies
-void tautologyGT (unsigned a) {
- char* b = malloc(1);
- if (a > UINT_MAX)
- return; // no-warning
- free(b);
-}
-
-void tautologyGE (unsigned a) {
- char* b = malloc(1);
- if (a >= 0) // expected-warning{{always true}}
- free(b);
- return; // no-warning
-}
-
-void tautologyLT (unsigned a) {
- char* b = malloc(1);
- if (a < 0) // expected-warning{{always false}}
- return; // expected-warning{{never executed}}
- free(b);
-}
-
-void tautologyLE (unsigned a) {
- char* b = malloc(1);
- if (a <= UINT_MAX)
- free(b);
- return; // no-warning
-}
diff --git a/test/Analysis/additive-folding.cpp b/test/Analysis/additive-folding.cpp
new file mode 100644
index 0000000..0d749ec
--- /dev/null
+++ b/test/Analysis/additive-folding.cpp
@@ -0,0 +1,196 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -analyzer-constraints=basic -Wno-tautological-compare %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -analyzer-constraints=range -Wno-tautological-compare %s
+
+void clang_analyzer_eval(bool);
+
+#define UINT_MAX (~0U)
+#define INT_MAX (UINT_MAX & (UINT_MAX >> 1))
+#define INT_MIN (-INT_MAX - 1)
+
+//---------------
+// Plus/minus
+//---------------
+
+void separateExpressions (int a) {
+ int b = a + 1;
+ --b;
+
+ clang_analyzer_eval(a != 0 && b == 0); // expected-warning{{FALSE}}
+}
+
+void oneLongExpression (int a) {
+ // Expression canonicalization should still allow this to work, even though
+ // the first term is on the left.
+ int b = 15 + a + 15 - 10 - 20;
+
+ clang_analyzer_eval(a != 0 && b == 0); // expected-warning{{FALSE}}
+}
+
+void mixedTypes (int a) {
+ // Different additive types should not cause crashes when constant-folding.
+ // This is part of PR7406.
+ int b = a + 1LL;
+ clang_analyzer_eval(a != 0 && (b-1) == 0); // not crash, expected-warning{{FALSE}}
+
+ int c = a + 1U;
+ clang_analyzer_eval(a != 0 && (c-1) == 0); // not crash, expected-warning{{FALSE}}
+}
+
+//---------------
+// Comparisons
+//---------------
+
+// Equality and inequality only
+void eq_ne (unsigned a) {
+ if (a == UINT_MAX) {
+ clang_analyzer_eval(a+1 == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a-1 == UINT_MAX-1); // expected-warning{{TRUE}}
+ } else {
+ clang_analyzer_eval(a+1 != 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a-1 != UINT_MAX-1); // expected-warning{{TRUE}}
+ }
+}
+
+// Mixed typed inequalities (part of PR7406)
+// These should not crash.
+void mixed_eq_ne (int a) {
+ if (a == 1) {
+ clang_analyzer_eval(a+1U == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a-1U == 0); // expected-warning{{TRUE}}
+ } else {
+ clang_analyzer_eval(a+1U != 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a-1U != 0); // expected-warning{{TRUE}}
+ }
+}
+
+
+// Simple order comparisons with no adjustment
+void baselineGT (unsigned a) {
+ if (a > 0)
+ clang_analyzer_eval(a != 0); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a == 0); // expected-warning{{TRUE}}
+}
+
+void baselineGE (unsigned a) {
+ if (a >= UINT_MAX)
+ clang_analyzer_eval(a == UINT_MAX); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a != UINT_MAX); // expected-warning{{TRUE}}
+}
+
+void baselineLT (unsigned a) {
+ if (a < UINT_MAX)
+ clang_analyzer_eval(a != UINT_MAX); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a == UINT_MAX); // expected-warning{{TRUE}}
+}
+
+void baselineLE (unsigned a) {
+ if (a <= 0)
+ clang_analyzer_eval(a == 0); // expected-warning{{TRUE}}
+ else
+ clang_analyzer_eval(a != 0); // expected-warning{{TRUE}}
+}
+
+
+// Adjustment gives each of these an extra solution!
+void adjustedGT (unsigned a) {
+ clang_analyzer_eval(a-1 > UINT_MAX-1); // expected-warning{{UNKNOWN}}
+}
+
+void adjustedGE (unsigned a) {
+ clang_analyzer_eval(a-1 > UINT_MAX-1); // expected-warning{{UNKNOWN}}
+
+ if (a-1 >= UINT_MAX-1)
+ clang_analyzer_eval(a == UINT_MAX); // expected-warning{{UNKNOWN}}
+}
+
+void adjustedLT (unsigned a) {
+ clang_analyzer_eval(a+1 < 1); // expected-warning{{UNKNOWN}}
+}
+
+void adjustedLE (unsigned a) {
+ clang_analyzer_eval(a+1 <= 1); // expected-warning{{UNKNOWN}}
+
+ if (a+1 <= 1)
+ clang_analyzer_eval(a == 0); // expected-warning{{UNKNOWN}}
+}
+
+
+// Tautologies
+// The negative forms are exercised as well
+// because clang_analyzer_eval tests both possibilities.
+void tautologies(unsigned a) {
+ clang_analyzer_eval(a <= UINT_MAX); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a >= 0); // expected-warning{{TRUE}}
+}
+
+
+// Tautologies from outside the range of the symbol
+void tautologiesOutside(unsigned char a) {
+ clang_analyzer_eval(a <= 0x100); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a < 0x100); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a != 0x100); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a != -1); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a > -1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a >= -1); // expected-warning{{TRUE}}
+}
+
+
+// Wraparound with mixed types. Note that the analyzer assumes
+// -fwrapv semantics.
+void mixedWraparoundSanityCheck(int a) {
+ int max = INT_MAX;
+ int min = INT_MIN;
+
+ int b = a + 1;
+ clang_analyzer_eval(a == max && b != min); // expected-warning{{FALSE}}
+}
+
+void mixedWraparoundLE_GT(int a) {
+ int max = INT_MAX;
+ int min = INT_MIN;
+
+ clang_analyzer_eval((a + 2) <= (max + 1LL)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a - 2) > (min - 1LL)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a + 2LL) <= max); // expected-warning{{UNKNOWN}}
+}
+
+void mixedWraparoundGE_LT(int a) {
+ int max = INT_MAX;
+ int min = INT_MIN;
+
+ clang_analyzer_eval((a + 2) < (max + 1LL)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a - 2) >= (min - 1LL)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a - 2LL) >= min); // expected-warning{{UNKNOWN}}
+}
+
+void mixedWraparoundEQ_NE(int a) {
+ int max = INT_MAX;
+
+ clang_analyzer_eval((a + 2) != (max + 1LL)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a + 2LL) == (max + 1LL)); // expected-warning{{UNKNOWN}}
+}
+
+
+// Mixed-signedness comparisons.
+void mixedSignedness(int a, unsigned b) {
+ int sMin = INT_MIN;
+ unsigned uMin = INT_MIN;
+
+ clang_analyzer_eval(a == sMin && a != uMin); // expected-warning{{FALSE}}
+ clang_analyzer_eval(b == uMin && b != sMin); // expected-warning{{FALSE}}
+}
+
+
+void multiplicativeSanityTest(int x) {
+ // At one point we were ignoring the *4 completely -- the constraint manager
+ // would see x < 8 and then declare the assertion to be known false.
+ if (x*4 < 8)
+ return;
+
+ clang_analyzer_eval(x == 3); // expected-warning{{UNKNOWN}}
+}
diff --git a/test/Analysis/array-struct-region.c b/test/Analysis/array-struct-region.c
index 4b085c8..244bc97 100644
--- a/test/Analysis/array-struct-region.c
+++ b/test/Analysis/array-struct-region.c
@@ -1,32 +1,28 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,experimental.deadcode.UnreachableCode -analyzer-store=region -analyzer-constraints=basic -verify %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,experimental.deadcode.UnreachableCode -analyzer-store=region -analyzer-constraints=range -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=basic -analyzer-ipa=inlining -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=range -analyzer-ipa=inlining -verify %s
+
+void clang_analyzer_eval(int);
int string_literal_init() {
char a[] = "abc";
char b[2] = "abc"; // expected-warning{{too long}}
char c[5] = "abc";
- if (a[1] != 'b')
- return 0; // expected-warning{{never executed}}
- if (b[1] != 'b')
- return 0; // expected-warning{{never executed}}
- if (c[1] != 'b')
- return 0; // expected-warning{{never executed}}
+ clang_analyzer_eval(a[1] == 'b'); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b[1] == 'b'); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c[1] == 'b'); // expected-warning{{TRUE}}
- if (a[3] != 0)
- return 0; // expected-warning{{never executed}}
- if (c[3] != 0)
- return 0; // expected-warning{{never executed}}
+ clang_analyzer_eval(a[3] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c[3] == 0); // expected-warning{{TRUE}}
- if (c[4] != 0)
- return 0; // expected-warning{{never executed}}
+ clang_analyzer_eval(c[4] == 0); // expected-warning{{TRUE}}
return 42;
}
void nested_compound_literals(int rad) {
- int vec[6][2] = {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, // expected-warning 6 {{implicit conversion turns literal floating-point number into integer}}
- {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}}; // expected-warning 6 {{implicit conversion turns literal floating-point number into integer}}
+ int vec[6][2] = {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, // expected-warning 6 {{implicit conversion from 'double' to 'int' changes value from}}
+ {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}}; // expected-warning 6 {{implicit conversion from 'double' to 'int' changes value from}}
int a;
for (a = 0; a < 6; ++a) {
@@ -45,3 +41,146 @@ void nested_compound_literals_float(float rad) {
vec[a][1] *= rad; // no-warning
}
}
+
+
+void struct_as_array() {
+ struct simple { int x; int y; };
+ struct simple a;
+ struct simple *p = &a;
+
+ p->x = 5;
+ clang_analyzer_eval(a.x == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p[0].x == 5); // expected-warning{{TRUE}}
+
+ p[0].y = 5;
+ clang_analyzer_eval(a.y == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p->y == 5); // expected-warning{{TRUE}}
+}
+
+
+// PR13264 / <rdar://problem/11802440>
+struct point { int x; int y; };
+struct circle { struct point o; int r; };
+struct circle get_circle() {
+ struct circle result;
+ result.r = 5;
+ result.o = (struct point){0, 0};
+ return result;
+}
+
+void struct_in_struct() {
+ struct circle c;
+ c = get_circle();
+ // This used to think c.r was undefined because c.o is a LazyCompoundVal.
+ clang_analyzer_eval(c.r == 5); // expected-warning{{TRUE}}
+}
+
+// We also test with floats because we don't model floats right now,
+// and the original bug report used a float.
+struct circle_f { struct point o; float r; };
+struct circle_f get_circle_f() {
+ struct circle_f result;
+ result.r = 5.0;
+ result.o = (struct point){0, 0};
+ return result;
+}
+
+float struct_in_struct_f() {
+ struct circle_f c;
+ c = get_circle_f();
+
+ return c.r; // no-warning
+}
+
+
+int randomInt();
+
+int testSymbolicInvalidation(int index) {
+ int vals[10];
+
+ vals[0] = 42;
+ clang_analyzer_eval(vals[0] == 42); // expected-warning{{TRUE}}
+
+ vals[index] = randomInt();
+ clang_analyzer_eval(vals[0] == 42); // expected-warning{{UNKNOWN}}
+
+ return vals[index]; // no-warning
+}
+
+int testConcreteInvalidation(int index) {
+ int vals[10];
+
+ vals[index] = 42;
+ clang_analyzer_eval(vals[index] == 42); // expected-warning{{TRUE}}
+ vals[0] = randomInt();
+ clang_analyzer_eval(vals[index] == 42); // expected-warning{{UNKNOWN}}
+
+ return vals[0]; // no-warning
+}
+
+
+typedef struct {
+ int x, y, z;
+} S;
+
+S makeS();
+
+int testSymbolicInvalidationStruct(int index) {
+ S vals[10];
+
+ vals[0].x = 42;
+ clang_analyzer_eval(vals[0].x == 42); // expected-warning{{TRUE}}
+
+ vals[index] = makeS();
+ clang_analyzer_eval(vals[0].x == 42); // expected-warning{{UNKNOWN}}
+
+ return vals[index].x; // no-warning
+}
+
+int testConcreteInvalidationStruct(int index) {
+ S vals[10];
+
+ vals[index].x = 42;
+ clang_analyzer_eval(vals[index].x == 42); // expected-warning{{TRUE}}
+ vals[0] = makeS();
+ clang_analyzer_eval(vals[index].x == 42); // expected-warning{{UNKNOWN}}
+
+ return vals[0].x; // no-warning
+}
+
+typedef struct {
+ S a[5];
+ S b[5];
+} SS;
+
+int testSymbolicInvalidationDoubleStruct(int index) {
+ SS vals;
+
+ vals.a[0].x = 42;
+ vals.b[0].x = 42;
+ clang_analyzer_eval(vals.a[0].x == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(vals.b[0].x == 42); // expected-warning{{TRUE}}
+
+ vals.a[index] = makeS();
+ clang_analyzer_eval(vals.a[0].x == 42); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(vals.b[0].x == 42); // expected-warning{{TRUE}}
+
+ return vals.b[index].x; // no-warning
+}
+
+int testConcreteInvalidationDoubleStruct(int index) {
+ SS vals;
+
+ vals.a[index].x = 42;
+ vals.b[index].x = 42;
+ clang_analyzer_eval(vals.a[index].x == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(vals.b[index].x == 42); // expected-warning{{TRUE}}
+
+ vals.a[0] = makeS();
+ clang_analyzer_eval(vals.a[index].x == 42); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(vals.b[index].x == 42); // expected-warning{{TRUE}}
+
+ return vals.b[0].x; // no-warning
+}
+
+
diff --git a/test/Analysis/auto-obj-dtors-cfg-output.cpp b/test/Analysis/auto-obj-dtors-cfg-output.cpp
index 67a8f2e..566e6ca 100644
--- a/test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ b/test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -416,121 +416,121 @@ void test_catch_copy() {
// CHECK: [B6 (ENTRY)]
// CHECK: Succs (1): B5
// CHECK: [B1]
-// CHECK: 1: [B2.4].~A() (Implicit destructor)
+// CHECK: 1: [B4.4].~A() (Implicit destructor)
// CHECK: 2: [B5.2].~A() (Implicit destructor)
-// CHECK: Preds (1): B2
+// CHECK: Preds (1): B4
// CHECK: Succs (1): B0
// CHECK: [B2]
-// CHECK: 1: a
-// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
-// CHECK: 4: A b = a;
-// CHECK: 5: b
-// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 7: [B2.6].operator int
-// CHECK: 8: [B2.7]()
-// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
-// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
-// CHECK: T: while [B2.10]
-// CHECK: Preds (2): B3 B5
-// CHECK: Succs (2): B4 B1
+// CHECK: Preds (1): B3
+// CHECK: Succs (1): B4
// CHECK: [B3]
+// CHECK: 1: (CXXConstructExpr, class A)
+// CHECK: 2: A c;
+// CHECK: 3: [B3.2].~A() (Implicit destructor)
+// CHECK: 4: [B4.4].~A() (Implicit destructor)
// CHECK: Preds (1): B4
// CHECK: Succs (1): B2
// CHECK: [B4]
-// CHECK: 1: (CXXConstructExpr, class A)
-// CHECK: 2: A c;
-// CHECK: 3: [B4.2].~A() (Implicit destructor)
-// CHECK: 4: [B2.4].~A() (Implicit destructor)
-// CHECK: Preds (1): B2
-// CHECK: Succs (1): B3
+// CHECK: 1: a
+// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 3: [B4.2] (CXXConstructExpr, class A)
+// CHECK: 4: A b = a;
+// CHECK: 5: b
+// CHECK: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 7: [B4.6].operator int
+// CHECK: 8: [B4.7]()
+// CHECK: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK: T: while [B4.10]
+// CHECK: Preds (2): B2 B5
+// CHECK: Succs (2): B3 B1
// CHECK: [B5]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: Preds (1): B6
-// CHECK: Succs (1): B2
+// CHECK: Succs (1): B4
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B12 (ENTRY)]
// CHECK: Succs (1): B11
// CHECK: [B1]
-// CHECK: 1: [B2.4].~A() (Implicit destructor)
+// CHECK: 1: [B10.4].~A() (Implicit destructor)
// CHECK: 2: (CXXConstructExpr, class A)
// CHECK: 3: A e;
// CHECK: 4: [B1.3].~A() (Implicit destructor)
// CHECK: 5: [B11.2].~A() (Implicit destructor)
-// CHECK: Preds (2): B9 B2
+// CHECK: Preds (2): B8 B10
// CHECK: Succs (1): B0
// CHECK: [B2]
-// CHECK: 1: a
-// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
-// CHECK: 4: A b = a;
-// CHECK: 5: b
-// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 7: [B2.6].operator int
-// CHECK: 8: [B2.7]()
-// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
-// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
-// CHECK: T: while [B2.10]
-// CHECK: Preds (2): B3 B11
-// CHECK: Succs (2): B10 B1
+// CHECK: Preds (2): B3 B6
+// CHECK: Succs (1): B10
// CHECK: [B3]
-// CHECK: Preds (2): B4 B7
-// CHECK: Succs (1): B2
-// CHECK: [B4]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A d;
-// CHECK: 3: [B4.2].~A() (Implicit destructor)
-// CHECK: 4: [B10.2].~A() (Implicit destructor)
-// CHECK: 5: [B2.4].~A() (Implicit destructor)
-// CHECK: Preds (1): B6
-// CHECK: Succs (1): B3
-// CHECK: [B5]
+// CHECK: 3: [B3.2].~A() (Implicit destructor)
+// CHECK: 4: [B9.2].~A() (Implicit destructor)
+// CHECK: 5: [B10.4].~A() (Implicit destructor)
+// CHECK: Preds (1): B5
+// CHECK: Succs (1): B2
+// CHECK: [B4]
// CHECK: 1: return;
-// CHECK: 2: [B10.2].~A() (Implicit destructor)
-// CHECK: 3: [B2.4].~A() (Implicit destructor)
+// CHECK: 2: [B9.2].~A() (Implicit destructor)
+// CHECK: 3: [B10.4].~A() (Implicit destructor)
// CHECK: 4: [B11.2].~A() (Implicit destructor)
-// CHECK: Preds (1): B6
+// CHECK: Preds (1): B5
// CHECK: Succs (1): B0
-// CHECK: [B6]
+// CHECK: [B5]
// CHECK: 1: UV
-// CHECK: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK: T: if [B6.2]
-// CHECK: Preds (1): B8
-// CHECK: Succs (2): B5 B4
-// CHECK: [B7]
-// CHECK: 1: [B10.2].~A() (Implicit destructor)
-// CHECK: 2: [B2.4].~A() (Implicit destructor)
+// CHECK: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK: T: if [B5.2]
+// CHECK: Preds (1): B7
+// CHECK: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK: 1: [B9.2].~A() (Implicit destructor)
+// CHECK: 2: [B10.4].~A() (Implicit destructor)
// CHECK: T: continue;
-// CHECK: Preds (1): B8
-// CHECK: Succs (1): B3
-// CHECK: [B8]
+// CHECK: Preds (1): B7
+// CHECK: Succs (1): B2
+// CHECK: [B7]
// CHECK: 1: UV
-// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK: T: if [B8.2]
-// CHECK: Preds (1): B10
-// CHECK: Succs (2): B7 B6
-// CHECK: [B9]
-// CHECK: 1: [B10.2].~A() (Implicit destructor)
+// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK: T: if [B7.2]
+// CHECK: Preds (1): B9
+// CHECK: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: T: break;
-// CHECK: Preds (1): B10
+// CHECK: Preds (1): B9
// CHECK: Succs (1): B1
-// CHECK: [B10]
+// CHECK: [B9]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A c;
// CHECK: 3: UV
-// CHECK: 4: [B10.3] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK: T: if [B10.4]
-// CHECK: Preds (1): B2
-// CHECK: Succs (2): B9 B8
+// CHECK: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK: T: if [B9.4]
+// CHECK: Preds (1): B10
+// CHECK: Succs (2): B8 B7
+// CHECK: [B10]
+// CHECK: 1: a
+// CHECK: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 3: [B10.2] (CXXConstructExpr, class A)
+// CHECK: 4: A b = a;
+// CHECK: 5: b
+// CHECK: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 7: [B10.6].operator int
+// CHECK: 8: [B10.7]()
+// CHECK: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK: T: while [B10.10]
+// CHECK: Preds (2): B2 B11
+// CHECK: Succs (2): B9 B1
// CHECK: [B11]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: Preds (1): B12
-// CHECK: Succs (1): B2
+// CHECK: Succs (1): B10
// CHECK: [B0 (EXIT)]
-// CHECK: Preds (2): B1 B5
+// CHECK: Preds (2): B1 B4
// CHECK: [B4 (ENTRY)]
// CHECK: Succs (1): B2
// CHECK: [B1]
@@ -717,124 +717,124 @@ void test_catch_copy() {
// CHECK: [B6 (ENTRY)]
// CHECK: Succs (1): B5
// CHECK: [B1]
-// CHECK: 1: [B2.4].~A() (Implicit destructor)
+// CHECK: 1: [B4.4].~A() (Implicit destructor)
// CHECK: 2: [B5.2].~A() (Implicit destructor)
-// CHECK: Preds (1): B2
+// CHECK: Preds (1): B4
// CHECK: Succs (1): B0
// CHECK: [B2]
-// CHECK: 1: a
-// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
-// CHECK: 4: A b = a;
-// CHECK: 5: b
-// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 7: [B2.6].operator int
-// CHECK: 8: [B2.7]()
-// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
-// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
-// CHECK: T: for (...; [B2.10]; )
-// CHECK: Preds (2): B3 B5
-// CHECK: Succs (2): B4 B1
+// CHECK: Preds (1): B3
+// CHECK: Succs (1): B4
// CHECK: [B3]
-// CHECK: 1: [B2.4].~A() (Implicit destructor)
+// CHECK: 1: (CXXConstructExpr, class A)
+// CHECK: 2: A c;
+// CHECK: 3: [B3.2].~A() (Implicit destructor)
+// CHECK: 4: [B4.4].~A() (Implicit destructor)
// CHECK: Preds (1): B4
// CHECK: Succs (1): B2
// CHECK: [B4]
-// CHECK: 1: (CXXConstructExpr, class A)
-// CHECK: 2: A c;
-// CHECK: 3: [B4.2].~A() (Implicit destructor)
-// CHECK: Preds (1): B2
-// CHECK: Succs (1): B3
+// CHECK: 1: a
+// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 3: [B4.2] (CXXConstructExpr, class A)
+// CHECK: 4: A b = a;
+// CHECK: 5: b
+// CHECK: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 7: [B4.6].operator int
+// CHECK: 8: [B4.7]()
+// CHECK: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK: T: for (...; [B4.10]; )
+// CHECK: Preds (2): B2 B5
+// CHECK: Succs (2): B3 B1
// CHECK: [B5]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: Preds (1): B6
-// CHECK: Succs (1): B2
+// CHECK: Succs (1): B4
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B12 (ENTRY)]
// CHECK: Succs (1): B11
// CHECK: [B1]
-// CHECK: 1: [B2.4].~A() (Implicit destructor)
+// CHECK: 1: [B10.4].~A() (Implicit destructor)
// CHECK: 2: [B11.4].~A() (Implicit destructor)
// CHECK: 3: (CXXConstructExpr, class A)
// CHECK: 4: A f;
// CHECK: 5: [B1.4].~A() (Implicit destructor)
// CHECK: 6: [B11.2].~A() (Implicit destructor)
-// CHECK: Preds (2): B9 B2
+// CHECK: Preds (2): B8 B10
// CHECK: Succs (1): B0
// CHECK: [B2]
-// CHECK: 1: b
-// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
-// CHECK: 4: A c = b;
-// CHECK: 5: c
-// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
-// CHECK: 7: [B2.6].operator int
-// CHECK: 8: [B2.7]()
-// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
-// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
-// CHECK: T: for (...; [B2.10]; )
-// CHECK: Preds (2): B3 B11
-// CHECK: Succs (2): B10 B1
+// CHECK: Preds (2): B3 B6
+// CHECK: Succs (1): B10
// CHECK: [B3]
-// CHECK: 1: [B2.4].~A() (Implicit destructor)
-// CHECK: Preds (2): B4 B7
-// CHECK: Succs (1): B2
-// CHECK: [B4]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A e;
-// CHECK: 3: [B4.2].~A() (Implicit destructor)
-// CHECK: 4: [B10.2].~A() (Implicit destructor)
-// CHECK: Preds (1): B6
-// CHECK: Succs (1): B3
-// CHECK: [B5]
+// CHECK: 3: [B3.2].~A() (Implicit destructor)
+// CHECK: 4: [B9.2].~A() (Implicit destructor)
+// CHECK: 5: [B10.4].~A() (Implicit destructor)
+// CHECK: Preds (1): B5
+// CHECK: Succs (1): B2
+// CHECK: [B4]
// CHECK: 1: return;
-// CHECK: 2: [B10.2].~A() (Implicit destructor)
-// CHECK: 3: [B2.4].~A() (Implicit destructor)
+// CHECK: 2: [B9.2].~A() (Implicit destructor)
+// CHECK: 3: [B10.4].~A() (Implicit destructor)
// CHECK: 4: [B11.4].~A() (Implicit destructor)
// CHECK: 5: [B11.2].~A() (Implicit destructor)
-// CHECK: Preds (1): B6
+// CHECK: Preds (1): B5
// CHECK: Succs (1): B0
-// CHECK: [B6]
+// CHECK: [B5]
// CHECK: 1: UV
-// CHECK: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK: T: if [B6.2]
-// CHECK: Preds (1): B8
-// CHECK: Succs (2): B5 B4
-// CHECK: [B7]
-// CHECK: 1: [B10.2].~A() (Implicit destructor)
+// CHECK: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK: T: if [B5.2]
+// CHECK: Preds (1): B7
+// CHECK: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: T: continue;
-// CHECK: Preds (1): B8
-// CHECK: Succs (1): B3
-// CHECK: [B8]
+// CHECK: Preds (1): B7
+// CHECK: Succs (1): B2
+// CHECK: [B7]
// CHECK: 1: UV
-// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK: T: if [B8.2]
-// CHECK: Preds (1): B10
-// CHECK: Succs (2): B7 B6
-// CHECK: [B9]
-// CHECK: 1: [B10.2].~A() (Implicit destructor)
+// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK: T: if [B7.2]
+// CHECK: Preds (1): B9
+// CHECK: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: T: break;
-// CHECK: Preds (1): B10
+// CHECK: Preds (1): B9
// CHECK: Succs (1): B1
-// CHECK: [B10]
+// CHECK: [B9]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A d;
// CHECK: 3: UV
-// CHECK: 4: [B10.3] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK: T: if [B10.4]
-// CHECK: Preds (1): B2
-// CHECK: Succs (2): B9 B8
+// CHECK: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK: T: if [B9.4]
+// CHECK: Preds (1): B10
+// CHECK: Succs (2): B8 B7
+// CHECK: [B10]
+// CHECK: 1: b
+// CHECK: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 3: [B10.2] (CXXConstructExpr, class A)
+// CHECK: 4: A c = b;
+// CHECK: 5: c
+// CHECK: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 7: [B10.6].operator int
+// CHECK: 8: [B10.7]()
+// CHECK: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK: T: for (...; [B10.10]; )
+// CHECK: Preds (2): B2 B11
+// CHECK: Succs (2): B9 B1
// CHECK: [B11]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: 3: (CXXConstructExpr, class A)
// CHECK: 4: A b;
// CHECK: Preds (1): B12
-// CHECK: Succs (1): B2
+// CHECK: Succs (1): B10
// CHECK: [B0 (EXIT)]
-// CHECK: Preds (2): B1 B5
+// CHECK: Preds (2): B1 B4
// CHECK: [B3 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B1]
@@ -862,3 +862,4 @@ void test_catch_copy() {
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (3): B2 B1 B3
+
diff --git a/test/Analysis/base-init.cpp b/test/Analysis/base-init.cpp
index 8fd7abc..e63d508 100644
--- a/test/Analysis/base-init.cpp
+++ b/test/Analysis/base-init.cpp
@@ -1,6 +1,8 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region -analyzer-inline-call -cfg-add-initializers -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store region -analyzer-ipa=inlining -verify %s
// XFAIL: *
+void clang_analyzer_eval(bool);
+
class A {
int x;
public:
@@ -24,8 +26,5 @@ B::B() {
void f() {
B b;
- if (b.getx() != 0) {
- int *p = 0;
- *p = 0; // no-warning
- }
+ clang_analyzer_eval(b.getx() == 0); // expected-warning{{TRUE}}
}
diff --git a/test/Analysis/blocks-no-inline.c b/test/Analysis/blocks-no-inline.c
new file mode 100644
index 0000000..1ec14e8
--- /dev/null
+++ b/test/Analysis/blocks-no-inline.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=none -fblocks -verify %s
+
+void clang_analyzer_eval(int);
+
+void testInvalidation() {
+ __block int i = 0;
+ ^{
+ ++i;
+ }();
+
+ // Under inlining, we will know that i == 1.
+ clang_analyzer_eval(i == 0); // expected-warning{{UNKNOWN}}
+}
diff --git a/test/Analysis/blocks.m b/test/Analysis/blocks.m
index 7a604dd..ff376d1 100644
--- a/test/Analysis/blocks.m
+++ b/test/Analysis/blocks.m
@@ -79,9 +79,7 @@ void test2() {
void test2_b() {
static int y = 0;
__block int x;
- // This is also a bug, but should be found not by checking the value
- // 'x' is bound at block creation.
- ^{ y = x + 1; }(); // no-warning
+ ^{ y = x + 1; }(); // expected-warning {{left operand of '+' is a garbage value}}
}
void test2_c() {
diff --git a/test/Analysis/bstring.c b/test/Analysis/bstring.c
index 833c917..d383d42 100644
--- a/test/Analysis/bstring.c
+++ b/test/Analysis/bstring.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,experimental.unix.cstring -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,experimental.unix.cstring -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.cstring,experimental.unix.cstring -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.cstring.NullArg,experimental.unix.cstring.OutOfBounds,experimental.unix.cstring.BufferOverlap,experimental.unix.cstring.NotNullTerminated -analyzer-store=region -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
//===----------------------------------------------------------------------===
// Declarations
@@ -26,6 +26,8 @@
typedef typeof(sizeof(int)) size_t;
+void clang_analyzer_eval(int);
+
//===----------------------------------------------------------------------===
// memcpy()
//===----------------------------------------------------------------------===
@@ -52,12 +54,11 @@ void memcpy0 () {
memcpy(dst, src, 4); // no-warning
- if (memcpy(dst, src, 4) != dst) {
- (void)*(char*)0; // no-warning
- }
+ clang_analyzer_eval(memcpy(dst, src, 4) == dst); // expected-warning{{TRUE}}
- if (dst[0] != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ // If we actually model the copy, we can make this known.
+ // The important thing for now is that the old value has been invalidated.
+ clang_analyzer_eval(dst[0] != 0); // expected-warning{{UNKNOWN}}
}
void memcpy1 () {
@@ -138,14 +139,13 @@ void memcpy13() {
void memcpy_unknown_size (size_t n) {
char a[4], b[4] = {1};
- if (memcpy(a, b, n) != a)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(memcpy(a, b, n) == a); // expected-warning{{TRUE}}
}
void memcpy_unknown_size_warn (size_t n) {
char a[4];
- if (memcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to memory copy function}}
- (void)*(char*)0; // no-warning
+ void *result = memcpy(a, 0, n); // expected-warning{{Null pointer argument in call to memory copy function}}
+ clang_analyzer_eval(result == a); // no-warning (above is fatal)
}
//===----------------------------------------------------------------------===
@@ -174,12 +174,11 @@ void mempcpy0 () {
mempcpy(dst, src, 4); // no-warning
- if (mempcpy(dst, src, 4) != &dst[4]) {
- (void)*(char*)0; // no-warning
- }
+ clang_analyzer_eval(mempcpy(dst, src, 4) == &dst[4]); // expected-warning{{TRUE}}
- if (dst[0] != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ // If we actually model the copy, we can make this known.
+ // The important thing for now is that the old value has been invalidated.
+ clang_analyzer_eval(dst[0] != 0); // expected-warning{{UNKNOWN}}
}
void mempcpy1 () {
@@ -260,8 +259,8 @@ void mempcpy13() {
void mempcpy_unknown_size_warn (size_t n) {
char a[4];
- if (mempcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to memory copy function}}
- (void)*(char*)0; // no-warning
+ void *result = mempcpy(a, 0, n); // expected-warning{{Null pointer argument in call to memory copy function}}
+ clang_analyzer_eval(result == a); // no-warning (above is fatal)
}
void mempcpy_unknownable_size (char *src, float n) {
@@ -295,12 +294,11 @@ void memmove0 () {
memmove(dst, src, 4); // no-warning
- if (memmove(dst, src, 4) != dst) {
- (void)*(char*)0; // no-warning
- }
+ clang_analyzer_eval(memmove(dst, src, 4) == dst); // expected-warning{{TRUE}}
- if (dst[0] != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ // If we actually model the copy, we can make this known.
+ // The important thing for now is that the old value has been invalidated.
+ clang_analyzer_eval(dst[0] != 0); // expected-warning{{UNKNOWN}}
}
void memmove1 () {
@@ -327,7 +325,7 @@ void memmove2 () {
// __builtin_bcmp is not defined with const in Builtins.def.
int bcmp(/*const*/ void *s1, /*const*/ void *s2, size_t n);
#define memcmp bcmp
-
+//
#else /* VARIANT */
#define memcmp BUILTIN(memcmp)
@@ -360,34 +358,32 @@ void memcmp2 () {
void memcmp3 () {
char a[] = {1, 2, 3, 4};
- if (memcmp(a, a, 4))
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(memcmp(a, a, 4) == 0); // expected-warning{{TRUE}}
}
void memcmp4 (char *input) {
char a[] = {1, 2, 3, 4};
- if (memcmp(a, input, 4))
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(memcmp(a, input, 4) == 0); // expected-warning{{UNKNOWN}}
}
void memcmp5 (char *input) {
char a[] = {1, 2, 3, 4};
- if (memcmp(a, 0, 0)) // no-warning
- (void)*(char*)0; // no-warning
- if (memcmp(0, a, 0)) // no-warning
- (void)*(char*)0; // no-warning
- if (memcmp(a, input, 0)) // no-warning
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(memcmp(a, 0, 0) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(memcmp(0, a, 0) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(memcmp(a, input, 0) == 0); // expected-warning{{TRUE}}
}
void memcmp6 (char *a, char *b, size_t n) {
int result = memcmp(a, b, n);
if (result != 0)
- return;
- if (n == 0)
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(n != 0); // expected-warning{{TRUE}}
+ // else
+ // analyzer_assert_unknown(n == 0);
+
+ // We can't do the above comparison because n has already been constrained.
+ // On one path n == 0, on the other n != 0.
}
int memcmp7 (char *a, size_t x, size_t y, size_t n) {
@@ -411,8 +407,9 @@ void bcopy0 () {
bcopy(src, dst, 4); // no-warning
- if (dst[0] != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ // If we actually model the copy, we can make this known.
+ // The important thing for now is that the old value has been invalidated.
+ clang_analyzer_eval(dst[0] != 0); // expected-warning{{UNKNOWN}}
}
void bcopy1 () {
@@ -428,3 +425,13 @@ void bcopy2 () {
bcopy(src, dst, 4); // expected-warning{{overflow}}
}
+
+void *malloc(size_t);
+void free(void *);
+char radar_11125445_memcopythenlogfirstbyte(const char *input, size_t length) {
+ char *bytes = malloc(sizeof(char) * (length + 1));
+ memcpy(bytes, input, length);
+ char x = bytes[0]; // no warning
+ free(bytes);
+ return x;
+}
diff --git a/test/Analysis/casts.c b/test/Analysis/casts.c
index 8b88a2d..f862ddf 100644
--- a/test/Analysis/casts.c
+++ b/test/Analysis/casts.c
@@ -65,3 +65,11 @@ void pr6013_6035_test(void *p) {
foo = ((long)(p));
(void) foo;
}
+
+// PR12511 and radar://11215362 - Test that we support SymCastExpr, which represents symbolic int to float cast.
+char ttt(int intSeconds) {
+ double seconds = intSeconds;
+ if (seconds)
+ return 0;
+ return 0;
+}
diff --git a/test/Analysis/comparison-implicit-casts.cpp b/test/Analysis/comparison-implicit-casts.cpp
new file mode 100644
index 0000000..df9d870
--- /dev/null
+++ b/test/Analysis/comparison-implicit-casts.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,debug.ExprInspection -analyzer-constraints=basic -triple i386-apple-darwin9 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,debug.ExprInspection -analyzer-constraints=basic -triple x86_64-apple-darwin9 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,debug.ExprInspection -analyzer-constraints=range -triple i386-apple-darwin9 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,debug.ExprInspection -analyzer-constraints=range -triple x86_64-apple-darwin9 -verify %s
+
+// This file runs in C++ mode so that the comparison type is 'bool', not 'int'.
+void clang_analyzer_eval(int);
+typedef typeof(sizeof(int)) size_t;
+
+// PR12206/12510 - When SimpleSValBuilder figures out that a symbol is fully
+// constrained, it should cast the value to the result type in a binary
+// operation...unless the binary operation is a comparison, in which case the
+// two arguments should be the same type, but won't match the result type.
+//
+// This is not directly related to additive folding, but we use SValBuilder's
+// additive folding to tickle the bug. ExprEngine will simplify fully-constrained
+// symbols, so SValBuilder will only see them if they are (a) part of an evaluated
+// SymExpr (e.g. with additive folding) or (b) generated by a checker (e.g.
+// unix.cstring's strlen() modelling).
+void PR12206(int x) {
+ size_t comparisonSize = sizeof(1 == 1);
+
+ // Sanity check. This test is useless if size_t isn't bigger than bool.
+ clang_analyzer_eval(sizeof(size_t) > comparisonSize); // expected-warning{{TRUE}}
+
+ // Build a SymIntExpr, dependent on x.
+ int local = x - 1;
+
+ // Create a value that requires more bits to store than a comparison result.
+ int value = 1;
+ value <<= 8 * comparisonSize;
+ value += 1;
+
+ // Constrain the value of x.
+ if (x != value) return;
+
+ // Constant-folding will turn (local+1) back into the symbol for x.
+ // The point of this dance is to make SValBuilder be responsible for
+ // turning the symbol into a ConcreteInt, rather than ExprEngine.
+
+ // Test relational operators.
+ clang_analyzer_eval((local + 1) >= 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(2 <= (local + 1)); // expected-warning{{TRUE}}
+
+ // Test equality operators.
+ clang_analyzer_eval((local + 1) != 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(1 != (local + 1)); // expected-warning{{TRUE}}
+}
+
+void PR12206_truncation(signed char x) {
+ // Build a SymIntExpr, dependent on x.
+ signed char local = x - 1;
+
+ // Constrain the value of x.
+ if (x != 1) return;
+
+ // Constant-folding will turn (local+1) back into the symbol for x.
+ // The point of this dance is to make SValBuilder be responsible for
+ // turning the symbol into a ConcreteInt, rather than ExprEngine.
+
+ // Construct a value that cannot be represented by 'char',
+ // but that has the same lower bits as x.
+ signed int value = 1 + (1 << 8);
+
+ // Test relational operators.
+ clang_analyzer_eval((local + 1) < value); // expected-warning{{TRUE}}
+ clang_analyzer_eval(value > (local + 1)); // expected-warning{{TRUE}}
+
+ // Test equality operators.
+ clang_analyzer_eval((local + 1) != value); // expected-warning{{TRUE}}
+ clang_analyzer_eval(value != (local + 1)); // expected-warning{{TRUE}}
+}
+
+// This test is insurance in case we significantly change how SymExprs are
+// evaluated.
+size_t strlen(const char *s);
+void PR12206_strlen(const char *x) {
+ size_t comparisonSize = sizeof(1 == 1);
+
+ // Sanity check. This test is useless if size_t isn't bigger than bool.
+ clang_analyzer_eval(sizeof(size_t) > comparisonSize); // expected-warning{{TRUE}}
+
+ // Create a value that requires more bits to store than a comparison result.
+ size_t value = 1UL;
+ value <<= 8 * comparisonSize;
+ value += 1;
+
+ // Constrain the length of x.
+ if (strlen(x) != value) return;
+
+ // Test relational operators.
+ clang_analyzer_eval(strlen(x) >= 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(2 <= strlen(x)); // expected-warning{{TRUE}}
+
+ // Test equality operators.
+ clang_analyzer_eval(strlen(x) != 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(1 != strlen(x)); // expected-warning{{TRUE}}
+}
diff --git a/test/Analysis/constant-folding.c b/test/Analysis/constant-folding.c
index e7a5705..81d1193 100644
--- a/test/Analysis/constant-folding.c
+++ b/test/Analysis/constant-folding.c
@@ -1,7 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s
-// Trigger a warning if the analyzer reaches this point in the control flow.
-#define WARN ((void)*(char*)0)
+void clang_analyzer_eval(int);
// There should be no warnings unless otherwise indicated.
@@ -9,64 +8,71 @@ void testComparisons (int a) {
// Sema can already catch the simple comparison a==a,
// since that's usually a logic error (and not path-dependent).
int b = a;
- if (!(b==a)) WARN; // expected-warning{{never executed}}
- if (!(b>=a)) WARN; // expected-warning{{never executed}}
- if (!(b<=a)) WARN; // expected-warning{{never executed}}
- if (b!=a) WARN; // expected-warning{{never executed}}
- if (b>a) WARN; // expected-warning{{never executed}}
- if (b<a) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval(b == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b >= a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b <= a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b != a); // expected-warning{{FALSE}}
+ clang_analyzer_eval(b > a); // expected-warning{{FALSE}}
+ clang_analyzer_eval(b < a); // expected-warning{{FALSE}}
}
void testSelfOperations (int a) {
- if ((a|a) != a) WARN; // expected-warning{{never executed}}
- if ((a&a) != a) WARN; // expected-warning{{never executed}}
- if ((a^a) != 0) WARN; // expected-warning{{never executed}}
- if ((a-a) != 0) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval((a|a) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a&a) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a^a) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a-a) == 0); // expected-warning{{TRUE}}
}
void testIdempotent (int a) {
- if ((a*1) != a) WARN; // expected-warning{{never executed}}
- if ((a/1) != a) WARN; // expected-warning{{never executed}}
- if ((a+0) != a) WARN; // expected-warning{{never executed}}
- if ((a-0) != a) WARN; // expected-warning{{never executed}}
- if ((a<<0) != a) WARN; // expected-warning{{never executed}}
- if ((a>>0) != a) WARN; // expected-warning{{never executed}}
- if ((a^0) != a) WARN; // expected-warning{{never executed}}
- if ((a&(~0)) != a) WARN; // expected-warning{{never executed}}
- if ((a|0) != a) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval((a*1) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a/1) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a+0) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a-0) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a<<0) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a>>0) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a^0) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a&(~0)) == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a|0) == a); // expected-warning{{TRUE}}
}
void testReductionToConstant (int a) {
- if ((a*0) != 0) WARN; // expected-warning{{never executed}}
- if ((a&0) != 0) WARN; // expected-warning{{never executed}}
- if ((a|(~0)) != (~0)) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval((a*0) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a&0) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval((a|(~0)) == (~0)); // expected-warning{{TRUE}}
}
void testSymmetricIntSymOperations (int a) {
- if ((2+a) != (a+2)) WARN; // expected-warning{{never executed}}
- if ((2*a) != (a*2)) WARN; // expected-warning{{never executed}}
- if ((2&a) != (a&2)) WARN; // expected-warning{{never executed}}
- if ((2^a) != (a^2)) WARN; // expected-warning{{never executed}}
- if ((2|a) != (a|2)) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval((2+a) == (a+2)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((2*a) == (a*2)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((2&a) == (a&2)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((2^a) == (a^2)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((2|a) == (a|2)); // expected-warning{{TRUE}}
}
void testAsymmetricIntSymOperations (int a) {
- if (((~0) >> a) != (~0)) WARN; // expected-warning{{never executed}}
- if ((0 >> a) != 0) WARN; // expected-warning{{never executed}}
- if ((0 << a) != 0) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval(((~0) >> a) == (~0)); // expected-warning{{TRUE}}
+ clang_analyzer_eval((0 >> a) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval((0 << a) == 0); // expected-warning{{TRUE}}
// Unsigned right shift shifts in zeroes.
- if ((((unsigned)(~0)) >> ((unsigned) a)) != ((unsigned)(~0)))
- WARN; // expected-warning{{}}
+ clang_analyzer_eval(((~0U) >> a) != (~0U)); // expected-warning{{UNKNOWN}}
}
void testLocations (char *a) {
char *b = a;
- if (!(b==a)) WARN; // expected-warning{{never executed}}
- if (!(b>=a)) WARN; // expected-warning{{never executed}}
- if (!(b<=a)) WARN; // expected-warning{{never executed}}
- if (b!=a) WARN; // expected-warning{{never executed}}
- if (b>a) WARN; // expected-warning{{never executed}}
- if (b<a) WARN; // expected-warning{{never executed}}
- if (b-a) WARN; // expected-warning{{never executed}}
+ clang_analyzer_eval(b == a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b >= a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b <= a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b != a); // expected-warning{{FALSE}}
+ clang_analyzer_eval(b > a); // expected-warning{{FALSE}}
+ clang_analyzer_eval(b < a); // expected-warning{{FALSE}}
+}
+
+void testMixedTypeComparisons (char a, unsigned long b) {
+ if (a != 0) return;
+ if (b != 0x100) return;
+
+ clang_analyzer_eval(a <= b); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b >= a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a != b); // expected-warning{{TRUE}}
}
diff --git a/test/Analysis/coverage.c b/test/Analysis/coverage.c
index 73d78da..8116913 100644
--- a/test/Analysis/coverage.c
+++ b/test/Analysis/coverage.c
@@ -92,3 +92,11 @@ void coverage9(int *x) {
function_which_gives_up_settonull(&x);
y = (*x); // no warning
}
+
+static void empty_function(){
+}
+int use_empty_function(int x) {
+ x = 0;
+ empty_function();
+ return 5/x; //expected-warning {{Division by zero}}
+}
diff --git a/test/Analysis/cstring-syntax.c b/test/Analysis/cstring-syntax.c
index 64ecb67..4aa88ed 100644
--- a/test/Analysis/cstring-syntax.c
+++ b/test/Analysis/cstring-syntax.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=unix.cstring.BadSizeArg -analyzer-store=region -Wno-strlcpy-strlcat-size -Wno-sizeof-array-argument -Wno-sizeof-pointer-memaccess -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=unix.cstring.BadSizeArg -analyzer-store=region -Wno-strncat-size -Wno-strlcpy-strlcat-size -Wno-sizeof-array-argument -Wno-sizeof-pointer-memaccess -verify %s
typedef __SIZE_TYPE__ size_t;
char *strncat(char *, const char *, size_t);
diff --git a/test/Analysis/ctor-inlining.mm b/test/Analysis/ctor-inlining.mm
new file mode 100644
index 0000000..54d51b4
--- /dev/null
+++ b/test/Analysis/ctor-inlining.mm
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -fobjc-arc -cfg-add-implicit-dtors -Wno-null-dereference -verify %s
+
+void clang_analyzer_eval(bool);
+
+struct Wrapper {
+ __strong id obj;
+};
+
+void test() {
+ Wrapper w;
+ // force a diagnostic
+ *(char *)0 = 1; // expected-warning{{Dereference of null pointer}}
+}
+
+
+struct IntWrapper {
+ int x;
+};
+
+void testCopyConstructor() {
+ IntWrapper a;
+ a.x = 42;
+
+ IntWrapper b(a);
+ clang_analyzer_eval(b.x == 42); // expected-warning{{TRUE}}
+}
+
+struct NonPODIntWrapper {
+ int x;
+
+ virtual int get();
+};
+
+void testNonPODCopyConstructor() {
+ NonPODIntWrapper a;
+ a.x = 42;
+
+ NonPODIntWrapper b(a);
+ clang_analyzer_eval(b.x == 42); // expected-warning{{TRUE}}
+}
+
diff --git a/test/Analysis/cxx-crashes.cpp b/test/Analysis/cxx-crashes.cpp
index 17fc74d..8912bf6 100644
--- a/test/Analysis/cxx-crashes.cpp
+++ b/test/Analysis/cxx-crashes.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(bool);
int f1(char *dst) {
char *p = dst + 4;
@@ -54,3 +56,22 @@ struct C {
void C::f() { }
}
+
+
+void vla(int n) {
+ int nums[n];
+ nums[0] = 1;
+ clang_analyzer_eval(nums[0] == 1); // expected-warning{{TRUE}}
+
+ // This used to fail with MallocChecker on, and /only/ in C++ mode.
+ // This struct is POD, though, so it should be fine to put it in a VLA.
+ struct { int x; } structs[n];
+ structs[0].x = 1;
+ clang_analyzer_eval(structs[0].x == 1); // expected-warning{{TRUE}}
+}
+
+void useIntArray(int []);
+void testIntArrayLiteral() {
+ useIntArray((int []){ 1, 2, 3 });
+}
+
diff --git a/test/Analysis/cxx-for-range-cfg.cpp b/test/Analysis/cxx-for-range-cfg.cpp
new file mode 100644
index 0000000..e258c7a
--- /dev/null
+++ b/test/Analysis/cxx-for-range-cfg.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -Wall -fsyntax-only %s -std=c++11 -verify
+
+// The rdar11671507_vector<int *>[]> would previously crash CFG construction
+// because of the temporary array of vectors.
+template <typename T>
+class rdar11671507_vector {
+public:
+ rdar11671507_vector();
+ ~rdar11671507_vector();
+ T *Base;
+ T *End;
+};
+
+void rdar11671507(rdar11671507_vector<int*> v, rdar11671507_vector<int*> w) {
+ for (auto &vec : (rdar11671507_vector<int *>[]){ v, w }) {} // expected-warning {{unused}}
+}
diff --git a/test/Analysis/cxx-method-names.cpp b/test/Analysis/cxx-method-names.cpp
new file mode 100644
index 0000000..8afbb85
--- /dev/null
+++ b/test/Analysis/cxx-method-names.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix,osx,experimental.unix,experimental.security.taint -analyzer-store region -verify %s
+
+class Evil {
+public:
+ void system(int); // taint checker
+ void malloc(void *); // taint checker, malloc checker
+ void free(); // malloc checker, keychain checker
+ void fopen(); // stream checker
+ void feof(int, int); // stream checker
+ void open(); // unix api checker
+};
+
+void test(Evil &E) {
+ // no warnings, no crashes
+ E.system(0);
+ E.malloc(0);
+ E.free();
+ E.fopen();
+ E.feof(0,1);
+ E.open();
+}
diff --git a/test/Analysis/cxx11-crashes.cpp b/test/Analysis/cxx11-crashes.cpp
new file mode 100644
index 0000000..16bfc89
--- /dev/null
+++ b/test/Analysis/cxx11-crashes.cpp
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -std=c++11 -verify %s
+
+// radar://11485149, PR12871
+class PlotPoint {
+ bool valid;
+};
+
+PlotPoint limitedFit () {
+ PlotPoint fit0;
+ fit0 = limitedFit ();
+ return fit0;
+}
+
+// radar://11487541, NamespaceAlias
+namespace boost {namespace filesystem3 {
+class path {
+public:
+ path(){}
+};
+
+}}
+namespace boost
+{
+ namespace filesystem
+ {
+ using filesystem3::path;
+ }
+}
+
+void radar11487541() {
+ namespace fs = boost::filesystem;
+ fs::path p;
+}
+
+// PR12873 radar://11499139
+void testFloatInitializer() {
+ const float ysize={0.015}, xsize={0.01};
+}
+
+
+// PR12874, radar://11487525
+template<class T> struct addr_impl_ref {
+ T & v_;
+ inline addr_impl_ref( T & v ): v_( v ) {
+ }
+ inline operator T& () const {return v_;}
+};
+template<class T> struct addressof_impl {
+ static inline T * f( T & v, long ) {
+ return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
+ }
+};
+template<class T> T * addressof( T & v ) {
+ return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
+}
+void testRadar11487525_1(){
+ bool s[25];
+ addressof(s);
+}
+
+// radar://11487525 Don't crash on CK_LValueBitCast.
+bool begin(double *it) {
+ typedef bool type[25];
+ bool *a = reinterpret_cast<type &>(*( reinterpret_cast<char *>( it )));
+ return *a;
+}
diff --git a/test/Analysis/dead-stores.m b/test/Analysis/dead-stores.m
index 0834274..fe56554 100644
--- a/test/Analysis/dead-stores.m
+++ b/test/Analysis/dead-stores.m
@@ -88,3 +88,23 @@ void rdar10591355() {
RDar10591355 *p = rdar10591355_aux();
^{ (void) p.x; }();
}
+
+@interface Radar11059352_1 {
+@private
+ int *_pathString;
+}
+@property int *pathString;
+@end
+@interface Radar11059352 {
+@private
+Radar11059352_1 *_Path;
+}
+@end
+@implementation Radar11059352
+
+- (int*)usePath {
+ Radar11059352_1 *xxxxx = _Path; // no warning
+ int *wp = xxxxx.pathString;
+ return wp;
+}
+@end
diff --git a/test/Analysis/delegates.m b/test/Analysis/delegates.m
index 8f42b83..7fc4f2b 100644
--- a/test/Analysis/delegates.m
+++ b/test/Analysis/delegates.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-store=region -Wno-objc-root-class -verify %s
//===----------------------------------------------------------------------===//
@@ -96,13 +96,12 @@ extern void *_NSConstantStringClassReference;
@implementation test_6062730
- (void) foo {
- NSString *str = [[NSString alloc] init];
+ NSString *str = [[NSString alloc] init]; // no-warning
[test_6062730 performSelectorOnMainThread:@selector(postNotification:) withObject:str waitUntilDone:1];
}
- (void) bar {
- NSString *str = [[NSString alloc] init]; // expected-warning{{leak}}
- // FIXME: We need to resolve [self class] to 'test_6062730'.
+ NSString *str = [[NSString alloc] init]; // no-warning
[[self class] performSelectorOnMainThread:@selector(postNotification:) withObject:str waitUntilDone:1];
}
@@ -111,3 +110,21 @@ extern void *_NSConstantStringClassReference;
}
@end
+
+@interface ObjectThatRequiresDelegate : NSObject
+- (id)initWithDelegate:(id)delegate;
+- (id)initWithNumber:(int)num delegate:(id)delegate;
+@end
+
+
+@interface DelegateRequirerTest
+@end
+@implementation DelegateRequirerTest
+
+- (void)test {
+ (void)[[ObjectThatRequiresDelegate alloc] initWithDelegate:self];
+ (void)[[ObjectThatRequiresDelegate alloc] initWithNumber:0 delegate:self];
+ // no leak warnings -- these objects could be released in callback methods
+}
+
+@end
diff --git a/test/Analysis/derived-to-base.cpp b/test/Analysis/derived-to-base.cpp
index f65b9db..f833678 100644
--- a/test/Analysis/derived-to-base.cpp
+++ b/test/Analysis/derived-to-base.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify %s
+
+void clang_analyzer_eval(bool);
class A {
protected:
@@ -13,3 +15,123 @@ public:
void B::f() {
x = 3;
}
+
+
+class C : public B {
+public:
+ void g() {
+ // This used to crash because we are upcasting through two bases.
+ x = 5;
+ }
+};
+
+
+namespace VirtualBaseClasses {
+ class A {
+ protected:
+ int x;
+ };
+
+ class B : public virtual A {
+ public:
+ int getX() { return x; }
+ };
+
+ class C : public virtual A {
+ public:
+ void setX() { x = 42; }
+ };
+
+ class D : public B, public C {};
+ class DV : virtual public B, public C {};
+ class DV2 : public B, virtual public C {};
+
+ void test() {
+ D d;
+ d.setX();
+ clang_analyzer_eval(d.getX() == 42); // expected-warning{{TRUE}}
+
+ DV dv;
+ dv.setX();
+ clang_analyzer_eval(dv.getX() == 42); // expected-warning{{TRUE}}
+
+ DV2 dv2;
+ dv2.setX();
+ clang_analyzer_eval(dv2.getX() == 42); // expected-warning{{TRUE}}
+ }
+
+
+ // Make sure we're consistent about the offset of the A subobject within an
+ // Intermediate virtual base class.
+ class Padding1 { int unused; };
+ class Padding2 { int unused; };
+ class Intermediate : public Padding1, public A, public Padding2 {};
+
+ class BI : public virtual Intermediate {
+ public:
+ int getX() { return x; }
+ };
+
+ class CI : public virtual Intermediate {
+ public:
+ void setX() { x = 42; }
+ };
+
+ class DI : public BI, public CI {};
+
+ void testIntermediate() {
+ DI d;
+ d.setX();
+ clang_analyzer_eval(d.getX() == 42); // expected-warning{{TRUE}}
+ }
+}
+
+
+namespace DynamicVirtualUpcast {
+ class A {
+ public:
+ virtual ~A();
+ };
+
+ class B : virtual public A {};
+ class C : virtual public B {};
+ class D : virtual public C {};
+
+ bool testCast(A *a) {
+ return dynamic_cast<B*>(a) && dynamic_cast<C*>(a);
+ }
+
+ void test() {
+ D d;
+ clang_analyzer_eval(testCast(&d)); // expected-warning{{TRUE}}
+ }
+}
+
+namespace DynamicMultipleInheritanceUpcast {
+ class B {
+ public:
+ virtual ~B();
+ };
+ class C {
+ public:
+ virtual ~C();
+ };
+ class D : public B, public C {};
+
+ bool testCast(B *a) {
+ return dynamic_cast<C*>(a);
+ }
+
+ void test() {
+ D d;
+ clang_analyzer_eval(testCast(&d)); // expected-warning{{TRUE}}
+ }
+
+
+ class DV : virtual public B, virtual public C {};
+
+ void testVirtual() {
+ DV d;
+ clang_analyzer_eval(testCast(&d)); // expected-warning{{TRUE}}
+ }
+}
diff --git a/test/Analysis/diagnostics/undef-value-callee.h b/test/Analysis/diagnostics/undef-value-callee.h
new file mode 100644
index 0000000..ea48c46
--- /dev/null
+++ b/test/Analysis/diagnostics/undef-value-callee.h
@@ -0,0 +1,4 @@
+
+void callee() {
+ ;
+}
diff --git a/test/Analysis/diagnostics/undef-value-caller.c b/test/Analysis/diagnostics/undef-value-caller.c
new file mode 100644
index 0000000..928839d
--- /dev/null
+++ b/test/Analysis/diagnostics/undef-value-caller.c
@@ -0,0 +1,238 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-output=plist -o %t %s
+// RUN: FileCheck --input-file %t %s
+
+#include "undef-value-callee.h"
+
+// This code used to cause a crash since we were not adding fileID of the header to the plist diagnostic.
+// Note, in the future, we do not even need to step into this callee since it does not influence the result.
+int test_calling_unimportant_callee(int argc, char *argv[]) {
+ int x;
+ callee();
+ return x; // expected-warning {{Undefined or garbage value returned to caller}}
+}
+
+// CHECK: <?xml version="1.0" encoding="UTF-8"?>
+// CHECK: <plist version="1.0">
+// CHECK: <dict>
+// CHECK: <key>files</key>
+// CHECK: <array>
+// CHECK: </array>
+// CHECK: <key>diagnostics</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;x&apos; declared without an initial value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;x&apos; declared without an initial value</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;callee&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;callee&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>2</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>1</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;test_calling_unimportant_callee&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;test_calling_unimportant_callee&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Returning from &apos;callee&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Returning from &apos;callee&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>11</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>11</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>11</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>11</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>11</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Undefined or garbage value returned to caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Undefined or garbage value returned to caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Undefined or garbage value returned to caller</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Garbage return value</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_calling_unimportant_callee</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>11</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </plist>
diff --git a/test/Analysis/domtest.c b/test/Analysis/domtest.c
index 245186a..0f370db 100644
--- a/test/Analysis/domtest.c
+++ b/test/Analysis/domtest.c
@@ -1,4 +1,6 @@
-// RUN: %clang -cc1 -analyze -analyzer-checker=debug.DumpDominators %s 2>&1 | FileCheck %s
+// RUN: rm -f %t
+// RUN: %clang -cc1 -analyze -analyzer-checker=debug.DumpDominators %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
// Test the DominatorsTree implementation with various control flows
int test1()
@@ -24,13 +26,13 @@ int test1()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
-// CHECK: (1,2)
-// CHECK: (2,8)
-// CHECK: (3,4)
-// CHECK: (4,7)
-// CHECK: (5,7)
+// CHECK: (1,7)
+// CHECK: (2,3)
+// CHECK: (3,6)
+// CHECK: (4,6)
+// CHECK: (5,6)
// CHECK: (6,7)
-// CHECK: (7,2)
+// CHECK: (7,8)
// CHECK: (8,9)
// CHECK: (9,9)
@@ -55,9 +57,9 @@ int test2()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
// CHECK: (1,6)
-// CHECK: (2,6)
+// CHECK: (2,3)
// CHECK: (3,4)
-// CHECK: (4,2)
+// CHECK: (4,6)
// CHECK: (5,6)
// CHECK: (6,7)
// CHECK: (7,7)
@@ -83,11 +85,11 @@ int test3()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
// CHECK: (1,7)
-// CHECK: (2,7)
+// CHECK: (2,5)
// CHECK: (3,4)
-// CHECK: (4,2)
+// CHECK: (4,5)
// CHECK: (5,6)
-// CHECK: (6,4)
+// CHECK: (6,7)
// CHECK: (7,8)
// CHECK: (8,8)
@@ -108,16 +110,16 @@ int test4()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
-// CHECK: (1,2)
-// CHECK: (2,11)
-// CHECK: (3,10)
-// CHECK: (4,10)
-// CHECK: (5,6)
-// CHECK: (6,4)
-// CHECK: (7,10)
+// CHECK: (1,10)
+// CHECK: (2,9)
+// CHECK: (3,4)
+// CHECK: (4,5)
+// CHECK: (5,9)
+// CHECK: (6,7)
+// CHECK: (7,8)
// CHECK: (8,9)
-// CHECK: (9,7)
-// CHECK: (10,2)
+// CHECK: (9,10)
+// CHECK: (10,11)
// CHECK: (11,12)
// CHECK: (12,12)
@@ -163,3 +165,4 @@ int test5()
// CHECK: (10,11)
// CHECK: (11,11)
+
diff --git a/test/Analysis/dtor.cpp b/test/Analysis/dtor.cpp
index 8d63cc4..6209948 100644
--- a/test/Analysis/dtor.cpp
+++ b/test/Analysis/dtor.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region -analyzer-ipa=inlining -cfg-add-implicit-dtors -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store region -analyzer-ipa=inlining -cfg-add-implicit-dtors -Wno-null-dereference -verify %s
+
+void clang_analyzer_eval(bool);
class A {
public:
@@ -11,3 +13,163 @@ public:
int main() {
A a;
}
+
+
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void free(void *);
+
+class SmartPointer {
+ void *X;
+public:
+ SmartPointer(void *x) : X(x) {}
+ ~SmartPointer() {
+ free(X);
+ }
+};
+
+void testSmartPointer() {
+ char *mem = (char*)malloc(4);
+ {
+ SmartPointer Deleter(mem);
+ // destructor called here
+ }
+ *mem = 0; // expected-warning{{Use of memory after it is freed}}
+}
+
+
+void doSomething();
+void testSmartPointer2() {
+ char *mem = (char*)malloc(4);
+ {
+ SmartPointer Deleter(mem);
+ // Remove dead bindings...
+ doSomething();
+ // destructor called here
+ }
+ *mem = 0; // expected-warning{{Use of memory after it is freed}}
+}
+
+
+class Subclass : public SmartPointer {
+public:
+ Subclass(void *x) : SmartPointer(x) {}
+};
+
+void testSubclassSmartPointer() {
+ char *mem = (char*)malloc(4);
+ {
+ Subclass Deleter(mem);
+ // Remove dead bindings...
+ doSomething();
+ // destructor called here
+ }
+ *mem = 0; // expected-warning{{Use of memory after it is freed}}
+}
+
+
+class MultipleInheritance : public Subclass, public SmartPointer {
+public:
+ MultipleInheritance(void *a, void *b) : Subclass(a), SmartPointer(b) {}
+};
+
+void testMultipleInheritance1() {
+ char *mem = (char*)malloc(4);
+ {
+ MultipleInheritance Deleter(mem, 0);
+ // Remove dead bindings...
+ doSomething();
+ // destructor called here
+ }
+ *mem = 0; // expected-warning{{Use of memory after it is freed}}
+}
+
+void testMultipleInheritance2() {
+ char *mem = (char*)malloc(4);
+ {
+ MultipleInheritance Deleter(0, mem);
+ // Remove dead bindings...
+ doSomething();
+ // destructor called here
+ }
+ *mem = 0; // expected-warning{{Use of memory after it is freed}}
+}
+
+void testMultipleInheritance3() {
+ char *mem = (char*)malloc(4);
+ {
+ MultipleInheritance Deleter(mem, mem);
+ // Remove dead bindings...
+ doSomething();
+ // destructor called here
+ // expected-warning@27 {{Attempt to free released memory}}
+ }
+}
+
+
+class SmartPointerMember {
+ SmartPointer P;
+public:
+ SmartPointerMember(void *x) : P(x) {}
+};
+
+void testSmartPointerMember() {
+ char *mem = (char*)malloc(4);
+ {
+ SmartPointerMember Deleter(mem);
+ // Remove dead bindings...
+ doSomething();
+ // destructor called here
+ }
+ *mem = 0; // expected-warning{{Use of memory after it is freed}}
+}
+
+
+struct IntWrapper {
+ IntWrapper() : x(0) {}
+ ~IntWrapper();
+ int *x;
+};
+
+void testArrayInvalidation() {
+ int i = 42;
+ int j = 42;
+
+ {
+ IntWrapper arr[2];
+
+ // There should be no undefined value warnings here.
+ // Eventually these should be TRUE as well, but right now
+ // we can't handle array constructors.
+ clang_analyzer_eval(arr[0].x == 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(arr[1].x == 0); // expected-warning{{UNKNOWN}}
+
+ arr[0].x = &i;
+ arr[1].x = &j;
+ clang_analyzer_eval(*arr[0].x == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(*arr[1].x == 42); // expected-warning{{TRUE}}
+ }
+
+ // The destructors should have invalidated i and j.
+ clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}}
+}
+
+
+
+// Don't crash on a default argument inside an initializer.
+struct DefaultArg {
+ DefaultArg(int x = 0) {}
+ ~DefaultArg();
+};
+
+struct InheritsDefaultArg : DefaultArg {
+ InheritsDefaultArg() {}
+ virtual ~InheritsDefaultArg();
+};
+
+void testDefaultArg() {
+ InheritsDefaultArg a;
+ // Force a bug to be emitted.
+ *(char *)0 = 1; // expected-warning{{Dereference of null pointer}}
+}
diff --git a/test/Analysis/dynamic-cast.cpp b/test/Analysis/dynamic-cast.cpp
index 8e63b2b..b1133ac 100644
--- a/test/Analysis/dynamic-cast.cpp
+++ b/test/Analysis/dynamic-cast.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core -verify %s
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=none -verify %s
+
+void clang_analyzer_eval(bool);
class A {
public:
@@ -196,6 +198,9 @@ int testDynCastMostLikelyWillFail(C *c) {
} else {
res = 0;
}
+
+ // Note: IPA is turned off for this test because the code below shows how the
+ // dynamic_cast could succeed.
return *res; // expected-warning{{Dereference of null pointer}}
}
@@ -205,7 +210,25 @@ void callTestDynCastMostLikelyWillFail() {
testDynCastMostLikelyWillFail(&m);
}
+
+void testDynCastToMiddleClass () {
+ class BBB : public BB {};
+ BBB obj;
+ A &ref = obj;
+
+ // These didn't always correctly layer base regions.
+ B *ptr = dynamic_cast<B*>(&ref);
+ clang_analyzer_eval(ptr != 0); // expected-warning{{TRUE}}
+
+ // This is actually statically resolved to be a DerivedToBase cast.
+ ptr = dynamic_cast<B*>(&obj);
+ clang_analyzer_eval(ptr != 0); // expected-warning{{TRUE}}
+}
+
+
+// -----------------------------
// False positives/negatives.
+// -----------------------------
// Due to symbolic regions not being typed.
int testDynCastFalsePositive(BB *c) {
diff --git a/test/Analysis/engine/replay-without-inlining.c b/test/Analysis/engine/replay-without-inlining.c
new file mode 100644
index 0000000..9ec2d08
--- /dev/null
+++ b/test/Analysis/engine/replay-without-inlining.c
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -verify %s
+
+typedef struct {
+ char I[4];
+ int S;
+} Hdr;
+typedef struct {
+ short w;
+} Hdr2;
+typedef struct {
+ Hdr2 usedtobeundef;
+} Info;
+typedef struct {
+ const unsigned char *ib;
+ int cur;
+ int end;
+} IB;
+inline unsigned long gl(IB *input);
+inline void gbs(IB *input, unsigned char *buf, int count);
+void getB(IB *st, Hdr2 *usedtobeundef);
+inline unsigned char gb(IB *input) {
+ if (input->cur + 1 > input->end)
+ ;
+ return input->ib[(input->cur)++];
+}
+static void getID(IB *st, char str[4]) {
+ str[0] = gb(st);
+ str[1] = gb(st);
+ str[2] = gb(st);
+ str[3] = gb(st);
+}
+static void getH(IB *st, Hdr *header) {
+ getID (st, header->I);
+ header->S = gl(st);
+}
+static void readILBM(IB *st, Info *pic) {
+ // Initialize field;
+ pic->usedtobeundef.w = 5;
+
+ // Time out in the function so that we will be forced to retry with no inlining.
+ Hdr header;
+ getH (st, &header);
+ getID(st, header.I);
+ int i = 0;
+ while (st->cur < st->end && i < 4) {
+ i++;
+ getH (st, &header);
+ }
+}
+int bitmapImageRepFromIFF(IB st, const unsigned char *ib, int il) {
+ Info pic;
+ st.ib = ib;
+ st.cur = 0;
+ st.end = il;
+ readILBM(&st,&pic);
+ return pic.usedtobeundef.w; // No undefined value warning here.
+}
diff --git a/test/Analysis/global-region-invalidation.c b/test/Analysis/global-region-invalidation.c
index 184ffb8..71a7285 100644
--- a/test/Analysis/global-region-invalidation.c
+++ b/test/Analysis/global-region-invalidation.c
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -disable-free -analyzer-eagerly-assume -analyzer-checker=core,deadcode,experimental.security.taint,debug.TaintTest -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -disable-free -analyzer-eagerly-assume -analyzer-checker=core,deadcode,experimental.security.taint,debug.TaintTest,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(int);
// Note, we do need to include headers here, since the analyzer checks if the function declaration is located in a system header.
#include "system-header-simulator.h"
@@ -73,3 +75,12 @@ int constIntGlobExtern() {
}
return 0;
}
+
+void testAnalyzerEvalIsPure() {
+ extern int someGlobal;
+ if (someGlobal == 0) {
+ clang_analyzer_eval(someGlobal == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(someGlobal == 0); // expected-warning{{TRUE}}
+ }
+}
+
diff --git a/test/Analysis/html-diags.c b/test/Analysis/html-diags.c
index 59d81a5..b9361f7 100644
--- a/test/Analysis/html-diags.c
+++ b/test/Analysis/html-diags.c
@@ -1,5 +1,5 @@
// RUN: mkdir %t.dir
-// RUN: %clang_cc1 -analyze -analyzer-output=html -analyzer-checker=core -o %T.dir %s
+// RUN: %clang_cc1 -analyze -analyzer-output=html -analyzer-checker=core -o %t.dir %s
// RUN: rm -fR %t.dir
// Currently this test mainly checks that the HTML diagnostics doesn't crash
diff --git a/test/Analysis/initializer.cpp b/test/Analysis/initializer.cpp
index 656a8bf..d43c8cf 100644
--- a/test/Analysis/initializer.cpp
+++ b/test/Analysis/initializer.cpp
@@ -1,4 +1,8 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region -cfg-add-initializers -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store region -cfg-add-implicit-dtors -std=c++11 -verify %s
+
+// We don't inline constructors unless we have destructors turned on.
+
+void clang_analyzer_eval(bool);
class A {
int x;
@@ -7,8 +11,68 @@ public:
};
A::A() : x(0) {
- if (x != 0) {
- int *p = 0;
- *p = 0; // no-warning
- }
+ clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
+}
+
+
+class DirectMember {
+ int x;
+public:
+ DirectMember(int value) : x(value) {}
+
+ int getX() { return x; }
+};
+
+void testDirectMember() {
+ DirectMember obj(3);
+ clang_analyzer_eval(obj.getX() == 3); // expected-warning{{TRUE}}
+}
+
+
+class IndirectMember {
+ struct {
+ int x;
+ };
+public:
+ IndirectMember(int value) : x(value) {}
+
+ int getX() { return x; }
+};
+
+void testIndirectMember() {
+ IndirectMember obj(3);
+ clang_analyzer_eval(obj.getX() == 3); // expected-warning{{TRUE}}
+}
+
+
+struct DelegatingConstructor {
+ int x;
+ DelegatingConstructor(int y) { x = y; }
+ DelegatingConstructor() : DelegatingConstructor(42) {}
+};
+
+void testDelegatingConstructor() {
+ DelegatingConstructor obj;
+ clang_analyzer_eval(obj.x == 42); // expected-warning{{TRUE}}
+}
+
+
+// ------------------------------------
+// False negatives
+// ------------------------------------
+
+struct RefWrapper {
+ RefWrapper(int *p) : x(*p) {}
+ RefWrapper(int &r) : x(r) {}
+ int &x;
+};
+
+void testReferenceMember() {
+ int *p = 0;
+ RefWrapper X(p); // should warn in the constructor
+}
+
+void testReferenceMember2() {
+ int *p = 0;
+ RefWrapper X(*p); // should warn here
}
diff --git a/test/Analysis/initializers-cfg-output.cpp b/test/Analysis/initializers-cfg-output.cpp
index 8a7a3f5..8aaa94c 100644
--- a/test/Analysis/initializers-cfg-output.cpp
+++ b/test/Analysis/initializers-cfg-output.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -cfg-add-initializers %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG %s 2>&1 | FileCheck %s
// XPASS: *
class A {
diff --git a/test/Analysis/inline-plist.c b/test/Analysis/inline-plist.c
index 549082d..1523e82 100644
--- a/test/Analysis/inline-plist.c
+++ b/test/Analysis/inline-plist.c
@@ -1,4 +1,4 @@
-// RUN: %clang --analyze %s -Xclang -analyzer-ipa=inlining -o %t
+// RUN: %clang --analyze %s -Xclang -analyzer-ipa=inlining -fblocks -o %t
// RUN: FileCheck -input-file %t %s
// <rdar://problem/10967815>
@@ -23,6 +23,45 @@ void test_has_bug() {
has_bug(0);
}
+void triggers_bug(int *p) {
+ *p = 0xDEADBEEF;
+}
+
+// This function triggers a bug by calling triggers_bug(). The diagnostics
+// should show when p is assumed to be null.
+void bar(int *p) {
+ if (!!p)
+ return;
+
+ if (p == 0)
+ triggers_bug(p);
+}
+
+// ========================================================================== //
+// Test inlining of blocks.
+// ========================================================================== //
+
+void test_block__capture_null() {
+ int *p = 0;
+ ^(){ *p = 1; }();
+}
+
+void test_block_ret() {
+ int *p = ^(){ int *q = 0; return q; }();
+ *p = 1;
+}
+
+void test_block_blockvar() {
+ __block int *p;
+ ^(){ p = 0; }();
+ *p = 1;
+}
+
+void test_block_arg() {
+ int *p;
+ ^(int **q){ *q = 0; }(&p);
+ *p = 1;
+}
// CHECK: <?xml version="1.0" encoding="UTF-8"?>
// CHECK: <plist version="1.0">
@@ -49,7 +88,7 @@ void test_has_bug() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>11</integer>
-// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -62,7 +101,7 @@ void test_has_bug() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>12</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -83,7 +122,7 @@ void test_has_bug() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>12</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -159,7 +198,7 @@ void test_has_bug() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>14</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -180,7 +219,7 @@ void test_has_bug() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>14</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -235,6 +274,7 @@ void test_has_bug() {
// CHECK: <key>type</key><string>Division by zero</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>foo</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>14</integer>
@@ -302,7 +342,7 @@ void test_has_bug() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>18</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -357,6 +397,7 @@ void test_has_bug() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>has_bug</string>
+// CHECK: <key>issue_hash</key><integer>1</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>19</integer>
@@ -364,6 +405,785 @@ void test_has_bug() {
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>col</key><integer>16</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;triggers_bug&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;triggers_bug&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>26</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;bar&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;bar&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>26</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>26</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>27</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>27</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>27</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>27</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>27</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>triggers_bug</string>
+// CHECK: <key>issue_hash</key><integer>1</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>27</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling anonymous block</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling anonymous block</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;test_block__capture_null&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;test_block__capture_null&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_block_ret</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_block_blockvar</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>61</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>62</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>62</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>62</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>62</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_block_arg</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </plist>
diff --git a/test/Analysis/inline-unique-reports.c b/test/Analysis/inline-unique-reports.c
index ae94267..9248ad2 100644
--- a/test/Analysis/inline-unique-reports.c
+++ b/test/Analysis/inline-unique-reports.c
@@ -34,26 +34,26 @@ void test_bug_2() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>line</key><integer>14</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>9</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>14</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>line</key><integer>15</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>10</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>15</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -64,7 +64,7 @@ void test_bug_2() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>line</key><integer>15</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -72,12 +72,12 @@ void test_bug_2() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>line</key><integer>15</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>line</key><integer>15</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -99,9 +99,9 @@ void test_bug_2() {
// CHECK: </dict>
// CHECK: <key>depth</key><integer>1</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Entered call from &apos;test_bug_1&apos;</string>
+// CHECK: <string>Entered call from &apos;test_bug_2&apos;</string>
// CHECK: <key>message</key>
-// CHECK: <string>Entered call from &apos;test_bug_1&apos;</string>
+// CHECK: <string>Entered call from &apos;test_bug_2&apos;</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
@@ -117,7 +117,7 @@ void test_bug_2() {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>4</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -172,6 +172,7 @@ void test_bug_2() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>bug</string>
+// CHECK: <key>issue_hash</key><integer>1</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>5</integer>
@@ -182,3 +183,4 @@ void test_bug_2() {
// CHECK: </array>
// CHECK: </dict>
// CHECK: </plist>
+
diff --git a/test/Analysis/inline.c b/test/Analysis/inline.c
index 0827d93..944e1e2 100644
--- a/test/Analysis/inline.c
+++ b/test/Analysis/inline.c
@@ -1,8 +1,12 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-store region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -analyzer-store region -verify %s
+
+void clang_analyzer_eval(int);
+void clang_analyzer_checkInlined(int);
int test1_f1() {
int y = 1;
y++;
+ clang_analyzer_checkInlined(1); // expected-warning{{TRUE}}
return y;
}
@@ -90,3 +94,19 @@ int test_rdar10977037() {
}
+// Test inlining a forward-declared function.
+// This regressed when CallEvent was first introduced.
+int plus1(int x);
+void test() {
+ clang_analyzer_eval(plus1(2) == 3); // expected-warning{{TRUE}}
+}
+
+int plus1(int x) {
+ return x + 1;
+}
+
+
+void never_called_by_anyone() {
+ clang_analyzer_checkInlined(0); // no-warning
+}
+
diff --git a/test/Analysis/inline.cpp b/test/Analysis/inline.cpp
new file mode 100644
index 0000000..4eaed9f
--- /dev/null
+++ b/test/Analysis/inline.cpp
@@ -0,0 +1,168 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_checkInlined(bool);
+
+class A {
+public:
+ int getZero() { return 0; }
+ virtual int getNum() { return 0; }
+};
+
+void test(A &a) {
+ clang_analyzer_eval(a.getZero() == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.getNum() == 0); // expected-warning{{UNKNOWN}}
+
+ A copy(a);
+ clang_analyzer_eval(copy.getZero() == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(copy.getNum() == 0); // expected-warning{{TRUE}}
+}
+
+
+class One : public A {
+public:
+ virtual int getNum() { return 1; }
+};
+
+void testPathSensitivity(int x) {
+ A a;
+ One b;
+
+ A *ptr;
+ switch (x) {
+ case 0:
+ ptr = &a;
+ break;
+ case 1:
+ ptr = &b;
+ break;
+ default:
+ return;
+ }
+
+ // This should be true on both branches.
+ clang_analyzer_eval(ptr->getNum() == x); // expected-warning {{TRUE}}
+}
+
+
+namespace PureVirtualParent {
+ class Parent {
+ public:
+ virtual int pureVirtual() const = 0;
+ int callVirtual() const {
+ return pureVirtual();
+ }
+ };
+
+ class Child : public Parent {
+ public:
+ virtual int pureVirtual() const {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ return 42;
+ }
+ };
+
+ void testVirtual() {
+ Child x;
+
+ clang_analyzer_eval(x.pureVirtual() == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(x.callVirtual() == 42); // expected-warning{{TRUE}}
+ }
+}
+
+
+namespace PR13569 {
+ class Parent {
+ protected:
+ int m_parent;
+ virtual int impl() const = 0;
+
+ Parent() : m_parent(0) {}
+
+ public:
+ int interface() const {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ return impl();
+ }
+ };
+
+ class Child : public Parent {
+ protected:
+ virtual int impl() const {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ return m_parent + m_child;
+ }
+
+ public:
+ Child() : m_child(0) {}
+
+ int m_child;
+ };
+
+ void testVirtual() {
+ Child x;
+ x.m_child = 42;
+
+ // Don't crash when inlining and devirtualizing.
+ x.interface();
+ }
+
+
+ class Grandchild : public Child {};
+
+ void testDevirtualizeToMiddle() {
+ Grandchild x;
+ x.m_child = 42;
+
+ // Don't crash when inlining and devirtualizing.
+ x.interface();
+ }
+}
+
+namespace PR13569_virtual {
+ class Parent {
+ protected:
+ int m_parent;
+ virtual int impl() const = 0;
+
+ Parent() : m_parent(0) {}
+
+ public:
+ int interface() const {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ return impl();
+ }
+ };
+
+ class Child : virtual public Parent {
+ protected:
+ virtual int impl() const {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ return m_parent + m_child;
+ }
+
+ public:
+ Child() : m_child(0) {}
+
+ int m_child;
+ };
+
+ void testVirtual() {
+ Child x;
+ x.m_child = 42;
+
+ // Don't crash when inlining and devirtualizing.
+ x.interface();
+ }
+
+
+ class Grandchild : virtual public Child {};
+
+ void testDevirtualizeToMiddle() {
+ Grandchild x;
+ x.m_child = 42;
+
+ // Don't crash when inlining and devirtualizing.
+ x.interface();
+ }
+}
diff --git a/test/Analysis/inlining/DynDispatchBifurcate.m b/test/Analysis/inlining/DynDispatchBifurcate.m
new file mode 100644
index 0000000..e78b90b
--- /dev/null
+++ b/test/Analysis/inlining/DynDispatchBifurcate.m
@@ -0,0 +1,181 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic-bifurcate -verify %s
+
+#include "InlineObjCInstanceMethod.h"
+
+@interface MyParent : NSObject
+- (int)getZero;
+@end
+@implementation MyParent
+- (int)getZero {
+ return 0;
+}
+@end
+
+@interface PublicClass () {
+ int value2;
+}
+@property (readwrite) int value1;
+- (void)setValue2:(int)newValue2;
+@end
+
+@implementation PublicClass
+
+- (int)getZeroPublic {
+ return 0;
+}
+
+@synthesize value1;
+
+- (int)value2 {
+ return value2;
+}
+- (void)setValue2:(int)newValue {
+ value2 = newValue;
+}
+
+- (int)value3 {
+ return value3;
+}
+- (void)setValue3:(int)newValue {
+ value3 = newValue;
+}
+
+@end
+
+@interface MyClassWithPublicParent : PublicClass
+- (int)getZeroPublic;
+@end
+@implementation MyClassWithPublicParent
+- (int)getZeroPublic {
+ return 0;
+}
+@end
+
+// Category overrides a public method.
+@interface PublicSubClass (PrvateCat)
+ - (int) getZeroPublic;
+@end
+@implementation PublicSubClass (PrvateCat)
+- (int)getZeroPublic {
+ return 0;
+}
+@end
+
+
+@interface MyClass : MyParent {
+ int value;
+}
+- (int)getZero;
+@property int value;
+@end
+
+// Since class is private, we assume that it cannot be subclassed.
+// False negative: this class is "privately subclassed". this is very rare
+// in practice.
+@implementation MyClass
++ (int) testTypeFromParam:(MyParent*) p {
+ int m = 0;
+ int z = [p getZero];
+ if (z)
+ return 5/m; // false negative
+ return 5/[p getZero];// expected-warning {{Division by zero}}
+}
+
+// Here only one definition is possible, since the declaration is not visible
+// from outside.
++ (int) testTypeFromParamPrivateChild:(MyClass*) c {
+ int m = 0;
+ int z = [c getZero]; // MyClass overrides getZero to return '1'.
+ if (z)
+ return 5/m; // expected-warning {{Division by zero}}
+ return 5/[c getZero];//no warning
+}
+
+- (int)getZero {
+ return 1;
+}
+
+- (int)value {
+ return value;
+}
+
+- (void)setValue:(int)newValue {
+ value = newValue;
+}
+
+// Test ivar access.
+- (int) testIvarInSelf {
+ value = 0;
+ return 5/value; // expected-warning {{Division by zero}}
+}
+
++ (int) testIvar: (MyClass*) p {
+ p.value = 0;
+ return 5/p.value; // expected-warning {{Division by zero}}
+}
+
+// Test simple property access.
++ (int) testProperty: (MyClass*) p {
+ int x= 0;
+ [p setValue:0];
+ return 5/[p value]; // expected-warning {{Division by zero}}
+}
+
+@end
+
+// The class is prvate and is not subclassed.
+int testCallToPublicAPIInParent(MyClassWithPublicParent *p) {
+ int m = 0;
+ int z = [p getZeroPublic];
+ if (z)
+ return 5/m; // no warning
+ return 5/[p getZeroPublic];// expected-warning {{Division by zero}}
+}
+
+// When the called method is public (due to it being defined outside of main file),
+// split the path and analyze both branches.
+// In this case, p can be either the object of type MyParent* or MyClass*:
+// - If it's MyParent*, getZero returns 0.
+// - If it's MyClass*, getZero returns 1 and 'return 5/m' is reachable.
+// Declaration is provate, but p can be a subclass (MyClass*).
+int testCallToPublicAPI(PublicClass *p) {
+ int m = 0;
+ int z = [p getZeroPublic];
+ if (z)
+ return 5/m; // expected-warning {{Division by zero}}
+ return 5/[p getZeroPublic];// expected-warning {{Division by zero}}
+}
+
+// Even though the method is privately declared in the category, the parent
+// declares the method as public. Assume the instance can be subclassed.
+int testCallToPublicAPICat(PublicSubClass *p) {
+ int m = 0;
+ int z = [p getZeroPublic];
+ if (z)
+ return 5/m; // expected-warning {{Division by zero}}
+ return 5/[p getZeroPublic];// expected-warning {{Division by zero}}
+}
+
+// Test public property - properties should always be inlined, regardless
+// weither they are "public" or private.
+int testPublicProperty(PublicClass *p) {
+ int x = 0;
+ [p setValue3:0];
+ if ([p value3] != 0)
+ return 5/x; // expected-warning {{Division by zero}} // TODO: no warning, we should always inline the property.
+ return 5/[p value3];// expected-warning {{Division by zero}}
+}
+
+int testExtension(PublicClass *p) {
+ int x = 0;
+ [p setValue2:0];
+ if ([p value2] != 0)
+ return 5/x; // expected-warning {{Division by zero}} // TODO: no warning, we should always inline the property.
+ return 5/[p value2]; // expected-warning {{Division by zero}}
+}
+
+// TODO: we do not handle synthesized properties yet.
+int testPropertySynthesized(PublicClass *p) {
+ [p setValue1:0];
+ return 5/[p value1];
+}
diff --git a/test/Analysis/inlining/InlineObjCClassMethod.m b/test/Analysis/inlining/InlineObjCClassMethod.m
new file mode 100644
index 0000000..7e8b51f
--- /dev/null
+++ b/test/Analysis/inlining/InlineObjCClassMethod.m
@@ -0,0 +1,181 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic-bifurcate -verify %s
+
+// Test inlining of ObjC class methods.
+
+typedef signed char BOOL;
+typedef struct objc_class *Class;
+typedef struct objc_object {
+ Class isa;
+} *id;
+@protocol NSObject - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+@end
+
+// Vanila: ObjC class method is called by name.
+@interface MyParent : NSObject
++ (int)getInt;
+@end
+@interface MyClass : MyParent
++ (int)getInt;
+@end
+@implementation MyClass
++ (int)testClassMethodByName {
+ int y = [MyClass getInt];
+ return 5/y; // expected-warning {{Division by zero}}
+}
++ (int)getInt {
+ return 0;
+}
+@end
+
+// The definition is defined by the parent. Make sure we find it and inline.
+@interface MyParentDIP : NSObject
++ (int)getInt;
+@end
+@interface MyClassDIP : MyParentDIP
+@end
+@implementation MyClassDIP
++ (int)testClassMethodByName {
+ int y = [MyClassDIP getInt];
+ return 5/y; // expected-warning {{Division by zero}}
+}
+@end
+@implementation MyParentDIP
++ (int)getInt {
+ return 0;
+}
+@end
+
+// ObjC class method is called by name. Definition is in the category.
+@interface AAA : NSObject
+@end
+@interface AAA (MyCat)
++ (int)getInt;
+@end
+int foo() {
+ int y = [AAA getInt];
+ return 5/y; // expected-warning {{Division by zero}}
+}
+@implementation AAA
+@end
+@implementation AAA (MyCat)
++ (int)getInt {
+ return 0;
+}
+@end
+
+// ObjC class method is called by name. Definition is in the parent category.
+@interface PPP : NSObject
+@end
+@interface PPP (MyCat)
++ (int)getInt;
+@end
+@interface CCC : PPP
+@end
+int foo4() {
+ int y = [CCC getInt];
+ return 5/y; // expected-warning {{Division by zero}}
+}
+@implementation PPP
+@end
+@implementation PPP (MyCat)
++ (int)getInt {
+ return 0;
+}
+@end
+
+// There is no declaration in the class but there is one in the parent. Make
+// sure we pick the definition from the class and not the parent.
+@interface MyParentTricky : NSObject
++ (int)getInt;
+@end
+@interface MyClassTricky : MyParentTricky
+@end
+@implementation MyParentTricky
++ (int)getInt {
+ return 0;
+}
+@end
+@implementation MyClassTricky
++ (int)getInt {
+ return 1;
+}
++ (int)testClassMethodByName {
+ int y = [MyClassTricky getInt];
+ return 5/y; // no-warning
+}
+@end
+
+// ObjC class method is called by unknown class declaration (passed in as a
+// parameter). We should not inline in such case.
+@interface MyParentUnknown : NSObject
++ (int)getInt;
+@end
+@interface MyClassUnknown : MyParentUnknown
++ (int)getInt;
+@end
+@implementation MyClassUnknown
++ (int)testClassVariableByUnknownVarDecl: (Class)cl {
+ int y = [cl getInt];
+ return 3/y; // no-warning
+}
++ (int)getInt {
+ return 0;
+}
+@end
+
+
+// False negative.
+// ObjC class method call through a decl with a known type.
+// We should be able to track the type of currentClass and inline this call.
+// Note, [self class] could be a subclass. Do we still want to inline here?
+@interface MyClassKT : NSObject
+@end
+@interface MyClassKT (MyCatKT)
++ (int)getInt;
+@end
+@implementation MyClassKT (MyCatKT)
++ (int)getInt {
+ return 0;
+}
+@end
+@implementation MyClassKT
+- (int)testClassMethodByKnownVarDecl {
+ Class currentClass = [self class];
+ int y = [currentClass getInt];
+ return 5/y; // Would be great to get a warning here.
+}
+@end
+
+// Another false negative due to us not reasoning about self, which in this
+// case points to the object of the class in the call site and should be equal
+// to [MyParent class].
+@interface MyParentSelf : NSObject
++ (int)testSelf;
+@end
+@implementation MyParentSelf
++ (int)testSelf {
+ if (self == [MyParentSelf class])
+ return 0;
+ else
+ return 1;
+}
+@end
+@interface MyClassSelf : MyParentSelf
+@end
+@implementation MyClassSelf
++ (int)testClassMethodByKnownVarDecl {
+ int y = [MyParentSelf testSelf];
+ return 5/y; // Should warn here.
+}
+@end
+int foo2() {
+ int y = [MyParentSelf testSelf];
+ return 5/y; // Should warn here.
+}
diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.h b/test/Analysis/inlining/InlineObjCInstanceMethod.h
new file mode 100644
index 0000000..bb0da28
--- /dev/null
+++ b/test/Analysis/inlining/InlineObjCInstanceMethod.h
@@ -0,0 +1,46 @@
+
+// Define a public header for the ObjC methods that are "visible" externally
+// and, thus, could be sub-classed. We should explore the path on which these
+// are sub-classed with unknown class by not inlining them.
+
+typedef signed char BOOL;
+typedef struct objc_class *Class;
+typedef struct objc_object {
+ Class isa;
+} *id;
+@protocol NSObject - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
++(id)new;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+@end
+
+@interface PublicClass : NSObject {
+ int value3;
+}
+- (int)getZeroPublic;
+
+- (int) value2;
+
+@property (readonly) int value1;
+
+@property int value3;
+- (int)value3;
+- (void)setValue3:(int)newValue;
+@end
+
+@interface PublicSubClass : PublicClass
+@end
+
+@interface PublicParent : NSObject
+- (int)getZeroOverridden;
+@end
+
+@interface PublicSubClass2 : PublicParent
+- (int) getZeroOverridden;
+@end
+
diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.m b/test/Analysis/inlining/InlineObjCInstanceMethod.m
new file mode 100644
index 0000000..31b6d5b
--- /dev/null
+++ b/test/Analysis/inlining/InlineObjCInstanceMethod.m
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic-bifurcate -verify %s
+
+#include "InlineObjCInstanceMethod.h"
+
+// Method is defined in the parent; called through self.
+@interface MyParent : NSObject
+- (int)getInt;
+@end
+@implementation MyParent
+- (int)getInt {
+ return 0;
+}
+@end
+
+@interface MyClass : MyParent
+@end
+@implementation MyClass
+- (int)testDynDispatchSelf {
+ int y = [self getInt];
+ return 5/y; // expected-warning {{Division by zero}}
+}
+
+// Get the dynamic type info from a cast (from id to MyClass*).
++ (int)testAllocInit {
+ MyClass *a = [[self alloc] init];
+ return 5/[a getInt]; // expected-warning {{Division by zero}}
+}
+
+// Method is called on inited object.
++ (int)testAllocInit2 {
+ MyClass *a = [[MyClass alloc] init];
+ return 5/[a getInt]; // expected-warning {{Division by zero}}
+}
+
+// Method is called on a parameter.
++ (int)testParam: (MyClass*) a {
+ return 5/[a getInt]; // expected-warning {{Division by zero}}
+}
+
+// Method is called on a parameter of unnown type.
++ (int)testParamUnknownType: (id) a {
+ return 5/[a getInt]; // no warning
+}
+
+@end
+
+// TODO: When method is inlined, the attribute reset should be visible.
+@interface TestSettingAnAttributeInCallee : NSObject {
+ int _attribute;
+}
+ - (void) method2;
+@end
+
+@implementation TestSettingAnAttributeInCallee
+- (int) method1 {
+ [self method2];
+ return 5/_attribute; // expected-warning {{Division by zero}}
+}
+
+- (void) method2 {
+ _attribute = 0;
+}
+@end
+
+@interface TestSettingAnAttributeInCaller : NSObject {
+ int _attribute;
+}
+ - (int) method2;
+@end
+
+@implementation TestSettingAnAttributeInCaller
+- (void) method1 {
+ _attribute = 0;
+ [self method2];
+}
+
+- (int) method2 {
+ return 5/_attribute; // expected-warning {{Division by zero}}
+}
+@end
+
+
+// Don't crash if we don't know the receiver's region.
+void randomlyMessageAnObject(MyClass *arr[], int i) {
+ (void)[arr[i] getInt];
+} \ No newline at end of file
diff --git a/test/Analysis/inlining/ObjCDynTypePopagation.m b/test/Analysis/inlining/ObjCDynTypePopagation.m
new file mode 100644
index 0000000..4faaa2c
--- /dev/null
+++ b/test/Analysis/inlining/ObjCDynTypePopagation.m
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=dynamic-bifurcate -verify %s
+
+#include "InlineObjCInstanceMethod.h"
+
+void clang_analyzer_eval(int);
+
+PublicSubClass2 *getObj();
+
+@implementation PublicParent
+- (int) getZeroOverridden {
+ return 1;
+}
+- (int) getZero {
+ return 0;
+}
+@end
+
+@implementation PublicSubClass2
+- (int) getZeroOverridden {
+ return 0;
+}
+
+/* Test that we get the right type from call to alloc. */
++ (void) testAllocSelf {
+ id a = [self alloc];
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
+
++ (void) testAllocClass {
+ id a = [PublicSubClass2 alloc];
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
++ (void) testAllocSuperOverriden {
+ id a = [super alloc];
+ // Evaluates to 1 in the parent.
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}}
+}
+
++ (void) testAllocSuper {
+ id a = [super alloc];
+ clang_analyzer_eval([a getZero] == 0); // expected-warning{{TRUE}}
+}
+
++ (void) testAllocInit {
+ id a = [[self alloc] init];
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
++ (void) testNewSelf {
+ id a = [self new];
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
+// Casting to parent should not pessimize the dynamic type.
++ (void) testCastToParent {
+ id a = [[self alloc] init];
+ PublicParent *p = a;
+ clang_analyzer_eval([p getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
+// The type of parameter gets used.
++ (void)testTypeFromParam:(PublicParent*) p {
+ clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}}
+}
+
+// Test implicit cast.
+// Note, in this case, p could also be a subclass of MyParent.
++ (void) testCastFromId:(id) a {
+ PublicParent *p = a;
+ clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}}
+}
+@end
+
+// TODO: Would be nice to handle the case of dynamically obtained class info
+// as well. We need a MemRegion for class types for this.
+int testDynamicClass(BOOL coin) {
+ Class AllocClass = (coin ? [NSObject class] : [PublicSubClass2 class]);
+ id x = [[AllocClass alloc] init];
+ if (coin)
+ return [x getZero];
+ return 1;
+}
diff --git a/test/Analysis/inlining/ObjCImproperDynamictallyDetectableCast.m b/test/Analysis/inlining/ObjCImproperDynamictallyDetectableCast.m
new file mode 100644
index 0000000..739e10f
--- /dev/null
+++ b/test/Analysis/inlining/ObjCImproperDynamictallyDetectableCast.m
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=dynamic-bifurcate -verify %s
+
+typedef signed char BOOL;
+@protocol NSObject - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
+-(id)init;
++(id)new;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+@end
+void clang_analyzer_eval(BOOL);
+
+@interface SomeOtherClass : NSObject
+- (int)getZero;
+@end
+@implementation SomeOtherClass
+- (int)getZero { return 0; }
+@end
+
+@interface MyClass : NSObject
+- (int)getZero;
+@end
+
+@implementation MyClass
+- (int)getZero { return 1; }
+
+// TODO: Not only we should correctly determine that the type of o at runtime
+// is MyClass, but we should also warn about it.
++ (void) testCastToParent {
+ id a = [[self alloc] init];
+ SomeOtherClass *o = a;
+ clang_analyzer_eval([o getZero] == 0); // expected-warning{{FALSE}}
+}
+@end
diff --git a/test/Analysis/inlining/RetainCountExamples.m b/test/Analysis/inlining/RetainCountExamples.m
new file mode 100644
index 0000000..2b682c2
--- /dev/null
+++ b/test/Analysis/inlining/RetainCountExamples.m
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-ipa=dynamic-bifurcate -verify %s
+
+typedef signed char BOOL;
+typedef struct objc_class *Class;
+typedef struct objc_object {
+ Class isa;
+} *id;
+@protocol NSObject - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
++(id)new;
+- (oneway void)release;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+@end
+
+@interface SelfStaysLive : NSObject
+- (id)init;
+@end
+
+@implementation SelfStaysLive
+- (id)init {
+ return [super init];
+}
+@end
+
+void selfStaysLive() {
+ SelfStaysLive *foo = [[SelfStaysLive alloc] init];
+ [foo release];
+} \ No newline at end of file
diff --git a/test/Analysis/inlining/path-notes.c b/test/Analysis/inlining/path-notes.c
new file mode 100644
index 0000000..0885eafa
--- /dev/null
+++ b/test/Analysis/inlining/path-notes.c
@@ -0,0 +1,1291 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-output=text -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-output=plist-multi-file %s -o - | FileCheck %s
+
+void zero(int **p) {
+ *p = 0;
+ // expected-note@-1 {{Null pointer value stored to 'a'}}
+}
+
+void testZero(int *a) {
+ zero(&a);
+ // expected-note@-1 {{Calling 'zero'}}
+ // expected-note@-2 {{Returning from 'zero'}}
+ *a = 1; // expected-warning{{Dereference of null pointer}}
+ // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
+}
+
+
+void check(int *p) {
+ if (p) {
+ // expected-note@-1 + {{Assuming 'p' is null}}
+ // expected-note@-2 + {{Assuming pointer value is null}}
+ // expected-note@-3 + {{Taking false branch}}
+ return;
+ }
+ return;
+}
+
+void testCheck(int *a) {
+ check(a);
+ // expected-note@-1 {{Calling 'check'}}
+ // expected-note@-2 {{Returning from 'check'}}
+ *a = 1; // expected-warning{{Dereference of null pointer}}
+ // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
+}
+
+
+int *getPointer();
+
+void testInitCheck() {
+ int *a = getPointer();
+ // expected-note@-1 {{Variable 'a' initialized here}}
+ check(a);
+ // expected-note@-1 {{Calling 'check'}}
+ // expected-note@-2 {{Returning from 'check'}}
+ *a = 1; // expected-warning{{Dereference of null pointer}}
+ // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
+}
+
+void testStoreCheck(int *a) {
+ a = getPointer();
+ // expected-note@-1 {{Value assigned to 'a'}}
+ check(a);
+ // expected-note@-1 {{Calling 'check'}}
+ // expected-note@-2 {{Returning from 'check'}}
+ *a = 1; // expected-warning{{Dereference of null pointer}}
+ // expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
+}
+
+
+// CHECK: <?xml version="1.0" encoding="UTF-8"?>
+// CHECK: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+// CHECK: <plist version="1.0">
+// CHECK: <dict>
+// CHECK: <key>files</key>
+// CHECK: <array>
+// CHECK: <string>{{.*}}path-notes.c</string>
+// CHECK: </array>
+// CHECK: <key>diagnostics</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;zero&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;zero&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;testZero&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;testZero&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>5</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>5</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>5</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>5</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Null pointer value stored to &apos;a&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Null pointer value stored to &apos;a&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Returning from &apos;zero&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Returning from &apos;zero&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testZero</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;check&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;check&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;testCheck&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;testCheck&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming pointer value is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming pointer value is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>25</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>25</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Returning from &apos;check&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Returning from &apos;check&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testCheck</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>40</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>40</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>40</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;a&apos; initialized here</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;a&apos; initialized here</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>40</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>40</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;check&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;check&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;testInitCheck&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;testInitCheck&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>25</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>25</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Returning from &apos;check&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Returning from &apos;check&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testInitCheck</string>
+// CHECK: <key>issue_hash</key><integer>6</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Value assigned to &apos;a&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Value assigned to &apos;a&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;check&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;check&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;testStoreCheck&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;testStoreCheck&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming &apos;p&apos; is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>25</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>25</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Returning from &apos;check&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Returning from &apos;check&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;a&apos;)</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Dereference of null pointer</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testStoreCheck</string>
+// CHECK: <key>issue_hash</key><integer>6</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </plist>
diff --git a/test/Analysis/iterators.cpp b/test/Analysis/iterators.cpp
deleted file mode 100644
index 1b6340b..0000000
--- a/test/Analysis/iterators.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-// RUN: %clang --analyze -Xclang -analyzer-checker=core,experimental.cplusplus.Iterators -Xclang -verify %s
-// XFAIL: win32
-
-#include <vector>
-
-void fum(std::vector<int>::iterator t);
-
-void foo1()
-{
- // iterators that are defined but not initialized
- std::vector<int>::iterator it2;
- fum(it2); // expected-warning{{Use of iterator that is not defined}}
- *it2; // expected-warning{{Use of iterator that is not defined}}
-
- std::vector<int> v, vv;
- std::vector<int>::iterator it = v.begin();
- fum(it); // no-warning
- *it; // no-warning
- // a valid iterator plus an integer is still valid
- std::vector<int>::iterator et = it + 3;
- while(it != et) { // no-warning
- if (*it == 0) // no-warning
- *it = 1; // no-warning
- }
- // iterators from different instances Cannot be compared
- et = vv.end();
- while(it != et) // expected-warning{{Cannot compare iterators from different containers}}
- ;
-
- for( std::vector<int>::iterator it = v.begin(); it != v.end(); it++ ) { // no-warning
- if (*it == 1) // no-warning
- *it = 0; // no-warning
- }
-
- // copying a valid iterator results in a valid iterator
- et = it; // no-warning
- *et; // no-warning
-
- // any combo of valid iterator plus a constant is still valid
- et = it + 2; // no-warning
- *et; // no-warning
- et = 2 + it; // no-warning
- *et; // no-warning
- et = 2 + 4 + it; // no-warning
- *et; // no-warning
-
- // calling insert invalidates unless assigned to as result, but still
- // invalidates other iterators on the same instance
- it = v.insert( it, 1 ); // no-warning
- *et; // expected-warning{{Attempt to use an iterator made invalid by call to 'insert'}}
- ++it; // no-warning
-
- // calling erase invalidates the iterator
- v.erase(it); // no-warning
- et = it + 2; // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- et = 2 + it + 2; // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- et = 2 + it; // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- ++it; // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- it++; // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- *it; // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- it = v.insert( it, 1 ); // expected-warning{{Attempt to use an iterator made invalid by call to 'erase'}}
- // now valid after return from insert
- *it; // no-warning
-}
-
-// work with using namespace
-void foo2()
-{
- using namespace std;
-
- vector<int> v;
- vector<int>::iterator it = v.begin();
- *it; // no-warning
- v.insert( it, 1 ); // no-warning
- *it; // expected-warning{{Attempt to use an iterator made invalid by call to 'insert'}}
- it = v.insert( it, 1 ); // expected-warning{{Attempt to use an iterator made invalid by call to 'insert'}}
- *it; // no-warning
-}
-
-// using reserve eliminates some warnings
-void foo3()
-{
- std::vector<long> v;
- std::vector<long>::iterator b = v.begin();
- v.reserve( 100 );
-
- // iterator assigned before the reserve is still invalidated
- *b; // expected-warning{{Attempt to use an iterator made invalid by call to 'reserve'}}
- b = v.begin();
- v.insert( b, 1 ); // no-warning
-
- // iterator after assignment is still valid (probably)
- *b; // no-warning
-}
-
-// check on copying one iterator to another
-void foo4()
-{
- std::vector<float> v, vv;
- std::vector<float>::iterator it = v.begin();
- *it; // no-warning
- v = vv;
- *it; // expected-warning{{Attempt to use an iterator made invalid by copying another container to its container}}
-}
-
diff --git a/test/Analysis/ivars.m b/test/Analysis/ivars.m
new file mode 100644
index 0000000..42e92d2
--- /dev/null
+++ b/test/Analysis/ivars.m
@@ -0,0 +1,132 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -fblocks -verify -Wno-objc-root-class %s
+
+void clang_analyzer_eval(int);
+
+@interface Root {
+@public
+ int uniqueID;
+}
+
+- (void)refreshID;
+@end
+
+void testInvalidation(Root *obj) {
+ int savedID = obj->uniqueID;
+ clang_analyzer_eval(savedID == obj->uniqueID); // expected-warning{{TRUE}}
+
+ [obj refreshID];
+ clang_analyzer_eval(savedID == obj->uniqueID); // expected-warning{{UNKNOWN}}
+}
+
+
+@interface Child : Root
+@end
+
+@implementation Child
+- (void)testSuperInvalidation {
+ int savedID = self->uniqueID;
+ clang_analyzer_eval(savedID == self->uniqueID); // expected-warning{{TRUE}}
+
+ [super refreshID];
+ clang_analyzer_eval(savedID == self->uniqueID); // expected-warning{{UNKNOWN}}
+}
+@end
+
+
+@interface ManyIvars {
+ struct S { int a, b; } s;
+ int c;
+ int d;
+}
+@end
+
+struct S makeS();
+
+@implementation ManyIvars
+
+- (void)testMultipleIvarInvalidation:(int)useConstraints {
+ if (useConstraints) {
+ if (s.a != 1) return;
+ if (s.b != 2) return;
+ if (c != 3) return;
+ if (d != 4) return;
+ return;
+ } else {
+ s.a = 1;
+ s.b = 2;
+ c = 3;
+ d = 4;
+ }
+
+ clang_analyzer_eval(s.a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(s.b == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d == 4); // expected-warning{{TRUE}}
+
+ d = 0;
+
+ clang_analyzer_eval(s.a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(s.b == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d == 0); // expected-warning{{TRUE}}
+
+ d = 4;
+ s = makeS();
+
+ clang_analyzer_eval(s.a == 1); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(s.b == 2); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d == 4); // expected-warning{{TRUE}}
+
+ s.a = 1;
+
+ clang_analyzer_eval(s.a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(s.b == 2); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d == 4); // expected-warning{{TRUE}}
+}
+
++ (void)testMultipleIvarInvalidation:(int)useConstraints
+ forObject:(ManyIvars *)obj {
+ if (useConstraints) {
+ if (obj->s.a != 1) return;
+ if (obj->s.b != 2) return;
+ if (obj->c != 3) return;
+ if (obj->d != 4) return;
+ return;
+ } else {
+ obj->s.a = 1;
+ obj->s.b = 2;
+ obj->c = 3;
+ obj->d = 4;
+ }
+
+ clang_analyzer_eval(obj->s.a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->s.b == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->d == 4); // expected-warning{{TRUE}}
+
+ obj->d = 0;
+
+ clang_analyzer_eval(obj->s.a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->s.b == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->d == 0); // expected-warning{{TRUE}}
+
+ obj->d = 4;
+ obj->s = makeS();
+
+ clang_analyzer_eval(obj->s.a == 1); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(obj->s.b == 2); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(obj->c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->d == 4); // expected-warning{{TRUE}}
+
+ obj->s.a = 1;
+
+ clang_analyzer_eval(obj->s.a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->s.b == 2); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(obj->c == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(obj->d == 4); // expected-warning{{TRUE}}
+}
+
+@end
diff --git a/test/Analysis/malloc-annotations.c b/test/Analysis/malloc-annotations.c
index a0c1452..1dc0f78 100644
--- a/test/Analysis/malloc-annotations.c
+++ b/test/Analysis/malloc-annotations.c
@@ -70,10 +70,9 @@ void af1_c() {
myglobalpointer = my_malloc(12); // no-warning
}
-// TODO: We will be able to handle this after we add support for tracking allocations stored in struct fields.
void af1_d() {
struct stuff mystuff;
- mystuff.somefield = my_malloc(12); // false negative
+ mystuff.somefield = my_malloc(12); // expected-warning{{Memory is never released; potential leak}}
}
// Test that we can pass out allocated memory via pointer-to-pointer.
@@ -123,12 +122,11 @@ void af2e() {
free(p); // no-warning
}
-// This case would inflict a double-free elsewhere.
-// However, this case is considered an analyzer bug since it causes false-positives.
+// This case inflicts a possible double-free.
void af3() {
int *p = my_malloc(12);
my_hold(p);
- free(p); // no-warning
+ free(p); // expected-warning{{Attempt to free non-owned memory}}
}
int * af4() {
diff --git a/test/Analysis/malloc-plist.c b/test/Analysis/malloc-plist.c
index db2e0f0..11eef3e1 100644
--- a/test/Analysis/malloc-plist.c
+++ b/test/Analysis/malloc-plist.c
@@ -97,6 +97,77 @@ void LeakedSymbol(int in) {
in++;
}
+// Tests that exercise running remove dead bindings at Call exit.
+static void function_with_leak1() {
+ char *x = (char*)malloc(12);
+}
+void use_function_with_leak1() {
+ function_with_leak1();
+ int y = 0;
+}
+
+static void function_with_leak2() {
+ char *x = (char*)malloc(12);
+ int m = 0;
+}
+void use_function_with_leak2() {
+ function_with_leak2();
+}
+
+static void function_with_leak3(int y) {
+ char *x = (char*)malloc(12);
+ if (y)
+ y++;
+}
+void use_function_with_leak3(int y) {
+ function_with_leak3(y);
+}
+
+static void function_with_leak4(int y) {
+ char *x = (char*)malloc(12);
+ if (y)
+ y++;
+ else
+ y--;
+}
+void use_function_with_leak4(int y) {
+ function_with_leak4(y);
+}
+
+int anotherFunction5() {
+ return 5;
+}
+static int function_with_leak5() {
+ char *x = (char*)malloc(12);
+ return anotherFunction5();
+}
+void use_function_with_leak5() {
+ function_with_leak5();
+}
+
+void anotherFunction6(int m) {
+ m++;
+}
+static void function_with_leak6() {
+ char *x = (char*)malloc(12);
+ anotherFunction6(3);
+}
+void use_function_with_leak6() {
+ function_with_leak6();
+}
+
+static void empty_function(){
+}
+void use_empty_function() {
+ empty_function();
+}
+static char *function_with_leak7() {
+ return (char*)malloc(12);
+}
+void use_function_with_leak7() {
+ function_with_leak7();
+}
+
// CHECK: <?xml version="1.0" encoding="UTF-8"?>
// CHECK: <plist version="1.0">
// CHECK: <dict>
@@ -122,7 +193,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -135,7 +206,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
-// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -156,7 +227,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
-// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -169,7 +240,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>11</integer>
-// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -190,7 +261,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>11</integer>
-// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -203,7 +274,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>11</integer>
-// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>col</key><integer>23</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -253,7 +324,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>11</integer>
-// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>col</key><integer>23</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -281,21 +352,6 @@ void LeakedSymbol(int in) {
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
-// CHECK: <key>ranges</key>
-// CHECK: <array>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>14</integer>
-// CHECK: <key>col</key><integer>5</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>14</integer>
-// CHECK: <key>col</key><integer>6</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;p&apos;</string>
@@ -308,6 +364,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>diagnosticTest</string>
+// CHECK: <key>issue_hash</key><integer>5</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>14</integer>
@@ -332,7 +389,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>18</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -379,7 +436,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>19</integer>
-// CHECK: <key>col</key><integer>30</integer>
+// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -429,7 +486,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>19</integer>
-// CHECK: <key>col</key><integer>30</integer>
+// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -469,6 +526,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>myArrayAllocation</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>21</integer>
@@ -493,7 +551,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>24</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -506,7 +564,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>24</integer>
-// CHECK: <key>col</key><integer>28</integer>
+// CHECK: <key>col</key><integer>23</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -556,7 +614,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>24</integer>
-// CHECK: <key>col</key><integer>28</integer>
+// CHECK: <key>col</key><integer>23</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -569,7 +627,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>26</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -590,7 +648,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>26</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -603,7 +661,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>26</integer>
-// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>col</key><integer>24</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -653,7 +711,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>26</integer>
-// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>col</key><integer>24</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -666,7 +724,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>27</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -687,7 +745,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>27</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -700,7 +758,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>27</integer>
-// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -750,7 +808,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>27</integer>
-// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -841,21 +899,6 @@ void LeakedSymbol(int in) {
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
-// CHECK: <key>ranges</key>
-// CHECK: <array>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>28</integer>
-// CHECK: <key>col</key><integer>9</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>28</integer>
-// CHECK: <key>col</key><integer>14</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;buf&apos;</string>
@@ -868,6 +911,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>reallocDiagnostics</string>
+// CHECK: <key>issue_hash</key><integer>5</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>28</integer>
@@ -892,7 +936,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>43</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -905,7 +949,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>43</integer>
-// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>col</key><integer>21</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -969,7 +1013,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>34</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -982,7 +1026,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>35</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1003,7 +1047,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>35</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1016,7 +1060,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>35</integer>
-// CHECK: <key>col</key><integer>23</integer>
+// CHECK: <key>col</key><integer>18</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1066,7 +1110,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>35</integer>
-// CHECK: <key>col</key><integer>23</integer>
+// CHECK: <key>col</key><integer>18</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1079,7 +1123,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>37</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1100,7 +1144,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>37</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1176,7 +1220,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>38</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1226,7 +1270,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>43</integer>
-// CHECK: <key>col</key><integer>23</integer>
+// CHECK: <key>col</key><integer>21</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1266,6 +1310,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_wrapper</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>45</integer>
@@ -1290,7 +1335,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>59</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1303,7 +1348,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>60</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>22</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1367,7 +1412,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>52</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1414,7 +1459,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>53</integer>
-// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>col</key><integer>15</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1464,7 +1509,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>53</integer>
-// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>col</key><integer>15</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1477,7 +1522,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>54</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1498,7 +1543,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>54</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1511,7 +1556,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>55</integer>
-// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1575,7 +1620,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>49</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1588,7 +1633,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
-// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1667,7 +1712,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>55</integer>
-// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1680,7 +1725,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1730,7 +1775,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>60</integer>
-// CHECK: <key>col</key><integer>28</integer>
+// CHECK: <key>col</key><integer>22</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1743,7 +1788,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>61</integer>
-// CHECK: <key>col</key><integer>14</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1785,6 +1830,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Use-after-free</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_double_action_call</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>61</integer>
@@ -1809,7 +1855,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>74</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1822,7 +1868,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>74</integer>
-// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>col</key><integer>30</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1872,7 +1918,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>74</integer>
-// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>col</key><integer>30</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1885,7 +1931,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>75</integer>
-// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1949,7 +1995,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>65</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1962,7 +2008,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>66</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1983,7 +2029,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>66</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1996,7 +2042,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>67</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2017,7 +2063,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>67</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2030,7 +2076,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>67</integer>
-// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>col</key><integer>24</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2080,7 +2126,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>67</integer>
-// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>col</key><integer>24</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2093,7 +2139,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>68</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2114,7 +2160,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>68</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2127,7 +2173,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>68</integer>
-// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2177,7 +2223,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>68</integer>
-// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2253,7 +2299,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>69</integer>
-// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2303,7 +2349,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>75</integer>
-// CHECK: <key>col</key><integer>25</integer>
+// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2316,7 +2362,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>13</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2331,21 +2377,6 @@ void LeakedSymbol(int in) {
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
-// CHECK: <key>ranges</key>
-// CHECK: <array>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>5</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>13</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;buf&apos;</string>
@@ -2358,6 +2389,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>reallocIntra</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>76</integer>
@@ -2382,7 +2414,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>84</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2395,7 +2427,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>85</integer>
-// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>col</key><integer>26</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2459,7 +2491,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>80</integer>
-// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2472,7 +2504,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>81</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2493,7 +2525,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>81</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2506,7 +2538,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>81</integer>
-// CHECK: <key>col</key><integer>28</integer>
+// CHECK: <key>col</key><integer>24</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2585,7 +2617,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>85</integer>
-// CHECK: <key>col</key><integer>28</integer>
+// CHECK: <key>col</key><integer>26</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2625,6 +2657,7 @@ void LeakedSymbol(int in) {
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>use_ret</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>86</integer>
@@ -2649,7 +2682,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>90</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2696,7 +2729,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>92</integer>
-// CHECK: <key>col</key><integer>24</integer>
+// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2746,7 +2779,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>92</integer>
-// CHECK: <key>col</key><integer>24</integer>
+// CHECK: <key>col</key><integer>20</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2759,7 +2792,7 @@ void LeakedSymbol(int in) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>97</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -2774,41 +2807,1626 @@ void LeakedSymbol(int in) {
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;m&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;m&apos;</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;m&apos;</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>LeakedSymbol</string>
+// CHECK: <key>issue_hash</key><integer>8</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>97</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>105</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>97</integer>
+// CHECK: <key>line</key><integer>105</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>97</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>line</key><integer>105</integer>
+// CHECK: <key>col</key><integer>25</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;m&apos;</string>
+// CHECK: <string>Calling &apos;function_with_leak1&apos;</string>
// CHECK: <key>message</key>
-// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;m&apos;</string>
+// CHECK: <string>Calling &apos;function_with_leak1&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak1&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak1&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
// CHECK: </dict>
// CHECK: </array>
-// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;m&apos;</string>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
// CHECK: <key>category</key><string>Memory Error</string>
// CHECK: <key>type</key><string>Memory leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
-// CHECK: <key>issue_context</key><string>LeakedSymbol</string>
+// CHECK: <key>issue_context</key><string>function_with_leak1</string>
+// CHECK: <key>issue_hash</key><integer>1</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>97</integer>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>114</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>114</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>114</integer>
+// CHECK: <key>col</key><integer>25</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;function_with_leak2&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;function_with_leak2&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>109</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak2&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak2&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>109</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>109</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>110</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>111</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>111</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>111</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>function_with_leak2</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>111</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>26</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;function_with_leak3&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;function_with_leak3&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>117</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak3&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak3&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>117</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>117</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>120</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>120</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>120</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>function_with_leak3</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>120</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>26</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;function_with_leak4&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;function_with_leak4&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>126</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak4&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak4&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>126</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>126</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>127</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>131</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>131</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>131</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>function_with_leak4</string>
+// CHECK: <key>issue_hash</key><integer>5</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>131</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>145</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>145</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>145</integer>
+// CHECK: <key>col</key><integer>25</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;function_with_leak5&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;function_with_leak5&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak5&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak5&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>141</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>142</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>142</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>142</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>function_with_leak5</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>142</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>156</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>156</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>156</integer>
+// CHECK: <key>col</key><integer>25</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;function_with_leak6&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;function_with_leak6&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>151</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak6&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak6&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>151</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>151</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>152</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>153</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>153</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>153</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak of memory pointed to by &apos;x&apos;</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>function_with_leak6</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>153</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>25</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Calling &apos;function_with_leak7&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Calling &apos;function_with_leak7&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>164</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak7&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Entered call from &apos;use_function_with_leak7&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>164</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>164</integer>
+// CHECK: <key>col</key><integer>6</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>24</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>165</integer>
+// CHECK: <key>col</key><integer>28</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is allocated</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>25</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>1</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Returned allocated memory</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Returned allocated memory</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>23</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Memory is never released; potential leak</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Memory is never released; potential leak</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Memory is never released; potential leak</string>
+// CHECK: <key>category</key><string>Memory Error</string>
+// CHECK: <key>type</key><string>Memory leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>use_function_with_leak7</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>1</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </plist>
-
diff --git a/test/Analysis/malloc-sizeof.c b/test/Analysis/malloc-sizeof.c
index d2b3bcf..6eb466a 100644
--- a/test/Analysis/malloc-sizeof.c
+++ b/test/Analysis/malloc-sizeof.c
@@ -1,27 +1,36 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.unix.MallocSizeof -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=unix.MallocSizeof -verify %s
#include <stddef.h>
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
+void free(void *ptr);
struct A {};
struct B {};
-void foo() {
+void foo(unsigned int unsignedInt, unsigned int readSize) {
int *ip1 = malloc(sizeof(1));
int *ip2 = malloc(4 * sizeof(int));
- long *lp1 = malloc(sizeof(short)); // expected-warning {{Result of 'malloc' is converted to type 'long *', whose pointee type 'long' is incompatible with sizeof operand type 'short'}}
- long *lp2 = malloc(5 * sizeof(double)); // expected-warning {{Result of 'malloc' is converted to type 'long *', whose pointee type 'long' is incompatible with sizeof operand type 'double'}}
- long *lp3 = malloc(5 * sizeof(char) + 2); // expected-warning {{Result of 'malloc' is converted to type 'long *', whose pointee type 'long' is incompatible with sizeof operand type 'char'}}
+ long *lp1 = malloc(sizeof(short)); // expected-warning {{Result of 'malloc' is converted to a pointer of type 'long', which is incompatible with sizeof operand type 'short'}}
+ long *lp2 = malloc(5 * sizeof(double)); // expected-warning {{Result of 'malloc' is converted to a pointer of type 'long', which is incompatible with sizeof operand type 'double'}}
+ char *cp3 = malloc(5 * sizeof(char) + 2); // no warning
+ unsigned char *buf = malloc(readSize + sizeof(unsignedInt)); // no warning
struct A *ap1 = calloc(1, sizeof(struct A));
struct A *ap2 = calloc(2, sizeof(*ap1));
- struct A *ap3 = calloc(2, sizeof(ap1)); // expected-warning {{Result of 'calloc' is converted to type 'struct A *', whose pointee type 'struct A' is incompatible with sizeof operand type 'struct A *'}}
- struct A *ap4 = calloc(3, sizeof(struct A*)); // expected-warning {{Result of 'calloc' is converted to type 'struct A *', whose pointee type 'struct A' is incompatible with sizeof operand type 'struct A *'}}
- struct A *ap5 = calloc(4, sizeof(struct B)); // expected-warning {{Result of 'calloc' is converted to type 'struct A *', whose pointee type 'struct A' is incompatible with sizeof operand type 'struct B'}}
+ struct A *ap3 = calloc(2, sizeof(ap1)); // expected-warning {{Result of 'calloc' is converted to a pointer of type 'struct A', which is incompatible with sizeof operand type 'struct A *'}}
+ struct A *ap4 = calloc(3, sizeof(struct A*)); // expected-warning {{Result of 'calloc' is converted to a pointer of type 'struct A', which is incompatible with sizeof operand type 'struct A *'}}
+ struct A *ap5 = calloc(4, sizeof(struct B)); // expected-warning {{Result of 'calloc' is converted to a pointer of type 'struct A', which is incompatible with sizeof operand type 'struct B'}}
struct A *ap6 = realloc(ap5, sizeof(struct A));
- struct A *ap7 = realloc(ap5, sizeof(struct B)); // expected-warning {{Result of 'realloc' is converted to type 'struct A *', whose pointee type 'struct A' is incompatible with sizeof operand type 'struct B'}}
+ struct A *ap7 = realloc(ap5, sizeof(struct B)); // expected-warning {{Result of 'realloc' is converted to a pointer of type 'struct A', which is incompatible with sizeof operand type 'struct B'}}
+}
+
+// Don't warn when the types differ only by constness.
+void ignore_const() {
+ const char **x = (const char **)malloc(1 * sizeof(char *)); // no-warning
+ const char ***y = (const char ***)malloc(1 * sizeof(char *)); // expected-warning {{Result of 'malloc' is converted to a pointer of type 'const char **', which is incompatible with sizeof operand type 'char *'}}
+ free(x);
}
diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c
index 3b47123..f60271f 100644
--- a/test/Analysis/malloc.c
+++ b/test/Analysis/malloc.c
@@ -1,6 +1,8 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode,experimental.core.CastSize,unix.Malloc -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.deadcode.UnreachableCode,experimental.core.CastSize,unix.Malloc,debug.ExprInspection -analyzer-store=region -verify %s
#include "system-header-simulator.h"
+void clang_analyzer_eval(int);
+
typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t);
void *valloc(size_t);
@@ -8,6 +10,8 @@ void free(void *);
void *realloc(void *ptr, size_t size);
void *reallocf(void *ptr, size_t size);
void *calloc(size_t nmemb, size_t size);
+char *strdup(const char *s);
+char *strndup(const char *s, size_t n);
void myfoo(int *p);
void myfooint(int p);
@@ -15,7 +19,7 @@ char *fooRetPtr();
void f1() {
int *p = malloc(12);
- return; // expected-warning{{Memory is never released; potential leak}}
+ return; // expected-warning{{Memory is never released; potential leak of memory pointed to by 'p'}}
}
void f2() {
@@ -40,7 +44,7 @@ void reallocNotNullPtr(unsigned sizeIn) {
char *p = (char*)malloc(size);
if (p) {
char *q = (char*)realloc(p, sizeIn);
- char x = *q; // expected-warning {{Memory is never released; potential leak}}
+ char x = *q; // expected-warning {{Memory is never released; potential leak of memory pointed to by 'q'}}
}
}
@@ -65,7 +69,7 @@ void reallocSizeZero1() {
char *p = malloc(12);
char *r = realloc(p, 0);
if (!r) {
- free(p);
+ free(p); // expected-warning {{Attempt to free released memory}}
} else {
free(r);
}
@@ -75,7 +79,7 @@ void reallocSizeZero2() {
char *p = malloc(12);
char *r = realloc(p, 0);
if (!r) {
- free(p);
+ free(p); // expected-warning {{Attempt to free released memory}}
} else {
free(r);
}
@@ -98,7 +102,7 @@ void reallocSizeZero5() {
}
void reallocPtrZero1() {
- char *r = realloc(0, 12); // expected-warning {{Memory is never released; potential leak}}
+ char *r = realloc(0, 12); // expected-warning {{Memory is never released; potential leak of memory pointed to by 'r'}}
}
void reallocPtrZero2() {
@@ -243,6 +247,12 @@ void f7() {
x[0] = 'a'; // expected-warning{{Use of memory after it is freed}}
}
+void f8() {
+ char *x = (char*) malloc(4);
+ free(x);
+ char *y = strndup(x, 4); // expected-warning{{Use of memory after it is freed}}
+}
+
void f7_realloc() {
char *x = (char*) malloc(4);
realloc(x,0);
@@ -311,7 +321,7 @@ void nullFree() {
void paramFree(int *p) {
myfoo(p);
free(p); // no warning
- myfoo(p); // TODO: This should be a warning.
+ myfoo(p); // expected-warning {{Use of memory after it is freed}}
}
int* mallocEscapeRet() {
@@ -503,20 +513,26 @@ void testMalloc() {
int *x = malloc(12);
StructWithPtr St;
St.memP = x;
- arrOfStructs[0] = St;
+ arrOfStructs[0] = St; // no-warning
}
StructWithPtr testMalloc2() {
int *x = malloc(12);
StructWithPtr St;
St.memP = x;
- return St;
+ return St; // no-warning
}
int *testMalloc3() {
int *x = malloc(12);
int *y = x;
- return y;
+ return y; // no-warning
+}
+
+void testStructLeak() {
+ StructWithPtr St;
+ St.memP = malloc(12);
+ return; // expected-warning {{Memory is never released; potential leak of memory pointed to by 'St.memP'}}
}
void testElemRegion1() {
@@ -653,10 +669,6 @@ int *specialMallocWithStruct() {
}
// Test various allocation/deallocation functions.
-
-char *strdup(const char *s);
-char *strndup(const char *s, size_t n);
-
void testStrdup(const char *s, unsigned validIndex) {
char *s2 = strdup(s);
s2[validIndex + 1] = 'b';// expected-warning {{Memory is never released; potential leak}}
@@ -711,6 +723,18 @@ FILE *useFunOpenNoReleaseFunction() {
return f; // expected-warning{{leak}}
}
+static int readNothing(void *_ctx, char *buf, int size) {
+ return 0;
+}
+FILE *useFunOpenReadNoRelease() {
+ void *ctx = malloc(sizeof(int));
+ FILE *f = funopen(ctx, readNothing, 0, 0, 0);
+ if (f == 0) {
+ free(ctx);
+ }
+ return f; // expected-warning{{leak}}
+}
+
// Test setbuf, setvbuf.
int my_main_no_warning() {
char *p = malloc(100);
@@ -760,10 +784,83 @@ void radar10978247_positive(int myValueSize) {
return;
}
-// ----------------------------------------------------------------------------
-// Below are the known false positives.
+// <rdar://problem/11269741> Previously this triggered a false positive
+// because malloc() is known to return uninitialized memory and the binding
+// of 'o' to 'p->n' was not getting propertly handled. Now we report a leak.
+struct rdar11269741_a_t {
+ struct rdar11269741_b_t {
+ int m;
+ } n;
+};
+
+int rdar11269741(struct rdar11269741_b_t o)
+{
+ struct rdar11269741_a_t *p = (struct rdar11269741_a_t *) malloc(sizeof(*p));
+ p->n = o;
+ return p->n.m; // expected-warning {{leak}}
+}
+
+// Pointer arithmetic, returning an ElementRegion.
+void *radar11329382(unsigned bl) {
+ void *ptr = malloc (16);
+ ptr = ptr + (2 - bl);
+ return ptr; // no warning
+}
+
+void __assert_rtn(const char *, const char *, int, const char *) __attribute__((__noreturn__));
+int strcmp(const char *, const char *);
+char *a (void);
+void radar11270219(void) {
+ char *x = a(), *y = a();
+ (__builtin_expect(!(x && y), 0) ? __assert_rtn(__func__, "/Users/zaks/tmp/ex.c", 24, "x && y") : (void)0);
+ strcmp(x, y); // no warning
+}
+
+void radar_11358224_test_double_assign_ints_positive_2()
+{
+ void *ptr = malloc(16);
+ ptr = ptr; // expected-warning {{leak}}
+}
+
+// Assume that functions which take a function pointer can free memory even if
+// they are defined in system headers and take the const pointer to the
+// allocated memory. (radar://11160612)
+int const_ptr_and_callback(int, const char*, int n, void(*)(void*));
+void r11160612_1() {
+ char *x = malloc(12);
+ const_ptr_and_callback(0, x, 12, free); // no - warning
+}
+
+// Null is passed as callback.
+void r11160612_2() {
+ char *x = malloc(12);
+ const_ptr_and_callback(0, x, 12, 0); // expected-warning {{leak}}
+}
+
+// Callback is passed to a function defined in a system header.
+void r11160612_4() {
+ char *x = malloc(12);
+ sqlite3_bind_text_my(0, x, 12, free); // no - warning
+}
+
+// Passing callbacks in a struct.
+void r11160612_5(StWithCallback St) {
+ void *x = malloc(12);
+ dealocateMemWhenDoneByVal(x, St);
+}
+void r11160612_6(StWithCallback St) {
+ void *x = malloc(12);
+ dealocateMemWhenDoneByRef(&St, x);
+}
+
+int mySub(int, int);
+int myAdd(int, int);
+int fPtr(unsigned cond, int x) {
+ return (cond ? mySub : myAdd)(x, x);
+}
+
+// Test anti-aliasing.
-// TODO: There should be no warning here. This one might be difficult to get rid of.
void dependsOnValueOfPtr(int *g, unsigned f) {
int *p;
@@ -776,20 +873,147 @@ void dependsOnValueOfPtr(int *g, unsigned f) {
if (p != g)
free(p);
else
- return; // expected-warning{{Memory is never released; potential leak}}
+ return; // no warning
return;
}
-// ----------------------------------------------------------------------------
-// False negatives.
+int CMPRegionHeapToStack() {
+ int x = 0;
+ int *x1 = malloc(8);
+ int *x2 = &x;
+ clang_analyzer_eval(x1 == x2); // expected-warning{{FALSE}}
+ free(x1);
+ return x;
+}
+
+int CMPRegionHeapToHeap2() {
+ int x = 0;
+ int *x1 = malloc(8);
+ int *x2 = malloc(8);
+ int *x4 = x1;
+ int *x5 = x2;
+ clang_analyzer_eval(x4 == x5); // expected-warning{{FALSE}}
+ free(x1);
+ free(x2);
+ return x;
+}
+
+int CMPRegionHeapToHeap() {
+ int x = 0;
+ int *x1 = malloc(8);
+ int *x4 = x1;
+ if (x1 == x4) {
+ free(x1);
+ return 5/x; // expected-warning{{Division by zero}}
+ }
+ return x;// expected-warning{{This statement is never executed}}
+}
-// TODO: This requires tracking symbols stored inside the structs/arrays.
-void testMalloc5() {
+int HeapAssignment() {
+ int m = 0;
+ int *x = malloc(4);
+ int *y = x;
+ *x = 5;
+ clang_analyzer_eval(*x != *y); // expected-warning{{FALSE}}
+ free(x);
+ return 0;
+}
+
+int *retPtr();
+int *retPtrMightAlias(int *x);
+int cmpHeapAllocationToUnknown() {
+ int zero = 0;
+ int *yBefore = retPtr();
+ int *m = malloc(8);
+ int *yAfter = retPtrMightAlias(m);
+ clang_analyzer_eval(yBefore == m); // expected-warning{{FALSE}}
+ clang_analyzer_eval(yAfter == m); // expected-warning{{FALSE}}
+ free(m);
+ return 0;
+}
+
+void localArrayTest() {
+ char *p = (char*)malloc(12);
+ char *ArrayL[12];
+ ArrayL[0] = p; // expected-warning {{leak}}
+}
+
+void localStructTest() {
StructWithPtr St;
StructWithPtr *pSt = &St;
- pSt->memP = malloc(12);
+ pSt->memP = malloc(12); // expected-warning{{Memory is never released; potential leak}}
}
+// Test double assignment through integers.
+static long glob;
+void test_double_assign_ints()
+{
+ void *ptr = malloc (16); // no-warning
+ glob = (long)(unsigned long)ptr;
+}
+
+void test_double_assign_ints_positive()
+{
+ void *ptr = malloc(16);
+ (void*)(long)(unsigned long)ptr; // expected-warning {{unused}} expected-warning {{leak}}
+}
+
+
+void testCGContextNoLeak()
+{
+ void *ptr = malloc(16);
+ CGContextRef context = CGBitmapContextCreate(ptr);
+
+ // Because you can get the data back out like this, even much later,
+ // CGBitmapContextCreate is one of our "stop-tracking" exceptions.
+ free(CGBitmapContextGetData(context));
+}
+
+void testCGContextLeak()
+{
+ void *ptr = malloc(16);
+ CGContextRef context = CGBitmapContextCreate(ptr);
+ // However, this time we're just leaking the data, because the context
+ // object doesn't escape and it hasn't been freed in this function.
+}
+
+// Allow xpc context to escape. radar://11635258
+// TODO: Would be great if we checked that the finalize_connection_context actually releases it.
+static void finalize_connection_context(void *ctx) {
+ int *context = ctx;
+ free(context);
+}
+void foo (xpc_connection_t peer) {
+ int *ctx = calloc(1, sizeof(int));
+ xpc_connection_set_context(peer, ctx);
+ xpc_connection_set_finalizer_f(peer, finalize_connection_context);
+ xpc_connection_resume(peer);
+}
+
+// Make sure we catch errors when we free in a function which does not allocate memory.
+void freeButNoMalloc(int *p, int x){
+ if (x) {
+ free(p);
+ //user forgot a return here.
+ }
+ free(p); // expected-warning {{Attempt to free released memory}}
+}
+
+struct HasPtr {
+ int *p;
+};
+
+int* reallocButNoMalloc(struct HasPtr *a, int c, int size) {
+ int *s;
+ a->p = (int *)realloc(a->p, size);
+ if (a->p == 0)
+ return 0; // expected-warning{{Memory is never released; potential leak}}
+ return a->p;
+}
+
+// ----------------------------------------------------------------------------
+// False negatives.
+
// TODO: This is another false negative.
void testMallocWithParam(int **p) {
*p = (int*) malloc(sizeof(int));
@@ -799,11 +1023,3 @@ void testMallocWithParam(int **p) {
void testMallocWithParam_2(int **p) {
*p = (int*) malloc(sizeof(int));
}
-
-// TODO: This should produce a warning, similar to the previous issue.
-void localArrayTest() {
- char *p = (char*)malloc(12);
- char *ArrayL[12];
- ArrayL[0] = p;
-}
-
diff --git a/test/Analysis/malloc.cpp b/test/Analysis/malloc.cpp
index 8f80b2b..72b9272 100644
--- a/test/Analysis/malloc.cpp
+++ b/test/Analysis/malloc.cpp
@@ -14,3 +14,24 @@ struct Foo {
Foo aFunction() {
return malloc(10);
}
+
+// Assume that functions which take a function pointer can free memory even if
+// they are defined in system headers and take the const pointer to the
+// allocated memory. (radar://11160612)
+// Test default parameter.
+int const_ptr_and_callback_def_param(int, const char*, int n, void(*)(void*) = 0);
+void r11160612_3() {
+ char *x = (char*)malloc(12);
+ const_ptr_and_callback_def_param(0, x, 12);
+}
+
+// Test member function pointer.
+struct CanFreeMemory {
+ static void myFree(void*);
+};
+//This is handled because we look at the type of the parameter(not argument).
+void r11160612_3(CanFreeMemory* p) {
+ char *x = (char*)malloc(12);
+ const_ptr_and_callback_def_param(0, x, 12, p->myFree);
+}
+
diff --git a/test/Analysis/malloc.m b/test/Analysis/malloc.m
index 6c94118..08206f3 100644
--- a/test/Analysis/malloc.m
+++ b/test/Analysis/malloc.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -Wno-objc-root-class -fblocks %s
#include "system-header-simulator-objc.h"
@class NSString;
diff --git a/test/Analysis/malloc.mm b/test/Analysis/malloc.mm
index 3515a4f..7a9d881 100644
--- a/test/Analysis/malloc.mm
+++ b/test/Analysis/malloc.mm
@@ -9,7 +9,6 @@ void free(void *);
void testNSDatafFreeWhenDoneNoError(NSUInteger dataLength) {
unsigned char *data = (unsigned char *)malloc(42);
NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength];
- free(data); // no warning
}
void testNSDataFreeWhenDoneYES(NSUInteger dataLength) {
@@ -22,6 +21,16 @@ void testNSDataFreeWhenDoneYES2(NSUInteger dataLength) {
NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data length:dataLength freeWhenDone:1]; // no-warning
}
+void testNSStringFreeWhenDoneYES3(NSUInteger dataLength) {
+ unsigned char *data = (unsigned char *)malloc(42);
+ NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:1];
+}
+
+void testNSStringFreeWhenDoneYES4(NSUInteger dataLength) {
+ unichar *data = (unichar*)malloc(42);
+ NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:1];
+ free(data); //expected-warning {{Attempt to free non-owned memory}}
+}
void testNSStringFreeWhenDoneYES(NSUInteger dataLength) {
unsigned char *data = (unsigned char *)malloc(42);
@@ -55,11 +64,17 @@ void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) {
NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}}
}
-// TODO: False Negative.
-void testNSDatafFreeWhenDoneFN(NSUInteger dataLength) {
- unsigned char *data = (unsigned char *)malloc(42);
- NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:1];
- free(data); // false negative
+void testRelinquished1() {
+ void *data = malloc(42);
+ NSData *nsdata = [NSData dataWithBytesNoCopy:data length:42 freeWhenDone:1];
+ free(data); // expected-warning {{Attempt to free non-owned memory}}
+}
+
+void testRelinquished2() {
+ void *data = malloc(42);
+ NSData *nsdata;
+ free(data);
+ [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Attempt to free released memory}}
}
// Test CF/NS...NoCopy. PR12100: Pointers can escape when custom deallocators are provided.
@@ -153,4 +168,57 @@ static void releaseDataCallback (void *info, const void *data, size_t size) {
void testCGDataProviderCreateWithData() {
void* b = calloc(8, 8);
CGDataProviderRef p = CGDataProviderCreateWithData(0, b, 8*8, releaseDataCallback);
+}
+
+// Assume that functions which take a function pointer can free memory even if
+// they are defined in system headers and take the const pointer to the
+// allocated memory. (radar://11160612)
+extern CGDataProviderRef UnknownFunWithCallback(void *info,
+ const void *data, size_t size,
+ CGDataProviderReleaseDataCallback releaseData)
+ __attribute__((visibility("default")));
+void testUnknownFunWithCallBack() {
+ void* b = calloc(8, 8);
+ CGDataProviderRef p = UnknownFunWithCallback(0, b, 8*8, releaseDataCallback);
+}
+
+// Test blocks.
+void acceptBlockParam(void *, void (^block)(void *), unsigned);
+void testCallWithBlockCallback() {
+ void *l = malloc(12);
+ acceptBlockParam(l, ^(void *i) { free(i); }, sizeof(char *));
+}
+
+// Test blocks in system headers.
+void testCallWithBlockCallbackInSystem() {
+ void *l = malloc(12);
+ SystemHeaderFunctionWithBlockParam(l, ^(void *i) { free(i); }, sizeof(char *));
+}
+
+// Test escape into NSPointerArray. radar://11691035, PR13140
+void foo(NSPointerArray* pointerArray) {
+
+ void* p1 = malloc (1024);
+ if (p1) {
+ [pointerArray addPointer:p1];
+ }
+
+ void* p2 = malloc (1024);
+ if (p2) {
+ [pointerArray insertPointer:p2 atIndex:1];
+ }
+
+ void* p3 = malloc (1024);
+ if (p3) {
+ [pointerArray replacePointerAtIndex:1 withPointer:p3];
+ }
+
+ // Freeing the buffer is allowed.
+ void* buffer = [pointerArray pointerAtIndex:0];
+ free(buffer);
+}
+
+void noCrashOnVariableArgumentSelector() {
+ NSMutableString *myString = [NSMutableString stringWithString:@"some text"];
+ [myString appendFormat:@"some text = %d", 3];
} \ No newline at end of file
diff --git a/test/Analysis/method-call-path-notes.cpp b/test/Analysis/method-call-path-notes.cpp
new file mode 100644
index 0000000..fbf0cae
--- /dev/null
+++ b/test/Analysis/method-call-path-notes.cpp
@@ -0,0 +1,664 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-output=text -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-output=plist-multi-file %s -o - | FileCheck %s
+
+// Test warning about null or uninitialized pointer values used as instance member
+// calls.
+class TestInstanceCall {
+public:
+ void foo() {}
+};
+
+void test_ic() {
+ TestInstanceCall *p; // expected-note {{Variable 'p' declared without an initial value}}
+ p->foo(); // expected-warning {{Called C++ object pointer is uninitialized}} expected-note {{Called C++ object pointer is uninitialized}}
+}
+
+void test_ic_null() {
+ TestInstanceCall *p = 0; // expected-note {{Variable 'p' initialized to a null pointer value}}
+ p->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note {{Called C++ object pointer is null}}
+}
+
+void test_ic_set_to_null() {
+ TestInstanceCall *p;
+ p = 0; // expected-note {{Null pointer value stored to 'p'}}
+ p->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note {{Called C++ object pointer is null}}
+}
+
+void test_ic_null(TestInstanceCall *p) {
+ if (!p) // expected-note {{Assuming pointer value is null}} expected-note {{Taking true branch}}
+ p->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note{{Called C++ object pointer is null}}
+}
+
+void test_ic_member_ptr() {
+ TestInstanceCall *p = 0; // expected-note {{Variable 'p' initialized to a null pointer value}}
+ typedef void (TestInstanceCall::*IC_Ptr)();
+ IC_Ptr bar = &TestInstanceCall::foo;
+ (p->*bar)(); // expected-warning {{Called C++ object pointer is null}} expected-note{{Called C++ object pointer is null}}
+}
+
+// CHECK: <?xml version="1.0" encoding="UTF-8"?>
+// CHECK: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+// CHECK: <plist version="1.0">
+// CHECK: <dict>
+// CHECK: <key>files</key>
+// CHECK: <array>
+// CHECK: <string>{{.*}}method-call-path-notes.cpp</string>
+// CHECK: </array>
+// CHECK: <key>diagnostics</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; declared without an initial value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; declared without an initial value</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Called C++ object pointer is uninitialized</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Called C++ object pointer is uninitialized</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Called C++ object pointer is uninitialized</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Called C++ object pointer is uninitialized</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_ic</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>13</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_ic_null</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>18</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Null pointer value stored to &apos;p&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Null pointer value stored to &apos;p&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_ic_set_to_null</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming pointer value is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming pointer value is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_ic_null</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>33</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Called C++ object pointer is null</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>category</key><string>Logic error</string>
+// CHECK: <key>type</key><string>Called C++ object pointer is null</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>test_ic_member_ptr</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>36</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </plist>
diff --git a/test/Analysis/method-call.cpp b/test/Analysis/method-call.cpp
index 323fffe..91da532 100644
--- a/test/Analysis/method-call.cpp
+++ b/test/Analysis/method-call.cpp
@@ -1,6 +1,8 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-inline-call -analyzer-store region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -analyzer-store region -verify %s
// XFAIL: *
+void clang_analyzer_eval(bool);
+
struct A {
int x;
A(int a) { x = a; }
@@ -9,33 +11,15 @@ struct A {
void f1() {
A x(3);
- if (x.getx() == 3) {
- int *p = 0;
- *p = 3; // expected-warning{{Dereference of null pointer}}
- } else {
- int *p = 0;
- *p = 3; // no-warning
- }
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
}
void f2() {
const A &x = A(3);
- if (x.getx() == 3) {
- int *p = 0;
- *p = 3; // expected-warning{{Dereference of null pointer}}
- } else {
- int *p = 0;
- *p = 3; // no-warning
- }
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
}
void f3() {
const A &x = (A)3;
- if (x.getx() == 3) {
- int *p = 0;
- *p = 3; // expected-warning{{Dereference of null pointer}}
- } else {
- int *p = 0;
- *p = 3; // no-warning
- }
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
}
diff --git a/test/Analysis/misc-ps-arm.m b/test/Analysis/misc-ps-arm.m
new file mode 100644
index 0000000..a909ef1
--- /dev/null
+++ b/test/Analysis/misc-ps-arm.m
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple thumbv7-apple-ios0.0.0 -analyze -analyzer-checker=core -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks -Wno-objc-root-class %s
+
+// <rdar://problem/11405978> - Handle casts of vectors to structs, and loading
+// a value.
+typedef float float32_t;
+typedef __attribute__((neon_vector_type(2))) float32_t float32x2_t;
+
+typedef struct
+{
+ float x, y;
+} Rdar11405978Vec;
+
+float32x2_t rdar11405978_bar();
+float32_t rdar11405978() {
+ float32x2_t v = rdar11405978_bar();
+ Rdar11405978Vec w = *(Rdar11405978Vec *)&v;
+ return w.x; // no-warning
+}
diff --git a/test/Analysis/misc-ps-cxx0x.cpp b/test/Analysis/misc-ps-cxx0x.cpp
index b4dee31..164af5d 100644
--- a/test/Analysis/misc-ps-cxx0x.cpp
+++ b/test/Analysis/misc-ps-cxx0x.cpp
@@ -73,3 +73,17 @@ void test2() {
struct RDar11178609 {
~RDar11178609() = delete;
};
+
+// Tests that dynamic_cast handles references to C++ classes. Previously
+// this crashed.
+class rdar11817693_BaseBase {};
+class rdar11817693_BaseInterface {};
+class rdar11817693_Base : public rdar11817693_BaseBase, public rdar11817693_BaseInterface {};
+class rdar11817693 : public rdar11817693_Base {
+ virtual void operator=(const rdar11817693_BaseBase& src);
+ void operator=(const rdar11817693& src);
+};
+void rdar11817693::operator=(const rdar11817693& src) {
+ operator=(dynamic_cast<const rdar11817693_BaseBase&>(src));
+}
+
diff --git a/test/Analysis/misc-ps-region-store.cpp b/test/Analysis/misc-ps-region-store.cpp
index 8d75fb8..e30cedb 100644
--- a/test/Analysis/misc-ps-region-store.cpp
+++ b/test/Analysis/misc-ps-region-store.cpp
@@ -271,13 +271,27 @@ class Rdar9212495_A : public Rdar9212495_B {};
const Rdar9212495_A& rdar9212495(const Rdar9212495_C* ptr) {
const Rdar9212495_A& val = dynamic_cast<const Rdar9212495_A&>(*ptr);
+ // This is not valid C++; dynamic_cast with a reference type will throw an
+ // exception if the pointer does not match the expected type. However, our
+ // implementation of dynamic_cast will pass through a null pointer...or a
+ // "null reference"! So this branch is actually possible.
if (&val == 0) {
- val.bar(); // FIXME: This should eventually be a null dereference.
+ val.bar(); // expected-warning{{Called C++ object pointer is null}}
}
return val;
}
+const Rdar9212495_A* rdar9212495_ptr(const Rdar9212495_C* ptr) {
+ const Rdar9212495_A* val = dynamic_cast<const Rdar9212495_A*>(ptr);
+
+ if (val == 0) {
+ val->bar(); // expected-warning{{Called C++ object pointer is null}}
+ }
+
+ return val;
+}
+
// Test constructors invalidating arguments. Previously this raised
// an uninitialized value warning.
extern "C" void __attribute__((noreturn)) PR9645_exit(int i);
@@ -578,3 +592,37 @@ void rdar10924675(unsigned short x[], int index, int index2) {
if (y == 0)
return;
}
+
+// Test handling CXXScalarValueInitExprs.
+void rdar11401827() {
+ int x = int();
+ if (!x) {
+ int *p = 0;
+ *p = 0xDEADBEEF; // expected-warning {{null pointer}}
+ }
+ else {
+ int *p = 0;
+ *p = 0xDEADBEEF;
+ }
+}
+
+//===---------------------------------------------------------------------===//
+// Handle inlining of C++ method calls.
+//===---------------------------------------------------------------------===//
+
+struct A {
+ int *p;
+ void foo(int *q) {
+ p = q;
+ }
+ void bar() {
+ *p = 0; // expected-warning {{null pointer}}
+ }
+};
+
+void test_inline() {
+ A a;
+ a.foo(0);
+ a.bar();
+}
+
diff --git a/test/Analysis/misc-ps-region-store.m b/test/Analysis/misc-ps-region-store.m
index d263d4d..88860bb 100644
--- a/test/Analysis/misc-ps-region-store.m
+++ b/test/Analysis/misc-ps-region-store.m
@@ -673,7 +673,7 @@ typedef void (^RDar_7462324_Callback)(id obj);
builder = ^(id object) {
id x;
if (object) {
- builder(x); // expected-warning{{Function call argument is an uninitialized value}}
+ builder(x); // expected-warning{{Block call argument is an uninitialized value}}
}
};
builder(target);
@@ -1341,3 +1341,23 @@ static unsigned rdar_11127008(void) {
return values[index].value;
}
+// Test handling invalidating arrays passed to a block via captured
+// pointer value (not a __block variable).
+typedef void (^radar11125868_cb)(int *, unsigned);
+
+void rdar11125868_aux(radar11125868_cb cb);
+
+int rdar11125868() {
+ int integersStackArray[1];
+ int *integers = integersStackArray;
+ rdar11125868_aux(^(int *integerValue, unsigned index) {
+ integers[index] = integerValue[index];
+ });
+ return integers[0] == 0; // no-warning
+}
+
+int rdar11125868_positive() {
+ int integersStackArray[1];
+ int *integers = integersStackArray;
+ return integers[0] == 0; // expected-warning {{he left operand of '==' is a}}
+}
diff --git a/test/Analysis/misc-ps.c b/test/Analysis/misc-ps.c
index f81b0dd..8ff710b 100644
--- a/test/Analysis/misc-ps.c
+++ b/test/Analysis/misc-ps.c
@@ -126,3 +126,10 @@ void rdar10686586() {
}
}
+// This example tests CFG handling of '||' nested in a ternary expression,
+// and seeing that the analyzer doesn't crash.
+int isctype(char c, unsigned long f)
+{
+ return (c < 1 || c > 10) ? 0 : !!(c & f);
+}
+
diff --git a/test/Analysis/misc-ps.m b/test/Analysis/misc-ps.m
index 9d2ff5b..bcc0472 100644
--- a/test/Analysis/misc-ps.m
+++ b/test/Analysis/misc-ps.m
@@ -1,8 +1,8 @@
// NOTE: Use '-fobjc-gc' to test the analysis being run twice, and multiple reports are not issued.
-// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync -analyzer-store=region -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
-// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync -analyzer-store=region -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync -analyzer-store=region -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync -analyzer-store=region -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync,osx.AtomicCAS -analyzer-store=region -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync,osx.AtomicCAS -analyzer-store=region -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync,osx.AtomicCAS -analyzer-store=region -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,experimental.deadcode.IdempotentOperations,experimental.core,osx.cocoa.AtSync,osx.AtomicCAS -analyzer-store=region -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code -Wno-null-dereference -Wno-objc-root-class %s
#ifndef __clang_analyzer__
#error __clang_analyzer__ not defined
@@ -494,6 +494,17 @@ int OSAtomicCompareAndSwap32Barrier();
}
@end
+// Do not crash when performing compare and swap on symbolic values.
+typedef int int32_t;
+typedef int int32;
+typedef int32 Atomic32;
+int OSAtomicCompareAndSwap32( int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue);
+void radar11390991_NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ OSAtomicCompareAndSwap32(old_value, new_value, ptr);
+}
+
// PR 4594 - This was a crash when handling casts in SimpleSValuator.
void PR4594() {
char *buf[1];
@@ -1345,3 +1356,20 @@ void radar9414427() {
}
@end
+// Don't crash when a ?: is only preceded by a statement (not an expression)
+// in the CFG.
+void __assert_fail();
+
+enum rdar1196620_e { E_A, E_B, E_C, E_D };
+struct rdar1196620_s { int ints[E_D+1]; };
+
+static void rdar1196620_call_assert(struct rdar1196620_s* s) {
+ int i = 0;
+ s?(void)0:__assert_fail();
+}
+
+static void rdar1196620(struct rdar1196620_s* s) {
+ rdar1196620_call_assert(s);
+}
+
+
diff --git a/test/Analysis/new.cpp b/test/Analysis/new.cpp
index 5ca8c46..fb77de2 100644
--- a/test/Analysis/new.cpp
+++ b/test/Analysis/new.cpp
@@ -1,15 +1,100 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region -verify %s
-// XFAIL: *
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store region -std=c++11 -verify %s
-void f1() {
- int *n = new int;
- if (*n) { // expected-warning {{Branch condition evaluates to a garbage value}}
- }
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+extern "C" void *malloc(size_t);
+
+int someGlobal;
+void testImplicitlyDeclaredGlobalNew() {
+ if (someGlobal != 0)
+ return;
+
+ // This used to crash because the global operator new is being implicitly
+ // declared and it does not have a valid source location. (PR13090)
+ void *x = ::operator new(0);
+ ::operator delete(x);
+
+ // Check that the new/delete did not invalidate someGlobal;
+ clang_analyzer_eval(someGlobal == 0); // expected-warning{{TRUE}}
+}
+
+
+// This is the standard placement new.
+inline void* operator new(size_t, void* __p) throw()
+{
+ return __p;
+}
+
+void *testPlacementNew() {
+ int *x = (int *)malloc(sizeof(int));
+ *x = 1;
+ clang_analyzer_eval(*x == 1); // expected-warning{{TRUE}};
+
+ void *y = new (x) int;
+ clang_analyzer_eval(x == y); // expected-warning{{TRUE}};
+ clang_analyzer_eval(*x == 1); // expected-warning{{UNKNOWN}};
+
+ return y;
+}
+
+void *operator new(size_t, size_t, int *);
+void *testCustomNew() {
+ int x[1] = {1};
+ clang_analyzer_eval(*x == 1); // expected-warning{{TRUE}};
+
+ void *y = new (0, x) int;
+ clang_analyzer_eval(*x == 1); // expected-warning{{UNKNOWN}};
+
+ return y; // no-warning
}
-void f2() {
+void *operator new(size_t, void *, void *);
+void *testCustomNewMalloc() {
+ int *x = (int *)malloc(sizeof(int));
+
+ // Should be no-warning (the custom allocator could have freed x).
+ void *y = new (0, x) int; // no-warning
+
+ return y;
+}
+
+void testScalarInitialization() {
int *n = new int(3);
+ clang_analyzer_eval(*n == 3); // expected-warning{{TRUE}}
+
+ new (n) int();
+ clang_analyzer_eval(*n == 0); // expected-warning{{TRUE}}
+
+ new (n) int{3};
+ clang_analyzer_eval(*n == 3); // expected-warning{{TRUE}}
+
+ new (n) int{};
+ clang_analyzer_eval(*n == 0); // expected-warning{{TRUE}}
+}
+
+
+//--------------------------------
+// Incorrectly-modelled behavior
+//--------------------------------
+
+int testNoInitialization() {
+ int *n = new int;
+
+ // Should warn that *n is uninitialized.
if (*n) { // no-warning
+ return 0;
}
+ return 1;
}
+int testNoInitializationPlacement() {
+ int n;
+ new (&n) int;
+
+ // Should warn that n is uninitialized.
+ if (n) { // no-warning
+ return 0;
+ }
+ return 1;
+}
diff --git a/test/Analysis/nonnull.m b/test/Analysis/nonnull.m
new file mode 100644
index 0000000..c32a7f7
--- /dev/null
+++ b/test/Analysis/nonnull.m
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify %s
+
+@interface MyObject
+- (void)takePointer:(void *)ptr __attribute__((nonnull(1)));
+@end
+
+void testNonNullMethod(int *p, MyObject *obj) {
+ if (p)
+ return;
+ [obj takePointer:p]; // expected-warning{{nonnull}}
+}
+
+
+@interface Subclass : MyObject
+// [[nonnull]] is an inherited attribute.
+- (void)takePointer:(void *)ptr;
+@end
+
+void testSubclass(int *p, Subclass *obj) {
+ if (p)
+ return;
+ [obj takePointer:p]; // expected-warning{{nonnull}}
+}
diff --git a/test/Analysis/nullptr.cpp b/test/Analysis/nullptr.cpp
index 3119b4f..050c3f8 100644
--- a/test/Analysis/nullptr.cpp
+++ b/test/Analysis/nullptr.cpp
@@ -55,7 +55,7 @@ void zoo2() {
int **a = 0;
int **b = 0;
asm ("nop"
- :"=a"(*a)
+ :"=r"(*a)
:"0"(*b) // expected-warning{{Dereference of null pointer}}
);
}
diff --git a/test/Analysis/objc-boxing.m b/test/Analysis/objc-boxing.m
new file mode 100644
index 0000000..1691578
--- /dev/null
+++ b/test/Analysis/objc-boxing.m
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify %s
+
+typedef signed char BOOL;
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+@interface NSString @end
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
+@end
+
+@interface NSNumber
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value ;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value ;
+@end
+
+
+extern char *strdup(const char *str);
+
+id constant_string() {
+ return @("boxed constant string.");
+}
+
+id dynamic_string() {
+ return @(strdup("boxed dynamic string")); // expected-warning{{Memory is never released; potential leak}}
+}
+
+id const_char_pointer(int *x) {
+ if (x)
+ return @(3);
+ return @(*x); // expected-warning {{Dereference of null pointer (loaded from variable 'x')}}
+} \ No newline at end of file
diff --git a/test/Analysis/objc-for.m b/test/Analysis/objc-for.m
new file mode 100644
index 0000000..52a55b0
--- /dev/null
+++ b/test/Analysis/objc-for.m
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Loops,debug.ExprInspection -verify
+
+void clang_analyzer_eval(int);
+
+#define nil ((id)0)
+
+@protocol NSFastEnumeration
+- (int)countByEnumeratingWithState:(void *)state objects:(id *)objects count:(unsigned)count;
+@end
+
+@interface NSObject
++ (instancetype)testObject;
+@end
+
+@interface NSEnumerator <NSFastEnumeration>
+@end
+
+@interface NSArray : NSObject <NSFastEnumeration>
+- (NSEnumerator *)objectEnumerator;
+@end
+
+@interface NSDictionary : NSObject <NSFastEnumeration>
+@end
+
+@interface NSMutableDictionary : NSDictionary
+@end
+
+@interface NSSet : NSObject <NSFastEnumeration>
+@end
+
+@interface NSPointerArray : NSObject <NSFastEnumeration>
+@end
+
+void test() {
+ id x;
+ for (x in [NSArray testObject])
+ clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}
+
+ for (x in [NSMutableDictionary testObject])
+ clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}
+
+ for (x in [NSSet testObject])
+ clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}
+
+ for (x in [[NSArray testObject] objectEnumerator])
+ clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}
+
+ for (x in [NSPointerArray testObject])
+ clang_analyzer_eval(x != nil); // expected-warning{{UNKNOWN}}
+}
+
+void testWithVarInFor() {
+ for (id x in [NSArray testObject])
+ clang_analyzer_eval(x != nil); // expected-warning{{TRUE}}
+ for (id x in [NSPointerArray testObject])
+ clang_analyzer_eval(x != nil); // expected-warning{{UNKNOWN}}
+}
+
diff --git a/test/Analysis/objc-subscript.m b/test/Analysis/objc-subscript.m
new file mode 100644
index 0000000..324bf1c
--- /dev/null
+++ b/test/Analysis/objc-subscript.m
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-store=region -verify -Wno-objc-root-class %s
+
+typedef signed char BOOL;
+typedef unsigned int NSUInteger;
+
+@interface NSObject
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+-(id)retain;
+@end
+
+@interface Subscriptable : NSObject
+- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index;
+- (id)objectAtIndexedSubscript:(NSUInteger)index;
+
+- (void)setObject:(id)obj forKeyedSubscript:(id)key;
+- (id)objectForKeyedSubscript:(id)key;
+@end
+
+@interface Test : Subscriptable
+@end
+
+@implementation Test
+
+// <rdar://problem/6946338> for subscripting
+- (id)storeDoesNotRetain {
+ Test *cell = [[[Test alloc] init] autorelease];
+
+ NSObject *string1 = [[NSObject alloc] init]; // expected-warning {{Potential leak}}
+ cell[0] = string1;
+ cell[self] = string1;
+ cell[string1] = self;
+
+ return cell;
+}
+
+// <rdar://problem/8824416> for subscripting
+- (id)getDoesNotRetain:(BOOL)keyed {
+ if (keyed)
+ return [self[self] autorelease]; // expected-warning{{Object sent -autorelease too many times}}
+ else
+ return [self[0] autorelease]; // expected-warning{{Object sent -autorelease too many times}}
+}
+
+// <rdar://problem/9241180> for subscripting
+- (id)testUninitializedObject:(BOOL)keyed {
+ Test *o;
+ if (keyed) {
+ if (o[self]) // expected-warning {{Subscript access on an uninitialized object pointer}}
+ return o; // no-warning (sink)
+ } else {
+ if (o[0]) // expected-warning {{Subscript access on an uninitialized object pointer}}
+ return o; // no-warning (sink)
+ }
+ return self;
+}
+
+- (void)testUninitializedArgument:(id)input testCase:(unsigned)testCase {
+ NSUInteger i;
+ id o;
+
+ switch (testCase) {
+ case 0:
+ self[0] = o; // expected-warning {{Argument for subscript setter is an uninitialized value}}
+ break;
+ case 1:
+ self[i] = input; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ case 2:
+ (void)self[i]; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ case 3:
+ self[input] = o; // expected-warning {{Argument for subscript setter is an uninitialized value}}
+ break;
+ case 4:
+ self[o] = input; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ case 5:
+ (void)self[o]; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ default:
+ break;
+ }
+
+}
+
+@end
diff --git a/test/Analysis/operator-calls.cpp b/test/Analysis/operator-calls.cpp
index 73cd28a..dbc63bc 100644
--- a/test/Analysis/operator-calls.cpp
+++ b/test/Analysis/operator-calls.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-ipa=inlining -verify %s
+void clang_analyzer_eval(bool);
+
struct X0 { };
bool operator==(const X0&, const X0&);
@@ -14,3 +16,17 @@ void t2() {
bool PR7287(X0 a, X0 b) {
return operator==(a, b);
}
+
+
+// Inlining non-static member operators mistakenly treated 'this' as the first
+// argument for a while.
+
+struct IntComparable {
+ bool operator==(int x) const {
+ return x == 0;
+ }
+};
+
+void testMemberOperator(IntComparable B) {
+ clang_analyzer_eval(B == 0); // expected-warning{{TRUE}}
+}
diff --git a/test/Analysis/out-of-bounds.c b/test/Analysis/out-of-bounds.c
index a97bba5..c172170 100644
--- a/test/Analysis/out-of-bounds.c
+++ b/test/Analysis/out-of-bounds.c
@@ -128,11 +128,13 @@ void test2_multi_ok(int x) {
buf[0][0] = 1; // no-warning
}
-// Testing if solver handles (symbol * constant) < constant
+// *** FIXME ***
+// We don't get a warning here yet because our symbolic constraint solving
+// doesn't handle: (symbol * constant) < constant
void test3(int x) {
int buf[100];
if (x < 0)
- buf[x] = 1; // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}}
+ buf[x] = 1;
}
// *** FIXME ***
diff --git a/test/Analysis/outofbound-notwork.c b/test/Analysis/outofbound-notwork.c
index 45e713b..c1fa2d7 100644
--- a/test/Analysis/outofbound-notwork.c
+++ b/test/Analysis/outofbound-notwork.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-checker=core,experimental.unix,experimental.security.ArrayBound -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-checker=core,experimental.security.ArrayBound -analyzer-store=region -verify %s
// XFAIL: *
// Once we better handle modeling of sizes of VLAs, we can pull this back
diff --git a/test/Analysis/outofbound.c b/test/Analysis/outofbound.c
index 2e7a7d3..2783ac2 100644
--- a/test/Analysis/outofbound.c
+++ b/test/Analysis/outofbound.c
@@ -86,3 +86,39 @@ int symbolic_index2(int a) {
}
return 0;
}
+
+int overflow_binary_search(double in) {
+ int eee = 16;
+ if (in < 1e-8 || in > 1e23) {
+ return 0;
+ } else {
+ static const double ins[] = {1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
+ 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
+ 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
+ if (in < ins[eee]) {
+ eee -= 8;
+ } else {
+ eee += 8;
+ }
+ if (in < ins[eee]) {
+ eee -= 4;
+ } else {
+ eee += 4;
+ }
+ if (in < ins[eee]) {
+ eee -= 2;
+ } else {
+ eee += 2;
+ }
+ if (in < ins[eee]) {
+ eee -= 1;
+ } else {
+ eee += 1;
+ }
+ if (in < ins[eee]) { // expected-warning {{Access out-of-bound array element (buffer overflow)}}
+ eee -= 1;
+ }
+ }
+ return eee;
+}
diff --git a/test/Analysis/plist-output-alternate.m b/test/Analysis/plist-output-alternate.m
index 83100dc..a338a79 100644
--- a/test/Analysis/plist-output-alternate.m
+++ b/test/Analysis/plist-output-alternate.m
@@ -82,7 +82,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>5</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -137,6 +137,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_init</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>6</integer>
@@ -161,7 +162,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -216,6 +217,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_assign</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>12</integer>
@@ -240,7 +242,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>16</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -295,6 +297,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_assign_transitive</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>19</integer>
@@ -319,7 +322,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>23</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -332,7 +335,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>23</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -382,7 +385,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>23</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -437,6 +440,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_cond</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>24</integer>
@@ -461,7 +465,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -474,7 +478,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -495,7 +499,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -508,7 +512,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>30</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -529,7 +533,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>30</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -584,6 +588,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_cond_transitive</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>31</integer>
@@ -608,7 +613,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>36</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -697,6 +702,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_field</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>38</integer>
@@ -721,7 +727,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>53</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -734,7 +740,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>54</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -755,7 +761,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>54</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -768,7 +774,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>54</integer>
-// CHECK: <key>col</key><integer>82</integer>
+// CHECK: <key>col</key><integer>36</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -818,7 +824,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>54</integer>
-// CHECK: <key>col</key><integer>82</integer>
+// CHECK: <key>col</key><integer>36</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -831,7 +837,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>55</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -852,7 +858,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>55</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -899,7 +905,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
-// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -920,7 +926,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
-// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -960,6 +966,7 @@ void rdar8331641(int x) {
// CHECK: <key>type</key><string>Leak</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>rdar8331641</string>
+// CHECK: <key>issue_hash</key><integer>6</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>58</integer>
diff --git a/test/Analysis/plist-output.m b/test/Analysis/plist-output.m
index 72e8f8d..769c1bc 100644
--- a/test/Analysis/plist-output.m
+++ b/test/Analysis/plist-output.m
@@ -1,5 +1,4 @@
-// RUN: %clang --analyze %s -o %t > /dev/null 2>&1
-// RUN: FileCheck -input-file %t %s
+// RUN: %clang --analyze %s -o - 2>/dev/null | FileCheck %s
void test_null_init(void) {
int *p = 0;
@@ -27,7 +26,6 @@ void test_null_cond(int *p) {
void test_null_cond_transitive(int *q) {
if (!q) {
- // FIXME: we need a diagnostic saying that p is initialized to 0
int *p = q;
*p = 0xDEADBEEF;
}
@@ -81,10 +79,12 @@ int test_cond_assign() {
@end
// CHECK: <?xml version="1.0" encoding="UTF-8"?>
+// CHECK: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
// CHECK: <plist version="1.0">
// CHECK: <dict>
// CHECK: <key>files</key>
// CHECK: <array>
+// CHECK: <string>{{.*}}plist-output.m</string>
// CHECK: </array>
// CHECK: <key>diagnostics</key>
// CHECK: <array>
@@ -92,6 +92,35 @@ int test_cond_assign() {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@@ -99,25 +128,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>5</integer>
+// CHECK: <key>line</key><integer>4</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>5</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>4</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>6</integer>
+// CHECK: <key>line</key><integer>5</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>6</integer>
+// CHECK: <key>line</key><integer>5</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -129,7 +158,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>6</integer>
+// CHECK: <key>line</key><integer>5</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -137,12 +166,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>6</integer>
+// CHECK: <key>line</key><integer>5</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>6</integer>
+// CHECK: <key>line</key><integer>5</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -160,9 +189,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_init</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>6</integer>
+// CHECK: <key>line</key><integer>5</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -178,6 +208,69 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>9</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>10</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Null pointer value stored to &apos;p&apos;</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Null pointer value stored to &apos;p&apos;</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
@@ -191,12 +284,12 @@ int test_cond_assign() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -208,7 +301,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -216,12 +309,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -239,9 +332,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_assign</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>12</integer>
+// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -257,25 +351,88 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>16</integer>
+// CHECK: <key>line</key><integer>15</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>16</integer>
+// CHECK: <key>line</key><integer>15</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;q&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;q&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>17</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -287,7 +444,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -295,12 +452,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -318,9 +475,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_assign_transitive</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>19</integer>
+// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -336,26 +494,26 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>line</key><integer>22</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>line</key><integer>22</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -366,7 +524,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>line</key><integer>22</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -374,12 +532,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>line</key><integer>22</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>line</key><integer>22</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -392,6 +550,35 @@ int test_cond_assign() {
// CHECK: <string>Assuming &apos;p&apos; is null</string>
// CHECK: </dict>
// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Assuming pointer value is null</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Assuming pointer value is null</string>
+// CHECK: </dict>
+// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@@ -399,25 +586,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
+// CHECK: <key>line</key><integer>22</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>23</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>line</key><integer>22</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>line</key><integer>23</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>line</key><integer>23</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -429,7 +616,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>line</key><integer>23</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -437,12 +624,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>line</key><integer>23</integer>
// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>line</key><integer>23</integer>
// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -460,9 +647,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_cond</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>24</integer>
+// CHECK: <key>line</key><integer>23</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -478,26 +666,26 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>line</key><integer>28</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>29</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>line</key><integer>28</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>29</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -512,26 +700,26 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>line</key><integer>28</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>29</integer>
-// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>line</key><integer>28</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>31</integer>
+// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>31</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -539,6 +727,35 @@ int test_cond_assign() {
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@@ -546,25 +763,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>31</integer>
+// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>31</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>line</key><integer>29</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>line</key><integer>30</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>line</key><integer>30</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -576,7 +793,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>line</key><integer>30</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -584,12 +801,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>line</key><integer>30</integer>
// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>line</key><integer>30</integer>
// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -607,9 +824,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_cond_transitive</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>32</integer>
+// CHECK: <key>line</key><integer>30</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -625,25 +843,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>line</key><integer>35</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>37</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>35</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>line</key><integer>35</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>line</key><integer>35</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -659,12 +877,12 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>line</key><integer>35</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>37</integer>
+// CHECK: <key>line</key><integer>35</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -672,12 +890,12 @@ int test_cond_assign() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>39</integer>
+// CHECK: <key>line</key><integer>37</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>39</integer>
+// CHECK: <key>line</key><integer>37</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -689,7 +907,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>39</integer>
+// CHECK: <key>line</key><integer>37</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -697,12 +915,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>39</integer>
+// CHECK: <key>line</key><integer>37</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>39</integer>
+// CHECK: <key>line</key><integer>37</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -720,9 +938,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_null_field</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>39</integer>
+// CHECK: <key>line</key><integer>37</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -738,25 +957,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>44</integer>
+// CHECK: <key>line</key><integer>42</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>44</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>44</integer>
+// CHECK: <key>line</key><integer>42</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>44</integer>
+// CHECK: <key>line</key><integer>42</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -772,12 +991,12 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>44</integer>
+// CHECK: <key>line</key><integer>42</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>44</integer>
+// CHECK: <key>line</key><integer>42</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -785,13 +1004,13 @@ int test_cond_assign() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -806,25 +1025,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -840,12 +1059,12 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -853,13 +1072,13 @@ int test_cond_assign() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>line</key><integer>48</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -867,6 +1086,35 @@ int test_cond_assign() {
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: </dict>
+// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@@ -874,25 +1122,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>line</key><integer>48</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>50</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>49</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>49</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -904,7 +1152,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>49</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -912,12 +1160,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>49</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>49</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -935,9 +1183,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_assumptions</string>
+// CHECK: <key>issue_hash</key><integer>8</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>line</key><integer>49</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -953,26 +1202,26 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>line</key><integer>54</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>56</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>54</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -987,25 +1236,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1017,7 +1266,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1025,12 +1274,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1050,12 +1299,12 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1063,13 +1312,13 @@ int test_cond_assign() {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1084,26 +1333,26 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
-// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@@ -1114,7 +1363,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1122,12 +1371,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1145,9 +1394,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_cond_assign</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1163,99 +1413,60 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>line</key><integer>74</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>line</key><integer>74</integer>
+// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>7</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>7</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>kind</key><string>control</string>
-// CHECK: <key>edges</key>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>start</key>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>7</integer>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>line</key><integer>75</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
-// CHECK: <key>end</key>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>3</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>3</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>kind</key><string>control</string>
-// CHECK: <key>edges</key>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>start</key>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>3</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>76</integer>
-// CHECK: <key>col</key><integer>3</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: <key>end</key>
-// CHECK: <array>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>77</integer>
-// CHECK: <key>col</key><integer>5</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: <dict>
-// CHECK: <key>line</key><integer>77</integer>
-// CHECK: <key>col</key><integer>5</integer>
-// CHECK: <key>file</key><integer>0</integer>
-// CHECK: </dict>
-// CHECK: </array>
-// CHECK: </dict>
-// CHECK: </array>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
@@ -1265,25 +1476,25 @@ int test_cond_assign() {
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>line</key><integer>75</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>77</integer>
-// CHECK: <key>col</key><integer>5</integer>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>78</integer>
+// CHECK: <key>line</key><integer>76</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>78</integer>
+// CHECK: <key>line</key><integer>76</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1295,7 +1506,7 @@ int test_cond_assign() {
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>78</integer>
+// CHECK: <key>line</key><integer>76</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1303,12 +1514,12 @@ int test_cond_assign() {
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>78</integer>
+// CHECK: <key>line</key><integer>76</integer>
// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>78</integer>
+// CHECK: <key>line</key><integer>76</integer>
// CHECK: <key>col</key><integer>6</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1326,9 +1537,10 @@ int test_cond_assign() {
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
// CHECK: <key>issue_context</key><string>test</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
// CHECK: <key>location</key>
// CHECK: <dict>
-// CHECK: <key>line</key><integer>78</integer>
+// CHECK: <key>line</key><integer>76</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
@@ -1336,4 +1548,3 @@ int test_cond_assign() {
// CHECK: </array>
// CHECK: </dict>
// CHECK: </plist>
-
diff --git a/test/Analysis/ptr-arith.c b/test/Analysis/ptr-arith.c
index fb37f1c..6567000 100644
--- a/test/Analysis/ptr-arith.c
+++ b/test/Analysis/ptr-arith.c
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.core.FixedAddr,experimental.core.PointerArithm,experimental.core.PointerSub -analyzer-store=region -verify -triple x86_64-apple-darwin9 %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.core.FixedAddr,experimental.core.PointerArithm,experimental.core.PointerSub -analyzer-store=region -verify -triple i686-apple-darwin9 %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.core.FixedAddr,experimental.core.PointerArithm,experimental.core.PointerSub,debug.ExprInspection -analyzer-store=region -verify -triple x86_64-apple-darwin9 %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.core.FixedAddr,experimental.core.PointerArithm,experimental.core.PointerSub,debug.ExprInspection -analyzer-store=region -verify -triple i686-apple-darwin9 %s
-// Used to trigger warnings for unreachable paths.
-#define WARN do { int a, b; int c = &b-&a; } while (0)
+void clang_analyzer_eval(int);
void f1() {
int a[10];
@@ -67,111 +66,48 @@ void f6(int *p, int *q) {
void null_operand(int *a) {
start:
// LHS is a label, RHS is NULL
- if (&&start == 0)
- WARN; // no-warning
- if (&&start < 0)
- WARN; // no-warning
- if (&&start <= 0)
- WARN; // no-warning
- if (!(&&start != 0))
- WARN; // no-warning
- if (!(&&start > 0))
- WARN; // no-warning
- if (!(&&start >= 0))
- WARN; // no-warning
- if (!(&&start - 0))
- WARN; // no-warning
+ clang_analyzer_eval(&&start != 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&&start >= 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&&start > 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval((&&start - 0) != 0); // expected-warning{{TRUE}}
// LHS is a non-symbolic value, RHS is NULL
- if (&a == 0)
- WARN; // no-warning
- if (&a < 0)
- WARN; // no-warning
- if (&a <= 0)
- WARN; // no-warning
- if (!(&a != 0))
- WARN; // no-warning
- if (!(&a > 0))
- WARN; // no-warning
- if (!(&a >= 0))
- WARN; // no-warning
-
- if (!(&a - 0)) // expected-warning{{Pointer arithmetic done on non-array variables}}
- WARN; // no-warning
+ clang_analyzer_eval(&a != 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a >= 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a > 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval((&a - 0) != 0); // expected-warning{{TRUE}} expected-warning{{Pointer arithmetic done on non-array variables}}
// LHS is NULL, RHS is non-symbolic
// The same code is used for labels and non-symbolic values.
- if (0 == &a)
- WARN; // no-warning
- if (0 > &a)
- WARN; // no-warning
- if (0 >= &a)
- WARN; // no-warning
- if (!(0 != &a))
- WARN; // no-warning
- if (!(0 < &a))
- WARN; // no-warning
- if (!(0 <= &a))
- WARN; // no-warning
+ clang_analyzer_eval(0 != &a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(0 <= &a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(0 < &a); // expected-warning{{TRUE}}
// LHS is a symbolic value, RHS is NULL
- if (a == 0)
- WARN; // expected-warning{{}}
- if (a < 0)
- WARN; // no-warning
- if (a <= 0)
- WARN; // expected-warning{{}}
- if (!(a != 0))
- WARN; // expected-warning{{}}
- if (!(a > 0))
- WARN; // expected-warning{{}}
- if (!(a >= 0))
- WARN; // no-warning
- if (!(a - 0))
- WARN; // expected-warning{{}}
+ clang_analyzer_eval(a != 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a >= 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a <= 0); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval((a - 0) != 0); // expected-warning{{UNKNOWN}}
// LHS is NULL, RHS is a symbolic value
- if (0 == a)
- WARN; // expected-warning{{}}
- if (0 > a)
- WARN; // no-warning
- if (0 >= a)
- WARN; // expected-warning{{}}
- if (!(0 != a))
- WARN; // expected-warning{{}}
- if (!(0 < a))
- WARN; // expected-warning{{}}
- if (!(0 <= a))
- WARN; // no-warning
+ clang_analyzer_eval(0 != a); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(0 <= a); // expected-warning{{TRUE}}
+ clang_analyzer_eval(0 < a); // expected-warning{{UNKNOWN}}
}
void const_locs() {
char *a = (char*)0x1000;
char *b = (char*)0x1100;
start:
- if (a==b)
- WARN; // no-warning
- if (!(a!=b))
- WARN; // no-warning
- if (a>b)
- WARN; // no-warning
- if (b<a)
- WARN; // no-warning
- if (a>=b)
- WARN; // no-warning
- if (b<=a)
- WARN; // no-warning
- if (b-a != 0x100)
- WARN; // no-warning
-
- if (&&start == a)
- WARN; // expected-warning{{}}
- if (a == &&start)
- WARN; // expected-warning{{}}
- if (&a == (char**)a)
- WARN; // expected-warning{{}}
- if ((char**)a == &a)
- WARN; // expected-warning{{}}
+ clang_analyzer_eval(a != b); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a < b); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a <= b); // expected-warning{{TRUE}}
+ clang_analyzer_eval((b-a) == 0x100); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(&&start == a); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == &&start); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(&a == (char**)a); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval((char**)a == &a); // expected-warning{{UNKNOWN}}
}
void array_matching_types() {
@@ -179,20 +115,10 @@ void array_matching_types() {
int *a = &array[2];
int *b = &array[5];
- if (a==b)
- WARN; // no-warning
- if (!(a!=b))
- WARN; // no-warning
- if (a>b)
- WARN; // no-warning
- if (b<a)
- WARN; // no-warning
- if (a>=b)
- WARN; // no-warning
- if (b<=a)
- WARN; // no-warning
- if ((b-a) == 0)
- WARN; // no-warning
+ clang_analyzer_eval(a != b); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a < b); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a <= b); // expected-warning{{TRUE}}
+ clang_analyzer_eval((b-a) != 0); // expected-warning{{TRUE}}
}
// This takes a different code path than array_matching_types()
@@ -201,49 +127,22 @@ void array_different_types() {
int *a = &array[2];
char *b = (char*)&array[5];
- if (a==b) // expected-warning{{comparison of distinct pointer types}}
- WARN; // no-warning
- if (!(a!=b)) // expected-warning{{comparison of distinct pointer types}}
- WARN; // no-warning
- if (a>b) // expected-warning{{comparison of distinct pointer types}}
- WARN; // no-warning
- if (b<a) // expected-warning{{comparison of distinct pointer types}}
- WARN; // no-warning
- if (a>=b) // expected-warning{{comparison of distinct pointer types}}
- WARN; // no-warning
- if (b<=a) // expected-warning{{comparison of distinct pointer types}}
- WARN; // no-warning
+ clang_analyzer_eval(a != b); // expected-warning{{TRUE}} expected-warning{{comparison of distinct pointer types}}
+ clang_analyzer_eval(a < b); // expected-warning{{TRUE}} expected-warning{{comparison of distinct pointer types}}
+ clang_analyzer_eval(a <= b); // expected-warning{{TRUE}} expected-warning{{comparison of distinct pointer types}}
}
struct test { int x; int y; };
void struct_fields() {
struct test a, b;
- if (&a.x == &a.y)
- WARN; // no-warning
- if (!(&a.x != &a.y))
- WARN; // no-warning
- if (&a.x > &a.y)
- WARN; // no-warning
- if (&a.y < &a.x)
- WARN; // no-warning
- if (&a.x >= &a.y)
- WARN; // no-warning
- if (&a.y <= &a.x)
- WARN; // no-warning
+ clang_analyzer_eval(&a.x != &a.y); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a.x < &a.y); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a.x <= &a.y); // expected-warning{{TRUE}}
- if (&a.x == &b.x)
- WARN; // no-warning
- if (!(&a.x != &b.x))
- WARN; // no-warning
- if (&a.x > &b.x)
- WARN; // expected-warning{{}}
- if (&b.x < &a.x)
- WARN; // expected-warning{{}}
- if (&a.x >= &b.x)
- WARN; // expected-warning{{}}
- if (&b.x <= &a.x)
- WARN; // expected-warning{{}}
+ clang_analyzer_eval(&a.x != &b.x); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a.x > &b.x); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(&a.x >= &b.x); // expected-warning{{UNKNOWN}}
}
void mixed_region_types() {
@@ -251,35 +150,17 @@ void mixed_region_types() {
int array[2];
void *a = &array, *b = &s;
- if (&a == &b)
- WARN; // no-warning
- if (!(&a != &b))
- WARN; // no-warning
- if (&a > &b)
- WARN; // expected-warning{{}}
- if (&b < &a)
- WARN; // expected-warning{{}}
- if (&a >= &b)
- WARN; // expected-warning{{}}
- if (&b <= &a)
- WARN; // expected-warning{{}}
+ clang_analyzer_eval(&a != &b); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a > &b); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(&a >= &b); // expected-warning{{UNKNOWN}}
}
void symbolic_region(int *p) {
int a;
- if (&a == p)
- WARN; // no-warning
- if (&a != p)
- WARN; // expected-warning{{}}
- if (&a > p)
- WARN; // expected-warning{{}}
- if (&a < p)
- WARN; // expected-warning{{}}
- if (&a >= p)
- WARN; // expected-warning{{}}
- if (&a <= p)
- WARN; // expected-warning{{}}
+ clang_analyzer_eval(&a != p); // expected-warning{{TRUE}}
+ clang_analyzer_eval(&a > p); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(&a >= p); // expected-warning{{UNKNOWN}}
}
void PR7527 (int *p) {
diff --git a/test/Analysis/rdar-7168531.m b/test/Analysis/rdar-7168531.m
index 1516255..4ccc7d7 100644
--- a/test/Analysis/rdar-7168531.m
+++ b/test/Analysis/rdar-7168531.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -triple i386-apple-darwin10 -fobjc-fragile-abi -analyzer-store=region %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -analyzer-store=region %s
// Note that the target triple is important for this test case. It specifies that we use the
// fragile Objective-C ABI.
diff --git a/test/Analysis/refcnt_naming.m b/test/Analysis/refcnt_naming.m
index aff713b..1d3a9b7 100644
--- a/test/Analysis/refcnt_naming.m
+++ b/test/Analysis/refcnt_naming.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,experimental.core -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,experimental.core -analyzer-ipa=none -analyzer-store=region -verify %s
typedef const struct __CFString * CFStringRef;
typedef const struct __CFAllocator * CFAllocatorRef;
diff --git a/test/Analysis/reference.cpp b/test/Analysis/reference.cpp
index 5897e68..06e4a50 100644
--- a/test/Analysis/reference.cpp
+++ b/test/Analysis/reference.cpp
@@ -1,11 +1,12 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -analyzer-store=region -analyzer-constraints=range -verify -Wno-null-dereference %s
-// XFAIL
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=range -verify -Wno-null-dereference %s
+
+void clang_analyzer_eval(bool);
typedef typeof(sizeof(int)) size_t;
void malloc (size_t);
void f1() {
- int const &i = 3; // <--- **FIXME** This is currently not being modeled correctly.
+ int const &i = 3;
int b = i;
int *p = 0;
@@ -56,3 +57,75 @@ char t6 (char* p) {
if (*p) return *p;
return *(char*)0; // no-warning
}
+
+
+// PR13440 / <rdar://problem/11977113>
+// Test that the array-to-pointer decay works for array references as well.
+// More generally, when we want an lvalue for a reference field, we still need
+// to do one level of load.
+namespace PR13440 {
+ typedef int T[1];
+ struct S {
+ T &x;
+
+ int *m() { return x; }
+ };
+
+ struct S2 {
+ int (&x)[1];
+
+ int *m() { return x; }
+ };
+
+ void test() {
+ int a[1];
+ S s = { a };
+ S2 s2 = { a };
+
+ if (s.x != a) return;
+ if (s2.x != a) return;
+
+ a[0] = 42;
+ clang_analyzer_eval(s.x[0] == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(s2.x[0] == 42); // expected-warning{{TRUE}}
+ }
+}
+
+void testNullReference() {
+ int *x = 0;
+ int &y = *x; // expected-warning{{Dereference of null pointer}}
+ y = 5;
+}
+
+void testRetroactiveNullReference(int *x) {
+ // According to the C++ standard, there is no such thing as a
+ // "null reference". So the 'if' statement ought to be dead code.
+ // However, Clang (and other compilers) don't actually check that a pointer
+ // value is non-null in the implementation of references, so it is possible
+ // to produce a supposed "null reference" at runtime. The analyzer shoeuld
+ // still warn when it can prove such errors.
+ int &y = *x;
+ if (x != 0)
+ return;
+ y = 5; // expected-warning{{Dereference of null pointer}}
+}
+
+
+// ------------------------------------
+// False negatives
+// ------------------------------------
+
+namespace rdar11212286 {
+ class B{};
+
+ B test() {
+ B *x = 0;
+ return *x; // should warn here!
+ }
+
+ B &testRef() {
+ B *x = 0;
+ return *x; // should warn here!
+ }
+
+}
diff --git a/test/Analysis/region-store.c b/test/Analysis/region-store.c
new file mode 100644
index 0000000..09c3f10
--- /dev/null
+++ b/test/Analysis/region-store.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix -verify %s
+
+int printf(const char *restrict,...);
+
+// Testing core functionality of the region store.
+// radar://10127782
+int compoundLiteralTest() {
+ int index = 0;
+ for (index = 0; index < 2; index++) {
+ int thing = (int []){0, 1}[index];
+ printf("thing: %i\n", thing);
+ }
+ return 0;
+}
+
+int compoundLiteralTest2() {
+ int index = 0;
+ for (index = 0; index < 3; index++) {
+ int thing = (int [][3]){{0,0,0}, {1,1,1}, {2,2,2}}[index][index];
+ printf("thing: %i\n", thing);
+ }
+ return 0;
+}
diff --git a/test/Analysis/retain-release-path-notes-gc.m b/test/Analysis/retain-release-path-notes-gc.m
index 1e74f00..feee525 100644
--- a/test/Analysis/retain-release-path-notes-gc.m
+++ b/test/Analysis/retain-release-path-notes-gc.m
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=text -fobjc-gc-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -fobjc-gc-only -analyzer-output=text -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -fobjc-gc-only -analyzer-output=plist-multi-file %s -o - | FileCheck %s
/***
This file is for testing the path-sensitive notes for retain/release errors.
@@ -71,3 +72,1339 @@ void retainReleaseIgnored () {
}
@end
+
+// CHECK: <?xml version="1.0" encoding="UTF-8"?>
+// CHECK: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+// CHECK: <plist version="1.0">
+// CHECK: <dict>
+// CHECK: <key>files</key>
+// CHECK: <array>
+// CHECK: <string>{{.*}}retain-release-path-notes-gc.m</string>
+// CHECK: </array>
+// CHECK: <key>diagnostics</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>42</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>43</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>43</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>43</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>43</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>43</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak (when using garbage collection) of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak of object when using garbage collection</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>creationViaCFCreate</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>43</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>47</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count incremented. The object now has a +2 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count incremented. The object now has a +2 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>48</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>26</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>In GC mode a call to &apos;CFMakeCollectable&apos; decrements an object&apos;s retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>In GC mode a call to &apos;CFMakeCollectable&apos; decrements an object&apos;s retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>49</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>26</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>In GC mode a call to &apos;NSMakeCollectable&apos; decrements an object&apos;s retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector</string>
+// CHECK: <key>message</key>
+// CHECK: <string>In GC mode a call to &apos;NSMakeCollectable&apos; decrements an object&apos;s retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak (when using garbage collection) of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak of object when using garbage collection</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>makeCollectable</string>
+// CHECK: <key>issue_hash</key><integer>6</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>52</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>In GC mode the &apos;retain&apos; message has no effect</string>
+// CHECK: <key>message</key>
+// CHECK: <string>In GC mode the &apos;retain&apos; message has no effect</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>In GC mode the &apos;release&apos; message has no effect</string>
+// CHECK: <key>message</key>
+// CHECK: <string>In GC mode the &apos;release&apos; message has no effect</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>In GC mode an &apos;autorelease&apos; has no effect</string>
+// CHECK: <key>message</key>
+// CHECK: <string>In GC mode an &apos;autorelease&apos; has no effect</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>col</key><integer>13</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>col</key><integer>29</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Bad release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>retainReleaseIgnored</string>
+// CHECK: <key>issue_hash</key><integer>5</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>60</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>36</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>36</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;object&apos; and returned from method &apos;getViolation&apos; is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;object&apos; and returned from method &apos;getViolation&apos; is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak (when using garbage collection) of an object stored into &apos;object&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak of returned object when using garbage collection</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>getViolation</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>66</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>36</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>36</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;object&apos; and returned from method &apos;copyViolation&apos; is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;object&apos; and returned from method &apos;copyViolation&apos; is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak (when using garbage collection) of an object stored into &apos;object&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak of returned object when using garbage collection</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>copyViolation</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </plist>
diff --git a/test/Analysis/retain-release-path-notes.m b/test/Analysis/retain-release-path-notes.m
index c3f5fcd..ebcfd6a 100644
--- a/test/Analysis/retain-release-path-notes.m
+++ b/test/Analysis/retain-release-path-notes.m
@@ -1,8 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=text -verify %s
-
-// This actually still works after the pseudo-object refactor, it just
-// uses messages that say 'method' instead of 'property'. Ted wanted
-// this xfailed and filed as a bug. rdar://problem/10402993
+// RN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=plist-multi-file %s -o - | FileCheck %s
/***
This file is for testing the path-sensitive notes for retain/release errors.
@@ -28,6 +25,9 @@ GC-specific notes should go in retain-release-path-notes-gc.m.
@interface Foo : NSObject
- (id)methodWithValue;
@property(retain) id propertyValue;
+
+- (id)objectAtIndexedSubscript:(unsigned)index;
+- (id)objectForKeyedSubscript:(id)key;
@end
typedef struct CFType *CFTypeRef;
@@ -119,6 +119,16 @@ CFTypeRef CFGetRuleViolation () {
return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
+- (id)copyViolationIndexedSubscript {
+ id result = self[0]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}}
+ return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+}
+
+- (id)copyViolationKeyedSubscript {
+ id result = self[self]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}}
+ return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+}
+
- (id)getViolation {
id result = [[Foo alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}}
return result; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa}}
@@ -130,3 +140,4474 @@ CFTypeRef CFGetRuleViolation () {
return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
@end
+
+
+typedef unsigned long NSUInteger;
+
+@interface NSValue : NSObject
+@end
+
+@interface NSNumber : NSValue
++ (NSNumber *)numberWithInt:(int)i;
+@end
+
+@interface NSString : NSObject
++ (NSString *)stringWithUTF8String:(const char *)str;
+@end
+
+@interface NSArray : NSObject
++ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count;
+@end
+
+@interface NSDictionary : NSObject
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id /* <NSCopying> */ [])keys count:(NSUInteger)count;
+@end
+
+
+void testNumericLiteral() {
+ id result = @1; // expected-note{{NSNumber literal is an object with a +0 retain count}}
+ [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+}
+
+void testBoxedInt(int x) {
+ id result = @(x); // expected-note{{NSNumber boxed expression produces an object with a +0 retain count}}
+ [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+}
+
+void testBoxedString(const char *str) {
+ id result = @(str); // expected-note{{NSString boxed expression produces an object with a +0 retain count}}
+ [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+}
+
+void testArray(id obj) {
+ id result = @[obj]; // expected-note{{NSArray literal is an object with a +0 retain count}}
+ [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+}
+
+void testDictionary(id key, id value) {
+ id result = @{key: value}; // expected-note{{NSDictionary literal is an object with a +0 retain count}}
+ [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+}
+
+
+// CHECK: <?xml version="1.0" encoding="UTF-8"?>
+// CHECK: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+// CHECK: <plist version="1.0">
+// CHECK: <dict>
+// CHECK: <key>files</key>
+// CHECK: <array>
+// CHECK: <string>{{.*}}retain-release-path-notes.m</string>
+// CHECK: </array>
+// CHECK: <key>diagnostics</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>45</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>creationViaAlloc</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>46</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>50</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>creationViaCFCreate</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>51</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>55</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>56</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count incremented. The object now has a +2 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count incremented. The object now has a +2 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>57</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count decremented. The object now has a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count decremented. The object now has a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>58</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>acquisitionViaMethod</string>
+// CHECK: <key>issue_hash</key><integer>5</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>59</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Property returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Property returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>63</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>64</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>acquisitionViaProperty</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>65</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFGetSomething&apos; returns a Core Foundation object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFGetSomething&apos; returns a Core Foundation object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>69</integer>
+// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>12</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>17</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference count incremented. The object now has a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>70</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>acquisitionViaCFFunction</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>71</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>75</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object released by directly sending the &apos;-dealloc&apos; message</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object released by directly sending the &apos;-dealloc&apos; message</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>76</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference-counted object is used after it is released</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference-counted object is used after it is released</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Reference-counted object is used after it is released</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Use-after-release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>explicitDealloc</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>77</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>81</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object released</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object released</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>82</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>83</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>83</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>83</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>83</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>83</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Reference-counted object is used after it is released</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Reference-counted object is used after it is released</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Reference-counted object is used after it is released</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Use-after-release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>implicitDealloc</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>83</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>87</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>88</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>89</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>90</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>90</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>90</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>90</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>90</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object sent -autorelease too many times</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Object sent -autorelease too many times</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>overAutorelease</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>90</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Property returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Property returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>94</integer>
+// CHECK: <key>col</key><integer>31</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>95</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>96</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>96</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>96</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>96</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>96</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object sent -autorelease too many times</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Object sent -autorelease too many times</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>autoreleaseUnowned</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>96</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>100</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>26</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>When GC is not enabled a call to &apos;CFMakeCollectable&apos; has no effect on its argument</string>
+// CHECK: <key>message</key>
+// CHECK: <string>When GC is not enabled a call to &apos;CFMakeCollectable&apos; has no effect on its argument</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>101</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>21</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>26</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>When GC is not enabled a call to &apos;NSMakeCollectable&apos; has no effect on its argument</string>
+// CHECK: <key>message</key>
+// CHECK: <string>When GC is not enabled a call to &apos;NSMakeCollectable&apos; has no effect on its argument</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>102</integer>
+// CHECK: <key>col</key><integer>19</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>103</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>103</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>103</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>103</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>103</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;leaked&apos; is not referenced later in this execution path and has a retain count of +1</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;leaked&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>makeCollectableIgnored</string>
+// CHECK: <key>issue_hash</key><integer>4</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>103</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>37</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFGetSomething&apos; returns a Core Foundation object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFGetSomething&apos; returns a Core Foundation object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>107</integer>
+// CHECK: <key>col</key><integer>35</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Method should return an owned object</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>CFCopyRuleViolation</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>108</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>11</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>40</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Call to function &apos;CFCreateSomething&apos; returns a Core Foundation object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>112</integer>
+// CHECK: <key>col</key><integer>38</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;object&apos; is returned from a function whose name (&apos;CFGetRuleViolation&apos;) does not contain &apos;Copy&apos; or &apos;Create&apos;. This violates the naming convention rules given in the Memory Management Guide for Core Foundation</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;object&apos; is returned from a function whose name (&apos;CFGetRuleViolation&apos;) does not contain &apos;Copy&apos; or &apos;Create&apos;. This violates the naming convention rules given in the Memory Management Guide for Core Foundation</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;object&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak of returned object</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>CFGetRuleViolation</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>113</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>32</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>32</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Property returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Property returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>118</integer>
+// CHECK: <key>col</key><integer>32</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Method should return an owned object</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>copyViolation</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>119</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Subscript returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Subscript returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>123</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Method should return an owned object</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>copyViolationIndexedSubscript</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>124</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Subscript returns an Objective-C object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Subscript returns an Objective-C object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>128</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Method should return an owned object</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>copyViolationKeyedSubscript</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>129</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>32</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>133</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;result&apos; is returned from a method whose name (&apos;getViolation&apos;) does not start with &apos;copy&apos;, &apos;mutableCopy&apos;, &apos;alloc&apos; or &apos;new&apos;. This violates the naming convention rules given in the Memory Management Guide for Cocoa</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object leaked: object allocated and stored into &apos;result&apos; is returned from a method whose name (&apos;getViolation&apos;) does not start with &apos;copy&apos;, &apos;mutableCopy&apos;, &apos;alloc&apos; or &apos;new&apos;. This violates the naming convention rules given in the Memory Management Guide for Cocoa</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Potential leak of an object stored into &apos;result&apos;</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Leak of returned object</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>getViolation</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>134</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>32</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Method returns an Objective-C object with a +1 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>138</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>22</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object sent -autorelease message</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>139</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>8</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>10</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object returned to caller with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Method should return an owned object</string>
+// CHECK: <key>issue_context_kind</key><string>Objective-C method</string>
+// CHECK: <key>issue_context</key><string>copyAutorelease</string>
+// CHECK: <key>issue_hash</key><integer>3</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>140</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>16</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>NSNumber literal is an object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>NSNumber literal is an object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>168</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Bad release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testNumericLiteral</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>169</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>18</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>NSNumber boxed expression produces an object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>NSNumber boxed expression produces an object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>173</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>174</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>174</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>174</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>174</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>174</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Bad release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testBoxedInt</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>174</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>NSString boxed expression produces an object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>NSString boxed expression produces an object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>178</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>179</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>179</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>179</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>179</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>179</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Bad release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testBoxedString</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>179</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>20</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>NSArray literal is an object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>NSArray literal is an object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>183</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>184</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>184</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>184</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>184</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>184</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Bad release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testArray</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>184</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>path</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>27</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>NSDictionary literal is an object with a +0 retain count</string>
+// CHECK: <key>message</key>
+// CHECK: <string>NSDictionary literal is an object with a +0 retain count</string>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>control</string>
+// CHECK: <key>edges</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>start</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>188</integer>
+// CHECK: <key>col</key><integer>15</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>end</key>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>189</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>189</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>kind</key><string>event</string>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>189</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <key>ranges</key>
+// CHECK: <array>
+// CHECK: <array>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>189</integer>
+// CHECK: <key>col</key><integer>4</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>189</integer>
+// CHECK: <key>col</key><integer>9</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </array>
+// CHECK: <key>depth</key><integer>0</integer>
+// CHECK: <key>extended_message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Incorrect decrement of the reference count of an object that is not owned at this point by the caller</string>
+// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
+// CHECK: <key>type</key><string>Bad release</string>
+// CHECK: <key>issue_context_kind</key><string>function</string>
+// CHECK: <key>issue_context</key><string>testDictionary</string>
+// CHECK: <key>issue_hash</key><integer>2</integer>
+// CHECK: <key>location</key>
+// CHECK: <dict>
+// CHECK: <key>line</key><integer>189</integer>
+// CHECK: <key>col</key><integer>3</integer>
+// CHECK: <key>file</key><integer>0</integer>
+// CHECK: </dict>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: </dict>
+// CHECK: </plist>
diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m
index 06c510e..ba492b7 100644
--- a/test/Analysis/retain-release.m
+++ b/test/Analysis/retain-release.m
@@ -142,9 +142,13 @@ NSFastEnumerationState;
@end
@class NSString, NSDictionary;
@interface NSValue : NSObject <NSCopying, NSCoding> - (void)getValue:(void *)value;
-@end @interface NSNumber : NSValue - (char)charValue;
+@end
+@interface NSNumber : NSValue
+- (char)charValue;
- (id)initWithInt:(int)value;
-@end @class NSString;
++ (NSNumber *)numberWithInt:(int)value;
+@end
+@class NSString;
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
- (NSUInteger)count;
- (id)initWithObjects:(const id [])objects count:(NSUInteger)cnt;
@@ -195,7 +199,7 @@ typedef struct IONotificationPort * IONotificationPortRef;
typedef void (*IOServiceMatchingCallback)( void * refcon, io_iterator_t iterator );
io_service_t IOServiceGetMatchingService( mach_port_t masterPort, CFDictionaryRef matching );
kern_return_t IOServiceGetMatchingServices( mach_port_t masterPort, CFDictionaryRef matching, io_iterator_t * existing );
-kern_return_t IOServiceAddNotification( mach_port_t masterPort, const io_name_t notificationType, CFDictionaryRef matching, mach_port_t wakePort, uintptr_t reference, io_iterator_t * notification ) __attribute__((deprecated));
+kern_return_t IOServiceAddNotification( mach_port_t masterPort, const io_name_t notificationType, CFDictionaryRef matching, mach_port_t wakePort, uintptr_t reference, io_iterator_t * notification ) __attribute__((deprecated)); // expected-note {{'IOServiceAddNotification' declared here}}
kern_return_t IOServiceAddMatchingNotification( IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void * refCon, io_iterator_t * notification );
CFMutableDictionaryRef IOServiceMatching( const char * name );
CFMutableDictionaryRef IOServiceNameMatching( const char * name );
@@ -222,8 +226,10 @@ typedef struct CGLayer *CGLayerRef;
@end @protocol NSValidatedUserInterfaceItem - (SEL)action;
@end @protocol NSUserInterfaceValidations - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem;
@end @class NSDate, NSDictionary, NSError, NSException, NSNotification;
+@class NSTextField, NSPanel, NSArray, NSWindow, NSImage, NSButton, NSError;
@interface NSApplication : NSResponder <NSUserInterfaceValidations> {
}
+- (void)beginSheet:(NSWindow *)sheet modalForWindow:(NSWindow *)docWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo;
@end enum {
NSTerminateCancel = 0, NSTerminateNow = 1, NSTerminateLater = 2 };
typedef NSUInteger NSApplicationTerminateReply;
@@ -231,7 +237,7 @@ typedef NSUInteger NSApplicationTerminateReply;
@end @class NSAttributedString, NSEvent, NSFont, NSFormatter, NSImage, NSMenu, NSText, NSView, NSTextView;
@interface NSCell : NSObject <NSCopying, NSCoding> {
}
-@end @class NSTextField, NSPanel, NSArray, NSWindow, NSImage, NSButton, NSError;
+@end
typedef struct {
}
CVTimeStamp;
@@ -294,6 +300,9 @@ extern CGColorSpaceRef CGColorSpaceCreateDeviceRGB(void);
+ (id)array;
@end
+// This is how NSMakeCollectable is declared in the OS X 10.8 headers.
+id NSMakeCollectable(CFTypeRef __attribute__((cf_consumed))) __attribute__((ns_returns_retained));
+
//===----------------------------------------------------------------------===//
// Test cases.
@@ -747,7 +756,7 @@ typedef CFTypeRef OtherRef;
@end
//===----------------------------------------------------------------------===//
-//<rdar://problem/6320065> false positive - init method returns an object
+// <rdar://problem/6320065> false positive - init method returns an object
// owned by caller
//===----------------------------------------------------------------------===//
@@ -1055,10 +1064,14 @@ typedef struct _opaque_pthread_t *__darwin_pthread_t;
typedef struct _opaque_pthread_attr_t __darwin_pthread_attr_t;
typedef __darwin_pthread_t pthread_t;
typedef __darwin_pthread_attr_t pthread_attr_t;
+typedef unsigned long __darwin_pthread_key_t;
+typedef __darwin_pthread_key_t pthread_key_t;
int pthread_create(pthread_t *, const pthread_attr_t *,
void *(*)(void *), void *);
+int pthread_setspecific(pthread_key_t key, const void *value);
+
void *rdar_7299394_start_routine(void *p) {
[((id) p) release];
return 0;
@@ -1072,6 +1085,16 @@ void rdar_7299394_positive(pthread_attr_t *attr, pthread_t *thread) {
}
//===----------------------------------------------------------------------===//
+// <rdar://problem/11282706> false positive with not understanding thread
+// local storage
+//===----------------------------------------------------------------------===//
+
+void rdar11282706(pthread_key_t key) {
+ NSNumber *number = [[NSNumber alloc] initWithInt:5]; // no-warning
+ pthread_setspecific(key, (void*) number);
+}
+
+//===----------------------------------------------------------------------===//
// <rdar://problem/7283567> False leak associated with call to
// CVPixelBufferCreateWithBytes ()
//
@@ -1291,6 +1314,11 @@ void testattr2_b() {
TestOwnershipAttr *x = [[TestOwnershipAttr alloc] pseudoInit]; // expected-warning{{leak}}
}
+void testattr2_b_11358224_self_assign_looses_the_leak() {
+ TestOwnershipAttr *x = [[TestOwnershipAttr alloc] pseudoInit];// expected-warning{{leak}}
+ x = x;
+}
+
void testattr2_c() {
TestOwnershipAttr *x = [[TestOwnershipAttr alloc] pseudoInit]; // no-warning
[x release];
@@ -1427,8 +1455,7 @@ void test_blocks_1_indirect_release_via_call(void) {
}
void test_blocks_1_indirect_retain_via_call(void) {
- // Eventually this should be reported as a leak.
- NSNumber *number = [[NSNumber alloc] initWithInt:5]; // no-warning
+ NSNumber *number = [[NSNumber alloc] initWithInt:5]; // expected-warning {{leak}}
^(NSObject *o){ [o retain]; }(number);
}
@@ -1667,6 +1694,58 @@ void rdar_10824732() {
}
}
+// Stop tracking objects passed to functions, which take callbacks as parameters.
+// radar://10973977
+typedef int (*CloseCallback) (void *);
+void ReaderForIO(CloseCallback ioclose, void *ioctx);
+int IOClose(void *context);
+
+@protocol SInS <NSObject>
+@end
+
+@interface radar10973977 : NSObject
+- (id<SInS>)inputS;
+- (void)reader;
+@end
+
+@implementation radar10973977
+- (void)reader
+{
+ id<SInS> inputS = [[self inputS] retain];
+ ReaderForIO(IOClose, inputS);
+}
+- (id<SInS>)inputS
+{
+ return 0;
+}
+@end
+
+// Object escapes through a selector callback: radar://11398514
+extern id NSApp;
+@interface MySheetController
+- (id<SInS>)inputS;
+- (void)showDoSomethingSheetAction:(id)action;
+- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+@end
+
+@implementation MySheetController
+- (id<SInS>)inputS {
+ return 0;
+}
+- (void)showDoSomethingSheetAction:(id)action {
+ id<SInS> inputS = [[self inputS] retain];
+ [NSApp beginSheet:0
+ modalForWindow:0
+ modalDelegate:0
+ didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo:(void *)inputS]; // no - warning
+}
+- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
+
+ id contextObject = (id)contextInfo;
+ [contextObject release];
+}
+@end
//===----------------------------------------------------------------------===//
// Test returning allocated memory in a struct.
//
@@ -1739,3 +1818,40 @@ void test_objc_arrays() {
}
}
+void test_objc_integer_literals() {
+ id value = [@1 retain]; // expected-warning {{leak}}
+ [value description];
+}
+
+void test_objc_boxed_expressions(int x, const char *y) {
+ id value = [@(x) retain]; // expected-warning {{leak}}
+ [value description];
+
+ value = [@(y) retain]; // expected-warning {{leak}}
+ [value description];
+}
+
+// Test NSLog doesn't escape tracked objects.
+void rdar11400885(int y)
+{
+ @autoreleasepool {
+ NSString *printString;
+ if(y > 2)
+ printString = [[NSString alloc] init];
+ else
+ printString = [[NSString alloc] init];
+ NSLog(@"Once %@", printString);
+ [printString release];
+ NSLog(@"Again: %@", printString); // expected-warning {{Reference-counted object is used after it is released}}
+ }
+}
+
+id makeCollectableNonLeak() {
+ extern CFTypeRef CFCreateSomething();
+
+ CFTypeRef object = CFCreateSomething(); // +1
+ CFRetain(object); // +2
+ id objCObject = NSMakeCollectable(object); // +2
+ [objCObject release]; // +1
+ return [objCObject autorelease]; // +0
+}
diff --git a/test/Analysis/self-init.m b/test/Analysis/self-init.m
index d515173..b0c51a2 100644
--- a/test/Analysis/self-init.m
+++ b/test/Analysis/self-init.m
@@ -1,7 +1,9 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.SelfInit -fobjc-default-synthesize-properties %s -verify
+// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.SelfInit -fobjc-default-synthesize-properties -analyzer-ipa=dynamic -fno-builtin %s -verify
+// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.SelfInit -fobjc-default-synthesize-properties -fno-builtin %s -verify
@class NSZone, NSCoder;
-@protocol NSObject- (id)self;
+@protocol NSObject
+- (id)self;
@end
@protocol NSCopying - (id)copyWithZone:(NSZone *)zone;
@end
@@ -254,3 +256,28 @@ extern id _commonInit(MyObj *self);
return self;
}
@end
+
+// Test for radar://11125870: init constructing a special instance.
+typedef signed char BOOL;
+@interface MyClass : NSObject
+@end
+@implementation MyClass
++ (id)specialInstance {
+ return [[MyClass alloc] init];
+}
+- (id)initSpecially:(BOOL)handleSpecially {
+ if ((self = [super init])) {
+ if (handleSpecially) {
+ self = [MyClass specialInstance];
+ }
+ }
+ return self;
+}
+- (id)initSelfSelf {
+ if ((self = [super init])) {
+ self = self;
+ }
+ return self;
+}
+@end
+
diff --git a/test/Analysis/stack-addr-ps.cpp b/test/Analysis/stack-addr-ps.cpp
index b09e435..b21a03d 100644
--- a/test/Analysis/stack-addr-ps.cpp
+++ b/test/Analysis/stack-addr-ps.cpp
@@ -84,3 +84,9 @@ struct TS {
return x;
}
};
+
+// rdar://11345441
+int* f5() {
+ int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-note {{binding reference variable 'i' here}}
+ return &i; // expected-warning {{address of stack memory associated with local variable 'i' returned}}
+}
diff --git a/test/Analysis/string-fail.c b/test/Analysis/string-fail.c
index 3bff6d4..ac5c6d0 100644
--- a/test/Analysis/string-fail.c
+++ b/test/Analysis/string-fail.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.unix.CString,experimental.deadcode.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,experimental.unix.CString,experimental.deadcode.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
// XFAIL: *
// This file is for tests that may eventually go into string.c, or may be
@@ -32,6 +32,7 @@
#define NULL 0
typedef typeof(sizeof(int)) size_t;
+void clang_analyzer_eval(int);
//===----------------------------------------------------------------------===
// strnlen()
@@ -43,8 +44,7 @@ size_t strnlen(const char *s, size_t maxlen);
void strnlen_liveness(const char *x) {
if (strnlen(x, 10) < 5)
return;
- if (strnlen(x, 10) < 5)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strnlen(x, 10) < 5); // expected-warning{{FALSE}}
}
void strnlen_subregion() {
@@ -57,43 +57,43 @@ void strnlen_subregion() {
size_t a = strnlen(z.a, 10);
z.b[0] = 5;
size_t b = strnlen(z.a, 10);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
use_two_stringsn(&z);
size_t c = strnlen(z.a, 10);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
extern void use_stringn(char *);
void strnlen_argument(char *x) {
size_t a = strnlen(x, 10);
size_t b = strnlen(x, 10);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
use_stringn(x);
size_t c = strnlen(x, 10);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
extern char global_strn[];
void strnlen_global() {
size_t a = strnlen(global_strn, 10);
size_t b = strnlen(global_strn, 10);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
// Call a function with unknown effects, which should invalidate globals.
use_stringn(0);
size_t c = strnlen(global_strn, 10);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
void strnlen_indirect(char *x) {
@@ -101,13 +101,13 @@ void strnlen_indirect(char *x) {
char *p = x;
char **p2 = &p;
size_t b = strnlen(x, 10);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
extern void use_stringn_ptr(char*const*);
use_stringn_ptr(p2);
size_t c = strnlen(x, 10);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
diff --git a/test/Analysis/string.c b/test/Analysis/string.c
index c0814b8..32f5db3 100644
--- a/test/Analysis/string.c
+++ b/test/Analysis/string.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,experimental.unix.cstring,experimental.deadcode.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,experimental.unix.cstring,experimental.deadcode.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.cstring,experimental.unix.cstring,experimental.deadcode.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s
-// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=experimental.security.taint,core,unix.cstring,experimental.unix.cstring,experimental.deadcode.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s
+// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=experimental.security.taint,core,unix.cstring,experimental.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s
//===----------------------------------------------------------------------===
// Declarations
@@ -26,6 +26,9 @@
#define NULL 0
typedef typeof(sizeof(int)) size_t;
+
+void clang_analyzer_eval(int);
+
int scanf(const char *restrict format, ...);
//===----------------------------------------------------------------------===
@@ -36,23 +39,20 @@ int scanf(const char *restrict format, ...);
size_t strlen(const char *s);
void strlen_constant0() {
- if (strlen("123") != 3)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen("123") == 3); // expected-warning{{TRUE}}
}
void strlen_constant1() {
const char *a = "123";
- if (strlen(a) != 3)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(a) == 3); // expected-warning{{TRUE}}
}
void strlen_constant2(char x) {
char a[] = "123";
- if (strlen(a) != 3)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(a) == 3); // expected-warning{{TRUE}}
+
a[0] = x;
- if (strlen(a) != 3)
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strlen(a) == 3); // expected-warning{{UNKNOWN}}
}
size_t strlen_null() {
@@ -78,43 +78,46 @@ void strlen_subregion() {
size_t a = strlen(z.a);
z.b[0] = 5;
size_t b = strlen(z.a);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
use_two_strings(&z);
size_t c = strlen(z.a);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
extern void use_string(char *);
void strlen_argument(char *x) {
size_t a = strlen(x);
size_t b = strlen(x);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
use_string(x);
size_t c = strlen(x);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
extern char global_str[];
void strlen_global() {
size_t a = strlen(global_str);
size_t b = strlen(global_str);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0) {
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
+ // Make sure clang_analyzer_eval does not invalidate globals.
+ clang_analyzer_eval(strlen(global_str) == 0); // expected-warning{{TRUE}}
+ }
// Call a function with unknown effects, which should invalidate globals.
use_string(0);
size_t c = strlen(global_str);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
void strlen_indirect(char *x) {
@@ -122,15 +125,15 @@ void strlen_indirect(char *x) {
char *p = x;
char **p2 = &p;
size_t b = strlen(x);
- if (a == 0 && b != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
+ if (a == 0)
+ clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
extern void use_string_ptr(char*const*);
use_string_ptr(p2);
size_t c = strlen(x);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
void strlen_indirect2(char *x) {
@@ -141,15 +144,14 @@ void strlen_indirect2(char *x) {
use_string_ptr2(p2);
size_t c = strlen(x);
- if (a == 0 && c != 0)
- (void)*(char*)0; // expected-warning{{null}}
+ if (a == 0)
+ clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
}
void strlen_liveness(const char *x) {
if (strlen(x) < 5)
return;
- if (strlen(x) < 5)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(x) < 5); // expected-warning{{FALSE}}
}
//===----------------------------------------------------------------------===
@@ -159,43 +161,35 @@ void strlen_liveness(const char *x) {
size_t strnlen(const char *s, size_t maxlen);
void strnlen_constant0() {
- if (strnlen("123", 10) != 3)
- (void)*(char*)0; // expected-warning{{never executed}}
+ clang_analyzer_eval(strnlen("123", 10) == 3); // expected-warning{{TRUE}}
}
void strnlen_constant1() {
const char *a = "123";
- if (strnlen(a, 10) != 3)
- (void)*(char*)0; // expected-warning{{never executed}}
+ clang_analyzer_eval(strnlen(a, 10) == 3); // expected-warning{{TRUE}}
}
void strnlen_constant2(char x) {
char a[] = "123";
- if (strnlen(a, 10) != 3)
- (void)*(char*)0; // expected-warning{{never executed}}
+ clang_analyzer_eval(strnlen(a, 10) == 3); // expected-warning{{TRUE}}
a[0] = x;
- if (strnlen(a, 10) != 3)
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strnlen(a, 10) == 3); // expected-warning{{UNKNOWN}}
}
void strnlen_constant4() {
- if (strnlen("123456", 3) != 3)
- (void)*(char*)0; // expected-warning{{never executed}}
+ clang_analyzer_eval(strnlen("123456", 3) == 3); // expected-warning{{TRUE}}
}
void strnlen_constant5() {
const char *a = "123456";
- if (strnlen(a, 3) != 3)
- (void)*(char*)0; // expected-warning{{never executed}}
+ clang_analyzer_eval(strnlen(a, 3) == 3); // expected-warning{{TRUE}}
}
void strnlen_constant6(char x) {
char a[] = "123456";
- if (strnlen(a, 3) != 3)
- (void)*(char*)0; // expected-warning{{never executed}}
+ clang_analyzer_eval(strnlen(a, 3) == 3); // expected-warning{{TRUE}}
a[0] = x;
- if (strnlen(a, 3) != 3)
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strnlen(a, 3) == 3); // expected-warning{{UNKNOWN}}
}
size_t strnlen_null() {
@@ -212,10 +206,8 @@ label:
}
void strnlen_zero() {
- if (strnlen("abc", 0) != 0)
- (void)*(char*)0; // expected-warning{{never executed}}
- if (strnlen(NULL, 0) != 0) // no-warning
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strnlen("abc", 0) == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strnlen(NULL, 0) == 0); // expected-warning{{TRUE}}
}
size_t strnlen_compound_literal() {
@@ -230,40 +222,26 @@ size_t strnlen_unknown_limit(float f) {
}
void strnlen_is_not_strlen(char *x) {
- if (strnlen(x, 10) != strlen(x))
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strnlen(x, 10) == strlen(x)); // expected-warning{{UNKNOWN}}
}
void strnlen_at_limit(char *x) {
size_t len = strnlen(x, 10);
- if (len > 10)
- (void)*(char*)0; // expected-warning{{never executed}}
- if (len == 10)
- (void)*(char*)0; // expected-warning{{null}}
-}
-
-void strnlen_less_than_limit(char *x) {
- size_t len = strnlen(x, 10);
- if (len > 10)
- (void)*(char*)0; // expected-warning{{never executed}}
- if (len < 10)
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(len <= 10); // expected-warning{{TRUE}}
+ clang_analyzer_eval(len == 10); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(len < 10); // expected-warning{{UNKNOWN}}
}
void strnlen_at_actual(size_t limit) {
size_t len = strnlen("abc", limit);
- if (len > 3)
- (void)*(char*)0; // expected-warning{{never executed}}
- if (len == 3)
- (void)*(char*)0; // expected-warning{{null}}
-}
-
-void strnlen_less_than_actual(size_t limit) {
- size_t len = strnlen("abc", limit);
- if (len > 3)
- (void)*(char*)0; // expected-warning{{never executed}}
- if (len < 3)
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(len <= 3); // expected-warning{{TRUE}}
+ // This is due to eager assertion in strnlen.
+ if (limit == 0) {
+ clang_analyzer_eval(len == 0); // expected-warning{{TRUE}}
+ } else {
+ clang_analyzer_eval(len == 3); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(len < 3); // expected-warning{{UNKNOWN}}
+ }
}
//===----------------------------------------------------------------------===
@@ -304,14 +282,9 @@ void strcpy_fn_const(char *x) {
void strcpy_effects(char *x, char *y) {
char a = x[0];
- if (strcpy(x, y) != x)
- (void)*(char*)0; // no-warning
-
- if (strlen(x) != strlen(y))
- (void)*(char*)0; // no-warning
-
- if (a != x[0])
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strcpy(x, y) == x); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(x) == strlen(y)); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a == x[0]); // expected-warning{{UNKNOWN}}
}
void strcpy_overflow(char *y) {
@@ -348,14 +321,9 @@ char *stpcpy(char *restrict s1, const char *restrict s2);
void stpcpy_effect(char *x, char *y) {
char a = x[0];
- if (stpcpy(x, y) != &x[strlen(y)])
- (void)*(char*)0; // no-warning
-
- if (strlen(x) != strlen(y))
- (void)*(char*)0; // no-warning
-
- if (a != x[0])
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(stpcpy(x, y) == &x[strlen(y)]); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(x) == strlen(y)); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a == x[0]); // expected-warning{{UNKNOWN}}
}
void stpcpy_overflow(char *y) {
@@ -409,11 +377,8 @@ void strcat_effects(char *y) {
if (strlen(y) != 4)
return;
- if (strcat(x, y) != x)
- (void)*(char*)0; // no-warning
-
- if ((int)strlen(x) != (orig_len + strlen(y)))
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcat(x, y) == x); // expected-warning{{TRUE}}
+ clang_analyzer_eval((int)strlen(x) == (orig_len + strlen(y))); // expected-warning{{TRUE}}
}
void strcat_overflow_0(char *y) {
@@ -442,29 +407,25 @@ void strcat_no_overflow(char *y) {
void strcat_symbolic_dst_length(char *dst) {
strcat(dst, "1234");
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
}
void strcat_symbolic_src_length(char *src) {
char dst[8] = "1234";
strcat(dst, src);
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
}
void strcat_symbolic_dst_length_taint(char *dst) {
scanf("%s", dst); // Taint data.
strcat(dst, "1234");
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
}
void strcat_unknown_src_length(char *src, int offset) {
char dst[8] = "1234";
strcat(dst, &src[offset]);
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
}
// There is no strcat_unknown_dst_length because if we can't get a symbolic
@@ -513,14 +474,9 @@ void strncpy_fn(char *x) {
void strncpy_effects(char *x, char *y) {
char a = x[0];
- if (strncpy(x, y, 5) != x)
- (void)*(char*)0; // no-warning
-
- if (strlen(x) != strlen(y))
- (void)*(char*)0; // expected-warning{{null}}
-
- if (a != x[0])
- (void)*(char*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strncpy(x, y, 5) == x); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(x) == strlen(y)); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(a == x[0]); // expected-warning{{UNKNOWN}}
}
void strncpy_overflow(char *y) {
@@ -562,8 +518,7 @@ void strncpy_exactly_matching_buffer(char *y) {
// strncpy does not null-terminate, so we have no idea what the strlen is
// after this.
- if (strlen(x) > 4)
- (void)*(int*)0; // expected-warning{{null}}
+ clang_analyzer_eval(strlen(x) > 4); // expected-warning{{UNKNOWN}}
}
void strncpy_exactly_matching_buffer2(char *y) {
@@ -574,8 +529,18 @@ void strncpy_exactly_matching_buffer2(char *y) {
strncpy(x, y, 4); // no-warning
// This time, we know that y fits in x anyway.
- if (strlen(x) > 3)
- (void)*(int*)0; // no-warning
+ clang_analyzer_eval(strlen(x) <= 3); // expected-warning{{TRUE}}
+}
+
+void strncpy_zero(char *src) {
+ char dst[] = "123";
+ strncpy(dst, src, 0); // no-warning
+}
+
+void strncpy_empty() {
+ char dst[] = "123";
+ char src[] = "";
+ strncpy(dst, src, 4); // no-warning
}
//===----------------------------------------------------------------------===
@@ -617,11 +582,8 @@ void strncat_effects(char *y) {
if (strlen(y) != 4)
return;
- if (strncat(x, y, strlen(y)) != x)
- (void)*(char*)0; // no-warning
-
- if (strlen(x) != orig_len + strlen(y))
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncat(x, y, strlen(y)) == x); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(x) == (orig_len + strlen(y))); // expected-warning{{TRUE}}
}
void strncat_overflow_0(char *y) {
@@ -661,15 +623,13 @@ void strncat_no_overflow_2(char *y) {
void strncat_symbolic_dst_length(char *dst) {
strncat(dst, "1234", 5);
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
}
void strncat_symbolic_src_length(char *src) {
char dst[8] = "1234";
strncat(dst, src, 3);
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
char dst2[8] = "1234";
strncat(dst2, src, 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}}
@@ -678,8 +638,7 @@ void strncat_symbolic_src_length(char *src) {
void strncat_unknown_src_length(char *src, int offset) {
char dst[8] = "1234";
strncat(dst, &src[offset], 3);
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
char dst2[8] = "1234";
strncat(dst2, &src[offset], 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}}
@@ -692,20 +651,18 @@ void strncat_symbolic_limit(unsigned limit) {
char dst[6] = "1234";
char src[] = "567";
strncat(dst, src, limit); // no-warning
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
- if (strlen(dst) == 4)
- (void)*(char*)0; // expected-warning{{null}}
+
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(dst) == 4); // expected-warning{{UNKNOWN}}
}
void strncat_unknown_limit(float limit) {
char dst[6] = "1234";
char src[] = "567";
strncat(dst, src, (size_t)limit); // no-warning
- if (strlen(dst) < 4)
- (void)*(char*)0; // no-warning
- if (strlen(dst) == 4)
- (void)*(char*)0; // expected-warning{{null}}
+
+ clang_analyzer_eval(strlen(dst) >= 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(strlen(dst) == 4); // expected-warning{{UNKNOWN}}
}
void strncat_too_big(char *dst, char *src) {
@@ -716,6 +673,17 @@ void strncat_too_big(char *dst, char *src) {
strncat(dst, src, 2); // expected-warning{{This expression will create a string whose length is too big to be represented as a size_t}}
}
+void strncat_zero(char *src) {
+ char dst[] = "123";
+ strncat(dst, src, 0); // no-warning
+}
+
+void strncat_empty() {
+ char dst[8] = "123";
+ char src[] = "";
+ strncat(dst, src, 4); // no-warning
+}
+
//===----------------------------------------------------------------------===
// strcmp()
//===----------------------------------------------------------------------===
@@ -724,41 +692,35 @@ void strncat_too_big(char *dst, char *src) {
int strcmp(const char * s1, const char * s2);
void strcmp_constant0() {
- if (strcmp("123", "123") != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp("123", "123") == 0); // expected-warning{{TRUE}}
}
void strcmp_constant_and_var_0() {
char *x = "123";
- if (strcmp(x, "123") != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, "123") == 0); // expected-warning{{TRUE}}
}
void strcmp_constant_and_var_1() {
char *x = "123";
- if (strcmp("123", x) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp("123", x) == 0); // expected-warning{{TRUE}}
}
void strcmp_0() {
char *x = "123";
char *y = "123";
- if (strcmp(x, y) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == 0); // expected-warning{{TRUE}}
}
void strcmp_1() {
char *x = "234";
char *y = "123";
- if (strcmp(x, y) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == 1); // expected-warning{{TRUE}}
}
void strcmp_2() {
char *x = "123";
char *y = "234";
- if (strcmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcmp_null_0() {
@@ -776,39 +738,33 @@ void strcmp_null_1() {
void strcmp_diff_length_0() {
char *x = "12345";
char *y = "234";
- if (strcmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcmp_diff_length_1() {
char *x = "123";
char *y = "23456";
- if (strcmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcmp_diff_length_2() {
char *x = "12345";
char *y = "123";
- if (strcmp(x, y) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == 1); // expected-warning{{TRUE}}
}
void strcmp_diff_length_3() {
char *x = "123";
char *y = "12345";
- if (strcmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcmp_embedded_null () {
- if (strcmp("\0z", "\0y") != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp("\0z", "\0y") == 0); // expected-warning{{TRUE}}
}
void strcmp_unknown_arg (char *unknown) {
- if (strcmp(unknown, unknown) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcmp(unknown, unknown) == 0); // expected-warning{{TRUE}}
}
//===----------------------------------------------------------------------===
@@ -819,41 +775,35 @@ void strcmp_unknown_arg (char *unknown) {
int strncmp(const char *s1, const char *s2, size_t n);
void strncmp_constant0() {
- if (strncmp("123", "123", 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp("123", "123", 3) == 0); // expected-warning{{TRUE}}
}
void strncmp_constant_and_var_0() {
char *x = "123";
- if (strncmp(x, "123", 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, "123", 3) == 0); // expected-warning{{TRUE}}
}
void strncmp_constant_and_var_1() {
char *x = "123";
- if (strncmp("123", x, 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp("123", x, 3) == 0); // expected-warning{{TRUE}}
}
void strncmp_0() {
char *x = "123";
char *y = "123";
- if (strncmp(x, y, 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 3) == 0); // expected-warning{{TRUE}}
}
void strncmp_1() {
char *x = "234";
char *y = "123";
- if (strncmp(x, y, 3) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 3) == 1); // expected-warning{{TRUE}}
}
void strncmp_2() {
char *x = "123";
char *y = "234";
- if (strncmp(x, y, 3) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 3) == -1); // expected-warning{{TRUE}}
}
void strncmp_null_0() {
@@ -871,55 +821,47 @@ void strncmp_null_1() {
void strncmp_diff_length_0() {
char *x = "12345";
char *y = "234";
- if (strncmp(x, y, 5) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 5) == -1); // expected-warning{{TRUE}}
}
void strncmp_diff_length_1() {
char *x = "123";
char *y = "23456";
- if (strncmp(x, y, 5) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 5) == -1); // expected-warning{{TRUE}}
}
void strncmp_diff_length_2() {
char *x = "12345";
char *y = "123";
- if (strncmp(x, y, 5) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 5) == 1); // expected-warning{{TRUE}}
}
void strncmp_diff_length_3() {
char *x = "123";
char *y = "12345";
- if (strncmp(x, y, 5) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 5) == -1); // expected-warning{{TRUE}}
}
void strncmp_diff_length_4() {
char *x = "123";
char *y = "12345";
- if (strncmp(x, y, 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 3) == 0); // expected-warning{{TRUE}}
}
void strncmp_diff_length_5() {
char *x = "012";
char *y = "12345";
- if (strncmp(x, y, 3) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 3) == -1); // expected-warning{{TRUE}}
}
void strncmp_diff_length_6() {
char *x = "234";
char *y = "12345";
- if (strncmp(x, y, 3) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp(x, y, 3) == 1); // expected-warning{{TRUE}}
}
void strncmp_embedded_null () {
- if (strncmp("ab\0zz", "ab\0yy", 4) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncmp("ab\0zz", "ab\0yy", 4) == 0); // expected-warning{{TRUE}}
}
//===----------------------------------------------------------------------===
@@ -930,41 +872,35 @@ void strncmp_embedded_null () {
int strcasecmp(const char *s1, const char *s2);
void strcasecmp_constant0() {
- if (strcasecmp("abc", "Abc") != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp("abc", "Abc") == 0); // expected-warning{{TRUE}}
}
void strcasecmp_constant_and_var_0() {
char *x = "abc";
- if (strcasecmp(x, "Abc") != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, "Abc") == 0); // expected-warning{{TRUE}}
}
void strcasecmp_constant_and_var_1() {
char *x = "abc";
- if (strcasecmp("Abc", x) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp("Abc", x) == 0); // expected-warning{{TRUE}}
}
void strcasecmp_0() {
char *x = "abc";
char *y = "Abc";
- if (strcasecmp(x, y) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == 0); // expected-warning{{TRUE}}
}
void strcasecmp_1() {
char *x = "Bcd";
char *y = "abc";
- if (strcasecmp(x, y) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == 1); // expected-warning{{TRUE}}
}
void strcasecmp_2() {
char *x = "abc";
char *y = "Bcd";
- if (strcasecmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcasecmp_null_0() {
@@ -982,34 +918,29 @@ void strcasecmp_null_1() {
void strcasecmp_diff_length_0() {
char *x = "abcde";
char *y = "aBd";
- if (strcasecmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcasecmp_diff_length_1() {
char *x = "abc";
char *y = "aBdef";
- if (strcasecmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcasecmp_diff_length_2() {
char *x = "aBcDe";
char *y = "abc";
- if (strcasecmp(x, y) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == 1); // expected-warning{{TRUE}}
}
void strcasecmp_diff_length_3() {
char *x = "aBc";
char *y = "abcde";
- if (strcasecmp(x, y) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp(x, y) == -1); // expected-warning{{TRUE}}
}
void strcasecmp_embedded_null () {
- if (strcasecmp("ab\0zz", "ab\0yy") != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strcasecmp("ab\0zz", "ab\0yy") == 0); // expected-warning{{TRUE}}
}
//===----------------------------------------------------------------------===
@@ -1020,41 +951,35 @@ void strcasecmp_embedded_null () {
int strncasecmp(const char *s1, const char *s2, size_t n);
void strncasecmp_constant0() {
- if (strncasecmp("abc", "Abc", 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp("abc", "Abc", 3) == 0); // expected-warning{{TRUE}}
}
void strncasecmp_constant_and_var_0() {
char *x = "abc";
- if (strncasecmp(x, "Abc", 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, "Abc", 3) == 0); // expected-warning{{TRUE}}
}
void strncasecmp_constant_and_var_1() {
char *x = "abc";
- if (strncasecmp("Abc", x, 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp("Abc", x, 3) == 0); // expected-warning{{TRUE}}
}
void strncasecmp_0() {
char *x = "abc";
char *y = "Abc";
- if (strncasecmp(x, y, 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 3) == 0); // expected-warning{{TRUE}}
}
void strncasecmp_1() {
char *x = "Bcd";
char *y = "abc";
- if (strncasecmp(x, y, 3) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 3) == 1); // expected-warning{{TRUE}}
}
void strncasecmp_2() {
char *x = "abc";
char *y = "Bcd";
- if (strncasecmp(x, y, 3) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 3) == -1); // expected-warning{{TRUE}}
}
void strncasecmp_null_0() {
@@ -1072,53 +997,45 @@ void strncasecmp_null_1() {
void strncasecmp_diff_length_0() {
char *x = "abcde";
char *y = "aBd";
- if (strncasecmp(x, y, 5) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 5) == -1); // expected-warning{{TRUE}}
}
void strncasecmp_diff_length_1() {
char *x = "abc";
char *y = "aBdef";
- if (strncasecmp(x, y, 5) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 5) == -1); // expected-warning{{TRUE}}
}
void strncasecmp_diff_length_2() {
char *x = "aBcDe";
char *y = "abc";
- if (strncasecmp(x, y, 5) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 5) == 1); // expected-warning{{TRUE}}
}
void strncasecmp_diff_length_3() {
char *x = "aBc";
char *y = "abcde";
- if (strncasecmp(x, y, 5) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 5) == -1); // expected-warning{{TRUE}}
}
void strncasecmp_diff_length_4() {
char *x = "abcde";
char *y = "aBc";
- if (strncasecmp(x, y, 3) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 3) == 0); // expected-warning{{TRUE}}
}
void strncasecmp_diff_length_5() {
char *x = "abcde";
char *y = "aBd";
- if (strncasecmp(x, y, 3) != -1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 3) == -1); // expected-warning{{TRUE}}
}
void strncasecmp_diff_length_6() {
char *x = "aBDe";
char *y = "abc";
- if (strncasecmp(x, y, 3) != 1)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp(x, y, 3) == 1); // expected-warning{{TRUE}}
}
void strncasecmp_embedded_null () {
- if (strncasecmp("ab\0zz", "ab\0yy", 4) != 0)
- (void)*(char*)0; // no-warning
+ clang_analyzer_eval(strncasecmp("ab\0zz", "ab\0yy", 4) == 0); // expected-warning{{TRUE}}
}
diff --git a/test/Analysis/svalbuilder-logic.c b/test/Analysis/svalbuilder-logic.c
new file mode 100644
index 0000000..bc79f85
--- /dev/null
+++ b/test/Analysis/svalbuilder-logic.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix -verify %s
+
+// Testing core functionality of the SValBuilder.
+
+int SValBuilderLogicNoCrash(int *x) {
+ return 3 - (int)(x +3);
+}
diff --git a/test/Analysis/system-header-simulator-objc.h b/test/Analysis/system-header-simulator-objc.h
index 92d5899..a647b37 100644
--- a/test/Analysis/system-header-simulator-objc.h
+++ b/test/Analysis/system-header-simulator-objc.h
@@ -85,7 +85,13 @@ typedef double NSTimeInterval;
- (id)initWithBytes:(const void *)bytes length:(NSUInteger)len encoding:(NSStringEncoding)encoding;
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)freeBuffer;
+ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithString:(NSString *)string;
@end @class NSString, NSURL, NSError;
+
+@interface NSMutableString : NSString
+- (void)appendFormat:(NSString *)format, ... __attribute__((format(__NSString__, 1, 2)));
+@end
+
@interface NSData : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
@@ -112,3 +118,13 @@ extern void CFRelease(CFTypeRef cf);
extern CFMutableStringRef CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocatorRef alloc, UniChar *chars, CFIndex numChars, CFIndex capacity, CFAllocatorRef externalCharactersAllocator);
extern CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator);
extern void CFStringAppend(CFMutableStringRef theString, CFStringRef appendedString);
+
+void SystemHeaderFunctionWithBlockParam(void *, void (^block)(void *), unsigned);
+
+@interface NSPointerArray : NSObject <NSFastEnumeration, NSCopying, NSCoding>
+- (void)addPointer:(void *)pointer;
+- (void)insertPointer:(void *)item atIndex:(NSUInteger)index;
+- (void)replacePointerAtIndex:(NSUInteger)index withPointer:(void *)item;
+- (void *)pointerAtIndex:(NSUInteger)index;
+@end
+
diff --git a/test/Analysis/system-header-simulator.h b/test/Analysis/system-header-simulator.h
index 6212131..5790fb9 100644
--- a/test/Analysis/system-header-simulator.h
+++ b/test/Analysis/system-header-simulator.h
@@ -36,3 +36,27 @@ FILE *funopen(const void *,
fpos_t (*)(void *, fpos_t, int),
int (*)(void *));
+int sqlite3_bind_text_my(int, const char*, int n, void(*)(void*));
+
+typedef void (*freeCallback) (void*);
+typedef struct {
+ int i;
+ freeCallback fc;
+} StWithCallback;
+
+int dealocateMemWhenDoneByVal(void*, StWithCallback);
+int dealocateMemWhenDoneByRef(StWithCallback*, const void*);
+
+typedef struct CGContext *CGContextRef;
+CGContextRef CGBitmapContextCreate(void *data/*, size_t width, size_t height,
+ size_t bitsPerComponent, size_t bytesPerRow,
+ CGColorSpaceRef space,
+ CGBitmapInfo bitmapInfo*/);
+void *CGBitmapContextGetData(CGContextRef context);
+
+// Include xpc.
+typedef struct _xpc_connection_s * xpc_connection_t;
+typedef void (*xpc_finalizer_t)(void *value);
+void xpc_connection_set_context(xpc_connection_t connection, void *context);
+void xpc_connection_set_finalizer_f(xpc_connection_t connection, xpc_finalizer_t finalizer);
+void xpc_connection_resume(xpc_connection_t connection);
diff --git a/test/Analysis/taint-generic.c b/test/Analysis/taint-generic.c
index b00372a..8ee1896 100644
--- a/test/Analysis/taint-generic.c
+++ b/test/Analysis/taint-generic.c
@@ -183,3 +183,32 @@ void testTaintedVLASize() {
scanf("%d", &x);
int vla[x]; // expected-warning{{Declared variable-length array (VLA) has tainted size}}
}
+
+// This computation used to take a very long time.
+#define longcmp(a,b,c) { \
+ a -= c; a ^= c; c += b; b -= a; b ^= (a<<6) | (a >> (32-b)); a += c; c -= b; c ^= b; b += a; \
+ a -= c; a ^= c; c += b; b -= a; b ^= a; a += c; c -= b; c ^= b; b += a; }
+
+unsigned radar11369570_hanging(const unsigned char *arr, int l) {
+ unsigned a, b, c;
+ a = b = c = 0x9899e3 + l;
+ while (l >= 6) {
+ unsigned t;
+ scanf("%d", &t);
+ a += b;
+ a ^= a;
+ a += (arr[3] + ((unsigned) arr[2] << 8) + ((unsigned) arr[1] << 16) + ((unsigned) arr[0] << 24));
+ longcmp(a, t, c);
+ l -= 12;
+ }
+ return 5/a; // expected-warning {{Division by a tainted value, possibly zero}}
+}
+
+// Check that we do not assert of the following code.
+int SymSymExprWithDiffTypes(void* p) {
+ int i;
+ scanf("%d", &i);
+ int j = (i % (int)(long)p);
+ return 5/j; // expected-warning {{Division by a tainted value, possibly zero}}
+}
+
diff --git a/test/Analysis/taint-tester.c b/test/Analysis/taint-tester.c
index 3773335..a83ee32 100644
--- a/test/Analysis/taint-tester.c
+++ b/test/Analysis/taint-tester.c
@@ -40,7 +40,7 @@ void taintTracking(int x) {
// FIXME: We fail to propagate the taint here because RegionStore does not
// handle ElementRegions with symbolic indexes.
int addrDeref = *addr; // expected-warning + {{tainted}}
- int _addrDeref = addrDeref;
+ int _addrDeref = addrDeref; // expected-warning + {{tainted}}
// Tainted struct address, casts.
struct XYStruct *xyPtr = 0;
diff --git a/test/Analysis/temp-obj-dtors-cfg-output.cpp b/test/Analysis/temp-obj-dtors-cfg-output.cpp
index 53ab211..6dbbc82 100644
--- a/test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ b/test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -cfg-add-implicit-dtors -cfg-add-initializers %s 2>&1 | FileCheck %s
+// RUN: rm -f %t
+// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -cfg-add-implicit-dtors %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
// XPASS: *
class A {
@@ -106,9 +108,88 @@ TestCtorInits::TestCtorInits()
: a(int(A()) + int(B()))
, b() {}
+// CHECK: [B1 (ENTRY)]
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B1 (ENTRY)]
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: A() (CXXConstructExpr, class A)
+// CHECK: 2: [B1.1] (BindTemporary)
+// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 4: [B1.3]
+// CHECK: 5: [B1.4] (CXXConstructExpr, class A)
+// CHECK: 6: ~A() (Temporary object destructor)
+// CHECK: 7: return [B1.5];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: false
+// CHECK: 2: return [B1.1];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: 0
+// CHECK: 2: return [B1.1];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B1 (ENTRY)]
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B1 (ENTRY)]
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: true
+// CHECK: 2: return [B1.1];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: 1
+// CHECK: 2: return [B1.1];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: A() (CXXConstructExpr, class A)
+// CHECK: 2: [B1.1] (BindTemporary)
+// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK: 4: [B1.3]
+// CHECK: 5: [B1.4] (CXXConstructExpr, class A)
+// CHECK: 6: ~A() (Temporary object destructor)
+// CHECK: 7: return [B1.5];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
-
// CHECK: [B1]
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B1.1] (BindTemporary)
@@ -150,7 +231,7 @@ TestCtorInits::TestCtorInits()
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B10 (ENTRY)]
-// CHECK: Succs (1): B8
+// CHECK: Succs (1): B9
// CHECK: [B1]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: int b;
@@ -161,62 +242,62 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B3
// CHECK: Succs (1): B1
// CHECK: [B3]
-// CHECK: 1: [B4.6] && [B5.5]
+// CHECK: 1: [B5.6] && [B4.5]
// CHECK: 2: foo
// CHECK: 3: [B3.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(_Bool))
// CHECK: 4: [B3.3]([B3.1])
-// CHECK: T: [B4.6] && ...
-// CHECK: Preds (2): B5 B4
+// CHECK: T: [B5.6] && ...
+// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B2 B1
// CHECK: [B4]
+// CHECK: 1: B() (CXXConstructExpr, class B)
+// CHECK: 2: [B4.1] (BindTemporary)
+// CHECK: 3: [B4.2].operator _Bool
+// CHECK: 4: [B4.3]()
+// CHECK: 5: [B4.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK: Preds (1): B5
+// CHECK: Succs (1): B3
+// CHECK: [B5]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
-// CHECK: 3: [B4.2] (BindTemporary)
-// CHECK: 4: [B4.3].operator _Bool
-// CHECK: 5: [B4.4]()
-// CHECK: 6: [B4.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: T: [B4.6] && ...
+// CHECK: 3: [B5.2] (BindTemporary)
+// CHECK: 4: [B5.3].operator _Bool
+// CHECK: 5: [B5.4]()
+// CHECK: 6: [B5.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK: T: [B5.6] && ...
// CHECK: Preds (2): B6 B7
-// CHECK: Succs (2): B5 B3
-// CHECK: [B5]
-// CHECK: 1: B() (CXXConstructExpr, class B)
-// CHECK: 2: [B5.1] (BindTemporary)
-// CHECK: 3: [B5.2].operator _Bool
-// CHECK: 4: [B5.3]()
-// CHECK: 5: [B5.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: Preds (1): B4
-// CHECK: Succs (1): B3
+// CHECK: Succs (2): B4 B3
// CHECK: [B6]
// CHECK: 1: ~B() (Temporary object destructor)
// CHECK: Preds (1): B7
-// CHECK: Succs (1): B4
+// CHECK: Succs (1): B5
// CHECK: [B7]
-// CHECK: 1: [B8.5] && [B9.5]
+// CHECK: 1: [B9.5] && [B8.5]
// CHECK: 2: bool a = A().operator _Bool() && B().operator _Bool();
-// CHECK: T: [B8.5] && ...
-// CHECK: Preds (2): B9 B8
-// CHECK: Succs (2): B6 B4
+// CHECK: T: [B9.5] && ...
+// CHECK: Preds (2): B8 B9
+// CHECK: Succs (2): B6 B5
// CHECK: [B8]
-// CHECK: 1: A() (CXXConstructExpr, class A)
+// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B8.1] (BindTemporary)
// CHECK: 3: [B8.2].operator _Bool
// CHECK: 4: [B8.3]()
// CHECK: 5: [B8.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: T: [B8.5] && ...
-// CHECK: Preds (1): B10
-// CHECK: Succs (2): B9 B7
+// CHECK: Preds (1): B9
+// CHECK: Succs (1): B7
// CHECK: [B9]
-// CHECK: 1: B() (CXXConstructExpr, class B)
+// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B9.1] (BindTemporary)
// CHECK: 3: [B9.2].operator _Bool
// CHECK: 4: [B9.3]()
// CHECK: 5: [B9.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: Preds (1): B8
-// CHECK: Succs (1): B7
+// CHECK: T: [B9.5] && ...
+// CHECK: Preds (1): B10
+// CHECK: Succs (2): B8 B7
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B10 (ENTRY)]
-// CHECK: Succs (1): B8
+// CHECK: Succs (1): B9
// CHECK: [B1]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: int b;
@@ -227,58 +308,58 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B3
// CHECK: Succs (1): B1
// CHECK: [B3]
-// CHECK: 1: [B4.6] || [B5.5]
+// CHECK: 1: [B5.6] || [B4.5]
// CHECK: 2: foo
// CHECK: 3: [B3.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(_Bool))
// CHECK: 4: [B3.3]([B3.1])
-// CHECK: T: [B4.6] || ...
-// CHECK: Preds (2): B5 B4
+// CHECK: T: [B5.6] || ...
+// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B1 B2
// CHECK: [B4]
+// CHECK: 1: B() (CXXConstructExpr, class B)
+// CHECK: 2: [B4.1] (BindTemporary)
+// CHECK: 3: [B4.2].operator _Bool
+// CHECK: 4: [B4.3]()
+// CHECK: 5: [B4.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK: Preds (1): B5
+// CHECK: Succs (1): B3
+// CHECK: [B5]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
-// CHECK: 3: [B4.2] (BindTemporary)
-// CHECK: 4: [B4.3].operator _Bool
-// CHECK: 5: [B4.4]()
-// CHECK: 6: [B4.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: T: [B4.6] || ...
+// CHECK: 3: [B5.2] (BindTemporary)
+// CHECK: 4: [B5.3].operator _Bool
+// CHECK: 5: [B5.4]()
+// CHECK: 6: [B5.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK: T: [B5.6] || ...
// CHECK: Preds (2): B6 B7
-// CHECK: Succs (2): B3 B5
-// CHECK: [B5]
-// CHECK: 1: B() (CXXConstructExpr, class B)
-// CHECK: 2: [B5.1] (BindTemporary)
-// CHECK: 3: [B5.2].operator _Bool
-// CHECK: 4: [B5.3]()
-// CHECK: 5: [B5.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: Preds (1): B4
-// CHECK: Succs (1): B3
+// CHECK: Succs (2): B3 B4
// CHECK: [B6]
// CHECK: 1: ~B() (Temporary object destructor)
// CHECK: Preds (1): B7
-// CHECK: Succs (1): B4
+// CHECK: Succs (1): B5
// CHECK: [B7]
-// CHECK: 1: [B8.5] || [B9.5]
+// CHECK: 1: [B9.5] || [B8.5]
// CHECK: 2: bool a = A().operator _Bool() || B().operator _Bool();
-// CHECK: T: [B8.5] || ...
-// CHECK: Preds (2): B9 B8
-// CHECK: Succs (2): B4 B6
+// CHECK: T: [B9.5] || ...
+// CHECK: Preds (2): B8 B9
+// CHECK: Succs (2): B5 B6
// CHECK: [B8]
-// CHECK: 1: A() (CXXConstructExpr, class A)
+// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B8.1] (BindTemporary)
// CHECK: 3: [B8.2].operator _Bool
// CHECK: 4: [B8.3]()
// CHECK: 5: [B8.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: T: [B8.5] || ...
-// CHECK: Preds (1): B10
-// CHECK: Succs (2): B7 B9
+// CHECK: Preds (1): B9
+// CHECK: Succs (1): B7
// CHECK: [B9]
-// CHECK: 1: B() (CXXConstructExpr, class B)
+// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B9.1] (BindTemporary)
// CHECK: 3: [B9.2].operator _Bool
// CHECK: 4: [B9.3]()
// CHECK: 5: [B9.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: Preds (1): B8
-// CHECK: Succs (1): B7
+// CHECK: T: [B9.5] || ...
+// CHECK: Preds (1): B10
+// CHECK: Succs (2): B7 B8
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B11 (ENTRY)]
diff --git a/test/Analysis/templates.cpp b/test/Analysis/templates.cpp
new file mode 100644
index 0000000..671aa78
--- /dev/null
+++ b/test/Analysis/templates.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -fblocks -verify %s
+
+void clang_analyzer_eval(bool);
+
+// Do not crash on this templated code which uses a block.
+typedef void (^my_block)(void);
+static void useBlock(my_block block){}
+template<class T> class MyClass;
+typedef MyClass<float> Mf;
+
+template<class T>
+class MyClass
+{
+public:
+ MyClass() {}
+ MyClass(T a);
+ void I();
+private:
+ static const T one;
+};
+
+template<class T> const T MyClass<T>::one = static_cast<T>(1);
+template<class T> inline MyClass<T>::MyClass(T a){}
+template<class T> void MyClass<T>::I() {
+ static MyClass<T>* mPtr = 0;
+ useBlock(^{ mPtr = new MyClass<T> (MyClass<T>::one); });
+};
+int main(){
+ Mf m;
+ m.I();
+}
+
+
+// <rdar://problem/11949235>
+template<class T, unsigned N>
+inline unsigned array_lengthof(T (&)[N]) {
+ return N;
+}
+
+void testNonTypeTemplateInstantiation() {
+ const char *S[] = { "a", "b" };
+ clang_analyzer_eval(array_lengthof(S) == 2); // expected-warning{{TRUE}}
+}
+
diff --git a/test/Analysis/test-variably-modified-types.c b/test/Analysis/test-variably-modified-types.c
new file mode 100644
index 0000000..a833434
--- /dev/null
+++ b/test/Analysis/test-variably-modified-types.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyze-function=testVariablyModifiedTypes -verify %s
+
+// Test that we process variably modified type correctly - the call graph construction should pick up function_with_bug while recursively visiting test_variably_modifiable_types.
+unsigned getArraySize(int *x) {
+ if (!x)
+ return *x; // expected-warning {{Dereference of null pointer}}
+ return 1;
+}
+
+int testVariablyModifiedTypes(int *x) {
+ int mytype[getArraySize(x)];
+ return 0;
+}
diff --git a/test/Analysis/traversal-algorithm.mm b/test/Analysis/traversal-algorithm.mm
new file mode 100644
index 0000000..8a3dc8b
--- /dev/null
+++ b/test/Analysis/traversal-algorithm.mm
@@ -0,0 +1,213 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpTraversal -analyzer-max-loop 4 -std=c++11 %s | FileCheck -check-prefix=DFS %s
+
+int a();
+int b();
+int c();
+
+int work();
+
+void test(id input) {
+ if (a()) {
+ if (a())
+ b();
+ else
+ c();
+ } else {
+ if (b())
+ a();
+ else
+ c();
+ }
+
+ if (a())
+ work();
+}
+
+void testLoops(id input) {
+ while (a()) {
+ work();
+ work();
+ work();
+ }
+
+ for (int i = 0; i != b(); ++i) {
+ work();
+ }
+
+ for (id x in input) {
+ work();
+ work();
+ work();
+ }
+
+ int z[] = {1,2,3};
+ for (int y : z) {
+ work();
+ work();
+ work();
+ }
+}
+
+// This ordering assumes that false cases happen before the true cases.
+
+// DFS:27 WhileStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:27 WhileStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:27 WhileStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:27 WhileStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:33 ForStmt
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:44 CXXForRangeStmt
+// DFS-next:--END PATH--
+// DFS-next:37 ObjCForCollectionStmt
+// DFS-next:10 IfStmt
+// DFS-next:16 IfStmt
+// DFS-next:22 IfStmt
+// DFS-next:--END PATH--
+// DFS-next:--END PATH--
+// DFS-next:22 IfStmt
+// DFS-next:--END PATH--
+// DFS-next:--END PATH--
+// DFS-next:11 IfStmt
+// DFS-next:22 IfStmt
+// DFS-next:--END PATH--
+// DFS-next:--END PATH--
+// DFS-next:22 IfStmt
+// DFS-next:--END PATH--
+// DFS-next:--END PATH--
+
diff --git a/test/Analysis/uninit-sometimes.cpp b/test/Analysis/uninit-sometimes.cpp
new file mode 100644
index 0000000..7825e87
--- /dev/null
+++ b/test/Analysis/uninit-sometimes.cpp
@@ -0,0 +1,387 @@
+// RUN: %clang_cc1 -std=gnu++11 -Wsometimes-uninitialized -verify %s
+// RUN: %clang_cc1 -std=gnu++11 -Wsometimes-uninitialized -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+bool maybe();
+
+int test_if_false(bool b) {
+ int x; // expected-note {{variable}}
+ if (b) // expected-warning {{whenever 'if' condition is false}} \
+ // expected-note {{remove the 'if' if its condition is always true}}
+ x = 1;
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{8:3-10:5}:""
+// CHECK: fix-it:"{{.*}}":{7:8-7:8}:" = 0"
+
+
+int test_if_true(bool b) {
+ int x; // expected-note {{variable}}
+ if (b) {} // expected-warning {{whenever 'if' condition is true}} \
+ // expected-note {{remove the 'if' if its condition is always false}}
+ else x = 1;
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{20:3-22:8}:""
+// CHECK: fix-it:"{{.*}}":{19:8-19:8}:" = 0"
+
+
+int test_while_false(bool b) {
+ int x; // expected-note {{variable}}
+ while (b) { // expected-warning {{whenever 'while' loop exits because its condition is false}} \
+ // expected-note {{remove the condition if it is always true}}
+ if (maybe()) {
+ x = 1;
+ break;
+ }
+ };
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{32:10-32:11}:"true"
+// CHECK: fix-it:"{{.*}}":{31:8-31:8}:" = 0"
+
+
+int test_while_true(bool b) {
+ int x; // expected-note {{variable}}
+ while (b) { // expected-warning {{whenever 'while' loop is entered}} \
+ // expected-note {{remove the condition if it is always false}}
+label:
+ return x; // expected-note {{uninitialized use}}
+ }
+ x = 0;
+ goto label;
+}
+
+// CHECK: fix-it:"{{.*}}":{48:10-48:11}:"false"
+// CHECK: fix-it:"{{.*}}":{47:8-47:8}:" = 0"
+
+
+int test_do_while_false(bool b) {
+ int x; // expected-note {{variable}}
+ do {
+ if (maybe()) {
+ x = 1;
+ break;
+ }
+ } while (b); // expected-warning {{whenever 'do' loop exits because its condition is false}} \
+ // expected-note {{remove the condition if it is always true}}
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{68:12-68:13}:"true"
+// CHECK: fix-it:"{{.*}}":{62:8-62:8}:" = 0"
+
+
+int test_do_while_true(bool b) {
+ int x; // expected-note {{variable}}
+goto label2;
+ do {
+label1:
+ return x; // expected-note {{uninitialized use}}
+label2: ;
+ } while (b); // expected-warning {{whenever 'do' loop condition is true}} \
+ // expected-note {{remove the condition if it is always false}}
+ x = 0;
+ goto label1;
+}
+
+// CHECK: fix-it:"{{.*}}":{84:12-84:13}:"false"
+// CHECK: fix-it:"{{.*}}":{78:8-78:8}:" = 0"
+
+
+int test_for_false(int k) {
+ int x; // expected-note {{variable}}
+ for (int n = 0;
+ n < k; // expected-warning {{whenever 'for' loop exits because its condition is false}} \
+ // expected-note {{remove the condition if it is always true}}
+ ++n) {
+ if (maybe()) {
+ x = n;
+ break;
+ }
+ }
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{97:8-97:13}:""
+// CHECK: fix-it:"{{.*}}":{95:8-95:8}:" = 0"
+
+
+int test_for_true(int k) {
+ int x; // expected-note {{variable}}
+ int n = 0;
+ for (;
+ n < k; // expected-warning {{whenever 'for' loop is entered}} \
+ // expected-note {{remove the condition if it is always false}}
+ ++n) {
+label:
+ return x; // expected-note {{uninitialized use}}
+ }
+ x = 1;
+ goto label;
+}
+
+// CHECK: fix-it:"{{.*}}":{116:8-116:13}:"false"
+// CHECK: fix-it:"{{.*}}":{113:8-113:8}:" = 0"
+
+
+int test_for_range_false(int k) {
+ int arr[3] = { 1, 2, 3 };
+ int x;
+ for (int &a : arr) { // no-warning, condition was not explicitly specified
+ if (a == k) {
+ x = &a - arr;
+ break;
+ }
+ }
+ return x;
+}
+
+
+
+
+
+int test_for_range_true(int k) {
+ int arr[3] = { 1, 2, 3 };
+ int x;
+ for (int &a : arr) { // no-warning
+ goto label;
+ }
+ x = 0;
+label:
+ return x;
+}
+
+
+
+
+
+int test_conditional_false(int k) {
+ int x; // expected-note {{variable}}
+ (void)(
+ maybe() // expected-warning {{whenever '?:' condition is false}} \
+ // expected-note {{remove the '?:' if its condition is always true}}
+ ? x = 1 : 0);
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{164:7-166:9}:""
+// CHECK: fix-it:"{{.*}}":{166:14-166:18}:""
+// CHECK: fix-it:"{{.*}}":{162:8-162:8}:" = 0"
+
+int test_conditional_true(int k) {
+ int x; // expected-note {{variable}}
+ (void)(
+ maybe() // expected-warning {{whenever '?:' condition is true}} \
+ // expected-note {{remove the '?:' if its condition is always false}}
+ ? 0 : x = 1);
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{177:7-179:13}:""
+// CHECK: fix-it:"{{.*}}":{175:8-175:8}:" = 0"
+
+
+int test_logical_and_false(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-warning {{whenever '&&' condition is false}} \
+ // expected-note {{remove the '&&' if its condition is always true}}
+ && (x = 1);
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{189:3-191:9}:""
+// CHECK: fix-it:"{{.*}}":{188:8-188:8}:" = 0"
+
+
+int test_logical_and_true(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-warning {{whenever '&&' condition is true}} \
+ // expected-note {{remove the '&&' if its condition is always false}}
+ && ({ goto skip_init; 0; });
+ x = 1;
+skip_init:
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{201:3-203:34}:"false"
+// CHECK: fix-it:"{{.*}}":{200:8-200:8}:" = 0"
+
+
+int test_logical_or_false(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-warning {{whenever '||' condition is false}} \
+ // expected-note {{remove the '||' if its condition is always true}}
+ || ({ goto skip_init; 0; });
+ x = 1;
+skip_init:
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{215:3-217:34}:"true"
+// CHECK: fix-it:"{{.*}}":{214:8-214:8}:" = 0"
+
+
+int test_logical_or_true(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-warning {{whenever '||' condition is true}} \
+ // expected-note {{remove the '||' if its condition is always false}}
+ || (x = 1);
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{229:3-231:9}:""
+// CHECK: fix-it:"{{.*}}":{228:8-228:8}:" = 0"
+
+
+int test_switch_case(int k) {
+ int x; // expected-note {{variable}}
+ switch (k) {
+ case 0:
+ x = 0;
+ break;
+ case 1: // expected-warning {{whenever switch case is taken}}
+ break;
+ }
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{240:8-240:8}:" = 0"
+
+
+
+int test_switch_default(int k) {
+ int x; // expected-note {{variable}}
+ switch (k) {
+ case 0:
+ x = 0;
+ break;
+ case 1:
+ x = 1;
+ break;
+ default: // expected-warning {{whenever switch default is taken}}
+ break;
+ }
+ return x; // expected-note {{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{256:8-256:8}:" = 0"
+
+
+
+int test_switch_suppress_1(int k) {
+ int x;
+ switch (k) {
+ case 0:
+ x = 0;
+ break;
+ case 1:
+ x = 1;
+ break;
+ }
+ return x; // no-warning
+}
+
+
+
+
+
+int test_switch_suppress_2(int k) {
+ int x;
+ switch (k) {
+ case 0:
+ case 1:
+ switch (k) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ }
+ case 2:
+ case 3:
+ x = 1;
+ }
+ return x; // no-warning
+}
+
+
+
+
+
+int test_multiple_notes(int k) {
+ int x; // expected-note {{variable}}
+ if (k > 0) {
+ if (k == 5)
+ x = 1;
+ else if (k == 2) // expected-warning {{whenever 'if' condition is false}} \
+ // expected-note {{remove the 'if' if its condition is always true}}
+ x = 2;
+ } else {
+ if (k == -5)
+ x = 3;
+ else if (k == -2) // expected-warning {{whenever 'if' condition is false}} \
+ // expected-note {{remove the 'if' if its condition is always true}}
+ x = 4;
+ }
+ return x; // expected-note 2{{uninitialized use}}
+}
+
+// CHECK: fix-it:"{{.*}}":{324:10-326:7}:""
+// CHECK: fix-it:"{{.*}}":{318:10-320:7}:""
+// CHECK: fix-it:"{{.*}}":{314:8-314:8}:" = 0"
+
+int test_no_false_positive_1(int k) {
+ int x;
+ if (k)
+ x = 5;
+ while (!k)
+ maybe();
+ return x;
+}
+
+
+
+
+
+int test_no_false_positive_2() {
+ int x;
+ bool b = false;
+ if (maybe()) {
+ x = 5;
+ b = true;
+ }
+ return b ? x : 0;
+}
+
+
+// FIXME: In this case, the variable is used uninitialized whenever the
+// function's entry block is reached. Produce a diagnostic saying that
+// the variable is uninitialized the first time it is used.
+void test_null_pred_succ() {
+ int x;
+ if (0)
+ foo: x = 0;
+ if (x)
+ goto foo;
+}
+
+
+
+
+void foo();
+int PR13360(bool b) {
+ int x; // expected-note {{variable}}
+ if (b) { // expected-warning {{variable 'x' is used uninitialized whenever 'if' condition is true}} expected-note {{remove}}
+ do {
+ foo();
+ } while (0);
+ } else {
+ x = 1;
+ }
+ return x; // expected-note {{uninitialized use occurs here}}
+}
+
+// CHECK: fix-it:"{{.*}}":{376:3-380:10}:""
+// CHECK: fix-it:"{{.*}}":{375:8-375:8}:" = 0"
diff --git a/test/Analysis/unused-ivars.m b/test/Analysis/unused-ivars.m
index 8941840..f04132b 100644
--- a/test/Analysis/unused-ivars.m
+++ b/test/Analysis/unused-ivars.m
@@ -108,3 +108,24 @@ int radar_7254495(RDar7254495 *a) {
@implementation RDar8481311
@end
+
+@class NSString;
+@interface Radar11059352_1 {
+@private
+ NSString *_pathString;
+}
+@property (readonly, strong) NSString *pathString;
+@end
+
+@interface Radar11059352 {
+@private
+Radar11059352_1 *_workspacePath;
+}
+@end
+
+@implementation Radar11059352
+
+- (void)useWorkspace {
+ NSString *workspacePathString = _workspacePath.pathString;
+}
+@end \ No newline at end of file
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ae9de78..8184c3d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,117 +1,93 @@
-set(CLANG_TEST_DIRECTORIES
- "Analysis"
- "CodeCompletion"
- "CodeGen"
- "CodeGenCUDA"
- "CodeGenCXX"
- "CodeGenObjC"
- "CodeGenOpenCL"
- "Coverage"
- "CXX"
- "Driver"
- "FixIt"
- "Frontend"
- "Headers"
- "Index"
- "Lexer"
- "Misc"
- "PCH"
- "Parser"
- "Preprocessor"
- "Rewriter"
- "Sema"
- "SemaCUDA"
- "SemaCXX"
- "SemaObjC"
- "SemaObjCXX"
- "SemaOpenCL"
- "SemaTemplate")
+# Test runner infrastructure for Clang. This configures the Clang test trees
+# for use by Lit, and delegates to LLVM's lit test handlers.
+#
+# If this is a stand-alone Clang build, we fake up our own Lit support here
+# rather than relying on LLVM's.
-set(LLVM_SOURCE_DIR "${LLVM_MAIN_SRC_DIR}")
-set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}")
-set(LLVM_BUILD_MODE "%(build_mode)s")
-set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/%(build_config)s")
-set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib/%(build_config)s")
set(CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
set(CLANG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
-if(BUILD_SHARED_LIBS)
- set(ENABLE_SHARED 1)
-else()
- set(ENABLE_SHARED 0)
-endif(BUILD_SHARED_LIBS)
-configure_file(
+configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
-configure_file(
+configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
- @ONLY)
+ )
-include(FindPythonInterp)
-if(PYTHONINTERP_FOUND)
- if( LLVM_MAIN_SRC_DIR )
- set(LIT "${LLVM_SOURCE_DIR}/utils/lit/lit.py")
- else()
- set(LIT "${PATH_TO_LLVM_BUILD}/bin/${CMAKE_CFG_INTDIR}/llvm-lit")
- # Installed LLVM does not contain ${CMAKE_CFG_INTDIR} in paths.
- if( NOT EXISTS ${LIT} )
- set(LIT "${PATH_TO_LLVM_BUILD}/bin/llvm-lit")
- endif()
- endif()
+if( PATH_TO_LLVM_BUILD )
+ set(CLANG_TEST_EXTRA_ARGS "--path=${CLANG_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}")
+endif()
- if( PATH_TO_LLVM_BUILD )
- set(CLANG_TEST_EXTRA_ARGS "--path=${CLANG_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}")
- endif()
+option(CLANG_TEST_USE_VG "Run Clang tests under Valgrind" OFF)
+if(CLANG_TEST_USE_VG)
+ set(CLANG_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg")
+endif ()
+
+if( NOT CLANG_BUILT_STANDALONE )
- option(CLANG_TEST_USE_VG "Run Clang tests under Valgrind" OFF)
- if(CLANG_TEST_USE_VG)
- set(CLANG_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg")
- endif ()
+ set(CLANG_TEST_DEPS
+ clang clang-headers
+ c-index-test diagtool arcmt-test c-arcmt-test
+ clang-check
+ llvm-dis llc opt FileCheck count not
+ )
+ set(CLANG_TEST_PARAMS
+ clang_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
- set(LIT_ARGS "${CLANG_TEST_EXTRA_ARGS} ${LLVM_LIT_ARGS}")
- separate_arguments(LIT_ARGS)
+ if(LLVM_INCLUDE_TESTS)
+ list(APPEND CLANG_TEST_DEPS ClangUnitTests)
+ list(APPEND CLANG_TEST_PARAMS
+ clang_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
+ )
+ endif()
+ add_lit_testsuite(check-clang "Running the Clang regression tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ PARAMS ${CLANG_TEST_PARAMS}
+ DEPENDS ${CLANG_TEST_DEPS}
+ ARGS ${CLANG_TEST_EXTRA_ARGS}
+ )
+ set_target_properties(check-clang PROPERTIES FOLDER "Clang tests")
+
+else()
- add_custom_target(clang-test.deps)
- set_target_properties(clang-test.deps PROPERTIES FOLDER "Clang tests")
+ include(FindPythonInterp)
+ if(PYTHONINTERP_FOUND)
+ if( LLVM_MAIN_SRC_DIR )
+ set(LIT "${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py")
+ else()
+ set(LIT "${PATH_TO_LLVM_BUILD}/bin/${CMAKE_CFG_INTDIR}/llvm-lit")
+ # Installed LLVM does not contain ${CMAKE_CFG_INTDIR} in paths.
+ if( NOT EXISTS ${LIT} )
+ set(LIT "${PATH_TO_LLVM_BUILD}/bin/llvm-lit")
+ endif()
+ endif()
- add_custom_target(clang-test
- COMMAND ${PYTHON_EXECUTABLE}
- ${LIT}
- --param clang_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
- --param clang_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
- --param build_config=${CMAKE_CFG_INTDIR}
- --param build_mode=${RUNTIME_BUILD_MODE}
- ${LIT_ARGS}
- ${CMAKE_CURRENT_BINARY_DIR}
- COMMENT "Running Clang regression tests")
+ set(LIT_ARGS "${CLANG_TEST_EXTRA_ARGS} ${LLVM_LIT_ARGS}")
+ separate_arguments(LIT_ARGS)
- if( NOT CLANG_BUILT_STANDALONE )
- add_custom_target(check-all
+ add_custom_target(check-clang
COMMAND ${PYTHON_EXECUTABLE}
- ${LIT}
- --param build_config=${CMAKE_CFG_INTDIR}
- --param build_mode=${RUNTIME_BUILD_MODE}
- ${LIT_ARGS}
- ${LLVM_BINARY_DIR}/test
- ${CMAKE_CURRENT_BINARY_DIR}
- COMMENT "Running Clang and LLVM regression tests")
- add_dependencies(check-all clang-test.deps)
- if ( LLVM_INCLUDE_TESTS )
- add_dependencies(clang-test.deps ClangUnitTests)
- add_dependencies(check-all check.deps)
- endif ( LLVM_INCLUDE_TESTS )
- add_dependencies(clang-test.deps
- llvm-dis llc opt
- FileCheck count not
- )
+ ${LIT}
+ --param clang_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ --param build_config=${CMAKE_CFG_INTDIR}
+ --param build_mode=${RUNTIME_BUILD_MODE}
+ ${LIT_ARGS}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Running Clang regression tests"
+ DEPENDS clang clang-headers
+ c-index-test diagtool arcmt-test c-arcmt-test
+ clang-check
+ )
+ set_target_properties(check-clang PROPERTIES FOLDER "Clang tests")
endif()
- add_dependencies(clang-test clang-test.deps)
- add_dependencies(clang-test.deps
- clang clang-headers c-index-test diagtool arcmt-test c-arcmt-test
- clang-check
- )
-
endif()
+
+# Add a legacy target spelling: clang-test
+add_custom_target(clang-test)
+add_dependencies(clang-test check-clang)
+set_target_properties(clang-test PROPERTIES FOLDER "Clang tests")
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp b/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp
new file mode 100644
index 0000000..f812ea1
--- /dev/null
+++ b/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fdiagnostics-show-option -verify %s
+
+template<typename T>
+struct set{};
+struct Value {
+ template<typename T>
+ void set(T value) {}
+
+ void resolves_to_same() {
+ Value v;
+ v.set<double>(3.2);
+ }
+};
+void resolves_to_different() {
+ {
+ Value v;
+ // The fact that the next line is a warning rather than an error is an
+ // extension.
+ v.set<double>(3.2);
+ }
+ {
+ int set; // Non-template.
+ Value v;
+ v.set<double>(3.2);
+ }
+}
+
+namespace rdar9915664 {
+ struct A {
+ template<typename T> void a();
+ };
+
+ struct B : A { };
+
+ struct C : A { };
+
+ struct D : B, C {
+ A &getA() { return static_cast<B&>(*this); }
+
+ void test_a() {
+ getA().a<int>();
+ }
+ };
+}
+
+namespace PR11856 {
+ template<typename T> T end(T);
+
+ template <typename T>
+ void Foo() {
+ T it1;
+ if (it1->end < it1->end) {
+ }
+ }
+
+ template<typename T> T *end(T*);
+
+ class X { };
+ template <typename T>
+ void Foo2() {
+ T it1;
+ if (it1->end < it1->end) {
+ }
+
+ X *x;
+ if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}}
+ }
+ }
+}
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.classref/p4-cxx11.cpp b/test/CXX/basic/basic.lookup/basic.lookup.classref/p4-cxx11.cpp
new file mode 100644
index 0000000..7925454
--- /dev/null
+++ b/test/CXX/basic/basic.lookup/basic.lookup.classref/p4-cxx11.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -std=c++11 %s -verify
+
+struct A { void f(); };
+struct C { void f(); };
+struct B : A { typedef A X; };
+struct D : C { typedef C X; void g(); };
+
+void D::g()
+{
+ B * b = new B;
+ b->X::f(); // lookup for X finds B::X
+}
+
+typedef int X;
+void h(void)
+{
+ B * b = new B;
+ b->X::f(); // lookup for X finds B::X
+}
+
+
diff --git a/test/CXX/basic/basic.types/p10.cpp b/test/CXX/basic/basic.types/p10.cpp
index 83b910b..191d42b 100644
--- a/test/CXX/basic/basic.types/p10.cpp
+++ b/test/CXX/basic/basic.types/p10.cpp
@@ -22,11 +22,11 @@ struct BeingDefined {
// (implied) - it is complete
-struct Incomplete;
+struct Incomplete; // expected-note 2{{forward declaration of 'Incomplete'}}
template<class T> struct ClassTemp {};
-constexpr Incomplete incomplete = {}; // expected-error {{constexpr variable cannot have non-literal type 'const Incomplete'}}
-constexpr Incomplete incomplete2[] = {}; // expected-error {{constexpr variable cannot have non-literal type 'Incomplete const[]'}}
+constexpr Incomplete incomplete = {}; // expected-error {{constexpr variable cannot have non-literal type 'const Incomplete'}} expected-note {{incomplete type 'const Incomplete' is not a literal type}}
+constexpr Incomplete incomplete2[] = {}; // expected-error {{constexpr variable cannot have non-literal type 'Incomplete const[]'}} expected-note {{incomplete type 'Incomplete const[]' is not a literal type}}
constexpr ClassTemp<int> classtemplate = {};
constexpr ClassTemp<int> classtemplate2[] = {};
diff --git a/test/CXX/class.access/class.friend/p1.cpp b/test/CXX/class.access/class.friend/p1.cpp
index 68ff83f..7cb192b 100644
--- a/test/CXX/class.access/class.friend/p1.cpp
+++ b/test/CXX/class.access/class.friend/p1.cpp
@@ -64,6 +64,7 @@ namespace test0 {
};
class MemberFriend {
+ public:
void test();
};
@@ -309,6 +310,7 @@ namespace test10 {
// PR8705
namespace test11 {
class A {
+ public:
void test0(int);
void test1(int);
void test2(int);
diff --git a/test/CXX/class.access/class.friend/p9-cxx0x.cpp b/test/CXX/class.access/class.friend/p9-cxx0x.cpp
new file mode 100644
index 0000000..f748a2b
--- /dev/null
+++ b/test/CXX/class.access/class.friend/p9-cxx0x.cpp
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// C++98 [class.friend]p7:
+// C++11 [class.friend]p9:
+// A name nominated by a friend declaration shall be accessible in
+// the scope of the class containing the friend declaration.
+
+// PR12328
+// Simple, non-templated case.
+namespace test0 {
+ class X {
+ void f(); // expected-note {{implicitly declared private here}}
+ };
+
+ class Y {
+ friend void X::f(); // expected-error {{friend function 'f' is a private member of 'test0::X'}}
+ };
+}
+
+// Templated but non-dependent.
+namespace test1 {
+ class X {
+ void f(); // expected-note {{implicitly declared private here}}
+ };
+
+ template <class T> class Y {
+ friend void X::f(); // expected-error {{friend function 'f' is a private member of 'test1::X'}}
+ };
+}
+
+// Dependent but instantiated at the right type.
+namespace test2 {
+ template <class T> class Y;
+
+ class X {
+ void f();
+ friend class Y<int>;
+ };
+
+ template <class T> class Y {
+ friend void X::f();
+ };
+
+ template class Y<int>;
+}
+
+// Dependent and instantiated at the wrong type.
+namespace test3 {
+ template <class T> class Y;
+
+ class X {
+ void f(); // expected-note {{implicitly declared private here}}
+ friend class Y<int>;
+ };
+
+ template <class T> class Y {
+ friend void X::f(); // expected-error {{friend function 'f' is a private member of 'test3::X'}}
+ };
+
+ template class Y<float>; // expected-note {{in instantiation}}
+}
+
+// Dependent because dependently-scoped.
+namespace test4 {
+ template <class T> class X {
+ void f();
+ };
+
+ template <class T> class Y {
+ friend void X<T>::f();
+ };
+}
+
+// Dependently-scoped, no friends.
+namespace test5 {
+ template <class T> class X {
+ void f(); // expected-note {{implicitly declared private here}}
+ };
+
+ template <class T> class Y {
+ friend void X<T>::f(); // expected-error {{friend function 'f' is a private member of 'test5::X<int>'}}
+ };
+
+ template class Y<int>; // expected-note {{in instantiation}}
+}
+
+// Dependently-scoped, wrong friend.
+namespace test6 {
+ template <class T> class Y;
+
+ template <class T> class X {
+ void f(); // expected-note {{implicitly declared private here}}
+ friend class Y<float>;
+ };
+
+ template <class T> class Y {
+ friend void X<T>::f(); // expected-error {{friend function 'f' is a private member of 'test6::X<int>'}}
+ };
+
+ template class Y<int>; // expected-note {{in instantiation}}
+}
+
+// Dependently-scoped, right friend.
+namespace test7 {
+ template <class T> class Y;
+
+ template <class T> class X {
+ void f();
+ friend class Y<int>;
+ };
+
+ template <class T> class Y {
+ friend void X<T>::f();
+ };
+
+ template class Y<int>;
+}
diff --git a/test/CXX/class.derived/class.virtual/p3-0x.cpp b/test/CXX/class.derived/class.virtual/p3-0x.cpp
index c4a401b..16f9828 100644
--- a/test/CXX/class.derived/class.virtual/p3-0x.cpp
+++ b/test/CXX/class.derived/class.virtual/p3-0x.cpp
@@ -20,9 +20,15 @@ struct A {
template<typename T>
struct B : A {
+ // FIXME: Diagnose this.
virtual void f(T) override;
};
+template<typename T>
+struct C : A {
+ virtual void f(int) override; // expected-error {{does not override}}
+};
+
}
namespace Test3 {
@@ -51,3 +57,46 @@ struct D : B {
};
}
+
+namespace PR13499 {
+ struct X {
+ virtual void f();
+ virtual void h();
+ };
+ template<typename T> struct A : X {
+ void f() override;
+ void h() final;
+ };
+ template<typename T> struct B : X {
+ void g() override; // expected-error {{only virtual member functions can be marked 'override'}}
+ void i() final; // expected-error {{only virtual member functions can be marked 'final'}}
+ };
+ B<int> b; // no-note
+ template<typename T> struct C : T {
+ void g() override;
+ void i() final;
+ };
+ template<typename T> struct D : X {
+ virtual void g() override; // expected-error {{does not override}}
+ virtual void i() final;
+ };
+ template<typename...T> struct E : X {
+ void f(T...) override;
+ void g(T...) override; // expected-error {{only virtual member functions can be marked 'override'}}
+ void h(T...) final;
+ void i(T...) final; // expected-error {{only virtual member functions can be marked 'final'}}
+ };
+ // FIXME: Diagnose these in the template definition, not in the instantiation.
+ E<> e; // expected-note {{in instantiation of}}
+
+ template<typename T> struct Y : T {
+ void f() override;
+ void h() final;
+ };
+ template<typename T> struct Z : T {
+ void g() override; // expected-error {{only virtual member functions can be marked 'override'}}
+ void i() final; // expected-error {{only virtual member functions can be marked 'final'}}
+ };
+ Y<X> y;
+ Z<X> z; // expected-note {{in instantiation of}}
+}
diff --git a/test/CXX/class/class.mem/p14.cpp b/test/CXX/class/class.mem/p14.cpp
index 72b232e..3f14099 100644
--- a/test/CXX/class/class.mem/p14.cpp
+++ b/test/CXX/class/class.mem/p14.cpp
@@ -9,8 +9,9 @@ struct X0 {
};
struct X1 {
- int X1;
- X1(); // expected-error{{declarator requires an identifier}}
+ int X1; // expected-note{{hidden by a non-type declaration of 'X1' here}}
+ X1(); // expected-error{{must use 'struct' tag to refer to type 'X1' in this scope}} \
+ // expected-error{{expected member name or ';' after declaration specifiers}}
};
struct X2 {
diff --git a/test/CXX/class/class.union/p1.cpp b/test/CXX/class/class.union/p1.cpp
index f344ae5..ee97410 100644
--- a/test/CXX/class/class.union/p1.cpp
+++ b/test/CXX/class/class.union/p1.cpp
@@ -27,9 +27,8 @@ class CopyCtor {
CopyCtor(CopyCtor &cc) { abort(); } // expected-note 4 {{because type 'CopyCtor' has a user-declared copy constructor}}
};
-// FIXME: this should eventually trigger on the operator's declaration line
-class CopyAssign { // expected-note 4 {{because type 'CopyAssign' has a user-declared copy assignment operator}}
- CopyAssign& operator=(CopyAssign& CA) { abort(); }
+class CopyAssign {
+ CopyAssign& operator=(CopyAssign& CA) { abort(); } // expected-note 4 {{because type 'CopyAssign' has a user-declared copy assignment operator}}
};
class Dtor {
diff --git a/test/CXX/class/p6-0x.cpp b/test/CXX/class/p6-0x.cpp
index f2cf482..e153b4d 100644
--- a/test/CXX/class/p6-0x.cpp
+++ b/test/CXX/class/p6-0x.cpp
@@ -19,7 +19,7 @@ struct Trivial2 {
Trivial2(const Trivial2 &) = default;
Trivial2(Trivial2 &&) = default;
Trivial2 &operator=(const Trivial2 &) = default;
- Trivial2 &operator=(Trivial2 &) = default;
+ Trivial2 &operator=(Trivial2 &&) = default;
~Trivial2() = default;
};
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
index 65573c7..dfc1d3d 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
@@ -209,7 +209,7 @@ struct ConstexprBaseMemberCtors : Literal {
{}
};
-// - every assignment-expression that is an initializer-caluse appearing
+// - every assignment-expression that is an initializer-clause appearing
// directly or indirectly within a brace-or-equal-initializer for a non-static
// data member that is not named by a mem-initializer-id shall be a constant
// expression; and
@@ -224,6 +224,25 @@ struct X {
constexpr X(int c) : a(c) {} // ok, b initialized by 2 * c + 1
};
+union XU1 { int a; constexpr XU1() = default; }; // expected-error{{not constexpr}}
+union XU2 { int a = 1; constexpr XU2() = default; };
+
+struct XU3 {
+ union {
+ int a;
+ };
+ constexpr XU3() = default; // expected-error{{not constexpr}}
+};
+struct XU4 {
+ union {
+ int a = 1;
+ };
+ constexpr XU4() = default;
+};
+
+static_assert(XU2().a == 1, "");
+static_assert(XU4().a == 1, "");
+
// - every implicit conversion used in converting a constructor argument to the
// corresponding parameter type and converting a full-expression to the
// corresponding member type shall be one of those allowed in a constant
@@ -247,3 +266,30 @@ namespace StdExample {
int val;
};
}
+
+namespace CtorLookup {
+ // Ensure that we look up which constructor will actually be used.
+ struct A {
+ constexpr A(const A&) {}
+ A(A&) {}
+ constexpr A(int); // expected-note {{previous}}
+ };
+ constexpr A::A(int = 0) {} // expected-warning {{default constructor}}
+
+ struct B : A {
+ B() = default;
+ constexpr B(const B&);
+ constexpr B(B&);
+ };
+ constexpr B::B(const B&) = default;
+ constexpr B::B(B&) = default; // expected-error {{not constexpr}}
+
+ struct C {
+ A a;
+ C() = default;
+ constexpr C(const C&);
+ constexpr C(C&);
+ };
+ constexpr C::C(const C&) = default;
+ constexpr C::C(C&) = default; // expected-error {{not constexpr}}
+}
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp
index 1daf02f..f732255 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp
@@ -49,3 +49,40 @@ void p3example() {
same<__typeof(u), const int> uHasTypeConstInt;
same<__typeof(y), double> yHasTypeDouble;
}
+
+#if __cplusplus >= 201103L
+namespace PR13293 {
+ // Ensure that dependent declarators have their deduction delayed.
+ int f(char);
+ double f(short);
+ template<typename T> struct S {
+ static constexpr auto (*p)(T) = &f;
+ };
+
+ constexpr int (*f1)(char) = &f;
+ constexpr double (*f2)(short) = &f;
+ static_assert(S<char>::p == f1, "");
+ static_assert(S<short>::p == f2, "");
+
+ struct K { int n; };
+ template<typename T> struct U {
+ static constexpr auto (T::*p) = &K::n;
+ };
+ static_assert(U<K>::p == &K::n, "");
+
+ template<typename T>
+ using X = auto(int) -> auto(*)(T) -> auto(*)(char) -> long;
+ X<double> x;
+ template<typename T> struct V {
+ //static constexpr auto (*p)(int) -> auto(*)(T) -> auto(*)(char) = &x; // ill-formed
+ static constexpr auto (*(*(*p)(int))(T))(char) = &x; // ok
+ };
+ V<double> v;
+
+ int *g(double);
+ template<typename T> void h() {
+ new (auto(*)(T)) (&g);
+ }
+ template void h<double>();
+}
+#endif
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp
index 71f57dc..7499829 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp
@@ -64,5 +64,4 @@ template<typename T = auto> struct G { }; // expected-error{{'auto' not allowed
using A = auto; // expected-error{{'auto' not allowed in type alias}}
-// FIXME: don't issue the second diagnostic for this error.
-auto k() -> auto; // expected-error{{'auto' not allowed in function return type}} unexpected-error{{without trailing return type}}
+auto k() -> auto; // expected-error{{'auto' not allowed in function return type}}
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
index 2bd5d23..093bc14 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
@@ -21,7 +21,7 @@ void r() {
class PD {
friend struct A;
- ~PD(); // expected-note 4{{here}}
+ ~PD(); // expected-note 5{{here}}
public:
typedef int n;
};
@@ -37,8 +37,14 @@ struct A {
};
// Two errors here: one for the decltype, one for the variable.
-decltype(PD(), PD()) pd1; // expected-error 2{{private destructor}}
-decltype(DD(), DD()) dd1; // expected-error 2{{deleted function}}
+decltype(
+ PD(), // expected-error {{private destructor}}
+ PD()) pd1; // expected-error {{private destructor}}
+decltype(DD(), // expected-error {{deleted function}}
+ DD()) dd1; // expected-error {{deleted function}}
+decltype(
+ PD(), // expected-error {{temporary of type 'PD' has private destructor}}
+ 0) pd2;
decltype(((13, ((DD())))))::n dd_parens; // ok
decltype(((((42)), PD())))::n pd_parens_comma; // ok
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.typedef/p2-0x.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.typedef/p2-0x.cpp
index b06eb01..89a28ad 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.typedef/p2-0x.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.typedef/p2-0x.cpp
@@ -124,9 +124,7 @@ namespace TagName {
}
namespace CWG1044 {
- // FIXME: this diagnostic isn't ideal. one diagnostic is enough.
- using T = T; // expected-error {{type name requires a specifier}} \
- expected-error {{expected ';' after alias declaration}}
+ using T = T; // expected-error {{unknown type name 'T'}}
}
namespace StdExample {
diff --git a/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp b/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
index 06dd1bb..783aba1 100644
--- a/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
+++ b/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fcxx-exceptions %s
// An explicitly-defaulted function may be declared constexpr only if it would
// have been implicitly declared as constexpr.
@@ -27,7 +27,7 @@ struct S2 {
// -- it is implicitly considered to be constexpr if the implicit declaration
// would be
struct S3 {
- S3() = default; // expected-note {{here}}
+ S3() = default;
S3(const S3&) = default;
S3(S3&&) = default;
constexpr S3(int n) : n(n) {}
@@ -36,7 +36,7 @@ struct S3 {
constexpr S3 s3a = S3(0);
constexpr S3 s3b = s3a;
constexpr S3 s3c = S3();
-constexpr S3 s3d; // expected-error {{constant expression}} expected-note {{non-constexpr constructor}}
+constexpr S3 s3d; // expected-error {{default initialization of an object of const type 'const S3' requires a user-provided default constructor}}
struct S4 {
S4() = default;
@@ -44,7 +44,7 @@ struct S4 {
S4(S4&&) = default; // expected-note {{here}}
NoCopyMove ncm;
};
-constexpr S4 s4a; // ok
+constexpr S4 s4a{}; // ok
constexpr S4 s4b = S4(); // expected-error {{constant expression}} expected-note {{non-constexpr constructor}}
constexpr S4 s4c = s4a; // expected-error {{constant expression}} expected-note {{non-constexpr constructor}}
@@ -54,3 +54,71 @@ struct S5 {
};
constexpr S5::S5() = default;
static_assert(S5().m == 4, "");
+
+
+// An explicitly-defaulted function may have an exception specification only if
+// it is compatible with the exception specification on an implicit declaration.
+struct E1 {
+ E1() noexcept = default;
+ E1(const E1&) noexcept = default;
+ E1(E1&&) noexcept = default;
+ E1 &operator=(const E1&) noexcept = default;
+ E1 &operator=(E1&&) noexcept = default;
+ ~E1() noexcept = default;
+};
+struct E2 {
+ E2() noexcept(false) = default; // expected-error {{exception specification of explicitly defaulted default constructor does not match the calculated one}}
+ E2(const E2&) noexcept(false) = default; // expected-error {{exception specification of explicitly defaulted copy constructor does not match the calculated one}}
+ E2(E2&&) noexcept(false) = default; // expected-error {{exception specification of explicitly defaulted move constructor does not match the calculated one}}
+ E2 &operator=(const E2&) noexcept(false) = default; // expected-error {{exception specification of explicitly defaulted copy assignment operator does not match the calculated one}}
+ E2 &operator=(E2&&) noexcept(false) = default; // expected-error {{exception specification of explicitly defaulted move assignment operator does not match the calculated one}}
+ ~E2() noexcept(false) = default; // expected-error {{exception specification of explicitly defaulted destructor does not match the calculated one}}
+};
+
+// If a function is explicitly defaulted on its first declaration
+// -- it is implicitly considered to have the same exception-specification as
+// if it had been implicitly declared
+struct E3 {
+ E3() = default;
+ E3(const E3&) = default;
+ E3(E3&&) = default;
+ E3 &operator=(const E3&) = default;
+ E3 &operator=(E3&&) = default;
+ ~E3() = default;
+};
+E3 e3;
+static_assert(noexcept(E3(), E3(E3()), E3(e3), e3 = E3(), e3 = e3), "");
+struct E4 {
+ E4() noexcept(false);
+ E4(const E4&) noexcept(false);
+ E4(E4&&) noexcept(false);
+ E4 &operator=(const E4&) noexcept(false);
+ E4 &operator=(E4&&) noexcept(false);
+ ~E4() noexcept(false);
+};
+struct E5 {
+ E5() = default;
+ E5(const E5&) = default;
+ E5(E5&&) = default;
+ E5 &operator=(const E5&) = default;
+ E5 &operator=(E5&&) = default;
+ ~E5() = default;
+
+ E4 e4;
+};
+E5 e5;
+static_assert(!noexcept(E5()), "");
+static_assert(!noexcept(E5(static_cast<E5&&>(e5))), "");
+static_assert(!noexcept(E5(e5)), "");
+static_assert(!noexcept(e5 = E5()), "");
+static_assert(!noexcept(e5 = e5), "");
+
+namespace PR13492 {
+ struct B {
+ B() = default;
+ };
+
+ void f() {
+ const B b; // expected-error {{default initialization of an object of const type 'const PR13492::B' requires a user-provided default constructor}}
+ }
+}
diff --git a/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.delete/p4.cpp b/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.delete/p4.cpp
new file mode 100644
index 0000000..16fd5e6
--- /dev/null
+++ b/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.delete/p4.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+template<typename> void func();
+template<> void func<int>() = delete;
+
+template<typename> void func2();
+template<> void func2<int>(); // expected-note {{previous declaration is here}}
+template<> void func2<int>() = delete; // expected-error {{deleted definition must be first declaration}}
diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp
index 7764980..fef3692 100644
--- a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp
+++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp
@@ -72,7 +72,7 @@ struct DefaultedAggr {
DefaultedAggr(const DefaultedAggr &) = default;
DefaultedAggr(DefaultedAggr &&) = default;
DefaultedAggr &operator=(const DefaultedAggr &) = default;
- DefaultedAggr &operator=(DefaultedAggr &) = default;
+ DefaultedAggr &operator=(DefaultedAggr &&) = default;
~DefaultedAggr() = default;
};
DefaultedAggr da = { 42 } ;
diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp
new file mode 100644
index 0000000..3450003
--- /dev/null
+++ b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp
@@ -0,0 +1,113 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+namespace std {
+ typedef decltype(sizeof(int)) size_t;
+
+ template <typename E>
+ struct initializer_list
+ {
+ const E *p;
+ size_t n;
+ initializer_list(const E *p, size_t n) : p(p), n(n) {}
+ };
+
+ struct string {
+ string(const char *);
+ };
+
+ template<typename A, typename B>
+ struct pair {
+ pair(const A&, const B&);
+ };
+}
+
+namespace bullet2 {
+ double ad[] = { 1, 2.0 };
+ int ai[] = { 1, 2.0 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}}
+
+ struct S2 {
+ int m1;
+ double m2, m3;
+ };
+
+ S2 s21 = { 1, 2, 3.0 };
+ S2 s22 { 1.0, 2, 3 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}}
+ S2 s23 { };
+}
+
+namespace bullet4_example1 {
+ struct S {
+ S(std::initializer_list<double> d) {}
+ S(std::initializer_list<int> i) {}
+ S() {}
+ };
+
+ S s1 = { 1.0, 2.0, 3.0 };
+ S s2 = { 1, 2, 3 };
+ S s3 = { };
+}
+
+namespace bullet4_example2 {
+ struct Map {
+ Map(std::initializer_list<std::pair<std::string,int>>) {}
+ };
+
+ Map ship = {{"Sophie",14}, {"Surprise",28}};
+}
+
+namespace bullet4_example3 {
+ struct S {
+ S(int, double, double) {}
+ S() {}
+ };
+
+ S s1 = { 1, 2, 3.0 };
+ // FIXME: This is an ill-formed narrowing initialization.
+ S s2 { 1.0, 2, 3 };
+ S s3 {};
+}
+
+namespace bullet5 {
+ struct S {
+ S(std::initializer_list<double>) {}
+ S(const std::string &) {}
+ };
+
+ const S& r1 = { 1, 2, 3.0 };
+ const S& r2 = { "Spinach" };
+ S& r3 = { 1, 2, 3 }; // expected-error {{non-const lvalue reference to type 'bullet5::S' cannot bind to an initializer list temporary}}
+ const int& i1 = { 1 };
+ const int& i2 = { 1.1 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} expected-warning {{implicit conversion}}
+ const int (&iar)[2] = { 1, 2 };
+}
+
+namespace bullet6 {
+ int x1 {2};
+ int x2 {2.0}; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}}
+}
+
+namespace bullet7 {
+ int** pp {};
+}
+
+namespace bullet8 {
+ struct A { int i; int j; };
+ A a1 { 1, 2 };
+ A a2 { 1.2 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} expected-warning {{implicit conversion}}
+
+ struct B {
+ B(std::initializer_list<int> i) {}
+ };
+ B b1 { 1, 2 };
+ B b2 { 1, 2.0 };
+
+ struct C {
+ C(int i, double j) {}
+ };
+ C c1 = { 1, 2.2 };
+ // FIXME: This is an ill-formed narrowing initialization.
+ C c2 = { 1.1, 2 }; // expected-warning {{implicit conversion}}
+
+ int j { 1 };
+ int k { };
+}
diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp
index db20ea6..9b1727f 100644
--- a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp
+++ b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp
@@ -167,6 +167,20 @@ void shrink_int() {
Agg<short> ce1 = { Convert<int>(100000) }; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}} expected-note {{override}} expected-warning {{changes value from 100000 to -31072}}
Agg<char> ce2 = { ConvertVar<short>() }; // expected-error {{non-constant-expression cannot be narrowed from type 'short' to 'char'}} expected-note {{override}}
+
+ // Negative -> larger unsigned type.
+ unsigned long long ll1 = { -1 }; // expected-error {{ -1 which cannot be narrowed}} expected-note {{override}}
+ unsigned long long ll2 = { 1 }; // OK
+ unsigned long long ll3 = { s }; // expected-error {{cannot be narrowed from type 'short'}} expected-note {{override}}
+ unsigned long long ll4 = { us }; // OK
+ unsigned long long ll5 = { ll }; // expected-error {{cannot be narrowed from type 'long long'}} expected-note {{override}}
+ Agg<unsigned long long> ll6 = { -1 }; // expected-error {{ -1 which cannot be narrowed}} expected-note {{override}}
+ Agg<unsigned long long> ll7 = { 18446744073709551615ULL }; // OK
+ Agg<unsigned long long> ll8 = { __int128(18446744073709551615ULL) + 1 }; // expected-error {{ 18446744073709551616 which cannot be narrowed}} expected-note {{override}} expected-warning {{changes value}}
+ signed char c = 'x';
+ unsigned short usc1 = { c }; // expected-error {{non-constant-expression cannot be narrowed from type 'signed char'}} expected-note {{override}}
+ unsigned short usc2 = { (signed char)'x' }; // OK
+ unsigned short usc3 = { (signed char)-1 }; // expected-error {{ -1 which cannot be narrowed}} expected-note {{override}}
}
// Be sure that type- and value-dependent expressions in templates get the error
diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-cxx03-extra-copy.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-cxx03-extra-copy.cpp
index d58a129..4ba75ef 100644
--- a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-cxx03-extra-copy.cpp
+++ b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-cxx03-extra-copy.cpp
@@ -22,7 +22,7 @@ struct X3 {
X3();
private:
- X3(X3&); // expected-note{{candidate constructor not viable: no known conversion from 'X3' to 'X3 &' for 1st argument}}
+ X3(X3&); // expected-note{{candidate constructor not viable: expects an l-value for 1st argument}}
};
// Check for instantiation of default arguments
diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp
new file mode 100644
index 0000000..21f71f0
--- /dev/null
+++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++11 -verify %s
+
+// A function that is explicitly defaulted shall
+// [...]
+// -- not have default arguments
+struct DefArg {
+ static DefArg &&make();
+ DefArg(int n = 5) = default; // expected-error {{an explicitly-defaulted constructor cannot have default arguments}}
+ DefArg(const DefArg &DA = make()) = default; // expected-error {{an explicitly-defaulted constructor cannot have default arguments}}
+ DefArg(const DefArg &DA, int k = 3) = default; // expected-error {{an explicitly-defaulted copy constructor cannot have default arguments}}
+ DefArg(DefArg &&DA = make()) = default; // expected-error {{an explicitly-defaulted constructor cannot have default arguments}}
+ DefArg(DefArg &&DA, int k = 3) = default; // expected-error {{an explicitly-defaulted move constructor cannot have default arguments}}
+ DefArg &operator=(const DefArg&, int k = 4) = default; // expected-error {{parameter of overloaded 'operator=' cannot have a default argument}}
+ DefArg &operator=(DefArg&&, int k = 4) = default; // expected-error {{parameter of overloaded 'operator=' cannot have a default argument}}
+ ~DefArg(int k = 5) = default; // expected-error {{destructor cannot have any parameters}}
+};
diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp
index a879829..9b5ef78 100644
--- a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp
+++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp
@@ -14,11 +14,11 @@ namespace move {
};
struct AssignmentRet1 {
- AssignmentRet1&& operator=(AssignmentRet1&&) = default; // expected-error {{an explicitly-defaulted move assignment operator must return an unqualified lvalue reference to its class type}}
+ AssignmentRet1&& operator=(AssignmentRet1&&) = default; // expected-error {{explicitly-defaulted move assignment operator must return 'move::AssignmentRet1 &'}}
};
struct AssignmentRet2 {
- const AssignmentRet2& operator=(AssignmentRet2&&) = default; // expected-error {{an explicitly-defaulted move assignment operator must return an unqualified lvalue reference to its class type}}
+ const AssignmentRet2& operator=(AssignmentRet2&&) = default; // expected-error {{explicitly-defaulted move assignment operator must return 'move::AssignmentRet2 &'}}
};
struct ConstAssignment {
@@ -38,22 +38,35 @@ namespace copy {
};
struct NonConst {
- NonConst(NonConst&) = default;
- NonConst& operator=(NonConst&) = default;
+ NonConst(NonConst&) = default; // expected-error {{must be defaulted outside the class}}
+ NonConst& operator=(NonConst&) = default; // expected-error {{must be defaulted outside the class}}
+ };
+
+ struct NonConst2 {
+ NonConst2(NonConst2&);
+ NonConst2& operator=(NonConst2&);
+ };
+ NonConst2::NonConst2(NonConst2&) = default;
+ NonConst2 &NonConst2::operator=(NonConst2&) = default;
+
+ struct NonConst3 {
+ NonConst3(NonConst3&) = default;
+ NonConst3& operator=(NonConst3&) = default;
+ NonConst nc;
};
struct BadConst {
- NonConst nc; // makes implicit copy non-const
BadConst(const BadConst&) = default; // expected-error {{is const, but}}
BadConst& operator=(const BadConst&) = default; // expected-error {{is const, but}}
+ NonConst nc; // makes implicit copy non-const
};
struct AssignmentRet1 {
- AssignmentRet1&& operator=(const AssignmentRet1&) = default; // expected-error {{an explicitly-defaulted copy assignment operator must return an unqualified lvalue reference to its class type}}
+ AssignmentRet1&& operator=(const AssignmentRet1&) = default; // expected-error {{explicitly-defaulted copy assignment operator must return 'copy::AssignmentRet1 &'}}
};
struct AssignmentRet2 {
- const AssignmentRet2& operator=(const AssignmentRet2&) = default; // expected-error {{an explicitly-defaulted copy assignment operator must return an unqualified lvalue reference to its class type}}
+ const AssignmentRet2& operator=(const AssignmentRet2&) = default; // expected-error {{explicitly-defaulted copy assignment operator must return 'copy::AssignmentRet2 &'}}
};
struct ConstAssignment {
diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p9-0x.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p9-0x.cpp
index 574a3e7..719aeed 100644
--- a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p9-0x.cpp
+++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p9-0x.cpp
@@ -1,3 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
-auto j() -> enum { e3 }; // expected-error{{unnamed enumeration must be a definition}} expected-error {{requires a specifier or qualifier}} expected-error {{without trailing return type}}
+// FIXME: We should catch the case of tag with an incomplete type here (which
+// will necessarily be ill-formed as a trailing return type for a function
+// definition), and recover with a "type cannot be defined in a trailing return
+// type" error.
+auto j() -> enum { e3 }; // expected-error{{unnamed enumeration must be a definition}} expected-error {{expected a type}}
diff --git a/test/CXX/except/except.spec/p14.cpp b/test/CXX/except/except.spec/p14.cpp
index 8763a70..4f50afb 100644
--- a/test/CXX/except/except.spec/p14.cpp
+++ b/test/CXX/except/except.spec/p14.cpp
@@ -39,3 +39,27 @@ struct IC1 {
// we cannot currently compute the set of thrown types.
static_assert(noexcept(IC0()), "IC0() does not throw");
static_assert(!noexcept(IC1()), "IC1() throws");
+
+namespace PR13381 {
+ struct NoThrowMove {
+ NoThrowMove(const NoThrowMove &);
+ NoThrowMove(NoThrowMove &&) noexcept;
+ NoThrowMove &operator=(const NoThrowMove &);
+ NoThrowMove &operator=(NoThrowMove &&) noexcept;
+ };
+ struct NoThrowMoveOnly {
+ NoThrowMoveOnly(NoThrowMoveOnly &&) noexcept;
+ NoThrowMoveOnly &operator=(NoThrowMoveOnly &&) noexcept;
+ };
+ struct X {
+ const NoThrowMove a;
+ NoThrowMoveOnly b;
+
+ static X val();
+ static X &ref();
+ };
+ // These both perform a move, but that copy might throw, because it calls
+ // NoThrowMove's copy constructor (because PR13381::a is const).
+ static_assert(!noexcept(X(X::val())), "");
+ static_assert(!noexcept(X::ref() = X::val()), "");
+}
diff --git a/test/CXX/except/except.spec/p5-pointers.cpp b/test/CXX/except/except.spec/p5-pointers.cpp
index dd3c060..f855520 100644
--- a/test/CXX/except/except.spec/p5-pointers.cpp
+++ b/test/CXX/except/except.spec/p5-pointers.cpp
@@ -65,9 +65,9 @@ void fnptrs()
void (*(*t7)())() throw(B1) = &s8; // valid
void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}}
void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}}
- void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}}
- void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
- void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
+ void (*t10)(void (*)() throw(B1)) = &s9; // valid
+ void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}}
+ void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}}
}
// Member function stuff
diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp
index 054669e..9e6716d 100644
--- a/test/CXX/expr/expr.const/p2-0x.cpp
+++ b/test/CXX/expr/expr.const/p2-0x.cpp
@@ -131,14 +131,14 @@ namespace IncompleteClassTypeAddr {
namespace UndefinedBehavior {
void f(int n) {
switch (n) {
- case (int)4.4e9: // expected-error {{constant expression}} expected-note {{value 4.4E+9 is outside the range of representable values of type 'int'}}
+ case (int)4.4e9: // expected-error {{constant expression}} expected-note {{value 4.4E+9 is outside the range of representable values of type 'int'}} expected-note {{previous case defined here}}
case (int)0x80000000u: // ok
case (int)10000000000ll: // expected-note {{here}}
case (unsigned int)10000000000ll: // expected-error {{duplicate case value}}
case (int)(unsigned)(long long)4.4e9: // ok
- case (int)(float)1e300: // expected-error {{constant expression}} expected-note {{value 1.0E+300 is outside the range of representable values of type 'float'}}
+ case (int)(float)1e300: // expected-error {{constant expression}} expected-note {{value 1.0E+300 is outside the range of representable values of type 'float'}} expected-error {{duplicate case value '2147483647'}} expected-note {{previous case defined here}}
case (int)((float)1e37 / 1e30): // ok
- case (int)(__fp16)65536: // expected-error {{constant expression}} expected-note {{value 65536 is outside the range of representable values of type 'half'}}
+ case (int)(__fp16)65536: // expected-error {{constant expression}} expected-note {{value 65536 is outside the range of representable values of type 'half'}} expected-error {{duplicate case value '2147483647'}}
break;
}
}
diff --git a/test/CXX/expr/expr.post/expr.reinterpret.cast/p1-0x.cpp b/test/CXX/expr/expr.post/expr.reinterpret.cast/p1-0x.cpp
index 22892a6..212adc8 100644
--- a/test/CXX/expr/expr.post/expr.reinterpret.cast/p1-0x.cpp
+++ b/test/CXX/expr/expr.post/expr.reinterpret.cast/p1-0x.cpp
@@ -10,7 +10,10 @@ template<typename T> T&& xvalue();
void test_classification(char *ptr) {
int (&fr0)(int) = reinterpret_cast<int (&&)(int)>(f);
int &&ir0 = reinterpret_cast<int &&>(*ptr);
- int &&ir1 = reinterpret_cast<int &&>(0);
- int &&ir2 = reinterpret_cast<int &&>('a');
+ int &&ir1 = reinterpret_cast<int &&>(0); // expected-error {{rvalue to reference type}}
+ int &&ir2 = reinterpret_cast<int &&>('a'); // expected-error {{rvalue to reference type}}
int &&ir3 = reinterpret_cast<int &&>(xvalue<char>());
+ // Per DR1268, reinterpret_cast can convert between lvalues and xvalues.
+ int &ir4 = reinterpret_cast<int &>(xvalue<char>());
+ int &&ir5 = reinterpret_cast<int &&>(*ptr);
}
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks-irgen.mm b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks-irgen.mm
new file mode 100644
index 0000000..9f98671
--- /dev/null
+++ b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks-irgen.mm
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++11 -fblocks -emit-llvm -o - -triple x86_64-apple-darwin11.3 %s | FileCheck %s
+
+namespace PR12746 {
+ // CHECK: define zeroext i1 @_ZN7PR127462f1EPi
+ bool f1(int *x) {
+ // CHECK: store i8* bitcast (i1 (i8*)* @___ZN7PR127462f1EPi_block_invoke to i8*)
+ bool (^outer)() = ^ {
+ auto inner = [&]() -> bool {
+ return x == 0;
+ };
+ return inner();
+ };
+ return outer();
+ }
+
+ // CHECK: define internal zeroext i1 @___ZN7PR127462f1EPi_block_invoke
+ // CHECK: call zeroext i1 @"_ZNK7PR127462f132___ZN7PR127462f1EPi_block_invoke3$_0clEv"
+
+ bool f2(int *x) {
+ auto outer = [&]() -> bool {
+ bool (^inner)() = ^ {
+ return x == 0;
+ };
+ return inner();
+ };
+ return outer();
+ }
+}
+
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
index 0c3fdb2..0db2bf5 100644
--- a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
+++ b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
@@ -86,3 +86,41 @@ namespace overloading {
int &ir = accept_lambda_conv([](int x) { return x + 1; });
}
}
+
+namespace PR13117 {
+ struct A {
+ template<typename ... Args> static void f(Args...);
+
+ template<typename ... Args> static void f1()
+ {
+ (void)^(Args args) { // expected-error{{block contains unexpanded parameter pack 'Args'}}
+ };
+ }
+
+ template<typename ... Args> static void f2()
+ {
+ // FIXME: Allow this.
+ f(
+ ^(Args args) // expected-error{{block contains unexpanded parameter pack 'Args'}}
+ { }
+ ... // expected-error{{pack expansion does not contain any unexpanded parameter packs}}
+ );
+ }
+
+ template<typename ... Args> static void f3()
+ {
+ (void)[](Args args) { // expected-error{{expression contains unexpanded parameter pack 'Args'}}
+ };
+ }
+
+ template<typename ... Args> static void f4()
+ {
+ f([](Args args) { } ...);
+ }
+ };
+
+ void g() {
+ A::f1<int, int>();
+ A::f2<int, int>();
+ }
+}
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp
index 174db25..82fc04a 100644
--- a/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp
+++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp
@@ -9,8 +9,8 @@ void print(T first, Ts... rest) {
}
template<typename... Ts>
-void unsupported(Ts ...values) {
- auto unsup = [values] {}; // expected-error{{unexpanded function parameter pack capture is unsupported}}
+void unexpanded_capture(Ts ...values) {
+ auto unexp = [values] {}; // expected-error{{initializer contains unexpanded parameter pack 'values'}}
}
template<typename... Ts>
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp
index d816e17..f580e7e 100644
--- a/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp
+++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp
@@ -25,7 +25,7 @@ void infer_void_return_type(int i) {
struct X { };
X infer_X_return_type(X x) {
- return [&x](int y) { // expected-warning{{omitted result type}}
+ return [&x](int y) {
if (y > 0)
return X();
else
@@ -33,11 +33,11 @@ X infer_X_return_type(X x) {
}(5);
}
-X infer_X_return_type_fail(X x) {
- return [x](int y) { // expected-warning{{omitted result type}}
+X infer_X_return_type_fail(X x) {
+ return [x](int y) {
if (y > 0)
return X();
- else
+ else
return x; // expected-error{{return type 'const X' must match previous return type 'X' when lambda expression has unspecified explicit return type}}
}(5);
}
diff --git a/test/CXX/expr/expr.unary/expr.unary.op/p6.cpp b/test/CXX/expr/expr.unary/expr.unary.op/p6.cpp
index ac11940..2646264 100644
--- a/test/CXX/expr/expr.unary/expr.unary.op/p6.cpp
+++ b/test/CXX/expr/expr.unary/expr.unary.op/p6.cpp
@@ -4,7 +4,7 @@
bool b = !0;
-bool b2 = !1.2;
+bool b2 = !1.2; //expected-warning{{implicit conversion from 'double' to 'bool' changes value from 1.2 to true}}
bool b3 = !4;
diff --git a/test/CXX/expr/p10-0x.cpp b/test/CXX/expr/p10-0x.cpp
new file mode 100644
index 0000000..564df88
--- /dev/null
+++ b/test/CXX/expr/p10-0x.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -emit-llvm -triple x86_64-pc-linux-gnu %s -o - -std=c++11 | FileCheck %s
+
+volatile int g1;
+struct S {
+ volatile int a;
+} g2;
+
+volatile int& refcall();
+
+// CHECK: define void @_Z2f1PViPV1S
+void f1(volatile int *x, volatile S* s) {
+ // We should perform the load in these cases.
+ // CHECK: load volatile i32*
+ (*x);
+ // CHECK: load volatile i32*
+ __extension__ g1;
+ // CHECK: load volatile i32*
+ s->a;
+ // CHECK: load volatile i32*
+ g2.a;
+ // CHECK: load volatile i32*
+ s->*(&S::a);
+ // CHECK: load volatile i32*
+ // CHECK: load volatile i32*
+ x[0], 1 ? x[0] : *x;
+
+ // CHECK: load volatile i32*
+ // CHECK: load volatile i32*
+ // CHECK: load volatile i32*
+ *x ?: *x;
+
+ // CHECK: load volatile i32*
+ ({ *x; });
+
+ // CHECK-NOT: load volatile
+ // CHECK: ret
+}
+
+// CHECK: define void @_Z2f2PVi
+// CHECK-NOT: load volatile
+// CHECK: ret
+void f2(volatile int *x) {
+ // We shouldn't perform the load in these cases.
+ refcall();
+ 1 ? refcall() : *x;
+}
diff --git a/test/CXX/special/class.conv/class.conv.ctor/p1.cpp b/test/CXX/special/class.conv/class.conv.ctor/p1.cpp
new file mode 100644
index 0000000..d2add82
--- /dev/null
+++ b/test/CXX/special/class.conv/class.conv.ctor/p1.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -std=c++11 %s -verify
+
+namespace PR13003 {
+ struct void_type
+ {
+ template <typename Arg0, typename... Args>
+ void_type(Arg0&&, Args&&...) { }
+ };
+
+ struct void_type2
+ {
+ template <typename... Args>
+ void_type2(Args&&...) { }
+ };
+
+ struct atom { };
+
+ void_type v1 = atom();
+ void_type2 v2 = atom();
+}
+
diff --git a/test/CXX/special/class.copy/implicit-move.cpp b/test/CXX/special/class.copy/implicit-move.cpp
index 3e9accfb..597e327 100644
--- a/test/CXX/special/class.copy/implicit-move.cpp
+++ b/test/CXX/special/class.copy/implicit-move.cpp
@@ -234,3 +234,10 @@ namespace DR1402 {
friend NoMove11 &NoMove11::operator=(NoMove11 &&); // expected-error {{no matching function}}
};
}
+
+namespace PR12625 {
+ struct X; // expected-note {{forward decl}}
+ struct Y {
+ X x; // expected-error {{incomplete}}
+ } y = Y();
+}
diff --git a/test/CXX/special/class.copy/p11.0x.copy.cpp b/test/CXX/special/class.copy/p11.0x.copy.cpp
index b2b4f6a..fab3b9d 100644
--- a/test/CXX/special/class.copy/p11.0x.copy.cpp
+++ b/test/CXX/special/class.copy/p11.0x.copy.cpp
@@ -8,7 +8,7 @@ struct NonTrivial {
// -- a variant member with a non-trivial corresponding constructor
union DeletedNTVariant {
- NonTrivial NT; // expected-note{{copy constructor of union 'DeletedNTVariant' is implicitly deleted because field 'NT' has a non-trivial copy constructor}}
+ NonTrivial NT; // expected-note{{copy constructor of 'DeletedNTVariant' is implicitly deleted because variant field 'NT' has a non-trivial copy constructor}}
DeletedNTVariant();
};
DeletedNTVariant DVa;
@@ -16,7 +16,7 @@ DeletedNTVariant DVb(DVa); // expected-error{{call to implicitly-deleted copy co
struct DeletedNTVariant2 {
union {
- NonTrivial NT; // expected-note{{copy constructor of union 'DeletedNTVariant2' is implicitly deleted because field 'NT' has a non-trivial copy constructor}}
+ NonTrivial NT; // expected-note{{copy constructor of 'DeletedNTVariant2' is implicitly deleted because variant field 'NT' has a non-trivial copy constructor}}
};
DeletedNTVariant2();
};
@@ -119,3 +119,15 @@ struct RValue {
};
RValue RVa;
RValue RVb(RVa); // expected-error{{call to implicitly-deleted copy constructor}}
+
+namespace PR13381 {
+ struct S {
+ S(const S&);
+ S(const volatile S&) = delete; // expected-note{{deleted here}}
+ };
+ struct T {
+ volatile S s; // expected-note{{field 's' has a deleted copy constructor}}
+ };
+ T &f();
+ T t = f(); // expected-error{{call to implicitly-deleted copy constructor}}
+}
diff --git a/test/CXX/special/class.copy/p13-0x.cpp b/test/CXX/special/class.copy/p13-0x.cpp
index 0a9aa62..5d43601 100644
--- a/test/CXX/special/class.copy/p13-0x.cpp
+++ b/test/CXX/special/class.copy/p13-0x.cpp
@@ -58,3 +58,59 @@ struct Constexpr5Base {};
struct Constexpr5 : Constexpr5Base { constexpr Constexpr5() {} };
constexpr Constexpr5 ce5move = Constexpr5();
constexpr Constexpr5 ce5copy = ce5move;
+
+// An explicitly-defaulted constructor doesn't become constexpr until the end of
+// its class. Make sure we note that the class has a constexpr constructor when
+// that happens.
+namespace PR13052 {
+ template<typename T> struct S {
+ S() = default; // expected-note 2{{here}}
+ S(S&&) = default;
+ S(const S&) = default;
+ T t;
+ };
+
+ struct U {
+ U() = default;
+ U(U&&) = default;
+ U(const U&) = default;
+ };
+
+ struct V {
+ V(); // expected-note {{here}}
+ V(V&&) = default;
+ V(const V&) = default;
+ };
+
+ struct W {
+ W(); // expected-note {{here}}
+ };
+
+ static_assert(__is_literal_type(U), "");
+ static_assert(!__is_literal_type(V), "");
+ static_assert(!__is_literal_type(W), "");
+ static_assert(__is_literal_type(S<U>), "");
+ static_assert(!__is_literal_type(S<V>), "");
+ static_assert(!__is_literal_type(S<W>), "");
+
+ struct X {
+ friend constexpr U::U() noexcept;
+ friend constexpr U::U(U&&) noexcept;
+ friend constexpr U::U(const U&) noexcept;
+ friend constexpr V::V(); // expected-error {{follows non-constexpr declaration}}
+ friend constexpr V::V(V&&) noexcept;
+ friend constexpr V::V(const V&) noexcept;
+ friend constexpr W::W(); // expected-error {{follows non-constexpr declaration}}
+ friend constexpr W::W(W&&) noexcept;
+ friend constexpr W::W(const W&) noexcept;
+ friend constexpr S<U>::S() noexcept;
+ friend constexpr S<U>::S(S<U>&&) noexcept;
+ friend constexpr S<U>::S(const S<U>&) noexcept;
+ friend constexpr S<V>::S(); // expected-error {{follows non-constexpr declaration}}
+ friend constexpr S<V>::S(S<V>&&) noexcept;
+ friend constexpr S<V>::S(const S<V>&) noexcept;
+ friend constexpr S<W>::S(); // expected-error {{follows non-constexpr declaration}}
+ friend constexpr S<W>::S(S<W>&&) noexcept;
+ friend constexpr S<W>::S(const S<W>&) noexcept;
+ };
+}
diff --git a/test/CXX/special/class.copy/p15-inclass.cpp b/test/CXX/special/class.copy/p15-inclass.cpp
index c4f8eaf..6cfa94f 100644
--- a/test/CXX/special/class.copy/p15-inclass.cpp
+++ b/test/CXX/special/class.copy/p15-inclass.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s
namespace PR11418 {
struct NonPOD {
diff --git a/test/CXX/special/class.copy/p23-cxx11.cpp b/test/CXX/special/class.copy/p23-cxx11.cpp
new file mode 100644
index 0000000..7c04a82
--- /dev/null
+++ b/test/CXX/special/class.copy/p23-cxx11.cpp
@@ -0,0 +1,148 @@
+// RUN: %clang_cc1 -verify %s -std=c++11
+
+template<typename T> struct CopyAssign {
+ static T t;
+ void test() {
+ t = t; // expected-error +{{deleted}}
+ }
+};
+template<typename T> struct MoveAssign {
+ static T t;
+ void test() {
+ t = static_cast<T&&>(t); // expected-error +{{deleted}}
+ }
+};
+
+struct NonTrivialCopyAssign {
+ NonTrivialCopyAssign &operator=(const NonTrivialCopyAssign &);
+};
+struct NonTrivialMoveAssign {
+ NonTrivialMoveAssign &operator=(NonTrivialMoveAssign &&);
+};
+struct AmbiguousCopyAssign {
+ AmbiguousCopyAssign &operator=(const AmbiguousCopyAssign &);
+ AmbiguousCopyAssign &operator=(volatile AmbiguousCopyAssign &);
+};
+struct AmbiguousMoveAssign {
+ AmbiguousMoveAssign &operator=(const AmbiguousMoveAssign &&);
+ AmbiguousMoveAssign &operator=(volatile AmbiguousMoveAssign &&);
+};
+struct DeletedCopyAssign {
+ DeletedCopyAssign &operator=(const DeletedCopyAssign &) = delete; // expected-note 2{{deleted}}
+};
+struct DeletedMoveAssign {
+ DeletedMoveAssign &operator=(DeletedMoveAssign &&) = delete; // expected-note 2{{deleted}}
+};
+class InaccessibleCopyAssign {
+ InaccessibleCopyAssign &operator=(const InaccessibleCopyAssign &);
+};
+class InaccessibleMoveAssign {
+ InaccessibleMoveAssign &operator=(InaccessibleMoveAssign &&);
+};
+
+// A defaulted copy/move assignment operator for class X is defined as deleted
+// if X has:
+
+// -- a variant member with a non-trivial corresponding assignment operator
+// and X is a union-like class
+struct A1 {
+ union {
+ NonTrivialCopyAssign x; // expected-note {{variant field 'x' has a non-trivial copy assign}}
+ };
+};
+template struct CopyAssign<A1>; // expected-note {{here}}
+
+struct A2 {
+ A2 &operator=(A2 &&) = default; // expected-note {{here}}
+ union {
+ NonTrivialMoveAssign x; // expected-note {{variant field 'x' has a non-trivial move assign}}
+ };
+};
+template struct MoveAssign<A2>; // expected-note {{here}}
+
+// -- a non-static const data member of (array of) non-class type
+struct B1 {
+ const int a; // expected-note 2{{field 'a' is of const-qualified type}}
+};
+struct B2 {
+ const void *const a[3][9][2]; // expected-note 2{{field 'a' is of const-qualified type 'const void *const [3][9][2]'}}
+};
+struct B3 {
+ const void *a[3];
+};
+template struct CopyAssign<B1>; // expected-note {{here}}
+template struct MoveAssign<B1>; // expected-note {{here}}
+template struct CopyAssign<B2>; // expected-note {{here}}
+template struct MoveAssign<B2>; // expected-note {{here}}
+template struct CopyAssign<B3>;
+template struct MoveAssign<B3>;
+
+// -- a non-static data member of reference type
+struct C1 {
+ int &a; // expected-note 2{{field 'a' is of reference type 'int &'}}
+};
+template struct CopyAssign<C1>; // expected-note {{here}}
+template struct MoveAssign<C1>; // expected-note {{here}}
+
+// -- a non-static data member of class type M that cannot be copied/moved
+struct D1 {
+ AmbiguousCopyAssign a; // expected-note {{field 'a' has multiple copy}}
+};
+struct D2 {
+ D2 &operator=(D2 &&) = default; // expected-note {{here}}
+ AmbiguousMoveAssign a; // expected-note {{field 'a' has multiple move}}
+};
+struct D3 {
+ DeletedCopyAssign a; // expected-note {{field 'a' has a deleted copy}}
+};
+struct D4 {
+ D4 &operator=(D4 &&) = default; // expected-note {{here}}
+ DeletedMoveAssign a; // expected-note {{field 'a' has a deleted move}}
+};
+struct D5 {
+ InaccessibleCopyAssign a; // expected-note {{field 'a' has an inaccessible copy}}
+};
+struct D6 {
+ D6 &operator=(D6 &&) = default; // expected-note {{here}}
+ InaccessibleMoveAssign a; // expected-note {{field 'a' has an inaccessible move}}
+};
+template struct CopyAssign<D1>; // expected-note {{here}}
+template struct MoveAssign<D2>; // expected-note {{here}}
+template struct CopyAssign<D3>; // expected-note {{here}}
+template struct MoveAssign<D4>; // expected-note {{here}}
+template struct CopyAssign<D5>; // expected-note {{here}}
+template struct MoveAssign<D6>; // expected-note {{here}}
+
+// -- a direct or virtual base that cannot be copied/moved
+struct E1 : AmbiguousCopyAssign {}; // expected-note {{base class 'AmbiguousCopyAssign' has multiple copy}}
+struct E2 : AmbiguousMoveAssign { // expected-note {{base class 'AmbiguousMoveAssign' has multiple move}}
+ E2 &operator=(E2 &&) = default; // expected-note {{here}}
+};
+struct E3 : DeletedCopyAssign {}; // expected-note {{base class 'DeletedCopyAssign' has a deleted copy}}
+struct E4 : DeletedMoveAssign { // expected-note {{base class 'DeletedMoveAssign' has a deleted move}}
+ E4 &operator=(E4 &&) = default; // expected-note {{here}}
+};
+struct E5 : InaccessibleCopyAssign {}; // expected-note {{base class 'InaccessibleCopyAssign' has an inaccessible copy}}
+struct E6 : InaccessibleMoveAssign { // expected-note {{base class 'InaccessibleMoveAssign' has an inaccessible move}}
+ E6 &operator=(E6 &&) = default; // expected-note {{here}}
+};
+template struct CopyAssign<E1>; // expected-note {{here}}
+template struct MoveAssign<E2>; // expected-note {{here}}
+template struct CopyAssign<E3>; // expected-note {{here}}
+template struct MoveAssign<E4>; // expected-note {{here}}
+template struct CopyAssign<E5>; // expected-note {{here}}
+template struct MoveAssign<E6>; // expected-note {{here}}
+
+namespace PR13381 {
+ struct S {
+ S &operator=(const S&);
+ S &operator=(const volatile S&) = delete; // expected-note{{deleted here}}
+ };
+ struct T {
+ volatile S s; // expected-note{{field 's' has a deleted copy assignment}}
+ };
+ void g() {
+ T t;
+ t = T(); // expected-error{{implicitly-deleted copy assignment}}
+ }
+}
diff --git a/test/CXX/special/class.copy/p8-cxx11.cpp b/test/CXX/special/class.copy/p8-cxx11.cpp
index 02e6cd1..a2613f4 100644
--- a/test/CXX/special/class.copy/p8-cxx11.cpp
+++ b/test/CXX/special/class.copy/p8-cxx11.cpp
@@ -43,6 +43,6 @@ struct Test {
friend C::C(C &);
friend D::D(const D &);
friend E::E(E &);
- friend F::F(const F &);
+ constexpr friend F::F(const F &);
friend G::G(G &);
};
diff --git a/test/CXX/special/class.ctor/p5-0x.cpp b/test/CXX/special/class.ctor/p5-0x.cpp
index 694ab5b..1aaeef2 100644
--- a/test/CXX/special/class.ctor/p5-0x.cpp
+++ b/test/CXX/special/class.ctor/p5-0x.cpp
@@ -21,7 +21,7 @@ int n;
// - X is a union-like class that has a variant member with a non-trivial
// default constructor,
-union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{default constructor of union 'Deleted1a' is implicitly deleted because field 'u' has a non-trivial default constructor}}
+union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{default constructor of 'Deleted1a' is implicitly deleted because variant field 'u' has a non-trivial default constructor}}
Deleted1a d1a; // expected-error {{implicitly-deleted default constructor}}
union NotDeleted1a { DefaultedDefCtor1 nu; };
NotDeleted1a nd1a;
@@ -53,7 +53,7 @@ class Deleted3a { const int a; }; // expected-note {{because field 'a' of const-
expected-warning {{does not declare any constructor}} \
expected-note {{will never be initialized}}
Deleted3a d3a; // expected-error {{implicitly-deleted default constructor}}
-class Deleted3b { const DefaultedDefCtor1 a[42]; }; // expected-note {{because field 'a' of const-qualified type 'const DefaultedDefCtor1' would not be initialized}}
+class Deleted3b { const DefaultedDefCtor1 a[42]; }; // expected-note {{because field 'a' of const-qualified type 'const DefaultedDefCtor1 [42]' would not be initialized}}
Deleted3b d3b; // expected-error {{implicitly-deleted default constructor}}
class Deleted3c { const DefaultedDefCtor2 a; }; // expected-note {{because field 'a' of const-qualified type 'const DefaultedDefCtor2' would not be initialized}}
Deleted3c d3c; // expected-error {{implicitly-deleted default constructor}}
@@ -77,7 +77,7 @@ NotDeleted3g nd3g;
union Deleted4a {
const int a;
const int b;
- const UserProvidedDefCtor c; // expected-note {{because field 'c' has a non-trivial default constructor}}
+ const UserProvidedDefCtor c; // expected-note {{because variant field 'c' has a non-trivial default constructor}}
};
Deleted4a d4a; // expected-error {{implicitly-deleted default constructor}}
union NotDeleted4a { const int a; int b; };
diff --git a/test/CXX/special/class.ctor/p6-0x.cpp b/test/CXX/special/class.ctor/p6-0x.cpp
index 8c8800f..9860317 100644
--- a/test/CXX/special/class.ctor/p6-0x.cpp
+++ b/test/CXX/special/class.ctor/p6-0x.cpp
@@ -55,3 +55,42 @@ struct A {}; // expected-note {{here}}
struct B {
friend A::A(); // expected-error {{non-constexpr declaration of 'A' follows constexpr declaration}}
};
+
+namespace UnionCtors {
+ union A { // expected-note {{here}}
+ int a;
+ int b;
+ };
+ union B {
+ int a;
+ int b = 5;
+ };
+ union C {
+ int a = 5;
+ int b;
+ };
+ struct D {
+ union {
+ int a = 5;
+ int b;
+ };
+ union {
+ int c;
+ int d = 5;
+ };
+ };
+ struct E { // expected-note {{here}}
+ union {
+ int a;
+ int b;
+ };
+ };
+
+ struct Test {
+ friend constexpr A::A() noexcept; // expected-error {{follows non-constexpr declaration}}
+ friend constexpr B::B() noexcept;
+ friend constexpr C::C() noexcept;
+ friend constexpr D::D() noexcept;
+ friend constexpr E::E() noexcept; // expected-error {{follows non-constexpr declaration}}
+ };
+}
diff --git a/test/CXX/special/class.dtor/p5-0x.cpp b/test/CXX/special/class.dtor/p5-0x.cpp
index dbfa004..0d073ce 100644
--- a/test/CXX/special/class.dtor/p5-0x.cpp
+++ b/test/CXX/special/class.dtor/p5-0x.cpp
@@ -16,25 +16,25 @@ class InaccessibleDtor {
// destructor.
union A1 {
A1();
- NonTrivDtor n; // expected-note {{destructor of union 'A1' is implicitly deleted because field 'n' has a non-trivial destructor}}
+ NonTrivDtor n; // expected-note {{destructor of 'A1' is implicitly deleted because variant field 'n' has a non-trivial destructor}}
};
A1 a1; // expected-error {{deleted function}}
struct A2 {
A2();
union {
- NonTrivDtor n; // expected-note {{because field 'n' has a non-trivial destructor}}
+ NonTrivDtor n; // expected-note {{because variant field 'n' has a non-trivial destructor}}
};
};
A2 a2; // expected-error {{deleted function}}
union A3 {
A3();
- NonTrivDtor n[3]; // expected-note {{because field 'n' has a non-trivial destructor}}
+ NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a non-trivial destructor}}
};
A3 a3; // expected-error {{deleted function}}
struct A4 {
A4();
union {
- NonTrivDtor n[3]; // expected-note {{because field 'n' has a non-trivial destructor}}
+ NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a non-trivial destructor}}
};
};
A4 a4; // expected-error {{deleted function}}
diff --git a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
index a45b35f..96bb472 100644
--- a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
+++ b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
@@ -1,5 +1,13 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+struct pr12960 {
+ int begin;
+ void foo(int x) {
+ for (int& it : x) { // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type 'int'}}
+ }
+ }
+};
+
namespace std {
template<typename T>
auto begin(T &&t) -> decltype(t.begin()) { return t.begin(); } // expected-note 4{{ignored: substitution failure}}
@@ -207,3 +215,4 @@ void example() {
for (int &x : array)
x *= 2;
}
+
diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp
index 63f569b..640d03d4 100644
--- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp
+++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp
@@ -302,6 +302,7 @@ namespace test14 {
};
template <class T> class B {
+ public:
void foo() { return A<long>::foo(); } // expected-error {{'foo' is a private member of 'test14::A<long>'}}
};
@@ -320,10 +321,12 @@ namespace test15 {
};
template <class T> class B {
+ public:
void foo() { return A<long>::foo(); } // expected-error {{'foo' is a private member of 'test15::A<long>'}}
};
template <> class B<float> {
+ public:
void foo() { return A<float>::foo(); }
template <class U> void bar(U u) {
(void) A<float>::foo();
diff --git a/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp b/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp
index 21aa24f..485068e 100644
--- a/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp
+++ b/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp
@@ -249,3 +249,46 @@ namespace PR10230 {
int (&ir3)[3] = s<int>().f<int, float, double>();
}
}
+
+namespace PR13386 {
+ template<typename...> struct tuple {};
+ template<typename...T>
+ struct S {
+ template<typename...U>
+ void f(T &&...t, U &&...u) {} // expected-note {{candidate}}
+ template<typename...U>
+ void g(U &&...u, T &&...t) {} // expected-note {{candidate}}
+ template<typename...U>
+ void h(tuple<T, U> &&...) {} // expected-note 2{{candidate}}
+
+ template<typename...U>
+ struct X {
+ template<typename...V>
+ void x(tuple<T, U, V> &&...); // expected-error {{different lengths}}
+ };
+ };
+
+ void test() {
+ S<>().f();
+ S<>().f(0);
+ S<int>().f(0);
+ S<int>().f(0, 1);
+ S<int, int>().f(0); // expected-error {{no matching member function for call}}
+
+ S<>().g();
+ S<>().g(0);
+ S<int>().g(0);
+ S<int>().g(0, 1); // expected-error {{no matching member function for call}}
+ S<int>().g<int>(0, 1);
+ S<int, int>().g(0, 1);
+
+ S<>().h();
+ S<>().h(0); // expected-error {{no matching member function for call}}
+ S<int>().h({}); // expected-error {{no matching member function for call}}
+ S<int>().h<int>({});
+ S<int>().h(tuple<int,int>{});
+ S<int, int>().h(tuple<int,int>{}, tuple<int,int>{});
+
+ S<int, int>::X<char>(); // expected-note {{here}}
+ }
+}
diff --git a/test/CXX/temp/temp.names/p3-0x.cpp b/test/CXX/temp/temp.names/p3-0x.cpp
new file mode 100644
index 0000000..85dc75e
--- /dev/null
+++ b/test/CXX/temp/temp.names/p3-0x.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -std=c++11 %s -verify
+
+template<int i> class X { /* ... */ };
+X< 1>2 > x1; // expected-error{{expected unqualified-id}}
+X<(1>2)> x2; // OK
+template<class T> class Y { /* ... */ };
+Y<X<1>> x3; // OK, same as Y<X<1> > x3;
+Y<X<6>>1>> x4; // expected-error{{expected unqualified-id}}
+Y<X<(6>>1)>> x5;
+
+int a, b;
+Y<decltype(a < b)> x6;
diff --git a/test/CXX/temp/temp.spec/p5.cpp b/test/CXX/temp/temp.spec/p5.cpp
index 0e69a26..ba92d41 100644
--- a/test/CXX/temp/temp.spec/p5.cpp
+++ b/test/CXX/temp/temp.spec/p5.cpp
@@ -14,7 +14,7 @@ struct X0 {
};
template<typename T>
-T X0<T>::value = 3.14; // expected-warning{{implicit conversion turns literal floating-point number into integer}}
+T X0<T>::value = 3.14; // expected-warning{{implicit conversion from 'double' to 'int' changes value from 3.14 to 3}}
template struct X0<int>; // expected-note{{previous explicit instantiation}} \
expected-note{{requested here}}
diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p14.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p14.cpp
index 121cb8e..aba9d36 100644
--- a/test/CXX/temp/temp.spec/temp.expl.spec/p14.cpp
+++ b/test/CXX/temp/temp.spec/temp.expl.spec/p14.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck %s
template<class T> void f(T) { /* ... */ }
template<class T> inline void g(T) { /* ... */ }
diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p3.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p3.cpp
index 84841cb..c8b7def 100644
--- a/test/CXX/temp/temp.spec/temp.expl.spec/p3.cpp
+++ b/test/CXX/temp/temp.spec/temp.expl.spec/p3.cpp
@@ -1,10 +1,14 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
namespace N {
- template<class T> class X;
+ template<class T> class X; // expected-note {{'N::X' declared here}} \
+ // expected-note {{explicitly specialized declaration is here}}
}
-template<> class X<int> { /* ... */ }; // expected-error {{non-template class 'X'}}
+// TODO: Don't add a namespace qualifier to the template if it would trigger
+// the warning about the specialization being outside of the namespace.
+template<> class X<int> { /* ... */ }; // expected-error {{no template named 'X'; did you mean 'N::X'?}} \
+ // expected-warning {{first declaration of class template specialization of 'X' outside namespace 'N' is a C++11 extension}}
namespace N {
diff --git a/test/CXX/temp/temp.spec/temp.explicit/p12.cpp b/test/CXX/temp/temp.spec/temp.explicit/p12.cpp
index c756486..9518a0b 100644
--- a/test/CXX/temp/temp.spec/temp.explicit/p12.cpp
+++ b/test/CXX/temp/temp.spec/temp.explicit/p12.cpp
@@ -1,6 +1,49 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
-char* p = 0;
-template<class T> T g(T x = &p) { return x; }
-template int g<int>(int); // OK even though &p isn't an int.
+namespace test0 {
+ char* p = 0;
+ template<class T> T g(T x = &p) { return x; }
+ template int g<int>(int); // OK even though &p isn't an int.
+}
+// Don't impose access restrictions on explicit instantiations.
+namespace test1 {
+ class A {
+ class Private {};
+ public:
+ typedef Private Public;
+ };
+
+ template <class T> class Temp {
+ static Temp<A::Public> make() { return Temp<A::Public>(); }
+ };
+ template class Temp<A::Private>;
+
+ // FIXME: this ought to be an error, but it isn't because Sema is
+ // silently failing to create a declaration for the explicit
+ // instantiation.
+ template class Temp<A::Private> Temp<int>::make();
+}
+
+// Don't impose access restrictions on explicit specializations,
+// either. This goes here because it's an extension of the rule for
+// explicit instantiations and doesn't have any independent support.
+namespace test2 {
+ class A {
+ class Private {}; // expected-note {{implicitly declared private here}}
+ public:
+ typedef Private Public;
+ };
+
+ template <class T> class Temp {
+ static Temp<A::Public> make();
+ };
+ template <> class Temp<A::Private> {
+ public:
+ Temp(int x) {}
+ };
+
+ template <> class Temp<A::Private> Temp<int>::make() { // expected-error {{'Private' is a private member of 'test2::A'}}
+ return Temp<A::Public>(0);
+ }
+}
diff --git a/test/CXX/temp/temp.spec/temp.explicit/p9-linkage.cpp b/test/CXX/temp/temp.spec/temp.explicit/p9-linkage.cpp
index 04e7df5..ff24ad9 100644
--- a/test/CXX/temp/temp.spec/temp.explicit/p9-linkage.cpp
+++ b/test/CXX/temp/temp.spec/temp.explicit/p9-linkage.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -O1 -emit-llvm -std=c++11 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -emit-llvm -std=c++11 -o - %s | FileCheck %s
template<typename T>
struct X0 {
diff --git a/test/CodeCompletion/auto.cpp b/test/CodeCompletion/auto.cpp
new file mode 100644
index 0000000..1fc9fb0
--- /dev/null
+++ b/test/CodeCompletion/auto.cpp
@@ -0,0 +1,2 @@
+// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:2:9 %s
+auto i =
diff --git a/test/CodeCompletion/documentation.cpp b/test/CodeCompletion/documentation.cpp
new file mode 100644
index 0000000..c049ae3
--- /dev/null
+++ b/test/CodeCompletion/documentation.cpp
@@ -0,0 +1,33 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
+
+/// Aaa.
+void T1(float x, float y);
+
+/// Bbb.
+class T2 {
+public:
+ /// Ccc.
+ void T3();
+
+ int T4; ///< Ddd.
+};
+
+/// Eee.
+namespace T5 {
+}
+
+void test() {
+
+ T2 t2;
+ t2.
+}
+
+// RUN: %clang_cc1 -fsyntax-only -code-completion-brief-comments -code-completion-at=%s:21:1 %s -o - | FileCheck -check-prefix=CC1 %s
+// CHECK-CC1: COMPLETION: T1 : [#void#]T1(<#float x#>, <#float y#>) : Aaa.
+// CHECK-CC1: COMPLETION: T2 : T2 : Bbb.
+// CHECK-CC1: COMPLETION: T5 : T5:: : Eee.
+
+// RUN: %clang_cc1 -fsyntax-only -code-completion-brief-comments -code-completion-at=%s:23:6 %s -o - | FileCheck -check-prefix=CC2 %s
+// CHECK-CC2: COMPLETION: T3 : [#void#]T3() : Ccc.
+// CHECK-CC2: COMPLETION: T4 : [#int#]T4 : Ddd.
diff --git a/test/CodeCompletion/objc-expr.m b/test/CodeCompletion/objc-expr.m
new file mode 100644
index 0000000..b59586a
--- /dev/null
+++ b/test/CodeCompletion/objc-expr.m
@@ -0,0 +1,17 @@
+// Note: the run lines follow all tests, since line/column matter here
+
+id testCompleteAfterAtSign() {
+ return @"";
+}
+
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:4:11 %s -fno-const-strings -o - | FileCheck -check-prefix=AT %s
+// CHECK-AT: COMPLETION: Pattern : [#NSString *#]"<#string#>"
+// CHECK-AT: COMPLETION: Pattern : [#id#](<#expression#>)
+// CHECK-AT: COMPLETION: Pattern : [#NSArray *#][<#objects, ...#>]
+// CHECK-AT: COMPLETION: Pattern : [#char[]#]encode(<#type-name#>)
+// CHECK-AT: COMPLETION: Pattern : [#Protocol *#]protocol(<#protocol-name#>)
+// CHECK-AT: COMPLETION: Pattern : [#SEL#]selector(<#selector#>)
+// CHECK-AT: COMPLETION: Pattern : [#NSDictionary *#]{<#key#> : <#object, ...#>}
+
+// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:4:11 %s -fconst-strings -o - | FileCheck -check-prefix=CONST-STRINGS %s
+// CHECK-CONST-STRINGS: COMPLETION: Pattern : [#const char[]#]encode(<#type-name#>)
diff --git a/test/CodeGen/2005-07-20-SqrtNoErrno.c b/test/CodeGen/2005-07-20-SqrtNoErrno.c
index f40f61d..96761e4 100644
--- a/test/CodeGen/2005-07-20-SqrtNoErrno.c
+++ b/test/CodeGen/2005-07-20-SqrtNoErrno.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s
// llvm.sqrt has undefined behavior on negative inputs, so it is
// inappropriate to translate C/C++ sqrt to this.
float sqrtf(float x);
diff --git a/test/CodeGen/2007-02-25-C-DotDotDot.c b/test/CodeGen/2007-02-25-C-DotDotDot.c
index 7b2e418..abc4668 100644
--- a/test/CodeGen/2007-02-25-C-DotDotDot.c
+++ b/test/CodeGen/2007-02-25-C-DotDotDot.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -O0 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -O0 %s -emit-llvm -o - | FileCheck %s
// Make sure the call to foo is compiled as:
// call float @foo()
diff --git a/test/CodeGen/2008-01-25-ByValReadNone.c b/test/CodeGen/2008-01-25-ByValReadNone.c
index 06ad1ee..d977139 100644
--- a/test/CodeGen/2008-01-25-ByValReadNone.c
+++ b/test/CodeGen/2008-01-25-ByValReadNone.c
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -emit-llvm -o - %s | not grep readonly
// RUN: %clang_cc1 -emit-llvm -o - %s | not grep readnone
-// XFAIL: arm
+// XFAIL: arm,mips
// The struct being passed byval means that we cannot mark the
// function readnone. Readnone would allow stores to the arg to
diff --git a/test/CodeGen/2008-03-05-syncPtr.c b/test/CodeGen/2008-03-05-syncPtr.c
index 784295c..93f328a 100644
--- a/test/CodeGen/2008-03-05-syncPtr.c
+++ b/test/CodeGen/2008-03-05-syncPtr.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s
int* foo(int** a, int* b, int* c) {
return __sync_val_compare_and_swap (a, b, c);
diff --git a/test/CodeGen/2008-04-08-NoExceptions.c b/test/CodeGen/2008-04-08-NoExceptions.c
index 6d5d20f..ab2781b 100644
--- a/test/CodeGen/2008-04-08-NoExceptions.c
+++ b/test/CodeGen/2008-04-08-NoExceptions.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck %s
void f(void);
void g(void) {
diff --git a/test/CodeGen/2009-02-13-zerosize-union-field-ppc.c b/test/CodeGen/2009-02-13-zerosize-union-field-ppc.c
index 8787bd4..2bcc7c3 100644
--- a/test/CodeGen/2009-02-13-zerosize-union-field-ppc.c
+++ b/test/CodeGen/2009-02-13-zerosize-union-field-ppc.c
@@ -1,3 +1,4 @@
+// REQUIRES: ppc32-registered-target
// RUN: %clang_cc1 %s -triple powerpc-pc-linux -emit-llvm -o - | grep {i32 32} | count 3
// XFAIL: *
// Every printf has 'i32 0' for the GEP of the string; no point counting those.
diff --git a/test/CodeGen/2009-09-24-SqrtErrno.c b/test/CodeGen/2009-09-24-SqrtErrno.c
index b4a04ff..6a33967 100644
--- a/test/CodeGen/2009-09-24-SqrtErrno.c
+++ b/test/CodeGen/2009-09-24-SqrtErrno.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - -fmath-errno | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fmath-errno | FileCheck %s
// llvm.sqrt has undefined behavior on negative inputs, so it is
// inappropriate to translate C/C++ sqrt to this.
diff --git a/test/CodeGen/2009-10-20-GlobalDebug.c b/test/CodeGen/2009-10-20-GlobalDebug.c
index 8828eef..8a9dfdd 100644
--- a/test/CodeGen/2009-10-20-GlobalDebug.c
+++ b/test/CodeGen/2009-10-20-GlobalDebug.c
@@ -6,5 +6,5 @@ int main() {
return 0;
}
-// CHECK: !14 = metadata !{i32 {{.*}}, i32 0, metadata !5, metadata !"localstatic", metadata !"localstatic", metadata !"", metadata !6, i32 5, metadata !9, i32 1, i32 1, i32* @main.localstatic} ; [ DW_TAG_variable ]
-// CHECK: !15 = metadata !{i32 {{.*}}, i32 0, null, metadata !"global", metadata !"global", metadata !"", metadata !6, i32 3, metadata !9, i32 0, i32 1, i32* @global} ; [ DW_TAG_variable ]
+// CHECK: metadata !{i32 {{.*}}, i32 0, metadata !5, metadata !"localstatic", metadata !"localstatic", metadata !"", metadata !6, i32 5, metadata !9, i32 1, i32 1, i32* @main.localstatic} ; [ DW_TAG_variable ]
+// CHECK: metadata !{i32 {{.*}}, i32 0, null, metadata !"global", metadata !"global", metadata !"", metadata !6, i32 3, metadata !9, i32 0, i32 1, i32* @global} ; [ DW_TAG_variable ]
diff --git a/test/CodeGen/2010-02-10-PointerName.c b/test/CodeGen/2010-02-10-PointerName.c
index 910dd30..2837de4 100644
--- a/test/CodeGen/2010-02-10-PointerName.c
+++ b/test/CodeGen/2010-02-10-PointerName.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -g -o - | grep DW_TAG_pointer_type | grep -v char
+// RUN: %clang_cc1 %s -emit-llvm -g -o - | grep DW_TAG_pointer_type | grep -v {"char"}
char i = 1;
void foo() {
diff --git a/test/CodeGen/2010-05-26-AsmSideEffect.c b/test/CodeGen/2010-05-26-AsmSideEffect.c
index 7dd86ae..8ae7cb5 100644
--- a/test/CodeGen/2010-05-26-AsmSideEffect.c
+++ b/test/CodeGen/2010-05-26-AsmSideEffect.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 %s -emit-llvm -triple arm-apple-darwin -o - | FileCheck %s
// Radar 8026855
diff --git a/test/CodeGen/Atomics.c b/test/CodeGen/Atomics.c
index c440b6c..2bb38fd 100644
--- a/test/CodeGen/Atomics.c
+++ b/test/CodeGen/Atomics.c
@@ -1,6 +1,6 @@
// Test frontend handling of __sync builtins.
// Modified from a gcc testcase.
-// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - | FileCheck %s
signed char sc;
unsigned char uc;
diff --git a/test/CodeGen/address-safety-attr.cpp b/test/CodeGen/address-safety-attr.cpp
index 9d0fb95..da68b1d 100644
--- a/test/CodeGen/address-safety-attr.cpp
+++ b/test/CodeGen/address-safety-attr.cpp
@@ -33,3 +33,9 @@ int TemplateAddressSafetyOk() { return i; }
int force_instance = TemplateAddressSafetyOk<42>()
+ TemplateNoAddressSafety<42>();
+
+// Check that __cxx_global_var_init* get the address_safety attribute.
+int global1 = 0;
+int global2 = *(int*)((char*)&global1+1);
+// CHECK-NOT: @__cxx_global_var_init{{.*}}address_safety
+// ASAN: @__cxx_global_var_init{{.*}}address_safety
diff --git a/test/CodeGen/address-space.c b/test/CodeGen/address-space.c
index 04f88dc..9de0670 100644
--- a/test/CodeGen/address-space.c
+++ b/test/CodeGen/address-space.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -emit-llvm < %s | FileCheck %s
-// RUN: %clang_cc1 -emit-llvm < %s | grep 'load.*addrspace(2).. @A'
-// RUN: %clang_cc1 -emit-llvm < %s | grep 'load.*addrspace(2).. @B'
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm < %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm < %s | grep 'load.*addrspace(2).. @A'
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm < %s | grep 'load.*addrspace(2).. @B'
// CHECK: @foo = common addrspace(1) global
diff --git a/test/CodeGen/2002-05-24-Alloca.c b/test/CodeGen/alloca.c
index 30ba8bb..30ba8bb 100644
--- a/test/CodeGen/2002-05-24-Alloca.c
+++ b/test/CodeGen/alloca.c
diff --git a/test/CodeGen/altivec.c b/test/CodeGen/altivec.c
index 2982303..6c924a7 100644
--- a/test/CodeGen/altivec.c
+++ b/test/CodeGen/altivec.c
@@ -1,3 +1,4 @@
+// REQUIRES: ppc32-registered-target
// RUN: %clang_cc1 -faltivec -triple powerpc-unknown-unknown -emit-llvm %s -o - | FileCheck %s
// Check initialization
diff --git a/test/CodeGen/annotations-builtin.c b/test/CodeGen/annotations-builtin.c
index 42421a0..7938e49 100644
--- a/test/CodeGen/annotations-builtin.c
+++ b/test/CodeGen/annotations-builtin.c
@@ -25,9 +25,7 @@ int main(int argc, char **argv) {
// CHECK: call i32 @llvm.annotation.i32
long long lla = __builtin_annotation(llfoo, "annotation_a");
-// CHECK: trunc i64 {{.*}} to i32
-// CHECK-NEXT: call i32 @llvm.annotation.i32
-// CHECK-NEXT: zext i32 {{.*}} to i64
+// CHECK: call i64 @llvm.annotation.i64
int inta = __builtin_annotation(intfoo, "annotation_a");
// CHECK: load i32* @intfoo
@@ -35,15 +33,11 @@ int main(int argc, char **argv) {
// CHECK-NEXT: store
short shorta = __builtin_annotation(shortfoo, "annotation_a");
-// CHECK: sext i16 {{.*}} to i32
-// CHECK-NEXT: call i32 @llvm.annotation.i32
-// CHECK-NEXT: trunc i32 {{.*}} to i16
+// CHECK: call i16 @llvm.annotation.i16
char chara = __builtin_annotation(charfoo, "annotation_a");
-// CHECK: sext i8 {{.*}} to i32
-// CHECK-NEXT: call i32 @llvm.annotation.i32
-// CHECK-NEXT: trunc i32 {{.*}} to i8
-//
+// CHECK: call i8 @llvm.annotation.i8
+
char **arg = (char**) __builtin_annotation((int) argv, "annotation_a");
// CHECK: ptrtoint i8** {{.*}} to
// CHECK: call i32 @llvm.annotation.i32
diff --git a/test/CodeGen/arm-aapcs-vfp.c b/test/CodeGen/arm-aapcs-vfp.c
index 017c145..614b52d 100644
--- a/test/CodeGen/arm-aapcs-vfp.c
+++ b/test/CodeGen/arm-aapcs-vfp.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple thumbv7-apple-darwin9 \
// RUN: -target-abi aapcs \
// RUN: -target-cpu cortex-a8 \
@@ -33,6 +34,13 @@ void test_complex(__complex__ double cd) {
complex_callee(cd);
}
+// Long double is the same as double on AAPCS, it should be homogeneous.
+extern void complex_long_callee(__complex__ long double);
+// CHECK: define arm_aapcs_vfpcc void @test_complex_long(double %{{.*}}, double %{{.*}})
+void test_complex_long(__complex__ long double cd) {
+ complex_callee(cd);
+}
+
// Structs with more than 4 elements of the base type are not treated
// as homogeneous aggregates. Test that.
@@ -80,3 +88,7 @@ extern void neon_callee(struct neon_struct);
void test_neon(struct neon_struct arg) {
neon_callee(arg);
}
+
+// CHECK: define arm_aapcs_vfpcc void @f33(%struct.s33* byval %s)
+struct s33 { char buf[32*32]; };
+void f33(struct s33 s) { }
diff --git a/test/CodeGen/arm-aapcs-zerolength-bitfield.c b/test/CodeGen/arm-aapcs-zerolength-bitfield.c
index 9fece19..140ff6c 100644
--- a/test/CodeGen/arm-aapcs-zerolength-bitfield.c
+++ b/test/CodeGen/arm-aapcs-zerolength-bitfield.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -target-abi aapcs -triple armv7-apple-darwin10 %s -verify
#include <stddef.h>
diff --git a/test/CodeGen/arm-apcs-zerolength-bitfield.c b/test/CodeGen/arm-apcs-zerolength-bitfield.c
index 3f94525..049ffae 100644
--- a/test/CodeGen/arm-apcs-zerolength-bitfield.c
+++ b/test/CodeGen/arm-apcs-zerolength-bitfield.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -target-abi apcs-gnu -triple armv7-apple-darwin10 %s -verify
//
// Note: gcc forces the alignment to 4 bytes, regardless of the type of the
diff --git a/test/CodeGen/arm-arguments.c b/test/CodeGen/arm-arguments.c
index 4686d4a..2ec729e 100644
--- a/test/CodeGen/arm-arguments.c
+++ b/test/CodeGen/arm-arguments.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -target-abi apcs-gnu -emit-llvm -w -o - %s | FileCheck -check-prefix=APCS-GNU %s
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -target-abi aapcs -emit-llvm -w -o - %s | FileCheck -check-prefix=AAPCS %s
@@ -165,3 +166,15 @@ void f31(struct s31 s) { }
// APCS-GNU: %s = alloca %struct.s31, align 4
// APCS-GNU: alloca [1 x i32]
// APCS-GNU: store [1 x i32] %s.coerce, [1 x i32]*
+
+// PR13562
+struct s32 { double x; };
+void f32(struct s32 s) { }
+// AAPCS: @f32([1 x i64] %s.coerce)
+// APCS-GNU: @f32([2 x i32] %s.coerce)
+
+// PR13350
+struct s33 { char buf[32*32]; };
+void f33(struct s33 s) { }
+// APCS-GNU: define void @f33(%struct.s33* byval %s)
+// AAPCS: define arm_aapcscc void @f33(%struct.s33* byval %s)
diff --git a/test/CodeGen/arm-asm-variable.c b/test/CodeGen/arm-asm-variable.c
index 865d197..f874269 100644
--- a/test/CodeGen/arm-asm-variable.c
+++ b/test/CodeGen/arm-asm-variable.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -w -o - %s | FileCheck %s
typedef long long int64_t;
diff --git a/test/CodeGen/arm-asm.c b/test/CodeGen/arm-asm.c
index 9b1082a..bd2fe11 100644
--- a/test/CodeGen/arm-asm.c
+++ b/test/CodeGen/arm-asm.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple thumb %s -emit-llvm -o - | FileCheck %s
int t1() {
static float k = 1.0f;
diff --git a/test/CodeGen/arm-cc.c b/test/CodeGen/arm-cc.c
index 74eecc7..80ebe68 100644
--- a/test/CodeGen/arm-cc.c
+++ b/test/CodeGen/arm-cc.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -target-abi apcs-gnu -emit-llvm -w -o - %s | FileCheck -check-prefix=DARWIN-APCS %s
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -target-abi aapcs -emit-llvm -w -o - %s | FileCheck -check-prefix=DARWIN-AAPCS %s
// RUN: %clang_cc1 -triple arm-none-linux-gnueabi -target-abi apcs-gnu -emit-llvm -w -o - %s | FileCheck -check-prefix=LINUX-APCS %s
diff --git a/test/CodeGen/arm-clear.c b/test/CodeGen/arm-clear.c
index eda64ce..51506df 100644
--- a/test/CodeGen/arm-clear.c
+++ b/test/CodeGen/arm-clear.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -w -o - %s | FileCheck %s
void clear0(void *ptr) {
diff --git a/test/CodeGen/arm-homogenous.c b/test/CodeGen/arm-homogenous.c
index eb3d5a6..b8d046a 100644
--- a/test/CodeGen/arm-homogenous.c
+++ b/test/CodeGen/arm-homogenous.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7---eabi -target-abi aapcs -mfloat-abi hard -emit-llvm %s -o - | FileCheck %s
typedef long long int64_t;
@@ -157,4 +158,4 @@ void test_return_union_with_struct_with_fundamental_elems(void) {
// FIXME: Tests necessary:
// - Vectors
-// - C++ stuff \ No newline at end of file
+// - C++ stuff
diff --git a/test/CodeGen/arm-inline-asm.c b/test/CodeGen/arm-inline-asm.c
index 0152b05..95bb507 100644
--- a/test/CodeGen/arm-inline-asm.c
+++ b/test/CodeGen/arm-inline-asm.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -w -o - %s | FileCheck %s
void t1 (void *f, int g) {
diff --git a/test/CodeGen/arm-pcs.c b/test/CodeGen/arm-pcs.c
index d722f84..fc658c3 100644
--- a/test/CodeGen/arm-pcs.c
+++ b/test/CodeGen/arm-pcs.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple arm-none-linux-gnueabi -emit-llvm -w -o - < %s | FileCheck %s
typedef int __attribute__((pcs("aapcs"))) (*aapcs_fn)(void);
typedef int __attribute__((pcs("aapcs-vfp"))) (*aapcs_vfp_fn)(void);
diff --git a/test/CodeGen/arm-vaarg-align.c b/test/CodeGen/arm-vaarg-align.c
index 1187c02..2270c8b 100644
--- a/test/CodeGen/arm-vaarg-align.c
+++ b/test/CodeGen/arm-vaarg-align.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple arm -target-abi aapcs %s -emit-llvm -o - | FileCheck -check-prefix=AAPCS %s
// RUN: %clang_cc1 -triple arm -target-abi apcs-gnu %s -emit-llvm -o - | FileCheck -check-prefix=APCS-GNU %s
/*
diff --git a/test/CodeGen/arm-vector-align.c b/test/CodeGen/arm-vector-align.c
index b481a0c..9e1ae5d 100644
--- a/test/CodeGen/arm-vector-align.c
+++ b/test/CodeGen/arm-vector-align.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple thumbv7-apple-darwin \
// RUN: -target-abi apcs-gnu \
// RUN: -target-cpu cortex-a8 \
diff --git a/test/CodeGen/arm-vector-arguments.c b/test/CodeGen/arm-vector-arguments.c
index 6bfb2f4..9bdddb7 100644
--- a/test/CodeGen/arm-vector-arguments.c
+++ b/test/CodeGen/arm-vector-arguments.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple thumbv7-apple-darwin9 \
// RUN: -target-abi apcs-gnu \
// RUN: -target-cpu cortex-a8 \
diff --git a/test/CodeGen/asm.c b/test/CodeGen/asm.c
index 84f26e1..b009736 100644
--- a/test/CodeGen/asm.c
+++ b/test/CodeGen/asm.c
@@ -220,3 +220,13 @@ typedef long long __m256i __attribute__((__vector_size__(32)));
void t26 (__m256i *p) {
__asm__ volatile("vmovaps %0, %%ymm0" :: "m" (*(__m256i*)p) : "ymm0");
}
+
+// Check to make sure the inline asm non-standard dialect attribute _not_ is
+// emitted.
+void t27(void) {
+ asm volatile("nop");
+// CHECK: @t27
+// CHECK: call void asm sideeffect "nop"
+// CHECK-NOT: ia_nsdialect
+// CHECK: ret void
+}
diff --git a/test/CodeGen/asm_arm.c b/test/CodeGen/asm_arm.c
index 633bf55..4b764b7 100644
--- a/test/CodeGen/asm_arm.c
+++ b/test/CodeGen/asm_arm.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv6-unknown-unknown -emit-llvm -o - %s | FileCheck %s
void test0(void) {
diff --git a/test/CodeGen/atomic_ops.c b/test/CodeGen/atomic_ops.c
index 9a18c9e..481d1e0 100644
--- a/test/CodeGen/atomic_ops.c
+++ b/test/CodeGen/atomic_ops.c
@@ -1,11 +1,20 @@
// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
-void foo(void)
+void foo(int x)
{
_Atomic(int) i = 0;
+ _Atomic(short) j = 0;
// Check that multiply / divides on atomics produce a cmpxchg loop
- i *= 2; // CHECK: cmpxchg
- i /= 2; // CHECK: cmpxchg
+ i *= 2;
+ // CHECK: mul nsw i32
+ // CHECK: cmpxchg i32*
+ i /= 2;
+ // CHECK: sdiv i32
+ // CHECK: cmpxchg i32*
+ j /= x;
+ // CHECK: sdiv i32
+ // CHECK: cmpxchg i16*
+
// These should be emitting atomicrmw instructions, but they aren't yet
i += 2; // CHECK: cmpxchg
i -= 2; // CHECK: cmpxchg
diff --git a/test/CodeGen/attr-coldhot.c b/test/CodeGen/attr-coldhot.c
new file mode 100644
index 0000000..b9bb299
--- /dev/null
+++ b/test/CodeGen/attr-coldhot.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
+
+int test1() __attribute__((__cold__)) {
+ return 42;
+
+// Check that we set the optsize attribute on the function.
+// CHECK: @test1{{.*}}optsize
+// CHECK: ret
+}
diff --git a/test/CodeGen/attributes.c b/test/CodeGen/attributes.c
index 4e73af6..e971a79 100644
--- a/test/CodeGen/attributes.c
+++ b/test/CodeGen/attributes.c
@@ -81,3 +81,11 @@ void t21(void) {
}
// CHECK: [[FPTRVAR:%[a-z0-9]+]] = load void (i32)** @fptr
// CHECK-NEXT: call x86_fastcallcc void [[FPTRVAR]](i32 10)
+
+
+// PR9356: We might want to err on this, but for now at least make sure we
+// use the section in the definition.
+void __attribute__((section(".foo"))) t22(void);
+void __attribute__((section(".bar"))) t22(void) {}
+
+// CHECK: define void @t22() nounwind section ".bar"
diff --git a/test/CodeGen/avx-builtins.c b/test/CodeGen/avx-builtins.c
index b963c97..0e5a741 100644
--- a/test/CodeGen/avx-builtins.c
+++ b/test/CodeGen/avx-builtins.c
@@ -23,3 +23,73 @@ __m256i test__mm256_loadu_si256(void* p) {
// CHECK: load <4 x i64>* %{{.+}}, align 1
return _mm256_loadu_si256(p);
}
+
+__m128i test_mm_cmpestrm(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestrm128
+ return _mm_cmpestrm(A, LA, B, LB, 7);
+}
+
+int test_mm_cmpestri(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestri128
+ return _mm_cmpestri(A, LA, B, LB, 7);
+}
+
+int test_mm_cmpestra(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestria128
+ return _mm_cmpestra(A, LA, B, LB, 7);
+}
+
+int test_mm_cmpestrc(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestric128
+ return _mm_cmpestrc(A, LA, B, LB, 7);
+}
+
+int test_mm_cmpestro(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestrio128
+ return _mm_cmpestro(A, LA, B, LB, 7);
+}
+
+int test_mm_cmpestrs(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestris128
+ return _mm_cmpestrs(A, LA, B, LB, 7);
+}
+
+int test_mm_cmpestrz(__m128i A, int LA, __m128i B, int LB) {
+ // CHECK: @llvm.x86.sse42.pcmpestriz128
+ return _mm_cmpestrz(A, LA, B, LB, 7);
+}
+
+__m128i test_mm_cmpistrm(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistrm128
+ return _mm_cmpistrm(A, B, 7);
+}
+
+int test_mm_cmpistri(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistri128
+ return _mm_cmpistri(A, B, 7);
+}
+
+int test_mm_cmpistra(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistria128
+ return _mm_cmpistra(A, B, 7);
+}
+
+int test_mm_cmpistrc(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistric128
+ return _mm_cmpistrc(A, B, 7);
+}
+
+int test_mm_cmpistro(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistrio128
+ return _mm_cmpistro(A, B, 7);
+}
+
+int test_mm_cmpistrs(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistris128
+ return _mm_cmpistrs(A, B, 7);
+}
+
+int test_mm_cmpistrz(__m128i A, __m128i B) {
+ // CHECK: @llvm.x86.sse42.pcmpistriz128
+ return _mm_cmpistrz(A, B, 7);
+}
diff --git a/test/CodeGen/avx2-builtins.c b/test/CodeGen/avx2-builtins.c
index 7d166b5..b5bc605 100644
--- a/test/CodeGen/avx2-builtins.c
+++ b/test/CodeGen/avx2-builtins.c
@@ -10,6 +10,11 @@ __m256i test_mm256_mpsadbw_epu8(__m256i x, __m256i y) {
return _mm256_mpsadbw_epu8(x, y, 3);
}
+__m256i test_mm256_sad_epu8(__m256i x, __m256i y) {
+ // CHECK: @llvm.x86.avx2.psad.bw
+ return _mm256_sad_epu8(x, y);
+}
+
__m256i test_mm256_abs_epi8(__m256i a) {
// CHECK: @llvm.x86.avx2.pabs.b
return _mm256_abs_epi8(a);
@@ -780,3 +785,154 @@ __m128i test_mm_srlv_epi64(__m128i a, __m128i b) {
// CHECK: @llvm.x86.avx2.psrlv.q
return _mm_srlv_epi64(a, b);
}
+
+__m128d test_mm_mask_i32gather_pd(__m128d a, double const *b, __m128i c,
+ __m128d d) {
+ // CHECK: @llvm.x86.avx2.gather.d.pd
+ return _mm_mask_i32gather_pd(a, b, c, d, 2);
+}
+
+__m256d test_mm256_mask_i32gather_pd(__m256d a, double const *b, __m128i c,
+ __m256d d) {
+ // CHECK: @llvm.x86.avx2.gather.d.pd.256
+ return _mm256_mask_i32gather_pd(a, b, c, d, 2);
+}
+__m128d test_mm_mask_i64gather_pd(__m128d a, double const *b, __m128i c,
+ __m128d d) {
+ // CHECK: @llvm.x86.avx2.gather.q.pd
+ return _mm_mask_i64gather_pd(a, b, c, d, 2);
+}
+__m256d test_mm256_mask_i64gather_pd(__m256d a, double const *b, __m256i c,
+ __m256d d) {
+ // CHECK: @llvm.x86.avx2.gather.q.pd.256
+ return _mm256_mask_i64gather_pd(a, b, c, d, 2);
+}
+
+__m128 test_mm_mask_i32gather_ps(__m128 a, float const *b, __m128i c,
+ __m128 d) {
+ // CHECK: @llvm.x86.avx2.gather.d.ps
+ return _mm_mask_i32gather_ps(a, b, c, d, 2);
+}
+__m256 test_mm256_mask_i32gather_ps(__m256 a, float const *b, __m256i c,
+ __m256 d) {
+ // CHECK: @llvm.x86.avx2.gather.d.ps.256
+ return _mm256_mask_i32gather_ps(a, b, c, d, 2);
+}
+__m128 test_mm_mask_i64gather_ps(__m128 a, float const *b, __m128i c,
+ __m128 d) {
+ // CHECK: @llvm.x86.avx2.gather.q.ps
+ return _mm_mask_i64gather_ps(a, b, c, d, 2);
+}
+__m128 test_mm256_mask_i64gather_ps(__m128 a, float const *b, __m256i c,
+ __m128 d) {
+ // CHECK: @llvm.x86.avx2.gather.q.ps.256
+ return _mm256_mask_i64gather_ps(a, b, c, d, 2);
+}
+
+__m128i test_mm_mask_i32gather_epi32(__m128i a, int const *b, __m128i c,
+ __m128i d) {
+ // CHECK: @llvm.x86.avx2.gather.d.d
+ return _mm_mask_i32gather_epi32(a, b, c, d, 2);
+}
+__m256i test_mm256_mask_i32gather_epi32(__m256i a, int const *b, __m256i c,
+ __m256i d) {
+ // CHECK: @llvm.x86.avx2.gather.d.d.256
+ return _mm256_mask_i32gather_epi32(a, b, c, d, 2);
+}
+__m128i test_mm_mask_i64gather_epi32(__m128i a, int const *b, __m128i c,
+ __m128i d) {
+ // CHECK: @llvm.x86.avx2.gather.q.d
+ return _mm_mask_i64gather_epi32(a, b, c, d, 2);
+}
+__m128i test_mm256_mask_i64gather_epi32(__m128i a, int const *b, __m256i c,
+ __m128i d) {
+ // CHECK: @llvm.x86.avx2.gather.q.d.256
+ return _mm256_mask_i64gather_epi32(a, b, c, d, 2);
+}
+
+__m128i test_mm_mask_i32gather_epi64(__m128i a, int const *b, __m128i c,
+ __m128i d) {
+ // CHECK: @llvm.x86.avx2.gather.d.q
+ return _mm_mask_i32gather_epi64(a, b, c, d, 2);
+}
+__m256i test_mm256_mask_i32gather_epi64(__m256i a, int const *b, __m128i c,
+ __m256i d) {
+ // CHECK: @llvm.x86.avx2.gather.d.q.256
+ return _mm256_mask_i32gather_epi64(a, b, c, d, 2);
+}
+__m128i test_mm_mask_i64gather_epi64(__m128i a, int const *b, __m128i c,
+ __m128i d) {
+ // CHECK: @llvm.x86.avx2.gather.q.q
+ return _mm_mask_i64gather_epi64(a, b, c, d, 2);
+}
+__m256i test_mm256_mask_i64gather_epi64(__m256i a, int const *b, __m256i c,
+ __m256i d) {
+ // CHECK: @llvm.x86.avx2.gather.q.q.256
+ return _mm256_mask_i64gather_epi64(a, b, c, d, 2);
+}
+
+__m128d test_mm_i32gather_pd(double const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.pd
+ return _mm_i32gather_pd(b, c, 2);
+}
+__m256d test_mm256_i32gather_pd(double const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.pd.256
+ return _mm256_i32gather_pd(b, c, 2);
+}
+__m128d test_mm_i64gather_pd(double const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.pd
+ return _mm_i64gather_pd(b, c, 2);
+}
+__m256d test_mm256_i64gather_pd(double const *b, __m256i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.pd.256
+ return _mm256_i64gather_pd(b, c, 2);
+}
+__m128 test_mm_i32gather_ps(float const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.ps
+ return _mm_i32gather_ps(b, c, 2);
+}
+__m256 test_mm256_i32gather_ps(float const *b, __m256i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.ps.256
+ return _mm256_i32gather_ps(b, c, 2);
+}
+__m128 test_mm_i64gather_ps(float const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.ps
+ return _mm_i64gather_ps(b, c, 2);
+}
+__m128 test_mm256_i64gather_ps(float const *b, __m256i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.ps.256
+ return _mm256_i64gather_ps(b, c, 2);
+}
+
+__m128i test_mm_i32gather_epi32(int const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.d
+ return _mm_i32gather_epi32(b, c, 2);
+}
+__m256i test_mm256_i32gather_epi32(int const *b, __m256i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.d.256
+ return _mm256_i32gather_epi32(b, c, 2);
+}
+__m128i test_mm_i64gather_epi32(int const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.d
+ return _mm_i64gather_epi32(b, c, 2);
+}
+__m128i test_mm256_i64gather_epi32(int const *b, __m256i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.d.256
+ return _mm256_i64gather_epi32(b, c, 2);
+}
+__m128i test_mm_i32gather_epi64(int const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.q
+ return _mm_i32gather_epi64(b, c, 2);
+}
+__m256i test_mm256_i32gather_epi64(int const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.d.q.256
+ return _mm256_i32gather_epi64(b, c, 2);
+}
+__m128i test_mm_i64gather_epi64(int const *b, __m128i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.q
+ return _mm_i64gather_epi64(b, c, 2);
+}
+__m256i test_mm256_i64gather_epi64(int const *b, __m256i c) {
+ // CHECK: @llvm.x86.avx2.gather.q.q.256
+ return _mm256_i64gather_epi64(b, c, 2);
+}
diff --git a/test/CodeGen/block-3.c b/test/CodeGen/block-3.c
index 95cb6a7..29c1bb5 100644
--- a/test/CodeGen/block-3.c
+++ b/test/CodeGen/block-3.c
@@ -6,3 +6,15 @@ int main() {
__attribute__((__blocks__(byref))) int index = ({ int __a; int __b; __a < __b ? __b : __a; });
};
}
+
+// PR13229
+// rdar://11777609
+typedef struct {} Z;
+
+typedef int (^B)(Z);
+
+void testPR13229() {
+ Z z1;
+ B b1 = ^(Z z1) { return 1; };
+ b1(z1);
+}
diff --git a/test/CodeGen/block-byref-aggr.c b/test/CodeGen/block-byref-aggr.c
index 3027df0..eb342b8 100644
--- a/test/CodeGen/block-byref-aggr.c
+++ b/test/CodeGen/block-byref-aggr.c
@@ -1,17 +1,66 @@
// RUN: %clang_cc1 %s -emit-llvm -o - -fblocks -triple x86_64-apple-darwin10 | FileCheck %s
-// rdar://9309454
-typedef struct { int v; } RetType;
+// CHECK: [[AGG:%.*]] = type { i32 }
+typedef struct { int v; } Agg;
+Agg makeAgg(void);
-RetType func();
+// When assigning into a __block variable, ensure that we compute that
+// address *after* evaluating the RHS when the RHS has the capacity to
+// cause a block copy. rdar://9309454
+void test0() {
+ __block Agg a = {100};
-int main () {
- __attribute__((__blocks__(byref))) RetType a = {100};
+ a = makeAgg();
+}
+// CHECK: define void @test0()
+// CHECK: [[A:%.*]] = alloca [[BYREF:%.*]], align 8
+// CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4
+// CHECK: [[RESULT:%.*]] = call i32 @makeAgg()
+// CHECK-NEXT: [[T0:%.*]] = getelementptr [[AGG]]* [[TEMP]], i32 0, i32 0
+// CHECK-NEXT: store i32 [[RESULT]], i32* [[T0]]
+// Check that we properly assign into the forwarding pointer.
+// CHECK-NEXT: [[A_FORWARDING:%.*]] = getelementptr inbounds [[BYREF]]* [[A]], i32 0, i32 1
+// CHECK-NEXT: [[T0:%.*]] = load [[BYREF]]** [[A_FORWARDING]]
+// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[BYREF]]* [[T0]], i32 0, i32 4
+// CHECK-NEXT: [[T2:%.*]] = bitcast [[AGG]]* [[T1]] to i8*
+// CHECK-NEXT: [[T3:%.*]] = bitcast [[AGG]]* [[TEMP]] to i8*
+// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T2]], i8* [[T3]], i64 4, i32 4, i1 false)
+// Verify that there's nothing else significant in the function.
+// CHECK-NEXT: [[T0:%.*]] = bitcast [[BYREF]]* [[A]] to i8*
+// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8)
+// CHECK-NEXT: ret void
- a = func();
+// When chaining assignments into __block variables, make sure we
+// propagate the actual value into the outer variable.
+// rdar://11757470
+void test1() {
+ __block Agg a, b;
+ a = b = makeAgg();
}
-// CHECK: [[C1:%.*]] = call i32 (...)* @func()
-// CHECK-NEXT: [[CO:%.*]] = getelementptr
-// CHECK-NEXT: store i32 [[C1]], i32* [[CO]]
-// CHECK-NEXT: [[FORWARDING:%.*]] = getelementptr inbounds [[BR:%.*]]* [[A:%.*]], i32 0, i32 1
-// CHECK-NEXT: [[O:%.*]] = load [[BR]]** [[FORWARDING]]
+// CHECK: define void @test1()
+// CHECK: [[A:%.*]] = alloca [[A_BYREF:%.*]], align 8
+// CHECK-NEXT: [[B:%.*]] = alloca [[B_BYREF:%.*]], align 8
+// CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4
+// CHECK: [[RESULT:%.*]] = call i32 @makeAgg()
+// CHECK-NEXT: [[T0:%.*]] = getelementptr [[AGG]]* [[TEMP]], i32 0, i32 0
+// CHECK-NEXT: store i32 [[RESULT]], i32* [[T0]]
+// Check that we properly assign into the forwarding pointer, first for b:
+// CHECK-NEXT: [[B_FORWARDING:%.*]] = getelementptr inbounds [[B_BYREF]]* [[B]], i32 0, i32 1
+// CHECK-NEXT: [[T0:%.*]] = load [[B_BYREF]]** [[B_FORWARDING]]
+// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[B_BYREF]]* [[T0]], i32 0, i32 4
+// CHECK-NEXT: [[T2:%.*]] = bitcast [[AGG]]* [[T1]] to i8*
+// CHECK-NEXT: [[T3:%.*]] = bitcast [[AGG]]* [[TEMP]] to i8*
+// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T2]], i8* [[T3]], i64 4, i32 4, i1 false)
+// Then for 'a':
+// CHECK-NEXT: [[A_FORWARDING:%.*]] = getelementptr inbounds [[A_BYREF]]* [[A]], i32 0, i32 1
+// CHECK-NEXT: [[T0:%.*]] = load [[A_BYREF]]** [[A_FORWARDING]]
+// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[A_BYREF]]* [[T0]], i32 0, i32 4
+// CHECK-NEXT: [[T2:%.*]] = bitcast [[AGG]]* [[T1]] to i8*
+// CHECK-NEXT: [[T3:%.*]] = bitcast [[AGG]]* [[TEMP]] to i8*
+// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T2]], i8* [[T3]], i64 4, i32 4, i1 false)
+// Verify that there's nothing else significant in the function.
+// CHECK-NEXT: [[T0:%.*]] = bitcast [[B_BYREF]]* [[B]] to i8*
+// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8)
+// CHECK-NEXT: [[T0:%.*]] = bitcast [[A_BYREF]]* [[A]] to i8*
+// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8)
+// CHECK-NEXT: ret void
diff --git a/test/CodeGen/blocks.c b/test/CodeGen/blocks.c
index bef44c3..71f7171 100644
--- a/test/CodeGen/blocks.c
+++ b/test/CodeGen/blocks.c
@@ -12,7 +12,7 @@ struct s0 {
int a[64];
};
-// CHECK: define internal void @__f2_block_invoke_0(%struct.s0* noalias sret {{%.*}}, i8* {{%.*}}, %struct.s0* byval align 4 {{.*}})
+// CHECK: define internal void @__f2_block_invoke(%struct.s0* noalias sret {{%.*}}, i8* {{%.*}}, %struct.s0* byval align 4 {{.*}})
struct s0 f2(struct s0 a0) {
return ^(struct s0 a1){ return a1; }(a0);
}
@@ -40,3 +40,29 @@ void f3() {
_Bool b = 0;
f3_helper(^{ if (b) {} });
}
+
+// rdar://problem/11322251
+// The bool can fill in between the header and the long long.
+// Add the appropriate amount of padding between them.
+void f4_helper(long long (^)(void));
+// CHECK: define void @f4()
+void f4(void) {
+ _Bool b = 0;
+ long long ll = 0;
+ // CHECK: alloca <{ i8*, i32, i32, i8*, {{%.*}}*, i8, [3 x i8], i64 }>, align 8
+ f4_helper(^{ if (b) return ll; return 0LL; });
+}
+
+// rdar://problem/11354538
+// The alignment after rounding up to the align of F5 is actually
+// greater than the required alignment. Don't assert.
+struct F5 {
+ char buffer[32] __attribute((aligned));
+};
+void f5_helper(void (^)(struct F5 *));
+// CHECK: define void @f5()
+void f5(void) {
+ struct F5 value;
+ // CHECK: alloca <{ i8*, i32, i32, i8*, {{%.*}}*, [12 x i8], [[F5:%.*]] }>, align 16
+ f5_helper(^(struct F5 *slot) { *slot = value; });
+}
diff --git a/test/CodeGen/blocksignature.c b/test/CodeGen/blocksignature.c
index 63fe124..fd586eb 100644
--- a/test/CodeGen/blocksignature.c
+++ b/test/CodeGen/blocksignature.c
@@ -8,7 +8,7 @@
// X32: [[STR1:@.*]] = private unnamed_addr constant [6 x i8] c"v4@?0\00"
// X32: @__block_descriptor_tmp = internal constant [[FULL_DESCRIPTOR_T:.*]] { i32 0, i32 20, i8* getelementptr inbounds ([6 x i8]* [[STR1]], i32 0, i32 0), i8* null }
-// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @__block_global_{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) }
+// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @global_block_invoke{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) }
// X32: [[STR2:@.*]] = private unnamed_addr constant [11 x i8] c"i12@?0c4f8\00"
// X32: @__block_descriptor_tmp{{.*}} = internal constant [[FULL_DESCRIPTOR_T]] { i32 0, i32 24, i8* getelementptr inbounds ([11 x i8]* [[STR2]], i32 0, i32 0), i8* null }
// X32: store i32 1073741824, i32*
diff --git a/test/CodeGen/blockwithlocalstatic.c b/test/CodeGen/blockwithlocalstatic.c
index 1fdaaf37a..8b4210e 100644
--- a/test/CodeGen/blockwithlocalstatic.c
+++ b/test/CodeGen/blockwithlocalstatic.c
@@ -1,17 +1,17 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fblocks -emit-llvm -o - %s | FileCheck %s
// pr8707
-// CHECK: @__block_global_0.test = internal global i32
+// CHECK: @block_block_invoke.test = internal global i32
int (^block)(void) = ^ {
static int test=0;
return test;
};
-// CHECK: @__block_global_1.test = internal global i32
+// CHECK: @block1_block_invoke_2.test = internal global i32
void (^block1)(void) = ^ {
static int test = 2;
return;
};
-// CHECK: @__block_global_2.test = internal global i32
+// CHECK: @block2_block_invoke_3.test = internal global i32
int (^block2)(void) = ^ {
static int test = 5;
return test;
diff --git a/test/CodeGen/bmi-builtins.c b/test/CodeGen/bmi-builtins.c
index 47b0da2..2e1ba12 100644
--- a/test/CodeGen/bmi-builtins.c
+++ b/test/CodeGen/bmi-builtins.c
@@ -5,9 +5,9 @@
#include <x86intrin.h>
-unsigned short test__tzcnt16(unsigned short __X) {
+unsigned short test__tzcnt_u16(unsigned short __X) {
// CHECK: @llvm.cttz.i16
- return __tzcnt16(__X);
+ return __tzcnt_u16(__X);
}
unsigned int test__andn_u32(unsigned int __X, unsigned int __Y) {
@@ -39,9 +39,9 @@ unsigned int test__blsr_u32(unsigned int __X) {
return __blsr_u32(__X);
}
-unsigned int test_tzcnt32(unsigned int __X) {
+unsigned int test_tzcnt_u32(unsigned int __X) {
// CHECK: @llvm.cttz.i32
- return __tzcnt32(__X);
+ return __tzcnt_u32(__X);
}
unsigned long long test__andn_u64(unsigned long __X, unsigned long __Y) {
@@ -73,7 +73,7 @@ unsigned long long test__blsr_u64(unsigned long long __X) {
return __blsr_u64(__X);
}
-unsigned long long test__tzcnt64(unsigned long long __X) {
+unsigned long long test__tzcnt_u64(unsigned long long __X) {
// CHECK: @llvm.cttz.i64
- return __tzcnt64(__X);
+ return __tzcnt_u64(__X);
}
diff --git a/test/CodeGen/bool_test.c b/test/CodeGen/bool_test.c
index ffaaef8..715f846 100644
--- a/test/CodeGen/bool_test.c
+++ b/test/CodeGen/bool_test.c
@@ -1,3 +1,4 @@
+// REQUIRES: ppc32-registered-target
// RUN: %clang_cc1 -triple powerpc-apple-darwin -emit-llvm -o - %s| FileCheck -check-prefix=DARWINPPC-CHECK %s
int boolsize = sizeof(_Bool);
diff --git a/test/CodeGen/bounds-checking.c b/test/CodeGen/bounds-checking.c
new file mode 100644
index 0000000..e278620
--- /dev/null
+++ b/test/CodeGen/bounds-checking.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fbounds-checking=4 -emit-llvm -triple x86_64-apple-darwin10 < %s | FileCheck %s
+
+// CHECK: @f
+double f(int b, int i) {
+ double a[b];
+ // CHECK: trap
+ return a[i];
+}
+
+// CHECK: @f2
+void f2() {
+ // everything is constant; no trap possible
+ // CHECK-NOT: trap
+ int a[2];
+ a[1] = 42;
+
+ short *b = malloc(64);
+ b[5] = *a + a[1] + 2;
+}
+
+// CHECK: @f3
+void f3() {
+ int a[1];
+ // CHECK: trap
+ a[2] = 1;
+}
diff --git a/test/CodeGen/branch-target-layout.c b/test/CodeGen/branch-target-layout.c
new file mode 100644
index 0000000..a6475d7
--- /dev/null
+++ b/test/CodeGen/branch-target-layout.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 %s -O3 -emit-llvm -o - | FileCheck %s
+//
+// PR13214
+// No assumption may be made about the order that a frontend emits branch
+// targets (basic blocks). However, the backend's basic block layout makes an
+// attempt to preserve source order of control flow, and any bias toward source
+// order must start with the frontend.
+//
+// Note that the frontend inverts branches to simplify the condition, so the
+// order of a branch instruction's labels cannot be used as a source order bias.
+
+void calla();
+void callb();
+void callc();
+
+// CHECK: @test1
+// CHECK: @calla
+// CHECK: @callb
+// CHECK: @callc
+// CHECK: ret void
+void test1(int a) {
+ if (a)
+ calla();
+ else
+ callb();
+ callc();
+}
+
+// CHECK: @test2
+// CHECK: @callb
+// CHECK: @calla
+// CHECK: @callc
+// CHECK: ret void
+void test2(int a) {
+ if (!a)
+ callb();
+ else
+ calla();
+ callc();
+}
diff --git a/test/CodeGen/builtin-attributes.c b/test/CodeGen/builtin-attributes.c
index 3781eba..1d3a943 100644
--- a/test/CodeGen/builtin-attributes.c
+++ b/test/CodeGen/builtin-attributes.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple arm-unknown-linux-gnueabi -emit-llvm -o - %s | FileCheck %s
// CHECK: declare i32 @printf(i8*, ...)
diff --git a/test/CodeGen/builtins-arm.c b/test/CodeGen/builtins-arm.c
index 09df1ef..3611650 100644
--- a/test/CodeGen/builtins-arm.c
+++ b/test/CodeGen/builtins-arm.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -Wall -Werror -triple thumbv7-eabi -target-cpu cortex-a8 -O3 -emit-llvm -o - %s | FileCheck %s
void *f0()
diff --git a/test/CodeGen/builtins-mips-args.c b/test/CodeGen/builtins-mips-args.c
new file mode 100644
index 0000000..a961b36
--- /dev/null
+++ b/test/CodeGen/builtins-mips-args.c
@@ -0,0 +1,14 @@
+// REQUIRES: mips-registered-target
+// RUN: %clang_cc1 -triple mips-unknown-linux-gnu -fsyntax-only -verify %s
+
+void foo() {
+ // MIPS DSP Rev 1
+
+ int a = 3;
+ __builtin_mips_wrdsp(2052, a); // expected-error{{argument to '__builtin_mips_wrdsp' must be a constant integer}}
+ __builtin_mips_rddsp(a); // expected-error{{argument to '__builtin_mips_rddsp' must be a constant integer}}
+ __builtin_mips_wrdsp(2052, -1); // expected-error{{argument should be a value from 0 to 63}}
+ __builtin_mips_rddsp(-1); // expected-error{{argument should be a value from 0 to 63}}
+ __builtin_mips_wrdsp(2052, 64); // expected-error{{argument should be a value from 0 to 63}}
+ __builtin_mips_rddsp(64); // expected-error{{argument should be a value from 0 to 63}}
+}
diff --git a/test/CodeGen/builtins-mips.c b/test/CodeGen/builtins-mips.c
new file mode 100644
index 0000000..8155a43
--- /dev/null
+++ b/test/CodeGen/builtins-mips.c
@@ -0,0 +1,324 @@
+// REQUIRES: mips-registered-target
+// RUN: %clang_cc1 -triple mips-unknown-linux-gnu -emit-llvm %s -o - \
+// RUN: | FileCheck %s
+
+typedef int q31;
+typedef int i32;
+typedef unsigned int ui32;
+typedef long long a64;
+
+typedef signed char v4i8 __attribute__ ((vector_size(4)));
+typedef short v2q15 __attribute__ ((vector_size(4)));
+
+void foo() {
+ v2q15 v2q15_r, v2q15_a, v2q15_b, v2q15_c;
+ v4i8 v4i8_r, v4i8_a, v4i8_b, v4i8_c;
+ q31 q31_r, q31_a, q31_b, q31_c;
+ i32 i32_r, i32_a, i32_b, i32_c;
+ ui32 ui32_r, ui32_a, ui32_b, ui32_c;
+ a64 a64_r, a64_a, a64_b;
+
+ // MIPS DSP Rev 1
+
+ v4i8_a = (v4i8) {1, 2, 3, 0xFF};
+ v4i8_b = (v4i8) {2, 4, 6, 8};
+ v4i8_r = __builtin_mips_addu_qb(v4i8_a, v4i8_b);
+// CHECK: call <4 x i8> @llvm.mips.addu.qb
+ v4i8_r = __builtin_mips_addu_s_qb(v4i8_a, v4i8_b);
+// CHECK: call <4 x i8> @llvm.mips.addu.s.qb
+ v4i8_r = __builtin_mips_subu_qb(v4i8_a, v4i8_b);
+// CHECK: call <4 x i8> @llvm.mips.subu.qb
+ v4i8_r = __builtin_mips_subu_s_qb(v4i8_a, v4i8_b);
+// CHECK: call <4 x i8> @llvm.mips.subu.s.qb
+
+ v2q15_a = (v2q15) {0x0000, 0x8000};
+ v2q15_b = (v2q15) {0x8000, 0x8000};
+ v2q15_r = __builtin_mips_addq_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.addq.ph
+ v2q15_r = __builtin_mips_addq_s_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.addq.s.ph
+ v2q15_r = __builtin_mips_subq_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.subq.ph
+ v2q15_r = __builtin_mips_subq_s_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.subq.s.ph
+
+ a64_a = 0x12345678;
+ i32_b = 0x80000000;
+ i32_c = 0x11112222;
+ a64_r = __builtin_mips_madd(a64_a, i32_b, i32_c);
+// CHECK: call i64 @llvm.mips.madd
+ a64_a = 0x12345678;
+ ui32_b = 0x80000000;
+ ui32_c = 0x11112222;
+ a64_r = __builtin_mips_maddu(a64_a, ui32_b, ui32_c);
+// CHECK: call i64 @llvm.mips.maddu
+ a64_a = 0x12345678;
+ i32_b = 0x80000000;
+ i32_c = 0x11112222;
+ a64_r = __builtin_mips_msub(a64_a, i32_b, i32_c);
+// CHECK: call i64 @llvm.mips.msub
+ a64_a = 0x12345678;
+ ui32_b = 0x80000000;
+ ui32_c = 0x11112222;
+ a64_r = __builtin_mips_msubu(a64_a, ui32_b, ui32_c);
+// CHECK: call i64 @llvm.mips.msubu
+
+ q31_a = 0x12345678;
+ q31_b = 0x7FFFFFFF;
+ q31_r = __builtin_mips_addq_s_w(q31_a, q31_b);
+// CHECK: call i32 @llvm.mips.addq.s.w
+ q31_r = __builtin_mips_subq_s_w(q31_a, q31_b);
+// CHECK: call i32 @llvm.mips.subq.s.w
+
+ i32_a = 0xFFFFFFFF;
+ i32_b = 1;
+ i32_r = __builtin_mips_addsc(i32_a, i32_b);
+// CHECK: call i32 @llvm.mips.addsc
+ i32_a = 0;
+ i32_b = 1;
+ i32_r = __builtin_mips_addwc(i32_a, i32_b);
+// CHECK: call i32 @llvm.mips.addwc
+
+ i32_a = 20;
+ i32_b = 0x1402;
+ i32_r = __builtin_mips_modsub(i32_a, i32_b);
+// CHECK: call i32 @llvm.mips.modsub
+
+ v4i8_a = (v4i8) {1, 2, 3, 4};
+ i32_r = __builtin_mips_raddu_w_qb(v4i8_a);
+// CHECK: call i32 @llvm.mips.raddu.w.qb
+
+ v2q15_a = (v2q15) {0xFFFF, 0x8000};
+ v2q15_r = __builtin_mips_absq_s_ph(v2q15_a);
+// CHECK: call <2 x i16> @llvm.mips.absq.s.ph
+ q31_a = 0x80000000;
+ q31_r = __builtin_mips_absq_s_w(q31_a);
+// CHECK: call i32 @llvm.mips.absq.s.w
+
+ v2q15_a = (v2q15) {0x1234, 0x5678};
+ v2q15_b = (v2q15) {0x1111, 0x2222};
+ v4i8_r = __builtin_mips_precrq_qb_ph(v2q15_a, v2q15_b);
+// CHECK: call <4 x i8> @llvm.mips.precrq.qb.ph
+
+ v2q15_a = (v2q15) {0x7F79, 0xFFFF};
+ v2q15_b = (v2q15) {0x7F81, 0x2000};
+ v4i8_r = __builtin_mips_precrqu_s_qb_ph(v2q15_a, v2q15_b);
+// CHECK: call <4 x i8> @llvm.mips.precrqu.s.qb.ph
+ q31_a = 0x12345678;
+ q31_b = 0x11112222;
+ v2q15_r = __builtin_mips_precrq_ph_w(q31_a, q31_b);
+// CHECK: call <2 x i16> @llvm.mips.precrq.ph.w
+ q31_a = 0x7000FFFF;
+ q31_b = 0x80000000;
+ v2q15_r = __builtin_mips_precrq_rs_ph_w(q31_a, q31_b);
+// CHECK: call <2 x i16> @llvm.mips.precrq.rs.ph.w
+ v2q15_a = (v2q15) {0x1234, 0x5678};
+ q31_r = __builtin_mips_preceq_w_phl(v2q15_a);
+// CHECK: call i32 @llvm.mips.preceq.w.phl
+ q31_r = __builtin_mips_preceq_w_phr(v2q15_a);
+// CHECK: call i32 @llvm.mips.preceq.w.phr
+ v4i8_a = (v4i8) {0x12, 0x34, 0x56, 0x78};
+ v2q15_r = __builtin_mips_precequ_ph_qbl(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.precequ.ph.qbl
+ v2q15_r = __builtin_mips_precequ_ph_qbr(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.precequ.ph.qbr
+ v2q15_r = __builtin_mips_precequ_ph_qbla(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.precequ.ph.qbla
+ v2q15_r = __builtin_mips_precequ_ph_qbra(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.precequ.ph.qbra
+ v2q15_r = __builtin_mips_preceu_ph_qbl(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.preceu.ph.qbl
+ v2q15_r = __builtin_mips_preceu_ph_qbr(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.preceu.ph.qbr
+ v2q15_r = __builtin_mips_preceu_ph_qbla(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.preceu.ph.qbla
+ v2q15_r = __builtin_mips_preceu_ph_qbra(v4i8_a);
+// CHECK: call <2 x i16> @llvm.mips.preceu.ph.qbra
+
+ v4i8_a = (v4i8) {1, 2, 3, 4};
+ v4i8_r = __builtin_mips_shll_qb(v4i8_a, 2);
+// CHECK: call <4 x i8> @llvm.mips.shll.qb
+ v4i8_a = (v4i8) {128, 64, 32, 16};
+ v4i8_r = __builtin_mips_shrl_qb(v4i8_a, 2);
+// CHECK: call <4 x i8> @llvm.mips.shrl.qb
+ v2q15_a = (v2q15) {0x0001, 0x8000};
+ v2q15_r = __builtin_mips_shll_ph(v2q15_a, 2);
+// CHECK: call <2 x i16> @llvm.mips.shll.ph
+ v2q15_r = __builtin_mips_shll_s_ph(v2q15_a, 2);
+// CHECK: call <2 x i16> @llvm.mips.shll.s.ph
+ v2q15_a = (v2q15) {0x7FFF, 0x8000};
+ v2q15_r = __builtin_mips_shra_ph(v2q15_a, 2);
+// CHECK: call <2 x i16> @llvm.mips.shra.ph
+ v2q15_r = __builtin_mips_shra_r_ph(v2q15_a, 2);
+// CHECK: call <2 x i16> @llvm.mips.shra.r.ph
+ q31_a = 0x70000000;
+ q31_r = __builtin_mips_shll_s_w(q31_a, 2);
+// CHECK: call i32 @llvm.mips.shll.s.w
+ q31_a = 0x7FFFFFFF;
+ q31_r = __builtin_mips_shra_r_w(q31_a, 2);
+// CHECK: call i32 @llvm.mips.shra.r.w
+ a64_a = 0x1234567887654321LL;
+ a64_r = __builtin_mips_shilo(a64_a, -8);
+// CHECK: call i64 @llvm.mips.shilo
+
+ v4i8_a = (v4i8) {0x1, 0x3, 0x5, 0x7};
+ v2q15_b = (v2q15) {0x1234, 0x5678};
+ v2q15_r = __builtin_mips_muleu_s_ph_qbl(v4i8_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.muleu.s.ph.qbl
+ v2q15_r = __builtin_mips_muleu_s_ph_qbr(v4i8_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.muleu.s.ph.qbr
+ v2q15_a = (v2q15) {0x7FFF, 0x8000};
+ v2q15_b = (v2q15) {0x7FFF, 0x8000};
+ v2q15_r = __builtin_mips_mulq_rs_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.mulq.rs.ph
+ v2q15_a = (v2q15) {0x1234, 0x8000};
+ v2q15_b = (v2q15) {0x5678, 0x8000};
+ q31_r = __builtin_mips_muleq_s_w_phl(v2q15_a, v2q15_b);
+// CHECK: call i32 @llvm.mips.muleq.s.w.phl
+ q31_r = __builtin_mips_muleq_s_w_phr(v2q15_a, v2q15_b);
+// CHECK: call i32 @llvm.mips.muleq.s.w.phr
+ a64_a = 0;
+ v2q15_a = (v2q15) {0x0001, 0x8000};
+ v2q15_b = (v2q15) {0x0002, 0x8000};
+ a64_r = __builtin_mips_mulsaq_s_w_ph(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.mulsaq.s.w.ph
+ a64_a = 0;
+ v2q15_b = (v2q15) {0x0001, 0x8000};
+ v2q15_c = (v2q15) {0x0002, 0x8000};
+ a64_r = __builtin_mips_maq_s_w_phl(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.maq.s.w.phl
+ a64_r = __builtin_mips_maq_s_w_phr(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.maq.s.w.phr
+ a64_a = 0x7FFFFFF0;
+ a64_r = __builtin_mips_maq_sa_w_phl(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.maq.sa.w.phl
+ a64_r = __builtin_mips_maq_sa_w_phr(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.maq.sa.w.phr
+ i32_a = 0x80000000;
+ i32_b = 0x11112222;
+ a64_r = __builtin_mips_mult(i32_a, i32_b);
+// CHECK: call i64 @llvm.mips.mult
+ ui32_a = 0x80000000;
+ ui32_b = 0x11112222;
+ a64_r = __builtin_mips_multu(ui32_a, ui32_b);
+// CHECK: call i64 @llvm.mips.multu
+
+ a64_a = 0;
+ v4i8_b = (v4i8) {1, 2, 3, 4};
+ v4i8_c = (v4i8) {4, 5, 6, 7};
+ a64_r = __builtin_mips_dpau_h_qbl(a64_a, v4i8_b, v4i8_c);
+// CHECK: call i64 @llvm.mips.dpau.h.qbl
+ a64_r = __builtin_mips_dpau_h_qbr(a64_a, v4i8_b, v4i8_c);
+// CHECK: call i64 @llvm.mips.dpau.h.qbr
+ a64_r = __builtin_mips_dpsu_h_qbl(a64_a, v4i8_b, v4i8_c);
+// CHECK: call i64 @llvm.mips.dpsu.h.qbl
+ a64_r = __builtin_mips_dpsu_h_qbr(a64_a, v4i8_b, v4i8_c);
+// CHECK: call i64 @llvm.mips.dpsu.h.qbr
+ a64_a = 0;
+ v2q15_b = (v2q15) {0x0001, 0x8000};
+ v2q15_c = (v2q15) {0x0002, 0x8000};
+ a64_r = __builtin_mips_dpaq_s_w_ph(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.dpaq.s.w.ph
+ a64_r = __builtin_mips_dpsq_s_w_ph(a64_a, v2q15_b, v2q15_c);
+// CHECK: call i64 @llvm.mips.dpsq.s.w.ph
+ a64_a = 0;
+ q31_b = 0x80000000;
+ q31_c = 0x80000000;
+ a64_r = __builtin_mips_dpaq_sa_l_w(a64_a, q31_b, q31_c);
+// CHECK: call i64 @llvm.mips.dpaq.sa.l.w
+ a64_r = __builtin_mips_dpsq_sa_l_w(a64_a, q31_b, q31_c);
+// CHECK: call i64 @llvm.mips.dpsq.sa.l.w
+
+ v4i8_a = (v4i8) {1, 4, 10, 8};
+ v4i8_b = (v4i8) {1, 2, 100, 8};
+ __builtin_mips_cmpu_eq_qb(v4i8_a, v4i8_b);
+// CHECK: call void @llvm.mips.cmpu.eq.qb
+ __builtin_mips_cmpu_lt_qb(v4i8_a, v4i8_b);
+// CHECK: call void @llvm.mips.cmpu.lt.qb
+ __builtin_mips_cmpu_le_qb(v4i8_a, v4i8_b);
+// CHECK: call void @llvm.mips.cmpu.le.qb
+ i32_r = __builtin_mips_cmpgu_eq_qb(v4i8_a, v4i8_b);
+// CHECK: call i32 @llvm.mips.cmpgu.eq.qb
+ i32_r = __builtin_mips_cmpgu_lt_qb(v4i8_a, v4i8_b);
+// CHECK: call i32 @llvm.mips.cmpgu.lt.qb
+ i32_r = __builtin_mips_cmpgu_le_qb(v4i8_a, v4i8_b);
+// CHECK: call i32 @llvm.mips.cmpgu.le.qb
+ v2q15_a = (v2q15) {0x1111, 0x1234};
+ v2q15_b = (v2q15) {0x4444, 0x1234};
+ __builtin_mips_cmp_eq_ph(v2q15_a, v2q15_b);
+// CHECK: call void @llvm.mips.cmp.eq.ph
+ __builtin_mips_cmp_lt_ph(v2q15_a, v2q15_b);
+// CHECK: call void @llvm.mips.cmp.lt.ph
+ __builtin_mips_cmp_le_ph(v2q15_a, v2q15_b);
+// CHECK: call void @llvm.mips.cmp.le.ph
+
+ a64_a = 0xFFFFF81230000000LL;
+ i32_r = __builtin_mips_extr_s_h(a64_a, 4);
+// CHECK: call i32 @llvm.mips.extr.s.h
+ a64_a = 0x8123456712345678LL;
+ i32_r = __builtin_mips_extr_w(a64_a, 31);
+// CHECK: call i32 @llvm.mips.extr.w
+ i32_r = __builtin_mips_extr_rs_w(a64_a, 31);
+// CHECK: call i32 @llvm.mips.extr.rs.w
+ i32_r = __builtin_mips_extr_r_w(a64_a, 31);
+// CHECK: call i32 @llvm.mips.extr.r.w
+ a64_a = 0x1234567887654321LL;
+ i32_r = __builtin_mips_extp(a64_a, 3);
+// CHECK: call i32 @llvm.mips.extp
+ a64_a = 0x123456789ABCDEF0LL;
+ i32_r = __builtin_mips_extpdp(a64_a, 7);
+// CHECK: call i32 @llvm.mips.extpdp
+
+ __builtin_mips_wrdsp(2052, 3);
+// CHECK: call void @llvm.mips.wrdsp
+ i32_r = __builtin_mips_rddsp(3);
+// CHECK: call i32 @llvm.mips.rddsp
+ i32_a = 0xFFFFFFFF;
+ i32_b = 0x12345678;
+ __builtin_mips_wrdsp((16<<7) + 4, 3);
+// CHECK: call void @llvm.mips.wrdsp
+ i32_r = __builtin_mips_insv(i32_a, i32_b);
+// CHECK: call i32 @llvm.mips.insv
+ i32_a = 0x1234;
+ i32_r = __builtin_mips_bitrev(i32_a);
+// CHECK: call i32 @llvm.mips.bitrev
+ v2q15_a = (v2q15) {0x1111, 0x2222};
+ v2q15_b = (v2q15) {0x3333, 0x4444};
+ v2q15_r = __builtin_mips_packrl_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.packrl.ph
+ i32_a = 100;
+ v4i8_r = __builtin_mips_repl_qb(i32_a);
+// CHECK: call <4 x i8> @llvm.mips.repl.qb
+ i32_a = 0x1234;
+ v2q15_r = __builtin_mips_repl_ph(i32_a);
+// CHECK: call <2 x i16> @llvm.mips.repl.ph
+ v4i8_a = (v4i8) {1, 4, 10, 8};
+ v4i8_b = (v4i8) {1, 2, 100, 8};
+ __builtin_mips_cmpu_eq_qb(v4i8_a, v4i8_b);
+// CHECK: call void @llvm.mips.cmpu.eq.qb
+ v4i8_r = __builtin_mips_pick_qb(v4i8_a, v4i8_b);
+// CHECK: call <4 x i8> @llvm.mips.pick.qb
+ v2q15_a = (v2q15) {0x1111, 0x1234};
+ v2q15_b = (v2q15) {0x4444, 0x1234};
+ __builtin_mips_cmp_eq_ph(v2q15_a, v2q15_b);
+// CHECK: call void @llvm.mips.cmp.eq.ph
+ v2q15_r = __builtin_mips_pick_ph(v2q15_a, v2q15_b);
+// CHECK: call <2 x i16> @llvm.mips.pick.ph
+ a64_a = 0x1234567887654321LL;
+ i32_b = 0x11112222;
+ __builtin_mips_wrdsp(0, 1);
+// CHECK: call void @llvm.mips.wrdsp
+ a64_r = __builtin_mips_mthlip(a64_a, i32_b);
+// CHECK: call i64 @llvm.mips.mthlip
+ i32_r = __builtin_mips_bposge32();
+// CHECK: call i32 @llvm.mips.bposge32
+ char array_a[100];
+ i32_r = __builtin_mips_lbux(array_a, 20);
+// CHECK: call i32 @llvm.mips.lbux
+ short array_b[100];
+ i32_r = __builtin_mips_lhx(array_b, 20);
+// CHECK: call i32 @llvm.mips.lhx
+ int array_c[100];
+ i32_r = __builtin_mips_lwx(array_c, 20);
+// CHECK: call i32 @llvm.mips.lwx
+}
diff --git a/test/CodeGen/builtins-ptx.c b/test/CodeGen/builtins-nvptx.c
index 6dd1018..fa6b14c 100644
--- a/test/CodeGen/builtins-ptx.c
+++ b/test/CodeGen/builtins-nvptx.c
@@ -1,6 +1,5 @@
-// RUN: %clang_cc1 -triple ptx32-unknown-unknown -emit-llvm -o %t %s
-// RUN: %clang_cc1 -triple ptx64-unknown-unknown -emit-llvm -o %t %s
-
+// RUN: %clang_cc1 -triple nvptx-unknown-unknown -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple nvptx64-unknown-unknown -emit-llvm -o %t %s
int read_tid() {
diff --git a/test/CodeGen/builtins-ppc-altivec.c b/test/CodeGen/builtins-ppc-altivec.c
index b12ff01..e885cb0 100644
--- a/test/CodeGen/builtins-ppc-altivec.c
+++ b/test/CodeGen/builtins-ppc-altivec.c
@@ -1,3 +1,4 @@
+// REQUIRES: ppc32-registered-target
// RUN: %clang_cc1 -faltivec -triple powerpc-unknown-unknown -emit-llvm %s -o - | FileCheck %s
vector bool char vbc = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
diff --git a/test/CodeGen/builtins-x86.c b/test/CodeGen/builtins-x86.c
index acb5554..fcf1512 100644
--- a/test/CodeGen/builtins-x86.c
+++ b/test/CodeGen/builtins-x86.c
@@ -344,7 +344,6 @@ void f0() {
tmp_V16c = __builtin_ia32_lddqu(tmp_cCp);
tmp_V2LLi = __builtin_ia32_palignr128(tmp_V2LLi, tmp_V2LLi, imm_i);
tmp_V1LLi = __builtin_ia32_palignr(tmp_V1LLi, tmp_V1LLi, imm_i);
- (void) __builtin_ia32_storelv4si(tmp_V2ip, tmp_V2LLi);
#ifdef USE_SSE4
tmp_V16c = __builtin_ia32_pblendvb128(tmp_V16c, tmp_V16c, tmp_V16c);
tmp_V8s = __builtin_ia32_pblendw128(tmp_V8s, tmp_V8s, imm_i_0_256);
diff --git a/test/CodeGen/builtins.c b/test/CodeGen/builtins.c
index fca087e..65b9ad1 100644
--- a/test/CodeGen/builtins.c
+++ b/test/CodeGen/builtins.c
@@ -203,3 +203,9 @@ void test_builtin_longjmp(void **buffer) {
__builtin_longjmp(buffer, 1);
// CHECK-NEXT: unreachable
}
+
+// CHECK: define i64 @test_builtin_readcyclecounter
+long long test_builtin_readcyclecounter() {
+ // CHECK: call i64 @llvm.readcyclecounter()
+ return __builtin_readcyclecounter();
+}
diff --git a/test/CodeGen/capture-complex-expr-in-block.c b/test/CodeGen/capture-complex-expr-in-block.c
index 9ce7570..86c93d0 100644
--- a/test/CodeGen/capture-complex-expr-in-block.c
+++ b/test/CodeGen/capture-complex-expr-in-block.c
@@ -12,7 +12,7 @@ int main ()
b();
}
-// CHECK: define internal void @__main_block_invoke_0
+// CHECK: define internal void @__main_block_invoke
// CHECK: [[C1:%.*]] = alloca { double, double }, align 8
// CHECK: [[RP:%.*]] = getelementptr inbounds { double, double }* [[C1]], i32 0, i32 0
// CHECK-NEXT: [[R:%.*]] = load double* [[RP]]
diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c
index fef1587..ee0b658 100644
--- a/test/CodeGen/catch-undef-behavior.c
+++ b/test/CodeGen/catch-undef-behavior.c
@@ -1,7 +1,17 @@
-// RUN: %clang_cc1 -fcatch-undefined-behavior -emit-llvm-only %s
+// RUN: %clang_cc1 -fcatch-undefined-behavior -emit-llvm %s -o - | FileCheck %s
// PR6805
+// CHECK: @foo
void foo() {
union { int i; } u;
+ // CHECK: objectsize
+ // CHECK: icmp uge
u.i=1;
}
+
+// CHECK: @bar
+int bar(int *a) {
+ // CHECK: objectsize
+ // CHECK: icmp uge
+ return *a;
+}
diff --git a/test/CodeGen/compound-literal.c b/test/CodeGen/compound-literal.c
index 969c5af..a8eec61 100644
--- a/test/CodeGen/compound-literal.c
+++ b/test/CodeGen/compound-literal.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - | FileCheck %s
int* a = &(int){1};
struct s {int a, b, c;} * b = &(struct s) {1, 2, 3};
diff --git a/test/CodeGen/debug-info-gline-tables-only.c b/test/CodeGen/debug-info-gline-tables-only.c
new file mode 100644
index 0000000..067d8e7
--- /dev/null
+++ b/test/CodeGen/debug-info-gline-tables-only.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 %s -gline-tables-only -S -emit-llvm -o - | FileCheck %s
+// Checks that clang with "-gline-tables-only" doesn't emit debug info
+// for variables and types.
+
+// CHECK-NOT: DW_TAG_variable
+int global = 42;
+
+// CHECK-NOT: DW_TAG_typedef
+// CHECK-NOT: DW_TAG_const_type
+// CHECK-NOT: DW_TAG_pointer_type
+// CHECK-NOT: DW_TAG_array_type
+typedef const char* constCharPtrArray[10];
+
+// CHECK-NOT: DW_TAG_structure_type
+struct S {
+ // CHECK-NOT: DW_TAG_member
+ char a;
+ double b;
+ constCharPtrArray c;
+};
+
+// CHECK-NOT: DW_TAG_enumerator
+// CHECK-NOT: DW_TAG_enumeration_type
+enum E { ZERO = 0, ONE = 1 };
+
+// CHECK-NOT: DW_TAG_arg_variable
+int sum(int p, int q) {
+ // CHECK-NOT: DW_TAG_auto_variable
+ int r = p + q;
+ struct S s;
+ enum E e;
+ return r;
+}
diff --git a/test/CodeGen/debug-info-gline-tables-only2.c b/test/CodeGen/debug-info-gline-tables-only2.c
new file mode 100644
index 0000000..8e9cc64
--- /dev/null
+++ b/test/CodeGen/debug-info-gline-tables-only2.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -gline-tables-only -S -emit-llvm -o - | FileCheck %s
+// Checks that clang with "-gline-tables-only" emits metadata for
+// compile unit, subprogram and file.
+
+int main() {
+ // CHECK: ret i32 0, !dbg
+ return 0;
+}
+
+// CHECK: !llvm.dbg.cu = !{!0}
+// CHECK: DW_TAG_compile_unit
+// CHECK: {{.*main.* DW_TAG_subprogram}}
+// CHECK: DW_TAG_file_type
diff --git a/test/CodeGen/debug-info-line2.c b/test/CodeGen/debug-info-line2.c
index b5eba8a..893b021 100644
--- a/test/CodeGen/debug-info-line2.c
+++ b/test/CodeGen/debug-info-line2.c
@@ -6,11 +6,12 @@ int foo(int i) {
int j = 0;
if (i) {
j = bar();
-//CHECK: store i32
-//CHECK-NOT: br label %{{%[a-zA-Z0-9\.]+}}, !dbg
}
else
{
+ // CHECK: add nsw
+ // CHECK-NEXT: store i32 %{{[a-zA-Z0-9]+}}
+ // CHECK-NOT: br label %{{[a-zA-Z0-9\.]+}}, !dbg
j = bar() + 2;
}
return j;
diff --git a/test/CodeGen/debug-info-line3.c b/test/CodeGen/debug-info-line3.c
index 645ffb9..a4e35e7 100644
--- a/test/CodeGen/debug-info-line3.c
+++ b/test/CodeGen/debug-info-line3.c
@@ -12,5 +12,5 @@ void func(char c, char* d)
}
-// CHECK: ret void, !dbg !19
-// CHECK: !19 = metadata !{i32 6,
+// CHECK: ret void, !dbg !17
+// CHECK: !17 = metadata !{i32 6,
diff --git a/test/CodeGen/debug-info-vla.c b/test/CodeGen/debug-info-vla.c
new file mode 100644
index 0000000..20fb6aa
--- /dev/null
+++ b/test/CodeGen/debug-info-vla.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin %s -o - | FileCheck %s
+
+// CHECK: metadata !{i32 {{.*}}, metadata {{.*}}, metadata !"vla", metadata {{.*}}, i32 7, metadata {{.*}}, i32 0, i32 0, i64 2} ; [ DW_TAG_auto_variable ]
+
+void testVLAwithSize(int s)
+{
+ int vla[s];
+ int i;
+ for (i = 0; i < s; i++) {
+ vla[i] = i*i;
+ }
+}
diff --git a/test/CodeGen/debug-line-1.c b/test/CodeGen/debug-line-1.c
index 00d4f42..0c2d185 100644
--- a/test/CodeGen/debug-line-1.c
+++ b/test/CodeGen/debug-line-1.c
@@ -4,7 +4,7 @@
// Check to make sure that we emit the block for the break so that we can count the line.
// CHECK: sw.bb: ; preds = %entry
-// CHECK: br label %sw.epilog, !dbg !21
+// CHECK: br label %sw.epilog, !dbg !19
extern int atoi(const char *);
diff --git a/test/CodeGen/fma-builtins.c b/test/CodeGen/fma-builtins.c
new file mode 100644
index 0000000..3424616
--- /dev/null
+++ b/test/CodeGen/fma-builtins.c
@@ -0,0 +1,166 @@
+// RUN: %clang_cc1 %s -O3 -triple=x86_64-apple-darwin -target-feature +fma -emit-llvm -o - | FileCheck %s
+
+// Don't include mm_malloc.h, it's system specific.
+#define __MM_MALLOC_H
+
+#include <immintrin.h>
+
+__m128 test_mm_fmadd_ps(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfmadd.ps
+ return _mm_fmadd_ps(a, b, c);
+}
+
+__m128d test_mm_fmadd_pd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfmadd.pd
+ return _mm_fmadd_pd(a, b, c);
+}
+
+__m128 test_mm_fmadd_ss(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfmadd.ss
+ return _mm_fmadd_ss(a, b, c);
+}
+
+__m128d test_mm_fmadd_sd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfmadd.sd
+ return _mm_fmadd_sd(a, b, c);
+}
+
+__m128 test_mm_fmsub_ps(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfmsub.ps
+ return _mm_fmsub_ps(a, b, c);
+}
+
+__m128d test_mm_fmsub_pd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfmsub.pd
+ return _mm_fmsub_pd(a, b, c);
+}
+
+__m128 test_mm_fmsub_ss(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfmsub.ss
+ return _mm_fmsub_ss(a, b, c);
+}
+
+__m128d test_mm_fmsub_sd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfmsub.sd
+ return _mm_fmsub_sd(a, b, c);
+}
+
+__m128 test_mm_fnmadd_ps(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfnmadd.ps
+ return _mm_fnmadd_ps(a, b, c);
+}
+
+__m128d test_mm_fnmadd_pd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfnmadd.pd
+ return _mm_fnmadd_pd(a, b, c);
+}
+
+__m128 test_mm_fnmadd_ss(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfnmadd.ss
+ return _mm_fnmadd_ss(a, b, c);
+}
+
+__m128d test_mm_fnmadd_sd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfnmadd.sd
+ return _mm_fnmadd_sd(a, b, c);
+}
+
+__m128 test_mm_fnmsub_ps(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfnmsub.ps
+ return _mm_fnmsub_ps(a, b, c);
+}
+
+__m128d test_mm_fnmsub_pd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfnmsub.pd
+ return _mm_fnmsub_pd(a, b, c);
+}
+
+__m128 test_mm_fnmsub_ss(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfnmsub.ss
+ return _mm_fnmsub_ss(a, b, c);
+}
+
+__m128d test_mm_fnmsub_sd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfnmsub.sd
+ return _mm_fnmsub_sd(a, b, c);
+}
+
+__m128 test_mm_fmaddsub_ps(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfmaddsub.ps
+ return _mm_fmaddsub_ps(a, b, c);
+}
+
+__m128d test_mm_fmaddsub_pd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfmaddsub.pd
+ return _mm_fmaddsub_pd(a, b, c);
+}
+
+__m128 test_mm_fmsubadd_ps(__m128 a, __m128 b, __m128 c) {
+ // CHECK: @llvm.x86.fma.vfmsubadd.ps
+ return _mm_fmsubadd_ps(a, b, c);
+}
+
+__m128d test_mm_fmsubadd_pd(__m128d a, __m128d b, __m128d c) {
+ // CHECK: @llvm.x86.fma.vfmsubadd.pd
+ return _mm_fmsubadd_pd(a, b, c);
+}
+
+__m256 test_mm256_fmadd_ps(__m256 a, __m256 b, __m256 c) {
+ // CHECK: @llvm.x86.fma.vfmadd.ps.256
+ return _mm256_fmadd_ps(a, b, c);
+}
+
+__m256d test_mm256_fmadd_pd(__m256d a, __m256d b, __m256d c) {
+ // CHECK: @llvm.x86.fma.vfmadd.pd.256
+ return _mm256_fmadd_pd(a, b, c);
+}
+
+__m256 test_mm256_fmsub_ps(__m256 a, __m256 b, __m256 c) {
+ // CHECK: @llvm.x86.fma.vfmsub.ps.256
+ return _mm256_fmsub_ps(a, b, c);
+}
+
+__m256d test_mm256_fmsub_pd(__m256d a, __m256d b, __m256d c) {
+ // CHECK: @llvm.x86.fma.vfmsub.pd.256
+ return _mm256_fmsub_pd(a, b, c);
+}
+
+__m256 test_mm256_fnmadd_ps(__m256 a, __m256 b, __m256 c) {
+ // CHECK: @llvm.x86.fma.vfnmadd.ps.256
+ return _mm256_fnmadd_ps(a, b, c);
+}
+
+__m256d test_mm256_fnmadd_pd(__m256d a, __m256d b, __m256d c) {
+ // CHECK: @llvm.x86.fma.vfnmadd.pd.256
+ return _mm256_fnmadd_pd(a, b, c);
+}
+
+__m256 test_mm256_fnmsub_ps(__m256 a, __m256 b, __m256 c) {
+ // CHECK: @llvm.x86.fma.vfnmsub.ps.256
+ return _mm256_fnmsub_ps(a, b, c);
+}
+
+__m256d test_mm256_fnmsub_pd(__m256d a, __m256d b, __m256d c) {
+ // CHECK: @llvm.x86.fma.vfnmsub.pd.256
+ return _mm256_fnmsub_pd(a, b, c);
+}
+
+__m256 test_mm256_fmaddsub_ps(__m256 a, __m256 b, __m256 c) {
+ // CHECK: @llvm.x86.fma.vfmaddsub.ps.256
+ return _mm256_fmaddsub_ps(a, b, c);
+}
+
+__m256d test_mm256_fmaddsub_pd(__m256d a, __m256d b, __m256d c) {
+ // CHECK: @llvm.x86.fma.vfmaddsub.pd.256
+ return _mm256_fmaddsub_pd(a, b, c);
+}
+
+__m256 test_mm256_fmsubadd_ps(__m256 a, __m256 b, __m256 c) {
+ // CHECK: @llvm.x86.fma.vfmsubadd.ps.256
+ return _mm256_fmsubadd_ps(a, b, c);
+}
+
+__m256d test_mm256_fmsubadd_pd(__m256d a, __m256d b, __m256d c) {
+ // CHECK: @llvm.x86.fma.vfmsubadd.pd.256
+ return _mm256_fmsubadd_pd(a, b, c);
+}
diff --git a/test/CodeGen/fma4-builtins.c b/test/CodeGen/fma4-builtins.c
index ddbaba7..b805e9a 100644
--- a/test/CodeGen/fma4-builtins.c
+++ b/test/CodeGen/fma4-builtins.c
@@ -6,161 +6,161 @@
#include <x86intrin.h>
__m128 test_mm_macc_ps(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfmadd.ps
+ // CHECK: @llvm.x86.fma.vfmadd.ps
return _mm_macc_ps(a, b, c);
}
__m128d test_mm_macc_pd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfmadd.pd
+ // CHECK: @llvm.x86.fma.vfmadd.pd
return _mm_macc_pd(a, b, c);
}
__m128 test_mm_macc_ss(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfmadd.ss
+ // CHECK: @llvm.x86.fma.vfmadd.ss
return _mm_macc_ss(a, b, c);
}
__m128d test_mm_macc_sd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfmadd.sd
+ // CHECK: @llvm.x86.fma.vfmadd.sd
return _mm_macc_sd(a, b, c);
}
__m128 test_mm_msub_ps(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfmsub.ps
+ // CHECK: @llvm.x86.fma.vfmsub.ps
return _mm_msub_ps(a, b, c);
}
__m128d test_mm_msub_pd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfmsub.pd
+ // CHECK: @llvm.x86.fma.vfmsub.pd
return _mm_msub_pd(a, b, c);
}
__m128 test_mm_msub_ss(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfmsub.ss
+ // CHECK: @llvm.x86.fma.vfmsub.ss
return _mm_msub_ss(a, b, c);
}
__m128d test_mm_msub_sd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfmsub.sd
+ // CHECK: @llvm.x86.fma.vfmsub.sd
return _mm_msub_sd(a, b, c);
}
__m128 test_mm_nmacc_ps(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfnmadd.ps
+ // CHECK: @llvm.x86.fma.vfnmadd.ps
return _mm_nmacc_ps(a, b, c);
}
__m128d test_mm_nmacc_pd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfnmadd.pd
+ // CHECK: @llvm.x86.fma.vfnmadd.pd
return _mm_nmacc_pd(a, b, c);
}
__m128 test_mm_nmacc_ss(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfnmadd.ss
+ // CHECK: @llvm.x86.fma.vfnmadd.ss
return _mm_nmacc_ss(a, b, c);
}
__m128d test_mm_nmacc_sd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfnmadd.sd
+ // CHECK: @llvm.x86.fma.vfnmadd.sd
return _mm_nmacc_sd(a, b, c);
}
__m128 test_mm_nmsub_ps(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfnmsub.ps
+ // CHECK: @llvm.x86.fma.vfnmsub.ps
return _mm_nmsub_ps(a, b, c);
}
__m128d test_mm_nmsub_pd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfnmsub.pd
+ // CHECK: @llvm.x86.fma.vfnmsub.pd
return _mm_nmsub_pd(a, b, c);
}
__m128 test_mm_nmsub_ss(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfnmsub.ss
+ // CHECK: @llvm.x86.fma.vfnmsub.ss
return _mm_nmsub_ss(a, b, c);
}
__m128d test_mm_nmsub_sd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfnmsub.sd
+ // CHECK: @llvm.x86.fma.vfnmsub.sd
return _mm_nmsub_sd(a, b, c);
}
__m128 test_mm_maddsub_ps(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfmaddsub.ps
+ // CHECK: @llvm.x86.fma.vfmaddsub.ps
return _mm_maddsub_ps(a, b, c);
}
__m128d test_mm_maddsub_pd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfmaddsub.pd
+ // CHECK: @llvm.x86.fma.vfmaddsub.pd
return _mm_maddsub_pd(a, b, c);
}
__m128 test_mm_msubadd_ps(__m128 a, __m128 b, __m128 c) {
- // CHECK: @llvm.x86.fma4.vfmsubadd.ps
+ // CHECK: @llvm.x86.fma.vfmsubadd.ps
return _mm_msubadd_ps(a, b, c);
}
__m128d test_mm_msubadd_pd(__m128d a, __m128d b, __m128d c) {
- // CHECK: @llvm.x86.fma4.vfmsubadd.pd
+ // CHECK: @llvm.x86.fma.vfmsubadd.pd
return _mm_msubadd_pd(a, b, c);
}
__m256 test_mm256_macc_ps(__m256 a, __m256 b, __m256 c) {
- // CHECK: @llvm.x86.fma4.vfmadd.ps.256
+ // CHECK: @llvm.x86.fma.vfmadd.ps.256
return _mm256_macc_ps(a, b, c);
}
__m256d test_mm256_macc_pd(__m256d a, __m256d b, __m256d c) {
- // CHECK: @llvm.x86.fma4.vfmadd.pd.256
+ // CHECK: @llvm.x86.fma.vfmadd.pd.256
return _mm256_macc_pd(a, b, c);
}
__m256 test_mm256_msub_ps(__m256 a, __m256 b, __m256 c) {
- // CHECK: @llvm.x86.fma4.vfmsub.ps.256
+ // CHECK: @llvm.x86.fma.vfmsub.ps.256
return _mm256_msub_ps(a, b, c);
}
__m256d test_mm256_msub_pd(__m256d a, __m256d b, __m256d c) {
- // CHECK: @llvm.x86.fma4.vfmsub.pd.256
+ // CHECK: @llvm.x86.fma.vfmsub.pd.256
return _mm256_msub_pd(a, b, c);
}
__m256 test_mm256_nmacc_ps(__m256 a, __m256 b, __m256 c) {
- // CHECK: @llvm.x86.fma4.vfnmadd.ps.256
+ // CHECK: @llvm.x86.fma.vfnmadd.ps.256
return _mm256_nmacc_ps(a, b, c);
}
__m256d test_mm256_nmacc_pd(__m256d a, __m256d b, __m256d c) {
- // CHECK: @llvm.x86.fma4.vfnmadd.pd.256
+ // CHECK: @llvm.x86.fma.vfnmadd.pd.256
return _mm256_nmacc_pd(a, b, c);
}
__m256 test_mm256_nmsub_ps(__m256 a, __m256 b, __m256 c) {
- // CHECK: @llvm.x86.fma4.vfnmsub.ps.256
+ // CHECK: @llvm.x86.fma.vfnmsub.ps.256
return _mm256_nmsub_ps(a, b, c);
}
__m256d test_mm256_nmsub_pd(__m256d a, __m256d b, __m256d c) {
- // CHECK: @llvm.x86.fma4.vfnmsub.pd.256
+ // CHECK: @llvm.x86.fma.vfnmsub.pd.256
return _mm256_nmsub_pd(a, b, c);
}
__m256 test_mm256_maddsub_ps(__m256 a, __m256 b, __m256 c) {
- // CHECK: @llvm.x86.fma4.vfmaddsub.ps.256
+ // CHECK: @llvm.x86.fma.vfmaddsub.ps.256
return _mm256_maddsub_ps(a, b, c);
}
__m256d test_mm256_maddsub_pd(__m256d a, __m256d b, __m256d c) {
- // CHECK: @llvm.x86.fma4.vfmaddsub.pd.256
+ // CHECK: @llvm.x86.fma.vfmaddsub.pd.256
return _mm256_maddsub_pd(a, b, c);
}
__m256 test_mm256_msubadd_ps(__m256 a, __m256 b, __m256 c) {
- // CHECK: @llvm.x86.fma4.vfmsubadd.ps.256
+ // CHECK: @llvm.x86.fma.vfmsubadd.ps.256
return _mm256_msubadd_ps(a, b, c);
}
__m256d test_mm256_msubadd_pd(__m256d a, __m256d b, __m256d c) {
- // CHECK: @llvm.x86.fma4.vfmsubadd.pd.256
+ // CHECK: @llvm.x86.fma.vfmsubadd.pd.256
return _mm256_msubadd_pd(a, b, c);
}
diff --git a/test/CodeGen/forceinline.c b/test/CodeGen/forceinline.c
new file mode 100644
index 0000000..9a21c0d
--- /dev/null
+++ b/test/CodeGen/forceinline.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple i686-win32 -emit-llvm -fms-extensions < %s | FileCheck %s
+
+void bar() {
+}
+
+// CHECK-NOT: foo
+__forceinline void foo() {
+ bar();
+}
+
+void i_want_bar() {
+// CHECK: call void @bar
+ foo();
+}
diff --git a/test/CodeGen/fp-contract.c b/test/CodeGen/fp-contract.c
new file mode 100644
index 0000000..eb95f1e
--- /dev/null
+++ b/test/CodeGen/fp-contract.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -O3 -ffp-contract=fast -triple=powerpc-apple-darwin10 -S -o - %s | FileCheck %s
+// REQUIRES: ppc32-registered-target
+
+float fma_test1(float a, float b, float c) {
+// CHECK: fmadds
+ float x = a * b;
+ float y = x + c;
+ return y;
+}
diff --git a/test/CodeGen/fp16-ops.c b/test/CodeGen/fp16-ops.c
index cbbfb88..e506513 100644
--- a/test/CodeGen/fp16-ops.c
+++ b/test/CodeGen/fp16-ops.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -emit-llvm -o - -triple arm-none-linux-gnueabi %s | FileCheck %s
typedef unsigned cond_t;
diff --git a/test/CodeGen/func-aligned.c b/test/CodeGen/func-aligned.c
index f8a4a29..64b7293 100644
--- a/test/CodeGen/func-aligned.c
+++ b/test/CodeGen/func-aligned.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s
// rdar://7270273
void foo() __attribute__((aligned (64)));
diff --git a/test/CodeGen/func-in-block.c b/test/CodeGen/func-in-block.c
index 1900135..503695f 100644
--- a/test/CodeGen/func-in-block.c
+++ b/test/CodeGen/func-in-block.c
@@ -15,5 +15,5 @@ int main()
return 0; // not reached
}
-// CHECK: @__func__.__main_block_invoke_0 = private unnamed_addr constant [22 x i8] c"__main_block_invoke_0\00"
-// CHECK: call void @PRINTF({{.*}}@__func__.__main_block_invoke_
+// CHECK: @__func__.__main_block_invoke = private unnamed_addr constant [20 x i8] c"__main_block_invoke\00"
+// CHECK: call void @PRINTF({{.*}}@__func__.__main_block_invoke
diff --git a/test/CodeGen/func-return-member.c b/test/CodeGen/func-return-member.c
index 8c55a96..14ecac5 100644
--- a/test/CodeGen/func-return-member.c
+++ b/test/CodeGen/func-return-member.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck %s
struct frk { float _Complex c; int x; };
struct faz { struct frk f; };
diff --git a/test/CodeGen/incomplete-function-type.c b/test/CodeGen/incomplete-function-type.c
index 0ba6633..b630947 100644
--- a/test/CodeGen/incomplete-function-type.c
+++ b/test/CodeGen/incomplete-function-type.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck %s
// CHECK: ModuleID
// CHECK-NOT: opaque
// CHECK: define void @f0
diff --git a/test/CodeGen/integer-overflow.c b/test/CodeGen/integer-overflow.c
index 1d46065..d7fff4e 100644
--- a/test/CodeGen/integer-overflow.c
+++ b/test/CodeGen/integer-overflow.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
-// RUN: %clang_cc1 %s -emit-llvm -o - -fwrapv | FileCheck %s --check-prefix=WRAPV
-// RUN: %clang_cc1 %s -emit-llvm -o - -ftrapv | FileCheck %s --check-prefix=TRAPV
-// RUN: %clang_cc1 %s -emit-llvm -o - -ftrapv -ftrapv-handler foo | FileCheck %s --check-prefix=TRAPV_HANDLER
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fwrapv | FileCheck %s --check-prefix=WRAPV
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -ftrapv | FileCheck %s --check-prefix=TRAPV
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -ftrapv -ftrapv-handler foo | FileCheck %s --check-prefix=TRAPV_HANDLER
// Tests for signed integer overflow stuff.
diff --git a/test/CodeGen/libcalls.c b/test/CodeGen/libcalls.c
index 458c591..ec895ac 100644
--- a/test/CodeGen/libcalls.c
+++ b/test/CodeGen/libcalls.c
@@ -73,3 +73,48 @@ void test_fma(float a0, double a1, long double a2) {
// CHECK-NO: declare float @llvm.fma.f32(float, float, float) nounwind readnone
// CHECK-NO: declare double @llvm.fma.f64(double, double, double) nounwind readnone
// CHECK-NO: declare x86_fp80 @llvm.fma.f80(x86_fp80, x86_fp80, x86_fp80) nounwind readnone
+
+// Just checking to make sure these library functions are marked readnone
+void test_builtins(double d, float f, long double ld) {
+// CHEC-NO: @test_builtins
+// CHEC-YES: @test_builtins
+ double atan_ = atan(d);
+ long double atanl_ = atanl(ld);
+ float atanf_ = atanf(f);
+// CHECK-NO: declare double @atan(double) nounwind readnone
+// CHECK-NO: declare x86_fp80 @atanl(x86_fp80) nounwind readnone
+// CHECK-NO: declare float @atanf(float) nounwind readnone
+// CHECK-YES-NOT: declare double @atan(double) nounwind readnone
+// CHECK-YES-NOT: declare x86_fp80 @atanl(x86_fp80) nounwind readnone
+// CHECK-YES-NOT: declare float @atanf(float) nounwind readnone
+
+ double atan2_ = atan2(d, 2);
+ long double atan2l_ = atan2l(ld, ld);
+ float atan2f_ = atan2f(f, f);
+// CHECK-NO: declare double @atan2(double, double) nounwind readnone
+// CHECK-NO: declare x86_fp80 @atan2l(x86_fp80, x86_fp80) nounwind readnone
+// CHECK-NO: declare float @atan2f(float, float) nounwind readnone
+// CHECK-YES-NOT: declare double @atan2(double, double) nounwind readnone
+// CHECK-YES-NOT: declare x86_fp80 @atan2l(x86_fp80, x86_fp80) nounwind readnone
+// CHECK-YES-NOT: declare float @atan2f(float, float) nounwind readnone
+
+ double exp_ = exp(d);
+ long double expl_ = expl(ld);
+ float expf_ = expf(f);
+// CHECK-NO: declare double @exp(double) nounwind readnone
+// CHECK-NO: declare x86_fp80 @expl(x86_fp80) nounwind readnone
+// CHECK-NO: declare float @expf(float) nounwind readnone
+// CHECK-YES-NOT: declare double @exp(double) nounwind readnone
+// CHECK-YES-NOT: declare x86_fp80 @expl(x86_fp80) nounwind readnone
+// CHECK-YES-NOT: declare float @expf(float) nounwind readnone
+
+ double log_ = log(d);
+ long double logl_ = logl(ld);
+ float logf_ = logf(f);
+// CHECK-NO: declare double @log(double) nounwind readnone
+// CHECK-NO: declare x86_fp80 @logl(x86_fp80) nounwind readnone
+// CHECK-NO: declare float @logf(float) nounwind readnone
+// CHECK-YES-NOT: declare double @log(double) nounwind readnone
+// CHECK-YES-NOT: declare x86_fp80 @logl(x86_fp80) nounwind readnone
+// CHECK-YES-NOT: declare float @logf(float) nounwind readnone
+}
diff --git a/test/CodeGen/mips-byval-arg.c b/test/CodeGen/mips-byval-arg.c
new file mode 100644
index 0000000..4e5f41a
--- /dev/null
+++ b/test/CodeGen/mips-byval-arg.c
@@ -0,0 +1,15 @@
+// RUN: %clang -target mipsel-unknown-linux -ccc-clang-archs mipsel -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32
+// RUN: %clang -target mips64el-unknown-linux -ccc-clang-archs mips64el -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64
+
+typedef struct {
+ float f[3];
+} S0;
+
+extern void foo2(S0);
+
+// O32: define void @foo1(i32 %a0.coerce0, i32 %a0.coerce1, i32 %a0.coerce2)
+// N64: define void @foo1(i64 %a0.coerce0, i32 %a0.coerce1)
+
+void foo1(S0 a0) {
+ foo2(a0);
+}
diff --git a/test/CodeGen/mips-vector-arg.c b/test/CodeGen/mips-vector-arg.c
new file mode 100644
index 0000000..39998d9
--- /dev/null
+++ b/test/CodeGen/mips-vector-arg.c
@@ -0,0 +1,28 @@
+// RUN: %clang -target mipsel-unknown-linux -ccc-clang-archs mipsel -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32
+// RUN: %clang -target mips64el-unknown-linux -ccc-clang-archs mips64el -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64
+
+// check that
+// 1. vector arguments are passed in integer registers
+// 2. argument alignment is no larger than 8-byte for O32 and 16-byte for N64.
+
+typedef float v4sf __attribute__ ((__vector_size__ (16)));
+typedef int v4i32 __attribute__ ((__vector_size__ (16)));
+
+// O32: define void @test_v4sf(i32 %a1.coerce0, i32 %a1.coerce1, i32 %a1.coerce2, i32 %a1.coerce3, i32 %a2, i32, i32 %a3.coerce0, i32 %a3.coerce1, i32 %a3.coerce2, i32 %a3.coerce3) nounwind
+// O32: declare i32 @test_v4sf_2(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32)
+// N64: define void @test_v4sf(i64 %a1.coerce0, i64 %a1.coerce1, i32 %a2, i64, i64 %a3.coerce0, i64 %a3.coerce1) nounwind
+// N64: declare i32 @test_v4sf_2(i64, i64, i32, i64, i64, i64)
+extern test_v4sf_2(v4sf, int, v4sf);
+void test_v4sf(v4sf a1, int a2, v4sf a3) {
+ test_v4sf_2(a3, a2, a1);
+}
+
+// O32: define void @test_v4i32(i32 %a1.coerce0, i32 %a1.coerce1, i32 %a1.coerce2, i32 %a1.coerce3, i32 %a2, i32, i32 %a3.coerce0, i32 %a3.coerce1, i32 %a3.coerce2, i32 %a3.coerce3) nounwind
+// O32: declare i32 @test_v4i32_2(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32)
+// N64: define void @test_v4i32(i64 %a1.coerce0, i64 %a1.coerce1, i32 %a2, i64, i64 %a3.coerce0, i64 %a3.coerce1) nounwind
+// N64: declare i32 @test_v4i32_2(i64, i64, i32, i64, i64, i64)
+extern test_v4i32_2(v4i32, int, v4i32);
+void test_v4i32(v4i32 a1, int a2, v4i32 a3) {
+ test_v4i32_2(a3, a2, a1);
+}
+
diff --git a/test/CodeGen/mips-vector-return.c b/test/CodeGen/mips-vector-return.c
new file mode 100644
index 0000000..12e71fa
--- /dev/null
+++ b/test/CodeGen/mips-vector-return.c
@@ -0,0 +1,31 @@
+// RUN: %clang -target mipsel-unknown-linux -ccc-clang-archs mipsel -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32
+// RUN: %clang -target mips64el-unknown-linux -ccc-clang-archs mips64el -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64
+
+// vectors larger than 16-bytes are returned via the hidden pointer argument.
+// N64/N32 returns vectors whose size is equal to or smaller than 16-bytes in
+// integer registers.
+typedef float v4sf __attribute__ ((__vector_size__ (16)));
+typedef double v4df __attribute__ ((__vector_size__ (32)));
+typedef int v4i32 __attribute__ ((__vector_size__ (16)));
+
+// O32: define void @test_v4sf(<4 x float>* noalias nocapture sret
+// N64: define { i64, i64 } @test_v4sf
+v4sf test_v4sf(float a) {
+ return (v4sf){0.0f, a, 0.0f, 0.0f};
+}
+
+// O32: define void @test_v4df(<4 x double>* noalias nocapture sret
+// N64: define void @test_v4df(<4 x double>* noalias nocapture sret
+v4df test_v4df(double a) {
+ return (v4df){0.0, a, 0.0, 0.0};
+}
+
+// O32 returns integer vectors whose size is equal to or smaller than 16-bytes
+// in integer registers.
+//
+// O32: define { i32, i32, i32, i32 } @test_v4i32
+// N64: define { i64, i64 } @test_v4i32
+v4i32 test_v4i32(int a) {
+ return (v4i32){0, a, 0, 0};
+}
+
diff --git a/test/CodeGen/mips64-class-return.cpp b/test/CodeGen/mips64-class-return.cpp
index dc9ec0f..8e32d5c 100644
--- a/test/CodeGen/mips64-class-return.cpp
+++ b/test/CodeGen/mips64-class-return.cpp
@@ -39,7 +39,7 @@ void foo3(D2 a0) {
gd2 = a0;
}
-// CHECK: define void @_Z4foo42D0(%class.D0* nocapture byval %a0)
+// CHECK: define void @_Z4foo42D0(i64 %a0.coerce0, i64 %a0.coerce1)
void foo4(D0 a0) {
gd0 = a0;
}
diff --git a/test/CodeGen/ms-declspecs.c b/test/CodeGen/ms-declspecs.c
index d3235ae..91862a7 100644
--- a/test/CodeGen/ms-declspecs.c
+++ b/test/CodeGen/ms-declspecs.c
@@ -1,5 +1,13 @@
// RUN: %clang_cc1 -triple i386-pc-win32 %s -emit-llvm -fms-compatibility -o - | FileCheck %s
+struct __declspec(align(16)) S {
+ char x;
+};
+union { struct S s; } u;
+
+// CHECK: @u = {{.*}}zeroinitializer, align 16
+
+
// CHECK: define void @t3() nounwind noinline naked {
__declspec(naked) void t3() {}
diff --git a/test/CodeGen/ms-inline-asm.c b/test/CodeGen/ms-inline-asm.c
new file mode 100644
index 0000000..8c3e5f7
--- /dev/null
+++ b/test/CodeGen/ms-inline-asm.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -O0 -fms-extensions -fenable-experimental-ms-inline-asm -w -emit-llvm -o - | FileCheck %s
+
+void t1() {
+// CHECK: @t1
+// CHECK: call void asm sideeffect "", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect
+// CHECK: ret void
+ __asm {}
+}
+
+void t2() {
+// CHECK: @t2
+// CHECK: call void asm sideeffect "nop\0Anop\0Anop", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect
+// CHECK: ret void
+ __asm nop
+ __asm nop
+ __asm nop
+}
+
+void t3() {
+// CHECK: @t3
+// CHECK: call void asm sideeffect "nop\0Anop\0Anop", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect
+// CHECK: ret void
+ __asm nop __asm nop __asm nop
+}
+
+void t4(void) {
+// CHECK: @t4
+// CHECK: call void asm sideeffect "mov ebx, eax\0Amov ecx, ebx", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect
+// CHECK: ret void
+ __asm mov ebx, eax
+ __asm mov ecx, ebx
+}
+
+void t5(void) {
+// CHECK: @t5
+// CHECK: call void asm sideeffect "mov ebx, eax\0Amov ecx, ebx", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect
+// CHECK: ret void
+ __asm mov ebx, eax __asm mov ecx, ebx
+}
+
diff --git a/test/CodeGen/no-common.c b/test/CodeGen/no-common.c
index 7beefc7..8d2c4d7 100644
--- a/test/CodeGen/no-common.c
+++ b/test/CodeGen/no-common.c
@@ -12,4 +12,4 @@ fn_t ABC __attribute__ ((nocommon));
// CHECK-DEFAULT: @y = common global
// CHECK-NOCOMMON: @y = common global
-int y __attribute__((common)); \ No newline at end of file
+int y __attribute__((common));
diff --git a/test/CodeGen/nobuiltin.c b/test/CodeGen/nobuiltin.c
new file mode 100644
index 0000000..0a8e8bb
--- /dev/null
+++ b/test/CodeGen/nobuiltin.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -fno-builtin -O1 -S -o - %s | FileCheck %s
+
+void PR13497() {
+ char content[2];
+ // make sure we don't optimize this call to strcpy()
+ // CHECK: __strcpy_chk
+ __builtin___strcpy_chk(content, "", 1);
+}
diff --git a/test/CodeGen/ptx-cc.c b/test/CodeGen/nvptx-cc.c
index 2212d42..1c0d943 100644
--- a/test/CodeGen/ptx-cc.c
+++ b/test/CodeGen/nvptx-cc.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple ptx32-unknown-unknown -O3 -S -o %t %s -emit-llvm
-// RUN: %clang_cc1 -triple ptx64-unknown-unknown -O3 -S -o %t %s -emit-llvm
+// RUN: %clang_cc1 -triple nvptx-unknown-unknown -O3 -S -o %t %s -emit-llvm
+// RUN: %clang_cc1 -triple nvptx64-unknown-unknown -O3 -S -o %t %s -emit-llvm
-// Just make sure Clang uses the proper calling convention for the PTX back-end.
+// Just make sure Clang uses the proper calling convention for the NVPTX back-end.
// If something is wrong, the back-end will fail.
void foo(float* a,
float* b) {
diff --git a/test/CodeGen/nvptx-inlineasm.c b/test/CodeGen/nvptx-inlineasm.c
new file mode 100644
index 0000000..860b50f
--- /dev/null
+++ b/test/CodeGen/nvptx-inlineasm.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple nvptx-unknown-unknown -O3 -S -o - %s -emit-llvm | FileCheck %s
+// RUN: %clang_cc1 -triple nvptx64-unknown-unknown -O3 -S -o - %s -emit-llvm | FileCheck %s
+
+int bar(int a) {
+ int result;
+ // CHECK: call i32 asm sideeffect "{ {{.*}}
+ asm __volatile__ ("{ \n\t"
+ ".reg .pred \t%%p1; \n\t"
+ ".reg .pred \t%%p2; \n\t"
+ "setp.ne.u32 \t%%p1, %1, 0; \n\t"
+ "vote.any.pred \t%%p2, %%p1; \n\t"
+ "selp.s32 \t%0, 1, 0, %%p2; \n\t"
+ "}" : "=r"(result) : "r"(a));
+ return result;
+}
diff --git a/test/CodeGen/object-size.c b/test/CodeGen/object-size.c
index 1f16d02..f6c7db8 100644
--- a/test/CodeGen/object-size.c
+++ b/test/CodeGen/object-size.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck %s
#define strcpy(dest, src) \
((__builtin_object_size(dest, 0) != -1ULL) \
@@ -55,7 +55,10 @@ void test6() {
// CHECK: define void @test7
void test7() {
int i;
- // CHECK: = call i64 @llvm.objectsize.i64(i8* {{.*}}@gbuf{{.*}}, i1 false)
+ // Ensure we only evaluate the side-effect once.
+ // CHECK: = add
+ // CHECK-NOT: = add
+ // CHECK: = call i8* @__strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i64 63)
strcpy((++i, gbuf), "Hi there");
}
@@ -124,6 +127,7 @@ void test16() {
strcpy(gp += 1, "Hi there");
}
+// CHECK: @test17
void test17() {
// CHECK: store i32 -1
gi = __builtin_object_size(gp++, 0);
@@ -134,3 +138,11 @@ void test17() {
// CHECK: store i32 0
gi = __builtin_object_size(gp++, 3);
}
+
+// CHECK: @test18
+unsigned test18(int cond) {
+ int a[4], b[4];
+ // CHECK: phi i32*
+ // CHECK: call i64 @llvm.objectsize.i64
+ return __builtin_object_size(cond ? a : b, 0);
+}
diff --git a/test/CodeGen/packed-nest-unpacked.c b/test/CodeGen/packed-nest-unpacked.c
index 0ccc0c4..6097e3f 100644
--- a/test/CodeGen/packed-nest-unpacked.c
+++ b/test/CodeGen/packed-nest-unpacked.c
@@ -45,3 +45,21 @@ void test6() {
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* bitcast (%struct.X* getelementptr inbounds (%struct.Y* @g, i32 0, i32 1) to i8*), i8* %{{.*}}, i64 24, i32 1, i1 false)
g.y = foo();
}
+
+
+struct XBitfield {
+ unsigned b1 : 10;
+ unsigned b2 : 12;
+ unsigned b3 : 10;
+};
+struct YBitfield {
+ char x;
+ struct XBitfield y;
+} __attribute((packed));
+struct YBitfield gbitfield;
+
+unsigned test7() {
+ // CHECK: @test7
+ // CHECK: load i32* bitcast (%struct.XBitfield* getelementptr inbounds (%struct.YBitfield* @gbitfield, i32 0, i32 1) to i32*), align 1
+ return gbitfield.y.b2;
+}
diff --git a/test/CodeGen/pclmul-builtins.c b/test/CodeGen/pclmul-builtins.c
new file mode 100644
index 0000000..cb0af28
--- /dev/null
+++ b/test/CodeGen/pclmul-builtins.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 %s -O3 -triple=x86_64-apple-darwin -target-feature +pclmul -emit-llvm -o - | FileCheck %s
+
+// Don't include mm_malloc.h, it's system specific.
+#define __MM_MALLOC_H
+
+#include <wmmintrin.h>
+
+__m128i test_mm_clmulepi64_si128(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.pclmulqdq
+ return _mm_clmulepi64_si128(a, b, 0);
+}
diff --git a/test/CodeGen/powerpc_types.c b/test/CodeGen/powerpc_types.c
index b7d0f5d..b4de318 100644
--- a/test/CodeGen/powerpc_types.c
+++ b/test/CodeGen/powerpc_types.c
@@ -1,3 +1,4 @@
+// REQUIRES: ppc32-registered-target
// RUN: %clang_cc1 -triple powerpc-unknown-freebsd -emit-llvm -o - %s| FileCheck -check-prefix=SVR4-CHECK %s
#include <stdarg.h>
diff --git a/test/CodeGen/pr12251.c b/test/CodeGen/pr12251.c
index a644bb7..b017131 100644
--- a/test/CodeGen/pr12251.c
+++ b/test/CodeGen/pr12251.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -O1 -relaxed-aliasing -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -O1 -relaxed-aliasing -o - | FileCheck %s
enum e1 {e1_a = -1 };
enum e1 g1(enum e1 *x) {
diff --git a/test/CodeGen/pr13168.c b/test/CodeGen/pr13168.c
new file mode 100644
index 0000000..a848af4
--- /dev/null
+++ b/test/CodeGen/pr13168.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -emit-llvm %s -o -
+
+typedef int (*_MD_Open64)(int oflag, ...);
+_MD_Open64 _open64;
+void PR_OpenFile(int mode) {
+_open64(0, mode);
+}
diff --git a/test/CodeGen/pr5406.c b/test/CodeGen/pr5406.c
index da74d6b..2d19822 100644
--- a/test/CodeGen/pr5406.c
+++ b/test/CodeGen/pr5406.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 %s -emit-llvm -triple arm-apple-darwin -o - | FileCheck %s
// PR 5406
diff --git a/test/CodeGen/pragma-visibility.c b/test/CodeGen/pragma-visibility.c
index 16460a2..a7fceb3 100644
--- a/test/CodeGen/pragma-visibility.c
+++ b/test/CodeGen/pragma-visibility.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck %s
#pragma GCC visibility push(hidden)
int x = 2;
diff --git a/test/CodeGen/rdrand-builtins.c b/test/CodeGen/rdrand-builtins.c
new file mode 100644
index 0000000..b7970f4
--- /dev/null
+++ b/test/CodeGen/rdrand-builtins.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -target-feature +rdrnd -emit-llvm -S -emit-llvm -o - %s | FileCheck %s
+
+// Don't include mm_malloc.h, it's system specific.
+#define __MM_MALLOC_H
+
+#include <immintrin.h>
+
+int rdrand16(unsigned short *p) {
+ return _rdrand16_step(p);
+// CHECK: @rdrand16
+// CHECK: call { i16, i32 } @llvm.x86.rdrand.16
+// CHECK: store i16
+}
+
+int rdrand32(unsigned *p) {
+ return _rdrand32_step(p);
+// CHECK: @rdrand32
+// CHECK: call { i32, i32 } @llvm.x86.rdrand.32
+// CHECK: store i32
+}
+
+int rdrand64(unsigned long long *p) {
+ return _rdrand64_step(p);
+// CHECK: @rdrand64
+// CHECK: call { i64, i32 } @llvm.x86.rdrand.64
+// CHECK: store i64
+}
diff --git a/test/CodeGen/regparm-flag.c b/test/CodeGen/regparm-flag.c
index 8ecf539..1330663 100644
--- a/test/CodeGen/regparm-flag.c
+++ b/test/CodeGen/regparm-flag.c
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -mregparm 4 %s -emit-llvm -o %t
-// RUN: FileCheck < %t %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -mregparm 4 %s -emit-llvm -o - | FileCheck %s
void f1(int a, int b, int c, int d,
int e, int f, int g, int h);
diff --git a/test/CodeGen/regparm-struct.c b/test/CodeGen/regparm-struct.c
new file mode 100644
index 0000000..b319012
--- /dev/null
+++ b/test/CodeGen/regparm-struct.c
@@ -0,0 +1,177 @@
+// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s
+
+__attribute__((regparm(3))) void f1(int a, int b, int c, int d);
+// CHECK: declare void @f1(i32 inreg, i32 inreg, i32 inreg, i32)
+void g1() {
+ f1(41, 42, 43, 44);
+}
+
+struct s1 {
+ int x1;
+};
+__attribute__((regparm(3))) void f2(int a, int b, struct s1 c, int d);
+// CHECK: declare void @f2(i32 inreg, i32 inreg, i32 inreg, i32)
+void g2() {
+ struct s1 x = {43};
+ f2(41, 42, x, 44);
+}
+
+struct s2 {
+ int x1;
+ int x2;
+};
+__attribute__((regparm(3))) void f3(int a, int b, struct s2 c, int d);
+// CHECK: declare void @f3(i32 inreg, i32 inreg, i32, i32, i32)
+void g3() {
+ struct s2 x = {43, 44};
+ f3(41, 42, x, 45);
+}
+__attribute__((regparm(3))) void f4(int a, struct s2 b, int c);
+// CHECK: declare void @f4(i32 inreg, i32 inreg, i32 inreg, i32)
+void g4() {
+ struct s2 x = {42, 43};
+ f4(41, x, 44);
+}
+
+struct s3 {
+ int x1;
+ int x2;
+ int x3;
+};
+__attribute__((regparm(3))) void f5(int a, struct s3 b, int c);
+// CHECK: declare void @f5(i32 inreg, i32, i32, i32, i32)
+void g5() {
+ struct s3 x = {42, 43, 44};
+ f5(41, x, 45);
+}
+__attribute__((regparm(3))) void f6(struct s3 a, int b);
+// CHECK: declare void @f6(i32 inreg, i32 inreg, i32 inreg, i32)
+void g6() {
+ struct s3 x = {41, 42, 43};
+ f6(x, 44);
+}
+
+struct s4 {
+ int x1;
+ int x2;
+ int x3;
+ int x4;
+};
+__attribute__((regparm(3))) void f7(struct s4 a, int b);
+// CHECK: declare void @f7(i32, i32, i32, i32, i32)
+void g7() {
+ struct s4 x = {41, 42, 43, 44};
+ f7(x, 45);
+}
+
+__attribute__((regparm(3))) void f8(float a, int b);
+// CHECK: declare void @f8(float, i32 inreg)
+void g8(void) {
+ f8(41, 42);
+}
+
+struct s5 {
+ float x1;
+};
+__attribute__((regparm(3))) void f9(struct s5 a, int b);
+// CHECK: declare void @f9(float, i32 inreg)
+void g9(void) {
+ struct s5 x = {41};
+ f9(x, 42);
+}
+
+struct s6 {
+ float x1;
+ int x2;
+};
+__attribute__((regparm(3))) void f10(struct s6 a, int b);
+// CHECK: declare void @f10(i32 inreg, i32 inreg, i32 inreg)
+void g10(void) {
+ struct s6 x = {41, 42};
+ f10(x, 43);
+}
+
+struct s7 {
+ float x1;
+ int x2;
+ float x3;
+};
+__attribute__((regparm(3))) void f11(struct s7 a, int b);
+// CHECK: declare void @f11(i32 inreg, i32 inreg, i32 inreg, i32)
+void g11(void) {
+ struct s7 x = {41, 42, 43};
+ f11(x, 44);
+}
+
+struct s8 {
+ float x1;
+ float x2;
+};
+__attribute__((regparm(3))) void f12(struct s8 a, int b);
+// CHECK: declare void @f12(i32 inreg, i32 inreg, i32 inreg)
+void g12(void) {
+ struct s8 x = {41, 42};
+ f12(x, 43);
+}
+
+struct s9 {
+ float x1;
+ float x2;
+ float x3;
+};
+__attribute__((regparm(3))) void f13(struct s9 a, int b);
+// CHECK: declare void @f13(i32 inreg, i32 inreg, i32 inreg, i32)
+void g13(void) {
+ struct s9 x = {41, 42, 43};
+ f13(x, 44);
+}
+
+struct s10 {
+ double x1;
+};
+__attribute__((regparm(3))) void f14(struct s10 a, int b, int c);
+// CHECK: declare void @f14(double, i32 inreg, i32 inreg)
+void g14(void) {
+ struct s10 x = { 41 };
+ f14(x, 42, 43);
+}
+
+struct s11 {
+ double x1;
+ double x2;
+};
+__attribute__((regparm(3))) void f15(struct s11 a, int b);
+// CHECK: declare void @f15(double, double, i32)
+void g15(void) {
+ struct s11 x = { 41, 42 };
+ f15(x, 43);
+}
+
+struct s12 {
+ double x1;
+ float x2;
+};
+__attribute__((regparm(3))) void f16(struct s12 a, int b);
+// CHECK: declare void @f16(i32 inreg, i32 inreg, i32 inreg, i32)
+void g16(void) {
+ struct s12 x = { 41, 42 };
+ f16(x, 43);
+}
+
+__attribute__((regparm(3))) struct s12 f17(int a, int b, int c);
+// CHECK: declare void @f17(%struct.s12* inreg sret, i32 inreg, i32 inreg, i32)
+void g17(void) {
+ f17(41, 42, 43);
+}
+
+struct s13 {
+ struct inner {
+ float x;
+ } y;
+};
+__attribute__((regparm(3))) void f18(struct s13 a, int b, int c, int d);
+// CHECK: declare void @f18(%struct.s13* byval align 4, i32 inreg, i32 inreg, i32 inreg)
+void g18(void) {
+ struct s13 x = {{41}};
+ f18(x, 42, 43, 44);
+}
diff --git a/test/CodeGen/sse-builtins.c b/test/CodeGen/sse-builtins.c
index 2d57425..0e48560 100644
--- a/test/CodeGen/sse-builtins.c
+++ b/test/CodeGen/sse-builtins.c
@@ -151,3 +151,9 @@ __m128d test_mm_round_sd(__m128d x, __m128d y) {
// CHECK: @llvm.x86.sse41.round.sd
return _mm_round_sd(x, y, 2);
}
+
+void test_storel_epi64(__m128i x, void* y) {
+ // CHECK: define void @test_storel_epi64
+ // CHECK: store {{.*}} i64* {{.*}}, align 1{{$}}
+ _mm_storel_epi64(y, x);
+}
diff --git a/test/CodeGen/sse4a-builtins.c b/test/CodeGen/sse4a-builtins.c
new file mode 100644
index 0000000..e1d7e8f
--- /dev/null
+++ b/test/CodeGen/sse4a-builtins.c
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -ffreestanding -triple i386-apple-darwin9 -target-cpu pentium4 -target-feature +sse4a -g -emit-llvm %s -o - | FileCheck %s
+
+#include <ammintrin.h>
+
+__m128i test_extracti_si64(__m128i x) {
+ return _mm_extracti_si64(x, 3, 2);
+// CHECK: @test_extracti_si64
+// CHECK: @llvm.x86.sse4a.extrqi(<2 x i64> %{{[^,]+}}, i8 3, i8 2)
+}
+
+__m128i test_extract_si64(__m128i x, __m128i y) {
+ return _mm_extract_si64(x, y);
+// CHECK: @test_extract_si64
+// CHECK: @llvm.x86.sse4a.extrq(<2 x i64> %{{[^,]+}}, <16 x i8> %{{[^,]+}})
+}
+
+__m128i test_inserti_si64(__m128i x, __m128i y) {
+ return _mm_inserti_si64(x, y, 5, 6);
+// CHECK: @test_inserti_si64
+// CHECK: @llvm.x86.sse4a.insertqi(<2 x i64> %{{[^,]+}}, <2 x i64> %{{[^,]+}}, i8 5, i8 6)
+}
+
+__m128i test_insert_si64(__m128i x, __m128i y) {
+ return _mm_insert_si64(x, y);
+// CHECK: @test_insert_si64
+// CHECK: @llvm.x86.sse4a.insertq(<2 x i64> %{{[^,]+}}, <2 x i64> %{{[^,]+}})
+}
+
+void test_stream_sd(double *p, __m128d a) {
+ _mm_stream_sd(p, a);
+// CHECK: @test_stream_sd
+// CHECK: @llvm.x86.sse4a.movnt.sd(i8* %{{[^,]+}}, <2 x double> %{{[^,]+}})
+}
+
+void test_stream_ss(float *p, __m128 a) {
+ _mm_stream_ss(p, a);
+// CHECK: @test_stream_ss
+// CHECK: @llvm.x86.sse4a.movnt.ss(i8* %{{[^,]+}}, <4 x float> %{{[^,]+}})
+}
diff --git a/test/CodeGen/struct-init.c b/test/CodeGen/struct-init.c
index 6247729..5273138 100644
--- a/test/CodeGen/struct-init.c
+++ b/test/CodeGen/struct-init.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -S -triple armv7-apple-darwin %s -emit-llvm -o - | FileCheck %s
typedef struct _zend_ini_entry zend_ini_entry;
diff --git a/test/CodeGen/struct-matching-constraint.c b/test/CodeGen/struct-matching-constraint.c
index 40c444f..bdd11c8 100644
--- a/test/CodeGen/struct-matching-constraint.c
+++ b/test/CodeGen/struct-matching-constraint.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -S -emit-llvm -triple armv7a-apple-darwin %s -o /dev/null
typedef unsigned short uint16_t;
typedef __attribute__((neon_vector_type(8))) uint16_t uint16x8_t;
diff --git a/test/CodeGen/tbaa-for-vptr.cpp b/test/CodeGen/tbaa-for-vptr.cpp
index 5ce6bf3..b9a68fe 100644
--- a/test/CodeGen/tbaa-for-vptr.cpp
+++ b/test/CodeGen/tbaa-for-vptr.cpp
@@ -1,5 +1,13 @@
+// RUN: %clang_cc1 -emit-llvm -o - -O0 -fthread-sanitizer %s | FileCheck %s
// RUN: %clang_cc1 -emit-llvm -o - -O1 %s | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -o - -O1 -relaxed-aliasing -fthread-sanitizer %s | FileCheck %s
+//
+// RUN: %clang_cc1 -emit-llvm -o - -O0 %s | FileCheck %s --check-prefix=NOTBAA
+// RUN: %clang_cc1 -emit-llvm -o - -O2 -relaxed-aliasing %s | FileCheck %s --check-prefix=NOTBAA
+//
// Check that we generate TBAA for vtable pointer loads and stores.
+// When -fthread-sanitizer is used TBAA should be generated at all opt levels
+// even if -relaxed-aliasing is present.
struct A {
virtual int foo() const ;
virtual ~A();
@@ -15,5 +23,5 @@ void CallFoo(A *a) {
// CHECK: %{{.*}} = load {{.*}} !tbaa !0
// CHECK: store {{.*}} !tbaa !0
-// CHECK: !0 = metadata !{metadata !"vtable pointer", metadata !1}
-// CHECK: !1 = metadata !{metadata !"Simple C/C++ TBAA"}
+// CHECK: = metadata !{metadata !"vtable pointer", metadata !{{.*}}}
+// NOTBAA-NOT: = metadata !{metadata !"Simple C/C++ TBAA"}
diff --git a/test/CodeGen/thread-specifier.c b/test/CodeGen/thread-specifier.c
index a1f3e16..a2d3e62 100644
--- a/test/CodeGen/thread-specifier.c
+++ b/test/CodeGen/thread-specifier.c
@@ -3,7 +3,13 @@
// CHECK: @b = external thread_local global
// CHECK: @d.e = internal thread_local global
// CHECK: @d.f = internal thread_local global
+// CHECK: @f.a = internal thread_local(initialexec) global
// CHECK: @a = thread_local global
+// CHECK: @g = thread_local global
+// CHECK: @h = thread_local(localdynamic) global
+// CHECK: @i = thread_local(initialexec) global
+// CHECK: @j = thread_local(localexec) global
+
__thread int a;
extern __thread int b;
int c() { return *&b; }
@@ -13,3 +19,12 @@ int d() {
return 0;
}
+__thread int g __attribute__((tls_model("global-dynamic")));
+__thread int h __attribute__((tls_model("local-dynamic")));
+__thread int i __attribute__((tls_model("initial-exec")));
+__thread int j __attribute__((tls_model("local-exec")));
+
+int f() {
+ __thread static int a __attribute__((tls_model("initial-exec")));
+ return a++;
+}
diff --git a/test/CodeGen/tls-model.c b/test/CodeGen/tls-model.c
new file mode 100644
index 0000000..b5bae77
--- /dev/null
+++ b/test/CodeGen/tls-model.c
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=global-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=initial-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-IE
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LE
+
+int __thread x;
+int f() {
+ static int __thread y;
+ return y++;
+}
+int __thread __attribute__((tls_model("initial-exec"))) z;
+
+// CHECK-GD: @f.y = internal thread_local global i32 0
+// CHECK-GD: @x = thread_local global i32 0
+// CHECK-GD: @z = thread_local(initialexec) global i32 0
+
+// CHECK-LD: @f.y = internal thread_local(localdynamic) global i32 0
+// CHECK-LD: @x = thread_local(localdynamic) global i32 0
+// CHECK-LD: @z = thread_local(initialexec) global i32 0
+
+// CHECK-IE: @f.y = internal thread_local(initialexec) global i32 0
+// CHECK-IE: @x = thread_local(initialexec) global i32 0
+// CHECK-IE: @z = thread_local(initialexec) global i32 0
+
+// CHECK-LE: @f.y = internal thread_local(localexec) global i32 0
+// CHECK-LE: @x = thread_local(localexec) global i32 0
+// CHECK-LE: @z = thread_local(initialexec) global i32 0
diff --git a/test/CodeGen/varargs.c b/test/CodeGen/varargs.c
index b3dba24..b6973d8 100644
--- a/test/CodeGen/varargs.c
+++ b/test/CodeGen/varargs.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
// PR6433 - Don't crash on va_arg(typedef).
@@ -9,3 +9,9 @@ void focus_changed_cb () {
mfloat = __builtin_va_arg((pa), gdouble);
}
+void vararg(int, ...);
+void function_as_vararg() {
+ // CHECK: define {{.*}}function_as_vararg
+ // CHECK-NOT: llvm.trap
+ vararg(0, focus_changed_cb);
+}
diff --git a/test/CodeGen/vector-alignment.c b/test/CodeGen/vector-alignment.c
new file mode 100644
index 0000000..92d1ae7
--- /dev/null
+++ b/test/CodeGen/vector-alignment.c
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -w -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
+// rdar://11759609
+
+// At or below target max alignment with no aligned attribute should align based
+// on the size of vector.
+double __attribute__((vector_size(16))) v1;
+// CHECK: @v1 {{.*}}, align 16
+double __attribute__((vector_size(32))) v2;
+// CHECK: @v2 {{.*}}, align 32
+
+// Alignment above target max alignment with no aligned attribute should align
+// based on the target max.
+double __attribute__((vector_size(64))) v3;
+// CHECK: @v3 {{.*}}, align 32
+double __attribute__((vector_size(1024))) v4;
+// CHECK: @v4 {{.*}}, align 32
+
+// Aliged attribute should always override.
+double __attribute__((vector_size(16), aligned(16))) v5;
+// CHECK: @v5 {{.*}}, align 16
+double __attribute__((vector_size(16), aligned(64))) v6;
+// CHECK: @v6 {{.*}}, align 64
+double __attribute__((vector_size(32), aligned(16))) v7;
+// CHECK: @v7 {{.*}}, align 16
+double __attribute__((vector_size(32), aligned(64))) v8;
+// CHECK: @v8 {{.*}}, align 64
+
+// Check non-power of 2 widths.
+double __attribute__((vector_size(24))) v9;
+// CHECK: @v9 {{.*}}, align 32
+double __attribute__((vector_size(40))) v10;
+// CHECK: @v10 {{.*}}, align 32
+
+// Check non-power of 2 widths with aligned attribute.
+double __attribute__((vector_size(24), aligned(64))) v11;
+// CHECK: @v11 {{.*}}, align 64
+double __attribute__((vector_size(80), aligned(16))) v12;
+// CHECK: @v12 {{.*}}, align 16
diff --git a/test/CodeGen/vla.c b/test/CodeGen/vla.c
index 9e62da5..e151827 100644
--- a/test/CodeGen/vla.c
+++ b/test/CodeGen/vla.c
@@ -142,3 +142,52 @@ int test4(unsigned n, char (*p)[n][n+1][6]) {
// CHECK-NEXT: ret i32 [[T7]]
return p2 - p;
}
+
+// rdar://11485774
+void test5(void)
+{
+ // CHECK: define void @test5(
+ int a[5], i = 0;
+ // CHECK: [[A:%.*]] = alloca [5 x i32], align 4
+ // CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
+ // CHECK-NEXT: [[CL:%.*]] = alloca i32*, align 4
+ // CHECK-NEXT: store i32 0, i32* [[I]], align 4
+
+ (typeof(++i, (int (*)[i])a)){&a} += 0;
+ // CHECK-NEXT: [[Z:%.*]] = load i32* [[I]], align 4
+ // CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[Z]], 1
+ // CHECK-NEXT: store i32 [[INC]], i32* [[I]], align 4
+ // CHECK-NEXT: [[O:%.*]] = load i32* [[I]], align 4
+ // CHECK-NEXT: [[AR:%.*]] = getelementptr inbounds [5 x i32]* [[A]], i32 0, i32 0
+ // CHECK-NEXT: [[T:%.*]] = bitcast [5 x i32]* [[A]] to i32*
+ // CHECK-NEXT: store i32* [[T]], i32** [[CL]]
+ // CHECK-NEXT: [[TH:%.*]] = load i32** [[CL]]
+ // CHECK-NEXT: [[VLAIX:%.*]] = mul nsw i32 0, [[O]]
+ // CHECK-NEXT: [[ADDPTR:%.*]] = getelementptr inbounds i32* [[TH]], i32 [[VLAIX]]
+ // CHECK-NEXT: store i32* [[ADDPTR]], i32** [[CL]]
+}
+
+void test6(void)
+{
+ // CHECK: define void @test6(
+ int n = 20, **a, i=0;
+ // CHECK: [[N:%.*]] = alloca i32, align 4
+ // CHECK-NEXT: [[A:%.*]] = alloca i32**, align 4
+ // CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
+ (int (**)[i]){&a}[0][1][5] = 0;
+ // CHECK-NEXT: [[CL:%.*]] = alloca i32**, align 4
+ // CHECK-NEXT: store i32 20, i32* [[N]], align 4
+ // CHECK-NEXT: store i32 0, i32* [[I]], align 4
+ // CHECK-NEXT: [[Z:%.*]] = load i32* [[I]], align 4
+ // CHECK-NEXT: [[O:%.*]] = bitcast i32*** [[A]] to i32**
+ // CHECK-NEXT: store i32** [[O]], i32*** [[CL]]
+ // CHECK-NEXT: [[T:%.*]] = load i32*** [[CL]]
+ // CHECK-NEXT: [[IX:%.*]] = getelementptr inbounds i32** [[T]], i32 0
+ // CHECK-NEXT: [[TH:%.*]] = load i32** [[IX]], align 4
+ // CHECK-NEXT: [[F:%.*]] = mul nsw i32 1, [[Z]]
+ // CHECK-NEXT: [[IX1:%.*]] = getelementptr inbounds i32* [[TH]], i32 [[F]]
+ // CHECK-NEXT: [[IX2:%.*]] = getelementptr inbounds i32* [[IX1]], i32 5
+ // CHECK-NEXT: store i32 0, i32* [[IX2]], align 4
+}
+
+
diff --git a/test/CodeGen/vld_dup.c b/test/CodeGen/vld_dup.c
index e1d63cc..2bc2519 100644
--- a/test/CodeGen/vld_dup.c
+++ b/test/CodeGen/vld_dup.c
@@ -1,3 +1,4 @@
+// REQUIRES: arm-registered-target
// RUN: %clang_cc1 -triple armv7a-linux-gnueabi \
// RUN: -target-cpu cortex-a8 \
// RUN: -emit-llvm -O0 -o - %s | FileCheck %s
diff --git a/test/CodeGen/x86_32-arguments-darwin.c b/test/CodeGen/x86_32-arguments-darwin.c
index 0ac18b7..5bbc80b 100644
--- a/test/CodeGen/x86_32-arguments-darwin.c
+++ b/test/CodeGen/x86_32-arguments-darwin.c
@@ -324,3 +324,16 @@ void f64(struct s64 x) {}
// CHECK: define float @f65()
struct s65 { signed char a[0]; float b; };
struct s65 f65() { return (struct s65){{},2}; }
+
+// CHECK: define <2 x i64> @f66
+// CHECK: ptrtoint
+// CHECK: and {{.*}}, -16
+// CHECK: inttoptr
+typedef int T66 __attribute((vector_size(16)));
+T66 f66(int i, ...) {
+ __builtin_va_list ap;
+ __builtin_va_start(ap, i);
+ T66 v = __builtin_va_arg(ap, T66);
+ __builtin_va_end(ap);
+ return v;
+}
diff --git a/test/CodeGen/xop-builtins.c b/test/CodeGen/xop-builtins.c
new file mode 100644
index 0000000..436deaa
--- /dev/null
+++ b/test/CodeGen/xop-builtins.c
@@ -0,0 +1,326 @@
+// RUN: %clang_cc1 %s -O3 -triple=x86_64-apple-darwin -target-feature +xop -emit-llvm -o - | FileCheck %s
+
+// Don't include mm_malloc.h, it's system specific.
+#define __MM_MALLOC_H
+
+#include <x86intrin.h>
+
+__m128i test_mm_maccs_epi16(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacssww
+ return _mm_maccs_epi16(a, b, c);
+}
+
+__m128i test_mm_macc_epi16(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacsww
+ return _mm_macc_epi16(a, b, c);
+}
+
+__m128i test_mm_maccsd_epi16(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacsswd
+ return _mm_maccsd_epi16(a, b, c);
+}
+
+__m128i test_mm_maccd_epi16(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacswd
+ return _mm_maccd_epi16(a, b, c);
+}
+
+__m128i test_mm_maccs_epi32(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacssdd
+ return _mm_maccs_epi32(a, b, c);
+}
+
+__m128i test_mm_macc_epi32(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacsdd
+ return _mm_macc_epi32(a, b, c);
+}
+
+__m128i test_mm_maccslo_epi32(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacssdql
+ return _mm_maccslo_epi32(a, b, c);
+}
+
+__m128i test_mm_macclo_epi32(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacsdql
+ return _mm_macclo_epi32(a, b, c);
+}
+
+__m128i test_mm_maccshi_epi32(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacssdqh
+ return _mm_maccshi_epi32(a, b, c);
+}
+
+__m128i test_mm_macchi_epi32(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmacsdqh
+ return _mm_macchi_epi32(a, b, c);
+}
+
+__m128i test_mm_maddsd_epi16(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmadcsswd
+ return _mm_maddsd_epi16(a, b, c);
+}
+
+__m128i test_mm_maddd_epi16(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpmadcswd
+ return _mm_maddd_epi16(a, b, c);
+}
+
+__m128i test_mm_haddw_epi8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddbw
+ return _mm_haddw_epi8(a);
+}
+
+__m128i test_mm_haddd_epi8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddbd
+ return _mm_haddd_epi8(a);
+}
+
+__m128i test_mm_haddq_epi8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddbq
+ return _mm_haddq_epi8(a);
+}
+
+__m128i test_mm_haddd_epi16(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddwd
+ return _mm_haddd_epi16(a);
+}
+
+__m128i test_mm_haddq_epi16(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddwq
+ return _mm_haddq_epi16(a);
+}
+
+__m128i test_mm_haddq_epi32(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphadddq
+ return _mm_haddq_epi32(a);
+}
+
+__m128i test_mm_haddw_epu8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddubw
+ return _mm_haddw_epu8(a);
+}
+
+__m128i test_mm_haddd_epu8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddubd
+ return _mm_haddd_epu8(a);
+}
+
+__m128i test_mm_haddq_epu8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddubq
+ return _mm_haddq_epu8(a);
+}
+
+__m128i test_mm_haddd_epu16(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphadduwd
+ return _mm_haddd_epu16(a);
+}
+
+__m128i test_mm_haddq_epu16(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphadduwq
+ return _mm_haddq_epu16(a);
+}
+
+__m128i test_mm_haddq_epu32(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphaddudq
+ return _mm_haddq_epu32(a);
+}
+
+__m128i test_mm_hsubw_epi8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphsubbw
+ return _mm_hsubw_epi8(a);
+}
+
+__m128i test_mm_hsubd_epi16(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphsubwd
+ return _mm_hsubd_epi16(a);
+}
+
+__m128i test_mm_hsubq_epi32(__m128i a) {
+ // CHECK: @llvm.x86.xop.vphsubdq
+ return _mm_hsubq_epi32(a);
+}
+
+__m128i test_mm_cmov_si128(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpcmov
+ return _mm_cmov_si128(a, b, c);
+}
+
+__m256i test_mm256_cmov_si256(__m256i a, __m256i b, __m256i c) {
+ // CHECK: @llvm.x86.xop.vpcmov.256
+ return _mm256_cmov_si256(a, b, c);
+}
+
+__m128i test_mm_perm_epi8(__m128i a, __m128i b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpperm
+ return _mm_perm_epi8(a, b, c);
+}
+
+__m128i test_mm_rot_epi8(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vprotb
+ return _mm_rot_epi8(a, b);
+}
+
+__m128i test_mm_rot_epi16(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vprotw
+ return _mm_rot_epi16(a, b);
+}
+
+__m128i test_mm_rot_epi32(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vprotd
+ return _mm_rot_epi32(a, b);
+}
+
+__m128i test_mm_rot_epi64(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vprotq
+ return _mm_rot_epi64(a, b);
+}
+
+__m128i test_mm_roti_epi8(__m128i a) {
+ // CHECK: @llvm.x86.xop.vprotbi
+ return _mm_roti_epi8(a, 1);
+}
+
+__m128i test_mm_roti_epi16(__m128i a) {
+ // CHECK: @llvm.x86.xop.vprotwi
+ return _mm_roti_epi16(a, 50);
+}
+
+__m128i test_mm_roti_epi32(__m128i a) {
+ // CHECK: @llvm.x86.xop.vprotdi
+ return _mm_roti_epi32(a, -30);
+}
+
+__m128i test_mm_roti_epi64(__m128i a) {
+ // CHECK: @llvm.x86.xop.vprotqi
+ return _mm_roti_epi64(a, 100);
+}
+
+__m128i test_mm_shl_epi8(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshlb
+ return _mm_shl_epi8(a, b);
+}
+
+__m128i test_mm_shl_epi16(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshlw
+ return _mm_shl_epi16(a, b);
+}
+
+__m128i test_mm_shl_epi32(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshld
+ return _mm_shl_epi32(a, b);
+}
+
+__m128i test_mm_shl_epi64(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshlq
+ return _mm_shl_epi64(a, b);
+}
+
+__m128i test_mm_sha_epi8(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshab
+ return _mm_sha_epi8(a, b);
+}
+
+__m128i test_mm_sha_epi16(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshaw
+ return _mm_sha_epi16(a, b);
+}
+
+__m128i test_mm_sha_epi32(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshad
+ return _mm_sha_epi32(a, b);
+}
+
+__m128i test_mm_sha_epi64(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpshaq
+ return _mm_sha_epi64(a, b);
+}
+
+__m128i test_mm_com_epu8(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomub
+ return _mm_com_epu8(a, b, 0);
+}
+
+__m128i test_mm_com_epu16(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomuw
+ return _mm_com_epu16(a, b, 0);
+}
+
+__m128i test_mm_com_epu32(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomud
+ return _mm_com_epu32(a, b, 0);
+}
+
+__m128i test_mm_com_epu64(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomuq
+ return _mm_com_epu64(a, b, 0);
+}
+
+__m128i test_mm_com_epi8(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomb
+ return _mm_com_epi8(a, b, 0);
+}
+
+__m128i test_mm_com_epi16(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomw
+ return _mm_com_epi16(a, b, 0);
+}
+
+__m128i test_mm_com_epi32(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomd
+ return _mm_com_epi32(a, b, 0);
+}
+
+__m128i test_mm_com_epi64(__m128i a, __m128i b) {
+ // CHECK: @llvm.x86.xop.vpcomq
+ return _mm_com_epi64(a, b, 0);
+}
+
+__m128d test_mm_permute2_pd(__m128d a, __m128d b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpermil2pd
+ return _mm_permute2_pd(a, b, c, 0);
+}
+
+__m256d test_mm256_permute2_pd(__m256d a, __m256d b, __m256i c) {
+ // CHECK: @llvm.x86.xop.vpermil2pd.256
+ return _mm256_permute2_pd(a, b, c, 0);
+}
+
+__m128 test_mm_permute2_ps(__m128 a, __m128 b, __m128i c) {
+ // CHECK: @llvm.x86.xop.vpermil2ps
+ return _mm_permute2_ps(a, b, c, 0);
+}
+
+__m256 test_mm256_permute2_ps(__m256 a, __m256 b, __m256i c) {
+ // CHECK: @llvm.x86.xop.vpermil2ps.256
+ return _mm256_permute2_ps(a, b, c, 0);
+}
+
+__m128 test_mm_frcz_ss(__m128 a) {
+ // CHECK: @llvm.x86.xop.vfrcz.ss
+ return _mm_frcz_ss(a);
+}
+
+__m128d test_mm_frcz_sd(__m128d a) {
+ // CHECK: @llvm.x86.xop.vfrcz.sd
+ return _mm_frcz_sd(a);
+}
+
+__m128 test_mm_frcz_ps(__m128 a) {
+ // CHECK: @llvm.x86.xop.vfrcz.ps
+ return _mm_frcz_ps(a);
+}
+
+__m128d test_mm_frcz_pd(__m128d a) {
+ // CHECK: @llvm.x86.xop.vfrcz.pd
+ return _mm_frcz_pd(a);
+}
+
+__m256 test_mm256_frcz_ps(__m256 a) {
+ // CHECK: @llvm.x86.xop.vfrcz.ps.256
+ return _mm256_frcz_ps(a);
+}
+
+__m256d test_mm256_frcz_pd(__m256d a) {
+ // CHECK: @llvm.x86.xop.vfrcz.pd.256
+ return _mm256_frcz_pd(a);
+}
diff --git a/test/CodeGenCUDA/address-spaces.cu b/test/CodeGenCUDA/address-spaces.cu
new file mode 100644
index 0000000..61d4d6b
--- /dev/null
+++ b/test/CodeGenCUDA/address-spaces.cu
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -fcuda-is-device -triple nvptx-unknown-unknown | FileCheck %s
+
+#include "../SemaCUDA/cuda.h"
+
+// CHECK: @i = addrspace(1) global
+__device__ int i;
+
+// CHECK: @j = addrspace(4) global
+__constant__ int j;
+
+// CHECK: @k = addrspace(3) global
+__shared__ int k;
+
+__device__ void foo() {
+ // CHECK: load i32* bitcast (i32 addrspace(1)* @i to i32*)
+ i++;
+
+ // CHECK: load i32* bitcast (i32 addrspace(4)* @j to i32*)
+ j++;
+
+ // CHECK: load i32* bitcast (i32 addrspace(3)* @k to i32*)
+ k++;
+}
+
diff --git a/test/CodeGenCUDA/ptx-kernels.cu b/test/CodeGenCUDA/ptx-kernels.cu
index ecca851..f0bf295 100644
--- a/test/CodeGenCUDA/ptx-kernels.cu
+++ b/test/CodeGenCUDA/ptx-kernels.cu
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple ptx32-unknown-unknown -fcuda-is-device -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple nvptx-unknown-unknown -fcuda-is-device -emit-llvm -o - | FileCheck %s
#include "../SemaCUDA/cuda.h"
diff --git a/test/CodeGenCXX/anonymous-union-member-initializer.cpp b/test/CodeGenCXX/anonymous-union-member-initializer.cpp
index a12ae53..8dc4f47 100644
--- a/test/CodeGenCXX/anonymous-union-member-initializer.cpp
+++ b/test/CodeGenCXX/anonymous-union-member-initializer.cpp
@@ -179,3 +179,13 @@ namespace PR9683 {
};
QueueEntry QE;
}
+
+namespace PR13154 {
+ struct IndirectReferenceField {
+ struct {
+ float &x;
+ };
+ IndirectReferenceField(float &x);
+ };
+ IndirectReferenceField::IndirectReferenceField(float &xx) : x(xx) {}
+}
diff --git a/test/CodeGenCXX/block-in-ctor-dtor.cpp b/test/CodeGenCXX/block-in-ctor-dtor.cpp
index e4389a4..4ee6b1c 100644
--- a/test/CodeGenCXX/block-in-ctor-dtor.cpp
+++ b/test/CodeGenCXX/block-in-ctor-dtor.cpp
@@ -36,13 +36,13 @@ X::~X() {
};
+// CHECK: define internal void @___ZN4ZoneC2Ev_block_invoke
// CHECK: define internal void @___ZN4ZoneC2Ev_block_invoke_
-// CHECK: define internal void @___ZN4ZoneC2Ev_block_invoke_
-// CHECK: define internal void @___ZN4ZoneD2Ev_block_invoke_
+// CHECK: define internal void @___ZN4ZoneD2Ev_block_invoke
// CHECK: define internal void @___ZN4ZoneD2Ev_block_invoke_
+// CHECK: define internal void @___ZN1XC1Ev_block_invoke
// CHECK: define internal void @___ZN1XC1Ev_block_invoke_
-// CHECK: define internal void @___ZN1XC1Ev_block_invoke_
+// CHECK: define internal void @___ZN1XC2Ev_block_invoke
// CHECK: define internal void @___ZN1XC2Ev_block_invoke_
-// CHECK: define internal void @___ZN1XC2Ev_block_invoke_
-// CHECK: define internal void @___ZN1XD2Ev_block_invoke_
+// CHECK: define internal void @___ZN1XD2Ev_block_invoke
// CHECK: define internal void @___ZN1XD2Ev_block_invoke_
diff --git a/test/CodeGenCXX/blocks.cpp b/test/CodeGenCXX/blocks.cpp
index eb54478..1500c0d 100644
--- a/test/CodeGenCXX/blocks.cpp
+++ b/test/CodeGenCXX/blocks.cpp
@@ -2,8 +2,8 @@
namespace test0 {
// CHECK: define void @_ZN5test04testEi(
- // CHECK: define internal void @__test_block_invoke_{{.*}}(
- // CHECK: define internal void @__block_global_{{.*}}(
+ // CHECK: define internal void @___ZN5test04testEi_block_invoke{{.*}}(
+ // CHECK: define internal void @___ZN5test04testEi_block_invoke_2{{.*}}(
void test(int x) {
^{ ^{ (void) x; }; };
}
@@ -119,7 +119,7 @@ namespace test4 {
consume(^{ return foo(A()); });
}
// CHECK: define void @_ZN5test44testEv()
- // CHECK: define internal void @__test_block_invoke
+ // CHECK: define internal void @___ZN5test44testEv_block_invoke
// CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1
// CHECK-NEXT: bitcast i8*
// CHECK-NEXT: call void @_ZN5test41AC1Ev([[A]]* [[TMP]])
diff --git a/test/CodeGenCXX/class-layout.cpp b/test/CodeGenCXX/class-layout.cpp
index dac0a0a..21e1a2b 100644
--- a/test/CodeGenCXX/class-layout.cpp
+++ b/test/CodeGenCXX/class-layout.cpp
@@ -77,3 +77,17 @@ namespace Test6 {
class E : public B {};
E *e;
}
+
+// <rdar://problem/11324125>: Make sure this doesn't crash. (It's okay
+// if we start rejecting it at some point.)
+namespace Test7 {
+ #pragma pack (1)
+ class A {};
+ // CHECK: %"class.Test7::B" = type <{ i32 (...)**, %"class.Test7::A" }>
+ class B {
+ virtual ~B();
+ A a;
+ };
+ B* b;
+ #pragma pack ()
+}
diff --git a/test/CodeGenCXX/const-init-cxx11.cpp b/test/CodeGenCXX/const-init-cxx11.cpp
index 62a345a..db1bb41 100644
--- a/test/CodeGenCXX/const-init-cxx11.cpp
+++ b/test/CodeGenCXX/const-init-cxx11.cpp
@@ -49,6 +49,17 @@ namespace StructUnion {
// CHECK: @_ZN11StructUnion1fE = global {{.*}} { i32 5 }
D f;
+
+ union E {
+ int a;
+ void *b = &f;
+ };
+
+ // CHECK: @_ZN11StructUnion1gE = global {{.*}} @_ZN11StructUnion1fE
+ E g;
+
+ // CHECK: @_ZN11StructUnion1hE = global {{.*}} @_ZN11StructUnion1fE
+ E h = E();
}
namespace BaseClass {
@@ -110,6 +121,13 @@ namespace Array {
};
// CHECK: @_ZN5Array1eE = constant {{.*}} { [4 x i8] c"foo\00", [4 x i8] c"x\00\00\00" }
extern constexpr E e = E();
+
+ // PR13290
+ struct F { constexpr F() : n(4) {} int n; };
+ // CHECK: @_ZN5Array2f1E = global {{.*}} zeroinitializer
+ F f1[1][1][0] = { };
+ // CHECK: @_ZN5Array2f2E = global {{.* i32 4 .* i32 4 .* i32 4 .* i32 4 .* i32 4 .* i32 4 .* i32 4 .* i32 4}}
+ F f2[2][2][2] = { };
}
namespace MemberPtr {
@@ -298,6 +316,20 @@ namespace VirtualMembers {
static nsMemoryImpl sGlobalMemory;
}
+namespace PR13273 {
+ struct U {
+ int t;
+ U() = default;
+ };
+
+ struct S : U {
+ S() = default;
+ };
+
+ // CHECK: @_ZN7PR132731sE = {{.*}} zeroinitializer
+ extern const S s {};
+}
+
// Constant initialization tests go before this point,
// dynamic initialization tests go after.
diff --git a/test/CodeGenCXX/constructor-init.cpp b/test/CodeGenCXX/constructor-init.cpp
index 9f808f6..b33184e 100644
--- a/test/CodeGenCXX/constructor-init.cpp
+++ b/test/CodeGenCXX/constructor-init.cpp
@@ -131,6 +131,26 @@ namespace rdar9694300 {
}
}
+// Check that we emit a zero initialization step for list-value-initialization
+// which calls a trivial default constructor.
+namespace PR13273 {
+ struct U {
+ int t;
+ U() = default;
+ };
+
+ struct S : U {
+ S() = default;
+ };
+
+ // CHECK: define {{.*}}@_ZN7PR132731fEv(
+ int f() {
+ // CHECK-NOT: }
+ // CHECK: llvm.memset{{.*}}i8 0
+ return (new S{})->t;
+ }
+}
+
template<typename T>
struct X {
X(const X &);
diff --git a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp
index f5684d9..ad6492f 100644
--- a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp
+++ b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp
@@ -54,3 +54,14 @@ delegator::delegator(char)
delegator::delegator(int)
: delegator()
{}
+
+namespace PR12890 {
+ class X {
+ int x;
+ X() = default;
+ X(int);
+ };
+ X::X(int) : X() {}
+}
+// CHECK: define {{.*}} @_ZN7PR128901XC1Ei(%"class.PR12890::X"* %this, i32)
+// CHECK: call void @llvm.memset.p0i8.{{i32|i64}}(i8* {{.*}}, i8 0, {{i32|i64}} 4, i32 4, i1 false)
diff --git a/test/CodeGenCXX/cxx0x-initializer-constructors.cpp b/test/CodeGenCXX/cxx0x-initializer-constructors.cpp
new file mode 100644
index 0000000..48fbe85
--- /dev/null
+++ b/test/CodeGenCXX/cxx0x-initializer-constructors.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=c++11 -S -emit-llvm -o - %s | FileCheck %s
+
+struct S {
+ S(int x) { }
+ S(int x, double y, double z) { }
+};
+
+void fn1() {
+ // CHECK: define void @_Z3fn1v
+ S s { 1 };
+ // CHECK: alloca %struct.S, align 1
+ // CHECK: call void @_ZN1SC1Ei(%struct.S* %s, i32 1)
+}
+
+void fn2() {
+ // CHECK: define void @_Z3fn2v
+ S s { 1, 2.0, 3.0 };
+ // CHECK: alloca %struct.S, align 1
+ // CHECK: call void @_ZN1SC1Eidd(%struct.S* %s, i32 1, double 2.000000e+00, double 3.000000e+00)
+}
+
+void fn3() {
+ // CHECK: define void @_Z3fn3v
+ S sa[] { { 1 }, { 2 }, { 3 } };
+ // CHECK: alloca [3 x %struct.S], align 1
+ // CHECK: call void @_ZN1SC1Ei(%struct.S* %{{.+}}, i32 1)
+ // CHECK: call void @_ZN1SC1Ei(%struct.S* %{{.+}}, i32 2)
+ // CHECK: call void @_ZN1SC1Ei(%struct.S* %{{.+}}, i32 3)
+}
+
+void fn4() {
+ // CHECK: define void @_Z3fn4v
+ S sa[] { { 1, 2.0, 3.0 }, { 4, 5.0, 6.0 } };
+ // CHECK: alloca [2 x %struct.S], align 1
+ // CHECK: call void @_ZN1SC1Eidd(%struct.S* %{{.+}}, i32 1, double 2.000000e+00, double 3.000000e+00)
+ // CHECK: call void @_ZN1SC1Eidd(%struct.S* %{{.+}}, i32 4, double 5.000000e+00, double 6.000000e+00)
+}
diff --git a/test/CodeGenCXX/cxx0x-initializer-references.cpp b/test/CodeGenCXX/cxx0x-initializer-references.cpp
index 4c847b8..660b018 100644
--- a/test/CodeGenCXX/cxx0x-initializer-references.cpp
+++ b/test/CodeGenCXX/cxx0x-initializer-references.cpp
@@ -28,6 +28,10 @@ namespace reference {
// CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** %
A &ra1a = {a};
+ using T = A&;
+ // CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** %
+ A &ra1b = T{a};
+
// CHECK-NEXT: ret
}
diff --git a/test/CodeGenCXX/cxx11-initializer-aggregate.cpp b/test/CodeGenCXX/cxx11-initializer-aggregate.cpp
new file mode 100644
index 0000000..acc7782
--- /dev/null
+++ b/test/CodeGenCXX/cxx11-initializer-aggregate.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -std=c++11 -S -emit-llvm -o - %s -triple x86_64-linux-gnu | FileCheck %s
+
+struct A { int a, b; int f(); };
+
+// CHECK: define {{.*}}@_Z3fn1i(
+int fn1(int x) {
+ // CHECK: %[[INITLIST:.*]] = alloca %struct.A
+ // CHECK: %[[A:.*]] = getelementptr inbounds %struct.A* %[[INITLIST]], i32 0, i32 0
+ // CHECK: store i32 %{{.*}}, i32* %[[A]], align 4
+ // CHECK: %[[B:.*]] = getelementptr inbounds %struct.A* %[[INITLIST]], i32 0, i32 1
+ // CHECK: store i32 5, i32* %[[B]], align 4
+ // CHECK: call i32 @_ZN1A1fEv(%struct.A* %[[INITLIST]])
+ return A{x, 5}.f();
+}
+
+struct B { int &r; int &f() { return r; } };
+
+// CHECK: define {{.*}}@_Z3fn2Ri(
+int &fn2(int &v) {
+ // CHECK: %[[INITLIST2:.*]] = alloca %struct.B, align 8
+ // CHECK: %[[R:.*]] = getelementptr inbounds %struct.B* %[[INITLIST2:.*]], i32 0, i32 0
+ // CHECK: store i32* %{{.*}}, i32** %[[R]], align 8
+ // CHECK: call i32* @_ZN1B1fEv(%struct.B* %[[INITLIST2:.*]])
+ return B{v}.f();
+}
diff --git a/test/CodeGenCXX/cxx11-vtable-key-function.cpp b/test/CodeGenCXX/cxx11-vtable-key-function.cpp
new file mode 100644
index 0000000..cd2ab59
--- /dev/null
+++ b/test/CodeGenCXX/cxx11-vtable-key-function.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - -std=c++11 | FileCheck %s
+// PR13424
+
+namespace Test1 {
+struct X {
+ virtual ~X(); // Key function.
+ virtual void f(); // Not a key function.
+};
+
+X::~X() = default;
+
+// Verify that the vtable is emitted.
+// CHECK: @_ZTVN5Test11XE = unnamed_addr constant
+}
+
+namespace Test2 {
+struct X {
+ virtual ~X() = default; // Not a key function.
+ virtual void f(); // Key function.
+};
+
+void X::f() {}
+
+// Verify that the vtable is emitted.
+// CHECK: @_ZTVN5Test21XE = unnamed_addr constant
+}
+
+namespace Test3 {
+struct X {
+ virtual ~X() = delete; // Not a key function.
+ virtual void f(); // Key function.
+};
+
+void X::f() {}
+
+// Verify that the vtable is emitted.
+// CHECK: @_ZTVN5Test31XE = unnamed_addr constant
+}
diff --git a/test/CodeGenCXX/debug-info-artificial-arg.cpp b/test/CodeGenCXX/debug-info-artificial-arg.cpp
index 92d1b16..35e5f27 100644
--- a/test/CodeGenCXX/debug-info-artificial-arg.cpp
+++ b/test/CodeGenCXX/debug-info-artificial-arg.cpp
@@ -23,8 +23,8 @@ int main(int argc, char **argv) {
}
// FIXME: The numbers are truly awful.
-// CHECK: !18 = metadata !{i32 {{.*}}, i32 0, metadata !"", i32 0, i32 0, i64 64, i64 64, i64 0, i32 64, metadata !19} ; [ DW_TAG_pointer_type ]
-// CHECK: !19 = metadata !{i32 {{.*}}, null, metadata !"A", metadata !6, i32 8, i64 128, i64 64, i32 0, i32 0, null, metadata !20, i32 0, metadata !19, null} ; [ DW_TAG_class_type ]
-// CHECK: metadata !19, metadata !"A", metadata !"A", metadata !"", metadata !6, i32 12, metadata !45, i1 false, i1 false, i32 0, i32 0, null, i32 256, i1 false, null, null, i32 0, metadata !47, i32 12} ; [ DW_TAG_subprogram ]
-// CHECK: metadata !"", i32 0, i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !46, i32 0, i32 0} ; [ DW_TAG_subroutine_type ]
-// CHECK: !46 = metadata !{null, metadata !18, metadata !9, metadata !34}
+// CHECK: !16 = metadata !{i32 {{.*}}, i32 0, metadata !"", i32 0, i32 0, i64 64, i64 64, i64 0, i32 64, metadata !17} ; [ DW_TAG_pointer_type ]
+// CHECK: !17 = metadata !{i32 {{.*}}, null, metadata !"A", metadata !6, i32 8, i64 128, i64 64, i32 0, i32 0, null, metadata !18, i32 0, metadata !17, null} ; [ DW_TAG_class_type ]
+// CHECK: metadata !17, metadata !"A", metadata !"A", metadata !"", metadata !6, i32 12, metadata !43, i1 false, i1 false, i32 0, i32 0, null, i32 256, i1 false, null, null, i32 0, metadata !45, i32 12} ; [ DW_TAG_subprogram ]
+// CHECK: metadata !"", i32 0, i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !44, i32 0, i32 0} ; [ DW_TAG_subroutine_type ]
+// CHECK: !44 = metadata !{null, metadata !16, metadata !9, metadata !32}
diff --git a/test/CodeGenCXX/debug-info-cxx0x.cpp b/test/CodeGenCXX/debug-info-cxx0x.cpp
index 37ccdb0..9d30375 100644
--- a/test/CodeGenCXX/debug-info-cxx0x.cpp
+++ b/test/CodeGenCXX/debug-info-cxx0x.cpp
@@ -6,3 +6,13 @@ namespace PR9414 {
return x;
}
}
+
+// Don't crash.
+namespace PR13570 {
+ template<typename T, typename U> struct P {};
+ template<typename T> struct A {
+ template<typename U> static P<T,U> isa(U);
+ decltype(isa(int())) f() {}
+ };
+ template struct A<int>;
+}
diff --git a/test/CodeGenCXX/debug-info-determinism.cpp b/test/CodeGenCXX/debug-info-determinism.cpp
new file mode 100644
index 0000000..a96a14e
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-determinism.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -S -emit-llvm -g -o %t1.ll %s
+// RUN: %clang_cc1 -S -emit-llvm -g -o %t2.ll %s
+// RUN: diff %t1.ll %t2.ll
+
+template <int N> struct C {
+ template <int M> int f() {
+ int arr[M] = {};
+ return arr[M/2] + C<M/2>().template f<M-1>();
+ }
+};
+template <> template <> int C<0>::f<0>() { return 0; }
+template <> template <> int C<0>::f<1>() { return 0; }
+template <> template <> int C<1>::f<0>() { return 0; }
+template <> template <> int C<1>::f<1>() { return 0; }
+
+int x = C<0>().f<64>();
diff --git a/test/CodeGenCXX/debug-info-enum-class.cpp b/test/CodeGenCXX/debug-info-enum-class.cpp
new file mode 100644
index 0000000..6f88439
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-enum-class.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin -std=c++11 %s -o - | FileCheck %s
+
+enum class A { A1=1 }; // underlying type is int by default
+enum class B: unsigned long { B1=1 }; // underlying type is unsigned long
+enum C { C1 = 1 };
+enum D : short; // enum forward declaration
+A a;
+B b;
+C c;
+D d;
+
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"A", metadata !4, i32 3, i64 32, i64 32, i32 0, i32 0, metadata !5, metadata !6, i32 0, i32 0} ; [ DW_TAG_enumeration_type ]
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"B", metadata !4, i32 4, i64 64, i64 64, i32 0, i32 0, metadata !9, metadata !10, i32 0, i32 0} ; [ DW_TAG_enumeration_type ]
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"C", metadata !4, i32 5, i64 32, i64 32, i32 0, i32 0, null, metadata !13, i32 0, i32 0} ; [ DW_TAG_enumeration_type ]
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"D", metadata !4, i32 6, i64 16, i64 16, i32 0, i32 4, null, metadata !16, i32 0, i32 0} ; [ DW_TAG_enumeration_type ]
diff --git a/test/CodeGenCXX/debug-info-flex-member.cpp b/test/CodeGenCXX/debug-info-flex-member.cpp
new file mode 100644
index 0000000..b6aa6da
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-flex-member.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin %s -o - | FileCheck %s
+
+// CHECK: metadata !{i32 {{.*}}, i64 1, i64 0} ; [ DW_TAG_subrange_type ]
+
+struct StructName {
+ int member[];
+};
+
+struct StructName SN;
diff --git a/test/CodeGenCXX/debug-info-fwd-ref.cpp b/test/CodeGenCXX/debug-info-fwd-ref.cpp
index 5480c6b..e7f2ed1 100644
--- a/test/CodeGenCXX/debug-info-fwd-ref.cpp
+++ b/test/CodeGenCXX/debug-info-fwd-ref.cpp
@@ -19,8 +19,8 @@ int main(int argc, char** argv) {
// Make sure we have two DW_TAG_class_types for baz and bar and no forward
// references.
// FIXME: These should be struct types to match the declaration.
-// CHECK: metadata !{i32 {{.*}}, null, metadata !"bar", metadata !6, i32 8, i64 128, i64 64, i32 0, i32 0, null, metadata !20, i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: metadata !{i32 {{.*}}, null, metadata !"baz", metadata !6, i32 3, i64 32, i64 32, i32 0, i32 0, null, metadata !23, i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK-NOT: metadata !{i32 {{.*}}, null, metadata !"bar", metadata !6, i32 9, i64 0, i64 0, i32 0, i32 4, i32 0, null, i32 0, i32 0} ; [ DW_TAG_class_type ]
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"bar", metadata !6, i32 8, i64 128, i64 64, i32 0, i32 0, null, metadata !18, i32 0, null, null} ; [ DW_TAG_class_type ]
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"baz", metadata !6, i32 3, i64 32, i64 32, i32 0, i32 0, null, metadata !21, i32 0, null, null} ; [ DW_TAG_class_type ]
+// CHECK-NOT: metadata !{i32 {{.*}}, null, metadata !"bar", metadata !6, i32 8, i64 0, i64 0, i32 0, i32 4, i32 0, null, i32 0, i32 0} ; [ DW_TAG_class_type ]
// CHECK-NOT: metadata !{i32 {{.*}}, null, metadata !"baz", metadata !6, i32 3, i64 0, i64 0, i32 0, i32 4, null, null, i32 0, null, null} ; [ DW_TAG_class_type ]
diff --git a/test/CodeGenCXX/debug-info-gline-tables-only.cpp b/test/CodeGenCXX/debug-info-gline-tables-only.cpp
new file mode 100644
index 0000000..8d2e63d
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-gline-tables-only.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 %s -O0 -gline-tables-only -S -emit-llvm -o - | FileCheck %s
+// Checks that clang with "-gline-tables-only" doesn't emit debug info
+// for variables and types.
+
+// CHECK-NOT: DW_TAG_namespace
+namespace NS {
+// CHECK-NOT: DW_TAG_class_type
+// CHECK-NOT: DW_TAG_friend
+class C { friend class D; };
+class D {};
+// CHECK-NOT: DW_TAG_inheritance
+class E : public C {
+ // CHECK-NOT: DW_TAG_reference type
+ void x(const D& d);
+};
+}
+
+// CHECK-NOT: DW_TAG_variable
+NS::C c;
+NS::D d;
+NS::E e;
diff --git a/test/CodeGenCXX/debug-info-globalinit.cpp b/test/CodeGenCXX/debug-info-globalinit.cpp
new file mode 100644
index 0000000..ff50fac
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-globalinit.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s -std=c++11 -g | FileCheck %s
+
+void crash() {
+ volatile char *ptr = 0;
+ char x = *ptr;
+}
+
+int test() {
+ crash();
+ return 1;
+}
+
+static int i = test();
+__attribute__((nodebug)) static int j = test();
+
+int main(void) {}
+
+// CHECK: define internal void @__cxx_global_var_init()
+// CHECK-NOT: __cxx_global_var_init
+// CHECK: %[[C0:.+]] = call i32 @_Z4testv(), !dbg ![[LINE:.*]]
+// CHECK-NOT: __cxx_global_var_init
+// CHECK: store i32 %[[C0]], i32* @_ZL1i, align 4, !dbg
+//
+// CHECK: define internal void @__cxx_global_var_init1()
+// CHECK-NOT: dbg
+// CHECK: %[[C1:.+]] = call i32 @_Z4testv()
+// CHECK-NOT: dbg
+// CHECK: store i32 %[[C1]], i32* @_ZL1j, align 4
+//
+// CHECK: ![[LINE]] = metadata !{i32 13, i32 16
diff --git a/test/CodeGenCXX/debug-info-nullptr.cpp b/test/CodeGenCXX/debug-info-nullptr.cpp
index 5540a92..4cc7e54 100644
--- a/test/CodeGenCXX/debug-info-nullptr.cpp
+++ b/test/CodeGenCXX/debug-info-nullptr.cpp
@@ -4,4 +4,4 @@ void foo() {
decltype(nullptr) t = 0;
}
-// CHECK: !13 = metadata !{i32 {{.*}}, null, metadata !"nullptr_t", null, i32 0, i64 0, i64 0, i64 0, i32 0, i32 0} ; [ DW_TAG_unspecified_type ]
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"nullptr_t", null, i32 0, i64 0, i64 0, i64 0, i32 0, i32 0} ; [ DW_TAG_unspecified_type ]
diff --git a/test/CodeGenCXX/debug-info-rvalue-ref.cpp b/test/CodeGenCXX/debug-info-rvalue-ref.cpp
new file mode 100644
index 0000000..b633c5c
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-rvalue-ref.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -std=c++11 -emit-llvm -g -triple x86_64-apple-darwin %s -o - | FileCheck %s
+
+extern "C" {
+extern int printf(const char * format, ...);
+}
+void foo (int &&i)
+{
+ printf("%d\n", i);
+}
+
+// CHECK: metadata !{i32 {{.*}}, null, null, null, i32 0, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_rvalue_reference_type ]
diff --git a/test/CodeGenCXX/debug-info-static-fns.cpp b/test/CodeGenCXX/debug-info-static-fns.cpp
index 485d28a..ee46f25 100644
--- a/test/CodeGenCXX/debug-info-static-fns.cpp
+++ b/test/CodeGenCXX/debug-info-static-fns.cpp
@@ -7,4 +7,4 @@ namespace A {
}
// Verify that a is present and mangled.
-// CHECK: metadata !{i32 786478, i32 0, metadata !6, metadata !"a", metadata !"a", metadata !"_ZN1AL1aEi", metadata !7, i32 4, metadata !8, i1 true, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i32)* @_ZN1AL1aEi, null, null, metadata !14, i32 4} ; [ DW_TAG_subprogram ]
+// CHECK: metadata !{i32 {{.*}}, i32 0, metadata !6, metadata !"a", metadata !"a", metadata !"_ZN1AL1aEi", metadata !7, i32 4, metadata !8, i1 true, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i32)* @_ZN1AL1aEi, null, null, metadata !1, i32 4} ; [ DW_TAG_subprogram ]
diff --git a/test/CodeGenCXX/debug-info-template-array.cpp b/test/CodeGenCXX/debug-info-template-array.cpp
new file mode 100644
index 0000000..305327b
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-template-array.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang -emit-llvm -g -S %s -o -
+// PR13531
+template <typename>
+struct unique_ptr {
+ unique_ptr() {}
+};
+
+template <unsigned>
+struct Vertex {};
+
+void crash() // Asserts
+{
+ unique_ptr<Vertex<2>[]> v = unique_ptr<Vertex<2>[]>();
+}
diff --git a/test/CodeGenCXX/debug-info-template-limit.cpp b/test/CodeGenCXX/debug-info-template-limit.cpp
index 796a80f..afad31b 100644
--- a/test/CodeGenCXX/debug-info-template-limit.cpp
+++ b/test/CodeGenCXX/debug-info-template-limit.cpp
@@ -1,8 +1,8 @@
// RUN: %clang -flimit-debug-info -emit-llvm -g -S %s -o - | FileCheck %s
// Check that this pointer type is TC<int>
-// CHECK: !10} ; [ DW_TAG_pointer_type
-// CHECK-NEXT: !10 ={{.*}}"TC<int>"
+// CHECK: ![[LINE:[0-9]+]]} ; [ DW_TAG_pointer_type ]{{.*}}[from TC<int>]
+// CHECK-NEXT: ![[LINE]] ={{.*}}"TC<int>"
template<typename T>
class TC {
@@ -12,4 +12,3 @@ public:
};
TC<int> tci;
-
diff --git a/test/CodeGenCXX/debug-info-template-member.cpp b/test/CodeGenCXX/debug-info-template-member.cpp
index 6208c80..f21718d 100644
--- a/test/CodeGenCXX/debug-info-template-member.cpp
+++ b/test/CodeGenCXX/debug-info-template-member.cpp
@@ -17,5 +17,5 @@ private:
MyClass m;
// CHECK: metadata !{i32 {{.*}}, null, metadata !"MyClass", metadata {{.*}}, i32 {{.*}}, i64 8, i64 8, i32 0, i32 0, null, metadata [[C_MEM:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[C_MEM]] = metadata !{metadata {{.*}}, metadata [[C_TEMP:.*]], metadata {{.*}}}
+// CHECK: [[C_MEM]] = metadata !{metadata {{.*}}, metadata [[C_TEMP:.*]]}
// CHECK: [[C_TEMP]] = metadata !{i32 {{.*}}, i32 0, metadata {{.*}}, metadata !"add<2>", metadata !"add<2>", metadata !"_ZN7MyClass3addILi2EEEii", metadata {{.*}}
diff --git a/test/CodeGenCXX/debug-info-template-quals.cpp b/test/CodeGenCXX/debug-info-template-quals.cpp
new file mode 100644
index 0000000..e5a9082
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-template-quals.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin %s -o - | FileCheck %s
+
+template<typename _CharT>
+struct basic_string {
+
+ basic_string&
+ assign(const _CharT* __s)
+ {
+ return *this;
+ }
+};
+
+void foo (const char *c) {
+ basic_string<char> str;
+ str.assign(c);
+}
+
+// CHECK: [[P:.*]] = metadata !{i32 {{.*}}, metadata [[CON:.*]]} ; [ DW_TAG_pointer_type ] [line 0, size 64, align 64, offset 0] [from ]
+// CHECK: [[CON]] = metadata !{i32 {{.*}}, metadata [[CH:.*]]} ; [ DW_TAG_const_type ] [line 0, size 0, align 0, offset 0] [from char]
+// CHECK: [[CH]] = metadata !{i32 {{.*}}, metadata !"char", {{.*}}} ; [ DW_TAG_base_type ] [char] [line 0, size 8, align 8, offset 0, enc DW_ATE_signed_char]
+// CHECK: metadata !{i32 {{.*}}, metadata !"_ZN12basic_stringIcE6assignEPKc", metadata !6, i32 7, metadata [[TYPE:.*]], i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, %struct.basic_string* (%struct.basic_string*, i8*)* @_ZN12basic_stringIcE6assignEPKc, null, metadata !18, metadata !1, i32 8} ; [ DW_TAG_subprogram ] [line 7] [def] [scope 8] [assign]
+// CHECK: [[TYPE]] = metadata !{i32 {{.*}}, null, metadata [[ARGS:.*]], i32 0, i32 0}
+// CHECK: [[ARGS]] = metadata !{metadata !15, metadata !23, metadata [[P]]}
diff --git a/test/CodeGenCXX/debug-info-union.cpp b/test/CodeGenCXX/debug-info-union.cpp
new file mode 100644
index 0000000..588fa20
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-union.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin -std=c++11 %s -o - | FileCheck %s
+
+union E {
+ int a;
+ float b;
+ int bb() { return a;}
+ float aa() { return b;}
+ E() { a = 0; }
+};
+
+E e;
+
+// CHECK: metadata !{i32 {{.*}}, null, metadata !"E", metadata !{{.*}}, i32 3, i64 32, i64 32, i64 0, i32 0, null, metadata !{{.*}}, i32 0, null} ; [ DW_TAG_union_type ]
+// CHECK: metadata !{i32 {{.*}}, i32 0, metadata !{{.*}}, metadata !"bb", metadata !"bb", metadata !"_ZN1E2bbEv", metadata !{{.*}}, i32 6, metadata !{{.*}}, i1 false, i1 false, i32 0, i32 0, null, i32 256, i1 false, null, null, i32 0, metadata !{{.*}}, i32 6} ; [ DW_TAG_subprogram ]
+// CHECK: metadata !{i32 {{.*}}, i32 0, metadata !{{.*}}, metadata !"aa", metadata !"aa", metadata !"_ZN1E2aaEv", metadata !{{.*}}, i32 7, metadata !{{.*}}, i1 false, i1 false, i32 0, i32 0, null, i32 256, i1 false, null, null, i32 0, metadata !{{.*}}, i32 7} ; [ DW_TAG_subprogram ]
+// CHECK: metadata !{i32 {{.*}}, i32 0, metadata !{{.*}}, metadata !"E", metadata !"E", metadata !"", metadata !{{.*}}, i32 8, metadata !{{.*}}, i1 false, i1 false, i32 0, i32 0, null, i32 256, i1 false, null, null, i32 0, metadata !{{.*}}, i32 8} ; [ DW_TAG_subprogram ]
diff --git a/test/CodeGenCXX/debug-info-user-def.cpp b/test/CodeGenCXX/debug-info-user-def.cpp
new file mode 100644
index 0000000..ecd3878
--- /dev/null
+++ b/test/CodeGenCXX/debug-info-user-def.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin -std=c++11 %s -o - | FileCheck %s
+
+class A {
+};
+
+template <typename T> class B {
+ T t;
+};
+
+A a;
+B<int> b;
+
+// Check that no subprograms are emitted into debug info.
+// CHECK-NOT: [ DW_TAG_subprogram ]
diff --git a/test/CodeGenCXX/debug-lambda-expressions.cpp b/test/CodeGenCXX/debug-lambda-expressions.cpp
index 859a71b..1c82399 100644
--- a/test/CodeGenCXX/debug-lambda-expressions.cpp
+++ b/test/CodeGenCXX/debug-lambda-expressions.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s -fexceptions -std=c++11 -g | FileCheck %s
auto var = [](int i) { return i+1; };
+void *use = &var;
extern "C" auto cvar = []{};
@@ -13,59 +14,56 @@ int c(int x) { return [&x]{return x;}(); }
struct D { D(); D(const D&); int x; };
int d(int x) { D y[10]; [x,y] { return y[x].x; }(); }
-
-// A: 5
-// CHECK: [[A_FUNC:.*]] = metadata !{i32 {{.*}}, i32 0, metadata [[FILE:.*]], metadata !"a", metadata !"a", metadata !"_Z1av", metadata {{.*}}, i32 [[A_LINE:.*]], metadata {{.*}}, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 ()* @_Z1av, null, null, {{.*}} [ DW_TAG_subprogram ]
-
// Randomness for file. -- 6
-// CHECK: [[FILE]] = metadata !{i32 {{.*}}, metadata !{{.*}}debug-lambda-expressions.cpp{{.*}}; [ DW_TAG_file_type ]
+// CHECK: [[FILE:.*]] = metadata !{i32 {{.*}}, metadata !{{.*}}debug-lambda-expressions.cpp{{.*}}; [ DW_TAG_file_type ]
+
+// A: 10
+// CHECK: [[A_FUNC:.*]] = metadata !{i32 {{.*}}, i32 0, metadata [[FILE]], metadata !"a", metadata !"a", metadata !"_Z1av", metadata {{.*}}, i32 [[A_LINE:.*]], metadata {{.*}}, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 ()* @_Z1av, null, null, {{.*}} [ DW_TAG_subprogram ]
-// B: 12
+// B: 14
// CHECK: [[B_FUNC:.*]] = metadata !{i32 786478, i32 0, metadata [[FILE]], metadata !"b", metadata !"b", metadata !"_Z1bi", metadata [[FILE]], i32 [[B_LINE:.*]], metadata {{.*}}, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i32)* @_Z1bi, null, null, {{.*}} ; [ DW_TAG_subprogram ]
// C: 17
// CHECK: [[C_FUNC:.*]] = metadata !{i32 {{.*}}, i32 0, metadata [[FILE]], metadata !"c", metadata !"c", metadata !"_Z1ci", metadata [[FILE]], i32 [[C_LINE:.*]], metadata {{.*}}, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i32)* @_Z1ci, null, null, metadata {{.*}} ; [ DW_TAG_subprogram ]
-// D: 20
+// D: 18
// CHECK: [[D_FUNC:.*]] = metadata !{i32 {{.*}}, i32 0, metadata [[FILE]], metadata !"d", metadata !"d", metadata !"_Z1di", metadata [[FILE]], i32 [[D_LINE:.*]], metadata {{.*}}, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i32)* @_Z1di, null, null, metadata {{.*}} ; [ DW_TAG_subprogram ]
-// Back to D. -- 120
+// Back to D. -- 24
// CHECK: [[LAM_D:.*]] = metadata !{i32 {{.*}}, metadata [[D_FUNC]], metadata !"", metadata [[FILE]], i32 [[D_LINE]], i64 352, i64 32, i32 0, i32 0, null, metadata [[LAM_D_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[LAM_D_ARGS]] = metadata !{metadata [[CAP_D_X:.*]], metadata [[CAP_D_Y:.*]], metadata [[CON_LAM_D:.*]], metadata [[DES_LAM_D:.*]]}
-// CHECK: [[CAP_D_X]] = metadata !{i32 {{.*}}, metadata [[LAM_D]], metadata !"x", metadata [[FILE]], i32 14, i64 32, i64 32, i64 0, i32 1, metadata {{.*}} ; [ DW_TAG_member ]
-// CHECK: [[CAP_D_Y]] = metadata !{i32 {{.*}}, metadata [[LAM_D]], metadata !"y", metadata [[FILE]], i32 14, i64 320, i64 32, i64 32, i32 1, metadata {{.*}} ; [ DW_TAG_member ]
+// CHECK: [[LAM_D_ARGS]] = metadata !{metadata [[CAP_D_X:.*]], metadata [[CAP_D_Y:.*]], metadata [[CON_LAM_D:.*]]}
+// CHECK: [[CAP_D_X]] = metadata !{i32 {{.*}}, metadata [[LAM_D]], metadata !"x", metadata [[FILE]], i32 [[D_LINE]], i64 32, i64 32, i64 0, i32 1, metadata {{.*}} ; [ DW_TAG_member ]
+// CHECK: [[CAP_D_Y]] = metadata !{i32 {{.*}}, metadata [[LAM_D]], metadata !"y", metadata [[FILE]], i32 [[D_LINE]], i64 320, i64 32, i64 32, i32 1, metadata {{.*}} ; [ DW_TAG_member ]
// CHECK: [[CON_LAM_D]] = metadata {{.*}}[[LAM_D]], metadata !"operator()", metadata !"operator()"{{.*}}[ DW_TAG_subprogram ]
-// CHECK: [[DES_LAM_D]] = metadata {{.*}}[[LAM_D]], metadata !"~", metadata !"~"{{.*}}[ DW_TAG_subprogram ]
-// Back to C. -- 159
+// Back to C. -- 55
// CHECK: [[LAM_C:.*]] = metadata !{i32 {{.*}}, metadata [[C_FUNC]], metadata !"", metadata [[FILE]], i32 [[C_LINE]], i64 64, i64 64, i32 0, i32 0, null, metadata [[LAM_C_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[LAM_C_ARGS]] = metadata !{metadata [[CAP_C:.*]], metadata [[CON_LAM_C:.*]], metadata [[DES_LAM_C:.*]]}
+// CHECK: [[LAM_C_ARGS]] = metadata !{metadata [[CAP_C:.*]], metadata [[CON_LAM_C:.*]]}
// Ignoring the member type for now.
// CHECK: [[CAP_C]] = metadata !{i32 {{.*}}, metadata [[LAM_C]], metadata !"x", metadata [[FILE]], i32 [[C_LINE]], i64 64, i64 64, i64 0, i32 1, metadata {{.*}}} ; [ DW_TAG_member ]
// CHECK: [[CON_LAM_C]] = metadata {{.*}}[[LAM_C]], metadata !"operator()", metadata !"operator()"{{.*}}[ DW_TAG_subprogram ]
-// CHECK: [[DES_LAM_C]] = metadata {{.*}}[[LAM_C]], metadata !"~", metadata !"~"{{.*}}[ DW_TAG_subprogram ]
-// Back to B. -- 179
+// Back to B. -- 67
// CHECK: [[LAM_B:.*]] = metadata !{i32 {{.*}}, metadata [[B_FUNC]], metadata !"", metadata [[FILE]], i32 [[B_LINE]], i64 32, i64 32, i32 0, i32 0, null, metadata [[LAM_B_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[LAM_B_ARGS]] = metadata !{metadata [[CAP_B:.*]], metadata [[CON_LAM_B:.*]], metadata [[DES_LAM_B:.*]]}
+// CHECK: [[LAM_B_ARGS]] = metadata !{metadata [[CAP_B:.*]], metadata [[CON_LAM_B:.*]]}
// CHECK: [[CAP_B]] = metadata !{i32 {{.*}}, metadata [[LAM_B]], metadata !"x", metadata [[FILE]], i32 [[B_LINE]], i64 32, i64 32, i64 0, i32 1, metadata {{.*}}} ; [ DW_TAG_member ]
// CHECK: [[CON_LAM_B]] = metadata {{.*}}[[LAM_B]], metadata !"operator()", metadata !"operator()"{{.*}}[ DW_TAG_subprogram ]
-// CHECK: [[DES_LAM_B]] = metadata {{.*}}[[LAM_B]], metadata !"~", metadata !"~"{{.*}}[ DW_TAG_subprogram ]
-// Back to A. -- 204
+
+// Back to A. -- 78
// CHECK: [[LAM_A:.*]] = metadata !{i32 {{.*}}, metadata [[A_FUNC]], metadata !"", metadata [[FILE]], i32 [[A_LINE]], i64 8, i64 8, i32 0, i32 0, null, metadata [[LAM_A_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[LAM_A_ARGS]] = metadata !{metadata [[CON_LAM_A:.*]], metadata [[DES_LAM_A:.*]]}
+// CHECK: [[LAM_A_ARGS]] = metadata !{metadata [[CON_LAM_A:.*]]}
// CHECK: [[CON_LAM_A]] = metadata {{.*}}[[LAM_A]], metadata !"operator()", metadata !"operator()"{{.*}}[ DW_TAG_subprogram ]
-// CHECK: [[DES_LAM_A]] = metadata {{.*}}[[LAM_A]], metadata !"~", metadata !"~"{{.*}}[ DW_TAG_subprogram ]
-// VAR:
-// CHECK: metadata !{i32 {{.*}}, i32 0, null, metadata !"var", metadata !"var", metadata !"", metadata [[FILE]], i32 [[VAR_LINE:.*]], metadata ![[VAR_T:.*]], i32 1, i32 1, %class.anon* @var} ; [ DW_TAG_variable ]
-// CHECK: [[VAR_T]] = metadata !{i32 {{.*}}, null, metadata !"", metadata [[FILE]], i32 [[VAR_LINE]], i64 8, i64 8, i32 0, i32 0, null, metadata ![[VAR_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[VAR_ARGS]] = metadata !{metadata !{{.*}}, metadata !{{.*}}, metadata !{{.*}}}
// CVAR:
// CHECK: metadata !{i32 {{.*}}, i32 0, null, metadata !"cvar", metadata !"cvar", metadata !"", metadata [[FILE]], i32 [[CVAR_LINE:.*]], metadata ![[CVAR_T:.*]], i32 0, i32 1, %class.anon.0* @cvar} ; [ DW_TAG_variable ]
// CHECK: [[CVAR_T]] = metadata !{i32 {{.*}}, null, metadata !"", metadata [[FILE]], i32 [[CVAR_LINE]], i64 8, i64 8, i32 0, i32 0, null, metadata ![[CVAR_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
-// CHECK: [[CVAR_ARGS]] = metadata !{metadata !{{.*}}, metadata !{{.*}}, metadata !{{.*}}}
+// CHECK: [[CVAR_ARGS]] = metadata !{metadata !{{.*}}}
+
+// VAR:
+// CHECK: metadata !{i32 {{.*}}, i32 0, null, metadata !"var", metadata !"var", metadata !"", metadata [[FILE]], i32 [[VAR_LINE:.*]], metadata ![[VAR_T:.*]], i32 1, i32 1, %class.anon* @var} ; [ DW_TAG_variable ]
+// CHECK: [[VAR_T]] = metadata !{i32 {{.*}}, null, metadata !"", metadata [[FILE]], i32 [[VAR_LINE]], i64 8, i64 8, i32 0, i32 0, null, metadata ![[VAR_ARGS:.*]], i32 0, null, null} ; [ DW_TAG_class_type ]
+// CHECK: [[VAR_ARGS]] = metadata !{metadata !{{.*}}}
diff --git a/test/CodeGenCXX/derived-to-base-conv.cpp b/test/CodeGenCXX/derived-to-base-conv.cpp
index 8c51809..9b15c68 100644
--- a/test/CodeGenCXX/derived-to-base-conv.cpp
+++ b/test/CodeGenCXX/derived-to-base-conv.cpp
@@ -1,85 +1,86 @@
-// REQUIRES: x86-registered-target,x86-64-registered-target
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -S %s -o %t-64.s
-// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
-// RUN: %clang_cc1 -triple i386-apple-darwin -std=c++11 -S %s -o %t-32.s
-// RUN: FileCheck -check-prefix LP32 --input-file=%t-32.s %s
-
-extern "C" int printf(...);
-extern "C" void exit(int);
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm %s -o - | FileCheck %s
struct A {
- A (const A&) { printf("A::A(const A&)\n"); }
- A() {};
- ~A() { printf("A::~A()\n"); }
+ A(const A&);
+ A();
+ ~A();
};
struct B : public A {
- B() {};
- B(const B& Other) : A(Other) { printf("B::B(const B&)\n"); }
- ~B() { printf("B::~B()\n"); }
+ B();
+ B(const B& Other);
+ ~B();
};
struct C : public B {
- C() {};
- C(const C& Other) : B(Other) { printf("C::C(const C&)\n"); }
- ~C() { printf("C::~C()\n"); }
+ C();
+ C(const C& Other);
+ ~C();
};
struct X {
- operator B&() {printf("X::operator B&()\n"); return b; }
- operator C&() {printf("X::operator C&()\n"); return c; }
- X (const X&) { printf("X::X(const X&)\n"); }
- X () { printf("X::X()\n"); }
- ~X () { printf("X::~X()\n"); }
- B b;
- C c;
+ operator B&();
+ operator C&();
+ X(const X&);
+ X();
+ ~X();
+ B b;
+ C c;
};
-void f(A) {
- printf("f(A)\n");
-}
-
-
-void func(X x)
-{
- f (x);
-}
-
-int main()
-{
- X x;
- func(x);
+void test0_helper(A);
+void test0(X x) {
+ test0_helper(x);
+ // CHECK: define void @_Z5test01X(
+ // CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align
+ // CHECK-NEXT: [[T0:%.*]] = call [[B:%.*]]* @_ZN1XcvR1BEv(
+ // CHECK-NEXT: [[T1:%.*]] = bitcast [[B]]* [[T0]] to [[A]]*
+ // CHECK-NEXT: call void @_ZN1AC1ERKS_([[A]]* [[TMP]], [[A]]* [[T1]])
+ // CHECK-NEXT: call void @_Z12test0_helper1A([[A]]* [[TMP]])
+ // CHECK-NEXT: call void @_ZN1AD1Ev([[A]]* [[TMP]])
+ // CHECK-NEXT: ret void
}
struct Base;
struct Root {
- operator Base&() { exit(1); }
+ operator Base&();
};
struct Derived;
struct Base : Root {
- Base(const Base&) { printf("Base::(const Base&)\n"); }
- Base() { printf("Base::Base()\n"); }
- operator Derived&() { exit(1); }
+ Base(const Base &);
+ Base();
+ operator Derived &();
};
struct Derived : Base {
};
-void foo(Base) {}
-
-void test(Derived bb)
-{
- // CHECK-LP64-NOT: callq __ZN4BasecvR7DerivedEv
- // CHECK-LP32-NOT: callq L__ZN4BasecvR7DerivedEv
- foo(bb);
+void test1_helper(Base);
+void test1(Derived bb) {
+ // CHECK: define void @_Z5test17Derived(
+ // CHECK-NOT: call {{.*}} @_ZN4BasecvR7DerivedEv(
+ // CHECK: call void @_ZN4BaseC1ERKS_(
+ // CHECK-NOT: call {{.*}} @_ZN4BasecvR7DerivedEv(
+ // CHECK: call void @_Z12test1_helper4Base(
+ test1_helper(bb);
}
-// CHECK-LP64: callq __ZN1XcvR1BEv
-// CHECK-LP64: callq __ZN1AC1ERKS_
-
-// CHECK-LP32: calll L__ZN1XcvR1BEv
-// CHECK-LP32: calll L__ZN1AC1ERKS_
-
+// Don't crash after devirtualizing a derived-to-base conversion
+// to an empty base allocated at offset zero.
+// rdar://problem/11993704
+class Test2a {};
+class Test2b final : public virtual Test2a {};
+void test2(Test2b &x) {
+ Test2a &y = x;
+ // CHECK: define void @_Z5test2R6Test2b(
+ // CHECK: [[X:%.*]] = alloca [[B:%.*]]*, align 8
+ // CHECK-NEXT: [[Y:%.*]] = alloca [[A:%.*]]*, align 8
+ // CHECK-NEXT: store [[B]]* {{%.*}}, [[B]]** [[X]], align 8
+ // CHECK-NEXT: [[T0:%.*]] = load [[B]]** [[X]], align 8
+ // CHECK-NEXT: [[T1:%.*]] = bitcast [[B]]* [[T0]] to [[A]]*
+ // CHECK-NEXT: store [[A]]* [[T1]], [[A]]** [[Y]], align 8
+ // CHECK-NEXT: ret void
+}
diff --git a/test/CodeGenCXX/destructor-debug-info.cpp b/test/CodeGenCXX/destructor-debug-info.cpp
index 9e32275..385c86d 100644
--- a/test/CodeGenCXX/destructor-debug-info.cpp
+++ b/test/CodeGenCXX/destructor-debug-info.cpp
@@ -1,6 +1,5 @@
-// RUN: %clang_cc1 -g -S -emit-llvm -o %t %s
-// RUN: grep "i32 20, i32 3, metadata" %t | count 1
-// Check there is a line number entry for line 20 where b1 is destructed.
+// RUN: %clang_cc1 -g -S -emit-llvm %s -o - | FileCheck %s
+
class A { int a; };
class B {
public:
@@ -19,3 +18,5 @@ void foo() {
fn (b1);
}
}
+// Check there is a line number entry for line 19 where b1 is destructed.
+// CHECK: i32 19, i32 3, metadata
diff --git a/test/CodeGenCXX/destructor-exception-spec.cpp b/test/CodeGenCXX/destructor-exception-spec.cpp
new file mode 100644
index 0000000..579daef
--- /dev/null
+++ b/test/CodeGenCXX/destructor-exception-spec.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -emit-llvm-only %s -std=c++11
+
+// PR13479: don't crash with -fno-exceptions.
+namespace {
+ struct SchedulePostRATDList {
+ virtual ~SchedulePostRATDList();
+ };
+
+ SchedulePostRATDList::~SchedulePostRATDList() {}
+
+ SchedulePostRATDList Scheduler;
+}
diff --git a/test/CodeGenCXX/destructors.cpp b/test/CodeGenCXX/destructors.cpp
index d9962e6..d665445 100644
--- a/test/CodeGenCXX/destructors.cpp
+++ b/test/CodeGenCXX/destructors.cpp
@@ -350,6 +350,22 @@ namespace test8 {
// CHECK: unreachable
}
+// PR12710
+namespace test9 {
+ struct ArgType {
+ ~ArgType();
+ };
+ template<typename T>
+ void f1(const ArgType& = ArgType());
+ void f2();
+ void bar() {
+ f1<int>();
+ f2();
+ }
+ // CHECK: call void @_ZN5test97ArgTypeD1Ev(%"struct.test9::ArgType"* %
+ // CHECK: call void @_ZN5test92f2Ev()
+}
+
// Checks from test3:
// CHECK: define internal void @_ZN5test312_GLOBAL__N_11DD0Ev(%"struct.test3::<anonymous namespace>::D"* %this) unnamed_addr
diff --git a/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp b/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
index 3de75ed..634bf84 100644
--- a/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
+++ b/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 %s -emit-llvm -o - | FileCheck %s
namespace Test1 {
struct A {
@@ -49,3 +49,140 @@ namespace Test3 {
return static_cast<B*>(v)->f();
}
}
+
+namespace Test4 {
+ struct A {
+ virtual void f();
+ };
+
+ struct B final : A {
+ virtual void f();
+ };
+
+ // CHECK: define void @_ZN5Test41fEPNS_1BE
+ void f(B* d) {
+ // CHECK: call void @_ZN5Test41B1fEv
+ static_cast<A*>(d)->f();
+ }
+}
+
+namespace Test5 {
+ struct A {
+ virtual void f();
+ };
+
+ struct B : A {
+ virtual void f();
+ };
+
+ struct C final : B {
+ };
+
+ // CHECK: define void @_ZN5Test51fEPNS_1CE
+ void f(C* d) {
+ // FIXME: It should be possible to devirtualize this case, but that is
+ // not implemented yet.
+ // CHECK: getelementptr
+ // CHECK-NEXT: %[[FUNC:.*]] = load
+ // CHECK-NEXT: call void %[[FUNC]]
+ static_cast<A*>(d)->f();
+ }
+}
+
+namespace Test6 {
+ struct A {
+ virtual ~A();
+ };
+
+ struct B : public A {
+ virtual ~B();
+ };
+
+ struct C {
+ virtual ~C();
+ };
+
+ struct D final : public C, public B {
+ };
+
+ // CHECK: define void @_ZN5Test61fEPNS_1DE
+ void f(D* d) {
+ // CHECK: call void @_ZN5Test61DD1Ev
+ static_cast<A*>(d)->~A();
+ }
+}
+
+namespace Test7 {
+ struct foo {
+ virtual void g() {}
+ };
+
+ struct bar {
+ virtual int f() { return 0; }
+ };
+
+ struct zed final : public foo, public bar {
+ int z;
+ virtual int f() {return z;}
+ };
+
+ // CHECK: define i32 @_ZN5Test71fEPNS_3zedE
+ int f(zed *z) {
+ // CHECK: alloca
+ // CHECK-NEXT: store
+ // CHECK-NEXT: load
+ // CHECK-NEXT: bitcast
+ // CHECK-NEXT: call {{.*}} @_ZN5Test73zed1fEv
+ // CHECK-NEXT: ret
+ return static_cast<bar*>(z)->f();
+ }
+}
+
+namespace Test8 {
+ struct A { virtual ~A() {} };
+ struct B {
+ int b;
+ virtual int foo() { return b; }
+ };
+ struct C final : A, B { };
+ // CHECK: define i32 @_ZN5Test84testEPNS_1CE
+ int test(C *c) {
+ // CHECK: %[[THIS:.*]] = phi
+ // CHECK-NEXT: call i32 @_ZN5Test81B3fooEv(%"struct.Test8::B"* %[[THIS]])
+ return static_cast<B*>(c)->foo();
+ }
+}
+
+namespace Test9 {
+ struct A {
+ int a;
+ };
+ struct B {
+ int b;
+ };
+ struct C : public B, public A {
+ };
+ struct RA {
+ virtual A *f() {
+ return 0;
+ }
+ };
+ struct RC final : public RA {
+ virtual C *f() {
+ C *x = new C();
+ x->a = 1;
+ x->b = 2;
+ return x;
+ }
+ };
+ // CHECK: define {{.*}} @_ZN5Test91fEPNS_2RCE
+ A *f(RC *x) {
+ // FIXME: It should be possible to devirtualize this case, but that is
+ // not implemented yet.
+ // CHECK: getelementptr
+ // CHECK-NEXT: %[[FUNC:.*]] = load
+ // CHECK-NEXT: bitcast
+ // CHECK-NEXT: = call {{.*}} %[[FUNC]]
+ return static_cast<RA*>(x)->f();
+ }
+}
diff --git a/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp b/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
index 5eede66..c5a4094 100644
--- a/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
+++ b/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
@@ -53,3 +53,33 @@ void f() {
B().h().f();
}
+namespace test2 {
+ struct foo {
+ virtual void f();
+ virtual ~foo();
+ };
+
+ struct bar : public foo {
+ virtual void f();
+ virtual ~bar();
+ };
+
+ void f(bar *b) {
+ // CHECK: call void @_ZN5test23foo1fEv
+ // CHECK: call void @_ZN5test23fooD1Ev
+ b->foo::f();
+ b->foo::~foo();
+ }
+}
+
+namespace test3 {
+ // Test that we don't crash in this case.
+ struct B {
+ };
+ struct D : public B {
+ };
+ void f(D d) {
+ // CHECK: define void @_ZN5test31fENS_1DE
+ d.B::~B();
+ }
+}
diff --git a/test/CodeGenCXX/dynamic-cast-always-null.cpp b/test/CodeGenCXX/dynamic-cast-always-null.cpp
index 2c3ea13..836cb11 100644
--- a/test/CodeGenCXX/dynamic-cast-always-null.cpp
+++ b/test/CodeGenCXX/dynamic-cast-always-null.cpp
@@ -17,3 +17,8 @@ C &f(B& b) {
// CHECK: ret %struct.C* undef
return dynamic_cast<C&>(b);
}
+
+void dont_crash() {
+ (void) dynamic_cast<void*>((A*)0);
+ (void) dynamic_cast<void*>((B*)0);
+}
diff --git a/test/CodeGenCXX/exceptions.cpp b/test/CodeGenCXX/exceptions.cpp
index 079c1e5..8c20c9e 100644
--- a/test/CodeGenCXX/exceptions.cpp
+++ b/test/CodeGenCXX/exceptions.cpp
@@ -414,3 +414,39 @@ namespace test9 {
// CHECK: [[TEST9_NEW:%.*]] = call noalias i8* @_Znam
// CHECK: call void @_ZdaPv(i8* [[TEST9_NEW]])
}
+
+// In a destructor with a function-try-block, a return statement in a
+// catch handler behaves differently from running off the end of the
+// catch handler. PR13102.
+namespace test10 {
+ extern void cleanup();
+ extern bool suppress;
+
+ struct A { ~A(); };
+ A::~A() try { cleanup(); } catch (...) { return; }
+ // CHECK: define void @_ZN6test101AD1Ev(
+ // CHECK: invoke void @_ZN6test107cleanupEv()
+ // CHECK-NOT: rethrow
+ // CHECK: ret void
+
+ struct B { ~B(); };
+ B::~B() try { cleanup(); } catch (...) {}
+ // CHECK: define void @_ZN6test101BD1Ev(
+ // CHECK: invoke void @_ZN6test107cleanupEv()
+ // CHECK: call i8* @__cxa_begin_catch
+ // CHECK-NEXT: invoke void @__cxa_rethrow()
+ // CHECK: unreachable
+
+ struct C { ~C(); };
+ C::~C() try { cleanup(); } catch (...) { if (suppress) return; }
+ // CHECK: define void @_ZN6test101CD1Ev(
+ // CHECK: invoke void @_ZN6test107cleanupEv()
+ // CHECK: call i8* @__cxa_begin_catch
+ // CHECK-NEXT: load i8* @_ZN6test108suppressE, align 1
+ // CHECK-NEXT: trunc
+ // CHECK-NEXT: br i1
+ // CHECK: call void @__cxa_end_catch()
+ // CHECK-NEXT: br label
+ // CHECK: invoke void @__cxa_rethrow()
+ // CHECK: unreachable
+}
diff --git a/test/CodeGenCXX/global-array-destruction.cpp b/test/CodeGenCXX/global-array-destruction.cpp
index 5b5dfac..076ef94 100644
--- a/test/CodeGenCXX/global-array-destruction.cpp
+++ b/test/CodeGenCXX/global-array-destruction.cpp
@@ -1,6 +1,4 @@
-// REQUIRES: x86-64-registered-target
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -S %s -o %t-64.s
-// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm %s -o - | FileCheck %s
extern "C" int printf(...);
@@ -24,11 +22,24 @@ static S sarr1[4];
S s2;
S arr3[3];
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
-// CHECK-LP64: callq ___cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: call {{.*}} @__cxa_atexit
+
+struct T {
+ double d;
+ int n;
+ ~T();
+};
+T t[2][3] = { 1.0, 2, 3.0, 4, 5.0, 6, 7.0, 8, 9.0, 10, 11.0, 12 };
+
+// CHECK: call {{.*}} @__cxa_atexit
+// CHECK: getelementptr inbounds ({{.*}} bitcast {{.*}}* @t to %struct.T*), i64 6
+// CHECK: call void @_ZN1TD1Ev
+// CHECK: icmp eq {{.*}} @t
+// CHECK: br i1 {{.*}}
diff --git a/test/CodeGenCXX/global-block-literal-helpers.cpp b/test/CodeGenCXX/global-block-literal-helpers.cpp
new file mode 100644
index 0000000..350ea54
--- /dev/null
+++ b/test/CodeGenCXX/global-block-literal-helpers.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++11 -emit-llvm -fblocks -o - -triple x86_64-apple-darwin10 %s | FileCheck %s
+// rdar://11343499
+
+namespace N {
+ typedef void (^BL)();
+ int func(BL, BL, BL);
+
+// CHECK: define internal void @_ZN1N8ArrBlockE_block_invoke(
+// CHECK: define internal void @_ZN1N8ArrBlockE_block_invoke_2(
+// CHECK: define internal void @_ZN1N8ArrBlockE_block_invoke_3
+ BL ArrBlock [] = { ^{}, ^{}, ^{} };
+
+// CHECK: define internal void @_ZN1N4ivalE_block_invoke_4(
+// CHECK: define internal void @_ZN1N4ivalE_block_invoke_5(
+// CHECK: define internal void @_ZN1N4ivalE_block_invoke_6(
+ int ival = func(^{}, ^{}, ^{});
+
+// CHECK: define internal void @_ZN1N9gvarlobalE_block_invoke_7(
+ void (^gvarlobal)(void) = ^{};
+
+ struct S {
+ BL field = ^{};
+ };
+
+// CHECK: define internal void @_ZN1N3blfE_block_invoke_8(
+ S blf;
+};
diff --git a/test/CodeGenCXX/global-init.cpp b/test/CodeGenCXX/global-init.cpp
index 8e6ef77..2a53ad9 100644
--- a/test/CodeGenCXX/global-init.cpp
+++ b/test/CodeGenCXX/global-init.cpp
@@ -70,17 +70,17 @@ namespace test3 {
const char *test() { return var; }
}
-namespace test6 {
+namespace test4 {
struct A {
A();
};
extern int foo();
// This needs an initialization function and guard variables.
- // CHECK: load i8* bitcast (i64* @_ZGVN5test61xE
- // CHECK: [[CALL:%.*]] = call i32 @_ZN5test63fooEv
- // CHECK-NEXT: store i32 [[CALL]], i32* @_ZN5test61xE
- // CHECK-NEXT: store i64 1, i64* @_ZGVN5test61xE
+ // CHECK: load i8* bitcast (i64* @_ZGVN5test41xE
+ // CHECK: [[CALL:%.*]] = call i32 @_ZN5test43fooEv
+ // CHECK-NEXT: store i32 [[CALL]], i32* @_ZN5test41xE
+ // CHECK-NEXT: store i64 1, i64* @_ZGVN5test41xE
__attribute__((weak)) int x = foo();
}
@@ -95,14 +95,6 @@ namespace PR5974 {
A* a = &c;
B* b = &c;
}
-// CHECK: define internal void [[TEST1_Z_INIT:@.*]]()
-// CHECK: load i32* @_ZN5test1L1yE
-// CHECK-NEXT: xor
-// CHECK-NEXT: store i32 {{.*}}, i32* @_ZN5test1L1zE
-// CHECK: define internal void [[TEST1_Y_INIT:@.*]]()
-// CHECK: load i32* @_ZN5test1L1xE
-// CHECK-NEXT: sub
-// CHECK-NEXT: store i32 {{.*}}, i32* @_ZN5test1L1yE
// PR9570: the indirect field shouldn't crash IR gen.
namespace test5 {
@@ -111,9 +103,98 @@ namespace test5 {
};
}
+namespace std { struct type_info; }
+
+namespace test6 {
+ struct A { virtual ~A(); };
+ struct B : A {};
+ extern A *p;
+
+ // We must emit a dynamic initializer for 'q', because it could throw.
+ B *const q = &dynamic_cast<B&>(*p);
+ // CHECK: call void @__cxa_bad_cast()
+ // CHECK: store {{.*}} @_ZN5test6L1qE
+
+ // We don't need to emit 'r' at all, because it has internal linkage, is
+ // unused, and its initialization has no side-effects.
+ B *const r = dynamic_cast<B*>(p);
+ // CHECK-NOT: call void @__cxa_bad_cast()
+ // CHECK-NOT: store {{.*}} @_ZN5test6L1rE
+
+ // This can throw, so we need to emit it.
+ const std::type_info *const s = &typeid(*p);
+ // CHECK: store {{.*}} @_ZN5test6L1sE
+
+ // This can't throw, so we don't.
+ const std::type_info *const t = &typeid(p);
+ // CHECK-NOT: @_ZN5test6L1tE
+
+ extern B *volatile v;
+ // CHECK: store {{.*}} @_ZN5test6L1wE
+ B *const w = dynamic_cast<B*>(v);
+
+ // CHECK: load volatile
+ // CHECK: store {{.*}} @_ZN5test6L1xE
+ const int x = *(volatile int*)0x1234;
+
+ namespace {
+ int a = int();
+ volatile int b = int();
+ int c = a;
+ int d = b;
+ // CHECK-NOT: store {{.*}} @_ZN5test6{{[A-Za-z0-9_]*}}1aE
+ // CHECK-NOT: store {{.*}} @_ZN5test6{{[A-Za-z0-9_]*}}1bE
+ // CHECK-NOT: store {{.*}} @_ZN5test6{{[A-Za-z0-9_]*}}1cE
+ // CHECK: load volatile {{.*}} @_ZN5test6{{[A-Za-z0-9_]*}}1bE
+ // CHECK: store {{.*}} @_ZN5test6{{[A-Za-z0-9_]*}}1dE
+ }
+}
+
+namespace test7 {
+ struct A { A(); };
+ struct B { ~B(); int n; };
+ struct C { C() = default; C(const C&); int n; };
+ struct D {};
+
+ // CHECK: call void @_ZN5test71AC1Ev({{.*}}@_ZN5test7L1aE)
+ const A a = A();
+
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN5test71BD1Ev{{.*}} @_ZN5test7L2b1E
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN5test71BD1Ev{{.*}} @_ZGRN5test72b2E
+ // CHECK: call void @_ZN5test71BD1Ev(
+ // CHECK: store {{.*}} @_ZN5test7L2b3E
+ const B b1 = B();
+ const B &b2 = B();
+ const int b3 = B().n;
+
+ // CHECK-NOT: @_ZN5test7L2c1E
+ // CHECK: @_ZN5test7L2c2E
+ // CHECK-NOT: @_ZN5test7L2c3E
+ // CHECK: @_ZN5test7L2c4E
+ const C c1 = C();
+ const C c2 = static_cast<const C&>(C());
+ const int c3 = C().n;
+ const int c4 = C(C()).n;
+
+ // CHECK-NOT: @_ZN5test7L1dE
+ const D d = D();
+
+ // CHECK: store {{.*}} @_ZN5test71eE
+ int f(), e = f();
+}
+
// At the end of the file, we check that y is initialized before z.
+// CHECK: define internal void [[TEST1_Z_INIT:@.*]]()
+// CHECK: load i32* @_ZN5test1L1yE
+// CHECK-NEXT: xor
+// CHECK-NEXT: store i32 {{.*}}, i32* @_ZN5test1L1zE
+// CHECK: define internal void [[TEST1_Y_INIT:@.*]]()
+// CHECK: load i32* @_ZN5test1L1xE
+// CHECK-NEXT: sub
+// CHECK-NEXT: store i32 {{.*}}, i32* @_ZN5test1L1yE
+
// CHECK: define internal void @_GLOBAL__I_a() section "__TEXT,__StaticInit,regular,pure_instructions" {
// CHECK: call void [[TEST1_Y_INIT]]
// CHECK: call void [[TEST1_Z_INIT]]
diff --git a/test/CodeGenCXX/inline-functions.cpp b/test/CodeGenCXX/inline-functions.cpp
index 69dfe0d..8c011de 100644
--- a/test/CodeGenCXX/inline-functions.cpp
+++ b/test/CodeGenCXX/inline-functions.cpp
@@ -53,3 +53,17 @@ namespace test1 {
c.func();
}
}
+
+// PR13252
+namespace test2 {
+ struct A;
+ void f(const A& a);
+ struct A {
+ friend void f(const A& a) { }
+ };
+ void g() {
+ A a;
+ f(a);
+ }
+ // CHECK: define linkonce_odr void @_ZN5test21fERKNS_1AE
+}
diff --git a/test/CodeGenCXX/lambda-expressions.cpp b/test/CodeGenCXX/lambda-expressions.cpp
index 797cbf4..e872cc4 100644
--- a/test/CodeGenCXX/lambda-expressions.cpp
+++ b/test/CodeGenCXX/lambda-expressions.cpp
@@ -1,7 +1,11 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s -fexceptions -std=c++11 | FileCheck %s
-// CHECK: @var = internal global
-auto var = [](int i) { return i+1; };
+// CHECK-NOT: @unused
+auto unused = [](int i) { return i+1; };
+
+// CHECK: @used = internal global
+auto used = [](int i) { return i+1; };
+void *use = &used;
// CHECK: @cvar = global
extern "C" auto cvar = []{};
diff --git a/test/CodeGenCXX/mangle-lambdas.cpp b/test/CodeGenCXX/mangle-lambdas.cpp
index cc53b01..16ddf48 100644
--- a/test/CodeGenCXX/mangle-lambdas.cpp
+++ b/test/CodeGenCXX/mangle-lambdas.cpp
@@ -1,5 +1,10 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s | FileCheck %s
+// CHECK: @_ZZNK7PR12917IJiiEE1nMUlvE_clEvE1n = linkonce_odr global i32 0
+// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd_N1nE = linkonce_odr global i32 0
+// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd0_N1nE = linkonce_odr global i32 0
+// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd1_N1nE = linkonce_odr global i32 0
+
// CHECK: define linkonce_odr void @_Z11inline_funci
inline void inline_func(int n) {
// CHECK: call i32 @_ZZ11inline_funciENKUlvE_clEv
@@ -78,10 +83,10 @@ struct ST {
// CHECK: define void @_Z7test_ST2STIdE
void test_ST(ST<double> st) {
- // CHECK: call double @_ZZN2ST1fET_S0_Ed0_NKUlvE_clEv
- // CHECK-NEXT: call double @_ZZN2ST1fET_S0_Ed0_NKUlvE0_clEv
+ // CHECK: call double @_ZZN2STIdE1fEddEd0_NKUlvE_clEv
+ // CHECK-NEXT: call double @_ZZN2STIdE1fEddEd0_NKUlvE0_clEv
// CHECK-NEXT: fadd double
- // CHECK-NEXT: call double @_ZZN2ST1fET_S0_Ed_NKUlvE_clEv
+ // CHECK-NEXT: call double @_ZZN2STIdE1fEddEd_NKUlvE_clEv
// CHECK-NEXT: call void @_ZN2STIdE1fEdd
st.f();
@@ -89,11 +94,11 @@ void test_ST(ST<double> st) {
}
// Check the linkage of the lambda call operators used in test_ST.
-// CHECK: define linkonce_odr double @_ZZN2ST1fET_S0_Ed0_NKUlvE_clEv
+// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd0_NKUlvE_clEv
// CHECK: ret double 1
-// CHECK: define linkonce_odr double @_ZZN2ST1fET_S0_Ed0_NKUlvE0_clEv
+// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd0_NKUlvE0_clEv
// CHECK: ret double 2
-// CHECK: define linkonce_odr double @_ZZN2ST1fET_S0_Ed_NKUlvE_clEv
+// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd_NKUlvE_clEv
// CHECK: ret double 3
template<typename T>
@@ -150,6 +155,24 @@ void use_func_template() {
func_template<int>();
}
+
+template<typename...T> struct PR12917 {
+ PR12917(T ...t = []{ static int n = 0; return ++n; }());
+
+ static int n[3];
+};
+template<typename...T> int PR12917<T...>::n[3] = {
+ []{ static int n = 0; return ++n; }()
+};
+
+// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd1_NKUlvE_clEv(
+// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd0_NKUlvE_clEv(
+// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd_NKUlvE_clEv(
+// CHECK: call void @_ZN7PR12917IJicdEEC1Eicd(
+PR12917<int, char, double> pr12917;
+int *pr12917_p = PR12917<int, int>::n;
+
+
// CHECK: define linkonce_odr void @_Z1fIZZNK23TestNestedInstantiationclEvENKUlvE_clEvEUlvE_EvT_
struct Members {
diff --git a/test/CodeGenCXX/mangle-ms-abi-examples.cpp b/test/CodeGenCXX/mangle-ms-abi-examples.cpp
new file mode 100644
index 0000000..d6726cab
--- /dev/null
+++ b/test/CodeGenCXX/mangle-ms-abi-examples.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -fms-extensions -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+// CHECK: @"\01??_7D@C@?1??foo@@YAXXZ@6B@" =
+// CHECK: @"\01??_7B@?1??foo@A@@QAEXH@Z@6B@" =
+// CHECK: define {{.*}} @"\01?baz@E@?3??bar@C@?1??foo@@YAXXZ@QAEXXZ@QAEXXZ"(
+
+// Microsoft Visual C++ ABI examples.
+struct A {
+ void foo (int) {
+ struct B { virtual ~B() {} };
+ B();
+ }
+};
+void foo () {
+ struct C {
+ struct D { virtual ~D() {} };
+ void bar () {
+ struct E {
+ void baz() { }
+ };
+ E().baz();
+ }
+ };
+ A().foo(0);
+ C::D();
+ C().bar();
+}
+
diff --git a/test/CodeGenCXX/mangle-ms-back-references-pr13207.cpp b/test/CodeGenCXX/mangle-ms-back-references-pr13207.cpp
new file mode 100644
index 0000000..27b4768
--- /dev/null
+++ b/test/CodeGenCXX/mangle-ms-back-references-pr13207.cpp
@@ -0,0 +1,165 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+// FIXME: add tests for return types with complex templates when PR13389 is fixed.
+
+template<class X, class Y, class Z>
+class A {};
+template<class X>
+class B {};
+template<class X>
+class C {};
+
+void foo_abbb(A<B<char>, B<char>, B<char> >) {}
+// CHECK: "\01?foo_abbb@@YAXV?$A@V?$B@D@@V1@V1@@@@Z"
+void foo_abb(A<char, B<char>, B<char> >) {}
+// CHECK: "\01?foo_abb@@YAXV?$A@DV?$B@D@@V1@@@@Z"
+void foo_abc(A<char, B<char>, C<char> >) {}
+// CHECK: "\01?foo_abc@@YAXV?$A@DV?$B@D@@V?$C@D@@@@@Z"
+
+namespace N {
+template<class X, class Y, class Z>
+class A {};
+template<class X>
+class B {};
+template<class X>
+class C {};
+template<class X, class Y>
+class D {};
+class Z {};
+}
+
+void foo_abbb(N::A<N::B<char>, N::B<char>, N::B<char> >) {}
+// CHECK: "\01?foo_abbb@@YAXV?$A@V?$B@D@N@@V12@V12@@N@@@Z"
+void foo_abb(N::A<char, N::B<char>, N::B<char> >) {}
+// CHECK: "\01?foo_abb@@YAXV?$A@DV?$B@D@N@@V12@@N@@@Z"
+void foo_abc(N::A<char, N::B<char>, N::C<char> >) {}
+// CHECK: "\01?foo_abc@@YAXV?$A@DV?$B@D@N@@V?$C@D@2@@N@@@Z"
+
+N::A<char, N::B<char>, N::C<char> > abc_foo() {
+// CHECK: ?abc_foo@@YA?AV?$A@DV?$B@D@N@@V?$C@D@2@@N@@XZ
+ return N::A<char, N::B<char>, N::C<char> >();
+}
+
+N::Z z_foo(N::Z arg) {
+// CHECK: ?z_foo@@YA?AVZ@N@@V12@@Z
+ return arg;
+}
+
+N::B<char> b_foo(N::B<char> arg) {
+// CHECK: ?b_foo@@YA?AV?$B@D@N@@V12@@Z
+ return arg;
+}
+
+N::D<char, char> d_foo(N::D<char, char> arg) {
+// CHECK: ?d_foo@@YA?AV?$D@DD@N@@V12@@Z
+ return arg;
+}
+
+N::A<char, N::B<char>, N::C<char> > abc_foo_abc(N::A<char, N::B<char>, N::C<char> >) {
+// CHECK: ?abc_foo_abc@@YA?AV?$A@DV?$B@D@N@@V?$C@D@2@@N@@V12@@Z
+ return N::A<char, N::B<char>, N::C<char> >();
+}
+
+namespace NA {
+class X {};
+template<class T> class Y {};
+}
+
+namespace NB {
+class X {};
+template<class T> class Y {};
+}
+
+void foo5(NA::Y<NB::Y<NA::Y<NB::Y<NA::X> > > > arg) {}
+// CHECK: "\01?foo5@@YAXV?$Y@V?$Y@V?$Y@V?$Y@VX@NA@@@NB@@@NA@@@NB@@@NA@@@Z"
+
+void foo11(NA::Y<NA::X>, NB::Y<NA::X>) {}
+// CHECK: "\01?foo11@@YAXV?$Y@VX@NA@@@NA@@V1NB@@@Z"
+
+void foo112(NA::Y<NA::X>, NB::Y<NB::X>) {}
+// CHECK: "\01?foo112@@YAXV?$Y@VX@NA@@@NA@@V?$Y@VX@NB@@@NB@@@Z"
+
+void foo22(NA::Y<NB::Y<NA::X> >, NB::Y<NA::Y<NA::X> >) {}
+// CHECK: "\01?foo22@@YAXV?$Y@V?$Y@VX@NA@@@NB@@@NA@@V?$Y@V?$Y@VX@NA@@@NA@@@NB@@@Z"
+
+namespace PR13207 {
+class A {};
+class B {};
+class C {};
+
+template<class X>
+class F {};
+template<class X>
+class I {};
+template<class X, class Y>
+class J {};
+template<class X, class Y, class Z>
+class K {};
+
+class L {
+ public:
+ void foo(I<A> x) {}
+};
+// CHECK: "\01?foo@L@PR13207@@QAEXV?$I@VA@PR13207@@@2@@Z"
+
+void call_l_foo(L* l) { l->foo(I<A>()); }
+
+void foo(I<A> x) {}
+// CHECK: "\01?foo@PR13207@@YAXV?$I@VA@PR13207@@@1@@Z"
+void foo2(I<A> x, I<A> y) { }
+// CHECK "\01?foo2@PR13207@@YAXV?$I@VA@PR13207@@@1@0@Z"
+void bar(J<A,B> x) {}
+// CHECK: "\01?bar@PR13207@@YAXV?$J@VA@PR13207@@VB@2@@1@@Z"
+void spam(K<A,B,C> x) {}
+// CHECK: "\01?spam@PR13207@@YAXV?$K@VA@PR13207@@VB@2@VC@2@@1@@Z"
+
+void baz(K<char, F<char>, I<char> >) {}
+// CHECK: "\01?baz@PR13207@@YAXV?$K@DV?$F@D@PR13207@@V?$I@D@2@@1@@Z"
+void qux(K<char, I<char>, I<char> >) {}
+// CHECK: "\01?qux@PR13207@@YAXV?$K@DV?$I@D@PR13207@@V12@@1@@Z"
+
+namespace NA {
+class X {};
+template<class T> class Y {};
+void foo(Y<X> x) {}
+// CHECK: "\01?foo@NA@PR13207@@YAXV?$Y@VX@NA@PR13207@@@12@@Z"
+void foofoo(Y<Y<X> > x) {}
+// CHECK: "\01?foofoo@NA@PR13207@@YAXV?$Y@V?$Y@VX@NA@PR13207@@@NA@PR13207@@@12@@Z"
+}
+
+namespace NB {
+class X {};
+template<class T> class Y {};
+void foo(Y<NA::X> x) {}
+// CHECK: "\01?foo@NB@PR13207@@YAXV?$Y@VX@NA@PR13207@@@12@@Z"
+
+void bar(NA::Y<X> x) {}
+// CHECK: "\01?bar@NB@PR13207@@YAXV?$Y@VX@NB@PR13207@@@NA@2@@Z"
+
+void spam(NA::Y<NA::X> x) {}
+// CHECK: "\01?spam@NB@PR13207@@YAXV?$Y@VX@NA@PR13207@@@NA@2@@Z"
+
+void foobar(NA::Y<Y<X> > a, Y<Y<X> >) {}
+// CHECK: "\01?foobar@NB@PR13207@@YAXV?$Y@V?$Y@VX@NB@PR13207@@@NB@PR13207@@@NA@2@V312@@Z"
+
+void foobarspam(Y<X> a, NA::Y<Y<X> > b, Y<Y<X> >) {}
+// CHECK: "\01?foobarspam@NB@PR13207@@YAXV?$Y@VX@NB@PR13207@@@12@V?$Y@V?$Y@VX@NB@PR13207@@@NB@PR13207@@@NA@2@V412@@Z"
+
+void foobarbaz(Y<X> a, NA::Y<Y<X> > b, Y<Y<X> >, Y<Y<X> > c) {}
+// CHECK: "\01?foobarbaz@NB@PR13207@@YAXV?$Y@VX@NB@PR13207@@@12@V?$Y@V?$Y@VX@NB@PR13207@@@NB@PR13207@@@NA@2@V412@2@Z"
+
+void foobarbazqux(Y<X> a, NA::Y<Y<X> > b, Y<Y<X> >, Y<Y<X> > c , NA::Y<Y<Y<X> > > d) {}
+// CHECK: "\01?foobarbazqux@NB@PR13207@@YAXV?$Y@VX@NB@PR13207@@@12@V?$Y@V?$Y@VX@NB@PR13207@@@NB@PR13207@@@NA@2@V412@2V?$Y@V?$Y@V?$Y@VX@NB@PR13207@@@NB@PR13207@@@NB@PR13207@@@52@@Z"
+}
+
+namespace NC {
+class X {};
+template<class T> class Y {};
+
+void foo(Y<NB::X> x) {}
+// CHECK: "\01?foo@NC@PR13207@@YAXV?$Y@VX@NB@PR13207@@@12@@Z"
+
+void foobar(NC::Y<NB::Y<NA::Y<NA::X> > > x) {}
+// CHECK: "\01?foobar@NC@PR13207@@YAXV?$Y@V?$Y@V?$Y@VX@NA@PR13207@@@NA@PR13207@@@NB@PR13207@@@12@@Z"
+}
+}
diff --git a/test/CodeGenCXX/mangle-ms-back-references.cpp b/test/CodeGenCXX/mangle-ms-back-references.cpp
new file mode 100644
index 0000000..5f93590
--- /dev/null
+++ b/test/CodeGenCXX/mangle-ms-back-references.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -fms-extensions -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+void f1(const char* a, const char* b) {}
+// CHECK: "\01?f1@@YAXPBD0@Z"
+
+void f2(const char* a, char* b) {}
+// CHECK: "\01?f2@@YAXPBDPAD@Z"
+
+void f3(int a, const char* b, const char* c) {}
+// CHECK: "\01?f3@@YAXHPBD0@Z"
+
+const char *f4(const char* a, const char* b) { return 0; }
+// CHECK: "\01?f4@@YAPBDPBD0@Z"
+
+void f5(char const* a, unsigned int b, char c, void const* d, char const* e, unsigned int f) {}
+// CHECK: "\01?f5@@YAXPBDIDPBX0I@Z"
+
+void f6(bool a, bool b) {}
+// CHECK: "\01?f6@@YAX_N0@Z"
+
+void f7(int a, int* b, int c, int* d, bool e, bool f, bool* g) {}
+// CHECK: "\01?f7@@YAXHPAHH0_N1PA_N@Z"
+
+// FIXME: tests for more than 10 types?
+
+struct S {
+ void mbb(bool a, bool b) {}
+};
+
+void g1(struct S a) {}
+// CHECK: "\01?g1@@YAXUS@@@Z"
+
+void g2(struct S a, struct S b) {}
+// CHECK: "\01?g2@@YAXUS@@0@Z"
+
+void g3(struct S a, struct S b, struct S* c, struct S* d) {}
+// CHECK: "\01?g3@@YAXUS@@0PAU1@1@Z"
+
+void g4(const char* a, struct S* b, const char* c, struct S* d) {
+// CHECK: "\01?g4@@YAXPBDPAUS@@01@Z"
+ b->mbb(false, false);
+// CHECK: "\01?mbb@S@@QAEX_N0@Z"
+}
+
+// Make sure that different aliases of built-in types end up mangled as the
+// built-ins.
+typedef unsigned int uintptr_t;
+typedef unsigned int size_t;
+void *h(size_t a, uintptr_t b) { return 0; }
+// CHECK: "\01?h@@YAPAXII@Z"
+
+// Function pointers might be mangled in a complex way.
+typedef void (*VoidFunc)();
+typedef int* (*PInt3Func)(int* a, int* b);
+
+void h1(const char* a, const char* b, VoidFunc c, VoidFunc d) {}
+// CHECK: "\01?h1@@YAXPBD0P6AXXZ1@Z"
+
+void h2(void (*f_ptr)(void *), void *arg) {}
+// CHECK: "\01?h2@@YAXP6AXPAX@Z0@Z"
+
+PInt3Func h3(PInt3Func x, PInt3Func y, int* z) { return 0; }
+// CHECK: "\01?h3@@YAP6APAHPAH0@ZP6APAH00@Z10@Z"
diff --git a/test/CodeGenCXX/mangle-ms-cxx11.cpp b/test/CodeGenCXX/mangle-ms-cxx11.cpp
new file mode 100644
index 0000000..6947a53
--- /dev/null
+++ b/test/CodeGenCXX/mangle-ms-cxx11.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -std=c++11 -fms-extensions -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+// CHECK: "\01?LRef@@YAXAAH@Z"
+void LRef(int& a) { }
+
+// CHECK: "\01?RRef@@YAH$$QAH@Z"
+int RRef(int&& a) { return a; }
+
+// CHECK: "\01?Null@@YAX$$T@Z"
+namespace std { typedef decltype(__nullptr) nullptr_t; }
+void Null(std::nullptr_t) {}
diff --git a/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp b/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp
new file mode 100644
index 0000000..a5d03b3
--- /dev/null
+++ b/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp
@@ -0,0 +1,173 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+void a1() {}
+// CHECK: "\01?a1@@YAXXZ"
+
+int a2() { return 0; }
+// CHECK: "\01?a2@@YAHXZ"
+
+const int a3() { return 0; }
+// CHECK: "\01?a3@@YA?BHXZ"
+
+volatile int a4() { return 0; }
+// CHECK: "\01?a4@@YA?CHXZ"
+
+const volatile int a5() { return 0; }
+// CHECK: "\01?a5@@YA?DHXZ"
+
+float a6() { return 0.0f; }
+// CHECK: "\01?a6@@YAMXZ"
+
+int *b1() { return 0; }
+// CHECK: "\01?b1@@YAPAHXZ"
+
+const char *b2() { return 0; }
+// CHECK: "\01?b2@@YAPBDXZ"
+
+float *b3() { return 0; }
+// CHECK: "\01?b3@@YAPAMXZ"
+
+const float *b4() { return 0; }
+// CHECK: "\01?b4@@YAPBMXZ"
+
+volatile float *b5() { return 0; }
+// CHECK: "\01?b5@@YAPCMXZ"
+
+const volatile float *b6() { return 0; }
+// CHECK: "\01?b6@@YAPDMXZ"
+
+float &b7() { return *(float*)0; }
+// CHECK: "\01?b7@@YAAAMXZ"
+
+const float &b8() { return *(float*)0; }
+// CHECK: "\01?b8@@YAABMXZ"
+
+volatile float &b9() { return *(float*)0; }
+// CHECK: "\01?b9@@YAACMXZ"
+
+const volatile float &b10() { return *(float*)0; }
+// CHECK: "\01?b10@@YAADMXZ"
+
+const char** b11() { return 0; }
+// CHECK: "\01?b11@@YAPAPBDXZ"
+
+class A {};
+
+A c1() { return A(); }
+// CHECK: "\01?c1@@YA?AVA@@XZ"
+
+const A c2() { return A(); }
+// CHECK: "\01?c2@@YA?BVA@@XZ"
+
+volatile A c3() { return A(); }
+// CHECK: "\01?c3@@YA?CVA@@XZ"
+
+const volatile A c4() { return A(); }
+// CHECK: "\01?c4@@YA?DVA@@XZ"
+
+const A* c5() { return 0; }
+// CHECK: "\01?c5@@YAPBVA@@XZ"
+
+volatile A* c6() { return 0; }
+// CHECK: "\01?c6@@YAPCVA@@XZ"
+
+const volatile A* c7() { return 0; }
+// CHECK: "\01?c7@@YAPDVA@@XZ"
+
+A &c8() { return *(A*)0; }
+// CHECK: "\01?c8@@YAAAVA@@XZ"
+
+const A &c9() { return *(A*)0; }
+// CHECK: "\01?c9@@YAABVA@@XZ"
+
+volatile A &c10() { return *(A*)0; }
+// CHECK: "\01?c10@@YAACVA@@XZ"
+
+const volatile A &c11() { return *(A*)0; }
+// CHECK: "\01?c11@@YAADVA@@XZ"
+
+template<typename T> class B {};
+
+B<int> d1() { return B<int>(); }
+// CHECK: "\01?d1@@YA?AV?$B@H@@XZ"
+
+B<const char*> d2() {return B<const char*>(); }
+// CHECK: "\01?d2@@YA?AV?$B@PBD@@XZ"
+
+B<A> d3() {return B<A>(); }
+// CHECK: "\01?d3@@YA?AV?$B@VA@@@@XZ"
+
+B<A>* d4() { return 0; }
+// CHECK: "\01?d4@@YAPAV?$B@VA@@@@XZ"
+
+const B<A>* d5() { return 0; }
+// CHECK: "\01?d5@@YAPBV?$B@VA@@@@XZ"
+
+volatile B<A>* d6() { return 0; }
+// CHECK: "\01?d6@@YAPCV?$B@VA@@@@XZ"
+
+const volatile B<A>* d7() { return 0; }
+// CHECK: "\01?d7@@YAPDV?$B@VA@@@@XZ"
+
+B<A>& d8() { return *(B<A>*)0; }
+// CHECK: "\01?d8@@YAAAV?$B@VA@@@@XZ"
+
+const B<A>& d9() { return *(B<A>*)0; }
+// CHECK: "\01?d9@@YAABV?$B@VA@@@@XZ"
+
+volatile B<A>& d10() { return *(B<A>*)0; }
+// CHECK: "\01?d10@@YAACV?$B@VA@@@@XZ"
+
+const volatile B<A>& d11() { return *(B<A>*)0; }
+// CHECK: "\01?d11@@YAADV?$B@VA@@@@XZ"
+
+enum Enum { DEFAULT };
+
+Enum e1() { return DEFAULT; }
+// CHECK: "\01?e1@@YA?AW4Enum@@XZ"
+
+const Enum e2() { return DEFAULT; }
+// CHECK: "\01?e2@@YA?BW4Enum@@XZ"
+
+Enum* e3() { return 0; }
+// CHECK: "\01?e3@@YAPAW4Enum@@XZ"
+
+Enum& e4() { return *(Enum*)0; }
+// CHECK: "\01?e4@@YAAAW4Enum@@XZ"
+
+struct S {};
+
+struct S f1() { struct S s; return s; }
+// CHECK: "\01?f1@@YA?AUS@@XZ"
+
+const struct S f2() { struct S s; return s; }
+// CHECK: "\01?f2@@YA?BUS@@XZ"
+
+struct S* f3() { return 0; }
+// CHECK: "\01?f3@@YAPAUS@@XZ"
+
+const struct S* f4() { return 0; }
+// CHECK: "\01?f4@@YAPBUS@@XZ"
+
+const volatile struct S* f5() { return 0; }
+// CHECK: "\01?f5@@YAPDUS@@XZ"
+
+struct S& f6() { return *(struct S*)0; }
+// CHECK: "\01?f6@@YAAAUS@@XZ"
+
+typedef int (*function_pointer)(int);
+
+function_pointer g1() { return 0; }
+// CHECK: "\01?g1@@YAP6AHH@ZXZ"
+
+const function_pointer g2() { return 0; }
+// CHECK: "\01?g2@@YAQ6AHH@ZXZ"
+
+function_pointer* g3() { return 0; }
+// CHECK: "\01?g3@@YAPAP6AHH@ZXZ"
+
+const function_pointer* g4() { return 0; }
+// The mangling of g4 is currently "\01?g4@@YAPQ6AHH@ZXZ" which is wrong.
+// This looks related to http://llvm.org/PR13444
+// FIXME: replace CHECK-NOT with CHECK once it is fixed.
+// CHECK-NOT: "\01?g4@@YAPBQ6AHH@ZXZ"
diff --git a/test/CodeGenCXX/mangle-ms-templates.cpp b/test/CodeGenCXX/mangle-ms-templates.cpp
new file mode 100644
index 0000000..977ef35
--- /dev/null
+++ b/test/CodeGenCXX/mangle-ms-templates.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -fms-extensions -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+template<typename T>
+class Class {
+ public:
+ void method() {}
+};
+
+class Typename { };
+
+template<typename T>
+class Nested { };
+
+template<bool flag>
+class BoolTemplate {
+ public:
+ BoolTemplate() {}
+};
+
+template<int param>
+class IntTemplate {
+ public:
+ IntTemplate() {}
+};
+
+template<>
+class BoolTemplate<true> {
+ public:
+ BoolTemplate() {}
+ template<class T> void Foo(T arg) {}
+};
+
+void template_mangling() {
+ Class<Typename> c1;
+ c1.method();
+// CHECK: call {{.*}} @"\01?method@?$Class@VTypename@@@@QAEXXZ"
+
+ Class<Nested<Typename> > c2;
+ c2.method();
+// CHECK: call {{.*}} @"\01?method@?$Class@V?$Nested@VTypename@@@@@@QAEXXZ"
+
+ BoolTemplate<false> _false;
+// CHECK: call {{.*}} @"\01??0?$BoolTemplate@$0A@@@QAE@XZ"
+
+ BoolTemplate<true> _true;
+ // PR13158
+ _true.Foo(1);
+// CHECK: call {{.*}} @"\01??0?$BoolTemplate@$00@@QAE@XZ"
+// CHECK: call {{.*}} @"\01??$Foo@H@?$BoolTemplate@$00@@QAEXH@Z"
+
+ IntTemplate<5> five;
+// CHECK: call {{.*}} @"\01??0?$IntTemplate@$04@@QAE@XZ"
+
+ IntTemplate<11> eleven;
+// CHECK: call {{.*}} @"\01??0?$IntTemplate@$0L@@@QAE@XZ"
+
+ IntTemplate<65535> ffff;
+// CHECK: call {{.*}} @"\01??0?$IntTemplate@$0PPPP@@@QAE@XZ"
+}
+
+namespace space {
+ template<class T> const T& foo(const T& l) { return l; }
+}
+// CHECK: "\01??$foo@H@space@@YAABHABH@Z"
+
+void use() {
+ space::foo(42);
+}
diff --git a/test/CodeGenCXX/mangle-ms.cpp b/test/CodeGenCXX/mangle-ms.cpp
index fe5fde1..f392c17 100644
--- a/test/CodeGenCXX/mangle-ms.cpp
+++ b/test/CodeGenCXX/mangle-ms.cpp
@@ -12,6 +12,13 @@
// CHECK: @"\01?j@@3P6GHCE@ZA"
// CHECK: @"\01?k@@3PTfoo@@DA"
// CHECK: @"\01?l@@3P8foo@@AEHH@ZA"
+// CHECK: @"\01?color1@@3PANA"
+
+// FIXME: The following three tests currently fail, see PR13182.
+// Replace "CHECK-NOT" with "CHECK" when it is fixed.
+// CHECK-NOT: @"\01?color2@@3QBNB"
+// CHECK-NOT: @"\01?color3@@3QAY02$$CBNA"
+// CHECK-NOT: @"\01?color4@@3QAY02$$CBNA"
int a;
@@ -39,8 +46,13 @@ public:
foo(char *q){}
//CHECK: @"\01??0foo@@QAE@PAD@Z"
+
+ static foo* static_method() { return 0; }
+
}f,s1(1),s2((char*)0);
+typedef foo (foo2);
+
struct bar {
static int g;
};
@@ -57,8 +69,17 @@ enum quux {
qthree
};
-int foo::operator+(int a) {return a;}
-// CHECK: @"\01??Hfoo@@QAEHH@Z"
+foo bar() { return foo(); }
+//CHECK: @"\01?bar@@YA?AVfoo@@XZ"
+
+int foo::operator+(int a) {
+//CHECK: @"\01??Hfoo@@QAEHH@Z"
+
+ foo::static_method();
+//CHECK: @"\01?static_method@foo@@SAPAV1@XZ"
+ bar();
+ return a;
+}
const short foo::d = 0;
volatile long foo::e;
@@ -72,9 +93,9 @@ int i[10][20];
int (__stdcall *j)(signed char, unsigned char);
-const volatile char foo::*k;
+const volatile char foo2::*k;
-int (foo::*l)(int);
+int (foo2::*l)(int);
// Static functions are mangled, too.
// Also make sure calling conventions, arglists, and throw specs work.
@@ -99,7 +120,45 @@ void delta(int * const a, const long &) {}
void epsilon(int a[][10][20]) {}
// CHECK: @"\01?epsilon@@YAXQAY19BE@H@Z"
-// Blocks mangling (Clang extension).
-void zeta(int (^)(int, int)) {}
-// CHECK: @"\01?zeta@@YAXP_EAHHH@Z@Z"
+void zeta(int (*)(int, int)) {}
+// CHECK: @"\01?zeta@@YAXP6AHHH@Z@Z"
+
+// Blocks mangling (Clang extension). A block should be mangled slightly
+// differently from a similar function pointer.
+void eta(int (^)(int, int)) {}
+// CHECK: @"\01?eta@@YAXP_EAHHH@Z@Z"
+
+void operator_new_delete() {
+ char *ptr = new char;
+// CHECK: @"\01??2@YAPAXI@Z"
+
+ delete ptr;
+// CHECK: @"\01??3@YAXPAX@Z"
+
+ char *array = new char[42];
+// CHECK: @"\01??_U@YAPAXI@Z"
+
+ delete [] array;
+// CHECK: @"\01??_V@YAXPAX@Z"
+}
+// PR13022
+void (redundant_parens)();
+void redundant_parens_use() { redundant_parens(); }
+// CHECK: @"\01?redundant_parens@@YAXXZ"
+
+// PR13182, PR13047
+typedef double RGB[3];
+RGB color1;
+extern const RGB color2 = {};
+extern RGB const color3[5] = {};
+extern RGB const ((color4)[5]) = {};
+
+// PR12603
+enum E {};
+// CHECK: "\01?fooE@@YA?AW4E@@XZ"
+E fooE() { return E(); }
+
+class X {};
+// CHECK: "\01?fooX@@YA?AVX@@XZ"
+X fooX() { return X(); }
diff --git a/test/CodeGenCXX/mangle-ref-qualifiers.cpp b/test/CodeGenCXX/mangle-ref-qualifiers.cpp
index 568cf9f..ce2af50 100644
--- a/test/CodeGenCXX/mangle-ref-qualifiers.cpp
+++ b/test/CodeGenCXX/mangle-ref-qualifiers.cpp
@@ -12,5 +12,11 @@ int X::g() && { return 0; }
// CHECK: define i32 @_ZNKO1X1hEv
int X::h() const && { return 0; }
-// CHECK: define void @_Z1fM1XRFivEMS_OFivEMS_KOFivE
+// CHECK: define void @_Z1fM1XFivREMS_FivOEMS_KFivOE
void f(int (X::*)() &, int (X::*)() &&, int (X::*)() const&&) { }
+
+// CHECK: define void @_Z1g1AIFivEES_IFivREES_IFivOEES_IKFivEES_IKFivREES_IKFivOEES_IVKFivEES_IVKFivREES_IVKFivOEE()
+template <class T> struct A {};
+void g(A<int()>, A<int()&>, A<int()&&>,
+ A<int() const>, A<int() const &>, A<int() const &&>,
+ A<int() const volatile>, A<int() const volatile &>, A<int() const volatile &&>) {}
diff --git a/test/CodeGenCXX/member-data-pointers.cpp b/test/CodeGenCXX/member-data-pointers.cpp
new file mode 100644
index 0000000..d347366
--- /dev/null
+++ b/test/CodeGenCXX/member-data-pointers.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-unknown-unknown | FileCheck -check-prefix GLOBAL-LP64 %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-unknown | FileCheck -check-prefix GLOBAL-LP32 %s
+
+struct A;
+typedef int A::*param_t;
+struct {
+ const char *name;
+ param_t par;
+} *ptr;
+void test_ptr() { (void) ptr; } // forced use
+
+// GLOBAL-LP64: type { i8*, i64 }
+// GLOBAL-LP32: type { i8*, i32 }
diff --git a/test/CodeGenCXX/member-function-pointers.cpp b/test/CodeGenCXX/member-function-pointers.cpp
index 2417aa4..84b54b6 100644
--- a/test/CodeGenCXX/member-function-pointers.cpp
+++ b/test/CodeGenCXX/member-function-pointers.cpp
@@ -1,6 +1,8 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin9 | FileCheck %s
-// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i386-apple-darwin9 | FileCheck -check-prefix LP32 %s
-// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-unknown | FileCheck -check-prefix ARM %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-unknown-unknown | FileCheck -check-prefix CODE-LP64 %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i386-unknown-unknown | FileCheck -check-prefix CODE-LP32 %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-unknown-unknown | FileCheck -check-prefix GLOBAL-LP64 %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i386-unknown-unknown | FileCheck -check-prefix GLOBAL-LP32 %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-unknown | FileCheck -check-prefix GLOBAL-ARM %s
struct A { int a; void f(); virtual void vf1(); virtual void vf2(); };
struct B { int b; virtual void g(); };
@@ -11,66 +13,56 @@ void (A::*volatile vpa)();
void (B::*pb)();
void (C::*pc)();
-// CHECK: @pa2 = global { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 }, align 8
+// GLOBAL-LP64: @pa2 = global { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 }, align 8
void (A::*pa2)() = &A::f;
-// CHECK: @pa3 = global { i64, i64 } { i64 1, i64 0 }, align 8
-// CHECK-LP32: @pa3 = global { i32, i32 } { i32 1, i32 0 }, align 4
+// GLOBAL-LP64: @pa3 = global { i64, i64 } { i64 1, i64 0 }, align 8
+// GLOBAL-LP32: @pa3 = global { i32, i32 } { i32 1, i32 0 }, align 4
void (A::*pa3)() = &A::vf1;
-// CHECK: @pa4 = global { i64, i64 } { i64 9, i64 0 }, align 8
-// CHECK-LP32: @pa4 = global { i32, i32 } { i32 5, i32 0 }, align 4
+// GLOBAL-LP64: @pa4 = global { i64, i64 } { i64 9, i64 0 }, align 8
+// GLOBAL-LP32: @pa4 = global { i32, i32 } { i32 5, i32 0 }, align 4
void (A::*pa4)() = &A::vf2;
-// CHECK: @pc2 = global { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 16 }, align 8
+// GLOBAL-LP64: @pc2 = global { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 16 }, align 8
void (C::*pc2)() = &C::f;
-// CHECK: @pc3 = global { i64, i64 } { i64 1, i64 0 }, align 8
+// GLOBAL-LP64: @pc3 = global { i64, i64 } { i64 1, i64 0 }, align 8
void (A::*pc3)() = &A::vf1;
-// Tests for test10.
-// CHECK: @_ZN6test101aE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 0 }, align 8
-// CHECK: @_ZN6test101bE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 8 }, align 8
-// CHECK: @_ZN6test101cE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 8 }, align 8
-// CHECK: @_ZN6test101dE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 16 }, align 8
-// CHECK-LP32: @_ZN6test101aE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 0 }, align 4
-// CHECK-LP32: @_ZN6test101bE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 4 }, align 4
-// CHECK-LP32: @_ZN6test101cE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 4 }, align 4
-// CHECK-LP32: @_ZN6test101dE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 8 }, align 4
-
void f() {
- // CHECK: store { i64, i64 } zeroinitializer, { i64, i64 }* @pa
+ // CODE-LP64: store { i64, i64 } zeroinitializer, { i64, i64 }* @pa
pa = 0;
// Is this okay? What are LLVM's volatile semantics for structs?
- // CHECK: store volatile { i64, i64 } zeroinitializer, { i64, i64 }* @vpa
+ // CODE-LP64: store volatile { i64, i64 } zeroinitializer, { i64, i64 }* @vpa
vpa = 0;
- // CHECK: [[TMP:%.*]] = load { i64, i64 }* @pa, align 8
- // CHECK: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1
- // CHECK: [[ADJ:%.*]] = add nsw i64 [[TMPADJ]], 16
- // CHECK: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1
- // CHECK: store { i64, i64 } [[RES]], { i64, i64 }* @pc, align 8
+ // CODE-LP64: [[TMP:%.*]] = load { i64, i64 }* @pa, align 8
+ // CODE-LP64: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1
+ // CODE-LP64: [[ADJ:%.*]] = add nsw i64 [[TMPADJ]], 16
+ // CODE-LP64: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1
+ // CODE-LP64: store { i64, i64 } [[RES]], { i64, i64 }* @pc, align 8
pc = pa;
- // CHECK: [[TMP:%.*]] = load { i64, i64 }* @pc, align 8
- // CHECK: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1
- // CHECK: [[ADJ:%.*]] = sub nsw i64 [[TMPADJ]], 16
- // CHECK: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1
- // CHECK: store { i64, i64 } [[RES]], { i64, i64 }* @pa, align 8
+ // CODE-LP64: [[TMP:%.*]] = load { i64, i64 }* @pc, align 8
+ // CODE-LP64: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1
+ // CODE-LP64: [[ADJ:%.*]] = sub nsw i64 [[TMPADJ]], 16
+ // CODE-LP64: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1
+ // CODE-LP64: store { i64, i64 } [[RES]], { i64, i64 }* @pa, align 8
pa = static_cast<void (A::*)()>(pc);
}
void f2() {
- // CHECK: store { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 }
+ // CODE-LP64: store { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 }
void (A::*pa2)() = &A::f;
- // CHECK: store { i64, i64 } { i64 1, i64 0 }
- // CHECK-LP32: store { i32, i32 } { i32 1, i32 0 }
+ // CODE-LP64: store { i64, i64 } { i64 1, i64 0 }
+ // CODE-LP32: store { i32, i32 } { i32 1, i32 0 }
void (A::*pa3)() = &A::vf1;
- // CHECK: store { i64, i64 } { i64 9, i64 0 }
- // CHECK-LP32: store { i32, i32 } { i32 5, i32 0 }
+ // CODE-LP64: store { i64, i64 } { i64 9, i64 0 }
+ // CODE-LP32: store { i32, i32 } { i32 5, i32 0 }
void (A::*pa4)() = &A::vf2;
}
@@ -195,12 +187,12 @@ namespace test7 {
struct B { void foo(); virtual void vfoo(); };
struct C : A, B { void foo(); virtual void vfoo(); };
- // CHECK-ARM: @_ZN5test74ptr0E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71A3fooEv to i32), i32 0 }
- // CHECK-ARM: @_ZN5test74ptr1E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71B3fooEv to i32), i32 8 }
- // CHECK-ARM: @_ZN5test74ptr2E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71C3fooEv to i32), i32 0 }
- // CHECK-ARM: @_ZN5test74ptr3E = global {{.*}} { i32 0, i32 1 }
- // CHECK-ARM: @_ZN5test74ptr4E = global {{.*}} { i32 0, i32 9 }
- // CHECK-ARM: @_ZN5test74ptr5E = global {{.*}} { i32 0, i32 1 }
+ // GLOBAL-ARM: @_ZN5test74ptr0E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71A3fooEv to i32), i32 0 }
+ // GLOBAL-ARM: @_ZN5test74ptr1E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71B3fooEv to i32), i32 8 }
+ // GLOBAL-ARM: @_ZN5test74ptr2E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71C3fooEv to i32), i32 0 }
+ // GLOBAL-ARM: @_ZN5test74ptr3E = global {{.*}} { i32 0, i32 1 }
+ // GLOBAL-ARM: @_ZN5test74ptr4E = global {{.*}} { i32 0, i32 9 }
+ // GLOBAL-ARM: @_ZN5test74ptr5E = global {{.*}} { i32 0, i32 1 }
void (C::*ptr0)() = &A::foo;
void (C::*ptr1)() = &B::foo;
void (C::*ptr2)() = &C::foo;
@@ -234,9 +226,9 @@ namespace test9 {
fooptr p;
};
- // CHECK: define void @_ZN5test94testEv(
- // CHECK: alloca i32
- // CHECK-NEXT: ret void
+ // CODE-LP64: define void @_ZN5test94testEv(
+ // CODE-LP64: alloca i32
+ // CODE-LP64-NEXT: ret void
void test() {
int x;
static S array[] = { (fooptr) &B::foo };
@@ -261,14 +253,37 @@ namespace test10 {
virtual void requireNonZeroAdjustment();
};
- // Non-ARM tests at top of file.
+
+// It's not that the offsets are doubled on ARM, it's that they're left-shifted by 1.
+
+// GLOBAL-LP64: @_ZN6test101aE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 0 }, align 8
+// GLOBAL-LP32: @_ZN6test101aE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 0 }, align 4
+// GLOBAL-ARM: @_ZN6test101aE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 0 }, align 4
void (A::*a)() = &A::foo;
+
+// GLOBAL-LP64: @_ZN6test101bE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 8 }, align 8
+// GLOBAL-LP32: @_ZN6test101bE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 4 }, align 4
+// GLOBAL-ARM: @_ZN6test101bE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 8 }, align 4
void (B::*b)() = (void (B::*)()) &A::foo;
+
+// GLOBAL-LP64: @_ZN6test101cE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 8 }, align 8
+// GLOBAL-LP32: @_ZN6test101cE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 4 }, align 4
+// GLOBAL-ARM: @_ZN6test101cE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 8 }, align 4
void (C::*c)() = (void (C::*)()) (void (B::*)()) &A::foo;
+
+// GLOBAL-LP64: @_ZN6test101dE = global { i64, i64 } { i64 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i64), i64 16 }, align 8
+// GLOBAL-LP32: @_ZN6test101dE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 8 }, align 4
+// GLOBAL-ARM: @_ZN6test101dE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 16 }, align 4
void (D::*d)() = (void (C::*)()) (void (B::*)()) &A::foo;
}
-// It's not that the offsets are doubled on ARM, it's that they're left-shifted by 1.
-// CHECK-ARM: @_ZN6test101aE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 0 }, align 4
-// CHECK-ARM: @_ZN6test101bE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 8 }, align 4
-// CHECK-ARM: @_ZN6test101cE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 8 }, align 4
-// CHECK-ARM: @_ZN6test101dE = global { i32, i32 } { i32 ptrtoint (void (%"struct.test10::A"*)* @_ZN6test101A3fooEv to i32), i32 16 }, align 4
+
+namespace test11 {
+ struct A { virtual void a(); };
+ struct B : A {};
+ struct C : B { virtual void a(); };
+ void (C::*x)() = &C::a;
+
+ // GLOBAL-LP64: @_ZN6test111xE = global { i64, i64 } { i64 1, i64 0 }
+ // GLOBAL-LP32: @_ZN6test111xE = global { i32, i32 } { i32 1, i32 0 }
+ // GLOBAL-ARM: @_ZN6test111xE = global { i32, i32 } { i32 0, i32 1 }
+}
diff --git a/test/CodeGenCXX/member-init-anon-union.cpp b/test/CodeGenCXX/member-init-anon-union.cpp
index 1ff7537..4db31f0 100644
--- a/test/CodeGenCXX/member-init-anon-union.cpp
+++ b/test/CodeGenCXX/member-init-anon-union.cpp
@@ -2,8 +2,10 @@
// PR10531.
+int make_a();
+
static union {
- int a = 42;
+ int a = make_a();
char *b;
};
@@ -32,4 +34,4 @@ int g() {
// CHECK: define {{.*}}@"[[CONSTRUCT_GLOBAL]]C2Ev"
// CHECK-NOT: }
-// CHECK: store i32 42
+// CHECK: call {{.*}}@_Z6make_a
diff --git a/test/CodeGenCXX/member-init-ctor.cpp b/test/CodeGenCXX/member-init-ctor.cpp
deleted file mode 100644
index 2172394..0000000
--- a/test/CodeGenCXX/member-init-ctor.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - | FileCheck %s
-
-bool b();
-struct S {
- int n = b() ? S().n + 1 : 0;
-};
-
-S s;
-
-// CHECK: define {{.*}} @_ZN1SC2Ev(
-// CHECK-NOT }
-// CHECK: call {{.*}} @_Z1bv()
-// CHECK-NOT }
-// CHECK: call {{.*}} @_ZN1SC1Ev(
diff --git a/test/CodeGenCXX/member-pointer-type-convert.cpp b/test/CodeGenCXX/member-pointer-type-convert.cpp
deleted file mode 100644
index 2970a2e..0000000
--- a/test/CodeGenCXX/member-pointer-type-convert.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
-
-struct A;
-typedef int A::*param_t;
-struct {
- const char *name;
- param_t par;
-} *ptr;
-void test_ptr() { (void) ptr; } // forced use
-
-// CHECK: type { i8*, {{i..}} }
diff --git a/test/CodeGenCXX/microsoft-abi-array-cookies.cpp b/test/CodeGenCXX/microsoft-abi-array-cookies.cpp
new file mode 100644
index 0000000..e07b097
--- /dev/null
+++ b/test/CodeGenCXX/microsoft-abi-array-cookies.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+struct ClassWithoutDtor {
+ char x;
+};
+
+void check_array_no_cookies() {
+// CHECK: define void @"\01?check_array_no_cookies@@YAXXZ"() nounwind
+
+// CHECK: call noalias i8* @"\01??_U@YAPAXI@Z"(i32 42)
+ ClassWithoutDtor *array = new ClassWithoutDtor[42];
+
+// CHECK: call void @"\01??_V@YAXPAX@Z"(
+ delete [] array;
+
+}
+
+struct ClassWithDtor {
+ char x;
+ ~ClassWithDtor() {}
+};
+
+void check_array_cookies_simple() {
+// CHECK: define {{.*}} @"\01?check_array_cookies_simple@@YAXXZ"()
+
+ ClassWithDtor *array = new ClassWithDtor[42];
+// CHECK: [[ALLOCATED:%.*]] = call noalias i8* @"\01??_U@YAPAXI@Z"(i32 46)
+// 46 = 42 + size of cookie (4)
+// CHECK: [[COOKIE:%.*]] = bitcast i8* [[ALLOCATED]] to i32*
+// CHECK: store i32 42, i32* [[COOKIE]]
+// CHECK: [[ARRAY:%.*]] = getelementptr inbounds i8* [[ALLOCATED]], i64 4
+// CHECK: bitcast i8* [[ARRAY]] to [[CLASS:%.*]]*
+
+ delete [] array;
+// CHECK: [[ARRAY_AS_CHAR:%.*]] = bitcast [[CLASS]]* {{%.*}} to i8*
+// CHECK: getelementptr inbounds i8* [[ARRAY_AS_CHAR]], i64 -4
+}
+
+struct __attribute__((aligned(8))) ClassWithAlignment {
+ // FIXME: replace __attribute__((aligned(8))) with __declspec(align(8)) once
+ // http://llvm.org/bugs/show_bug.cgi?id=12631 is fixed.
+ int *x, *y;
+ ~ClassWithAlignment() {}
+};
+
+void check_array_cookies_aligned() {
+// CHECK: define {{.*}} @"\01?check_array_cookies_aligned@@YAXXZ"()
+ ClassWithAlignment *array = new ClassWithAlignment[42];
+// CHECK: [[ALLOCATED:%.*]] = call noalias i8* @"\01??_U@YAPAXI@Z"(i32 344)
+// 344 = 42*8 + size of cookie (8, due to alignment)
+// CHECK: [[COOKIE:%.*]] = bitcast i8* [[ALLOCATED]] to i32*
+// CHECK: store i32 42, i32* [[COOKIE]]
+// CHECK: [[ARRAY:%.*]] = getelementptr inbounds i8* [[ALLOCATED]], i64 8
+// CHECK: bitcast i8* [[ARRAY]] to [[CLASS:%.*]]*
+
+ delete [] array;
+// CHECK: [[ARRAY_AS_CHAR:%.*]] = bitcast [[CLASS]]*
+// CHECK: getelementptr inbounds i8* [[ARRAY_AS_CHAR]], i64 -8
+}
diff --git a/test/CodeGenCXX/microsoft-abi-constructors.cpp b/test/CodeGenCXX/microsoft-abi-constructors.cpp
new file mode 100644
index 0000000..ac27f13
--- /dev/null
+++ b/test/CodeGenCXX/microsoft-abi-constructors.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+class A {
+ public:
+ A() { }
+ ~A() { }
+};
+
+void no_contstructor_destructor_infinite_recursion() {
+ A a;
+
+// Make sure that the constructor doesn't call itself:
+// CHECK: define {{.*}} @"\01??0A@@QAE@XZ"
+// CHECK-NOT: call void @"\01??0A@@QAE@XZ"
+// CHECK: ret
+
+// Make sure that the destructor doesn't call itself:
+// CHECK: define {{.*}} @"\01??1A@@QAE@XZ"
+// CHECK-NOT: call void @"\01??1A@@QAE@XZ"
+// CHECK: ret
+}
diff --git a/test/CodeGenCXX/microsoft-abi-methods.cpp b/test/CodeGenCXX/microsoft-abi-methods.cpp
new file mode 100644
index 0000000..6b7f004
--- /dev/null
+++ b/test/CodeGenCXX/microsoft-abi-methods.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+class C {
+ public:
+ void simple_method() {}
+
+ void __cdecl cdecl_method() {}
+
+ void vararg_method(const char *fmt, ...) {}
+
+ static void static_method() {}
+
+ int a;
+};
+
+void call_simple_method() {
+ C instance;
+
+ instance.simple_method();
+// Make sure that the call uses the right calling convention:
+// CHECK: call x86_thiscallcc void @"\01?simple_method@C@@QAEXXZ"
+// CHECK: ret
+
+// Make sure that the definition uses the right calling convention:
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01?simple_method@C@@QAEXXZ"
+// CHECK: ret
+}
+
+void call_cdecl_method() {
+ C instance;
+ instance.cdecl_method();
+// Make sure that the call uses the right calling convention:
+// CHECK: call void @"\01?cdecl_method@C@@QAAXXZ"
+// CHECK: ret
+
+// Make sure that the definition uses the right calling convention:
+// CHECK: define linkonce_odr void @"\01?cdecl_method@C@@QAAXXZ"
+// CHECK: ret
+}
+
+void call_vararg_method() {
+ C instance;
+ instance.vararg_method("Hello");
+// Make sure that the call uses the right calling convention:
+// CHECK: call void (%class.C*, i8*, ...)* @"\01?vararg_method@C@@QAAXPBDZZ"
+// CHECK: ret
+
+// Make sure that the definition uses the right calling convention:
+// CHECK: define linkonce_odr void @"\01?vararg_method@C@@QAAXPBDZZ"
+}
+
+void call_static_method() {
+ C::static_method();
+// Make sure that the call uses the right calling convention:
+// CHECK: call void @"\01?static_method@C@@SAXXZ"
+// CHECK: ret
+
+// Make sure that the definition uses the right calling convention:
+// CHECK: define linkonce_odr void @"\01?static_method@C@@SAXXZ"
+}
+
+class Base {
+ public:
+ Base() {}
+ ~Base() {}
+};
+
+class Child: public Base { };
+
+void constructors() {
+ Child c;
+// Make sure that the Base constructor call in the Child constructor uses
+// the right calling convention:
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??0Child@@QAE@XZ"
+// CHECK: call x86_thiscallcc void @"\01??0Base@@QAE@XZ"
+// CHECK: ret
+
+// Make sure that the Base destructor call in the Child denstructor uses
+// the right calling convention:
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1Child@@QAE@XZ"
+// CHECK: call x86_thiscallcc void @"\01??1Base@@QAE@XZ"
+// CHECK: ret
+
+// Make sure that the Base destructor definition uses the right CC:
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1Base@@QAE@XZ"
+
+// Make sure that the Base constructor definition uses the right CC:
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??0Base@@QAE@XZ"
+}
diff --git a/test/CodeGenCXX/microsoft-abi-static-initializers.cpp b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp
new file mode 100644
index 0000000..d8b7899
--- /dev/null
+++ b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+struct S {
+ S() {}
+ ~S() {}
+} s;
+
+// CHECK: define internal void [[INIT_s:@.*global_var.*]] nounwind
+// CHECK: call x86_thiscallcc void @"\01??0S@@QAE@XZ"
+// CHECK: call i32 @atexit(void ()* @"__dtor_\01?s@@3US@@A")
+// CHECK: ret void
+
+// CHECK: define internal void @"__dtor_\01?s@@3US@@A"() nounwind {
+// CHECK: call x86_thiscallcc void @"\01??1S@@QAE@XZ"
+// CHECK: ret void
+
+// Force WeakODRLinkage by using templates
+class A {
+ public:
+ A() {}
+ ~A() {}
+};
+
+template<typename T>
+class B {
+ public:
+ static A foo;
+};
+
+template<typename T> A B<T>::foo;
+
+void force_usage() {
+ (void)B<int>::foo; // (void) - force usage
+}
+
+// CHECK: define internal void [[INIT_foo:@.*global_var.*]] nounwind
+// CHECK: call x86_thiscallcc void @"\01??0A@@QAE@XZ"
+// CHECK: call i32 @atexit(void ()* [[FOO_DTOR:@"__dtor_.*foo@.*]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??0A@@QAE@XZ"
+
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1A@@QAE@XZ"
+
+// CHECK: define internal void [[FOO_DTOR]]
+// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"{{.*}}foo
+// CHECK: ret void
+
+// CHECK: define internal void @_GLOBAL__I_a() nounwind {
+// CHECK: call void [[INIT_s]]
+// CHECK: call void [[INIT_foo]]
+// CHECK: ret void
diff --git a/test/CodeGenCXX/ms_wide_predefined_expr.cpp b/test/CodeGenCXX/ms_wide_predefined_expr.cpp
new file mode 100644
index 0000000..5f0bcde
--- /dev/null
+++ b/test/CodeGenCXX/ms_wide_predefined_expr.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -fms-extensions -triple i686-pc-win32 -emit-llvm -o - | FileCheck %s
+
+// CHECK: @L__FUNCTION__._Z4funcv = private constant [5 x i16] [i16 102, i16 117, i16 110, i16 99, i16 0], align 2
+
+void wprint(const wchar_t*);
+
+#define __STR2WSTR(str) L##str
+#define _STR2WSTR(str) __STR2WSTR(str)
+#define STR2WSTR(str) _STR2WSTR(str)
+
+void func() {
+ wprint(STR2WSTR(__FUNCTION__));
+}
+
+int main() {
+ func();
+
+ return 0;
+}
+
diff --git a/test/CodeGenCXX/pointers-to-data-members.cpp b/test/CodeGenCXX/pointers-to-data-members.cpp
index 90024e4..fe69cd5 100644
--- a/test/CodeGenCXX/pointers-to-data-members.cpp
+++ b/test/CodeGenCXX/pointers-to-data-members.cpp
@@ -240,3 +240,17 @@ namespace PR11487 {
// CHECK-GLOBAL: @_ZN7PR114871xE = global %"union.PR11487::U" { i64 -1, [8 x i8] zeroinitializer }, align 8
}
+
+namespace PR13097 {
+ struct X { int x; X(const X&); };
+ struct A {
+ int qq;
+ X x;
+ };
+ A f();
+ X g() { return f().*&A::x; }
+ // CHECK: define void @_ZN7PR130971gEv
+ // CHECK: call void @_ZN7PR130971fEv
+ // CHECK-NOT: memcpy
+ // CHECK: call void @_ZN7PR130971XC1ERKS0_
+}
diff --git a/test/CodeGenCXX/pr13396.cpp b/test/CodeGenCXX/pr13396.cpp
new file mode 100644
index 0000000..7d4e2ce
--- /dev/null
+++ b/test/CodeGenCXX/pr13396.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple i686-pc-linux-gnu %s -emit-llvm -o - | FileCheck %s
+struct foo {
+ template<typename T>
+ __attribute__ ((regparm (3))) foo(T x) {}
+ __attribute__ ((regparm (3))) foo();
+ __attribute__ ((regparm (3))) ~foo();
+};
+
+foo::foo() {
+ // CHECK: define void @_ZN3fooC1Ev(%struct.foo* inreg %this)
+ // CHECK: define void @_ZN3fooC2Ev(%struct.foo* inreg %this)
+}
+
+foo::~foo() {
+ // CHECK: define void @_ZN3fooD1Ev(%struct.foo* inreg %this)
+ // CHECK: define void @_ZN3fooD2Ev(%struct.foo* inreg %this)
+}
+
+void dummy() {
+ // FIXME: how can we explicitly instantiate a template constructor? Gcc and
+ // older clangs accept:
+ // template foo::foo(int x);
+ foo x(10);
+ // CHECK: define linkonce_odr void @_ZN3fooC1IiEET_(%struct.foo* inreg %this, i32 inreg %x)
+ // CHECK: define linkonce_odr void @_ZN3fooC2IiEET_(%struct.foo* inreg %this, i32 inreg %x)
+}
diff --git a/test/CodeGenCXX/pragma-visibility.cpp b/test/CodeGenCXX/pragma-visibility.cpp
index e54626e..11a38c1 100644
--- a/test/CodeGenCXX/pragma-visibility.cpp
+++ b/test/CodeGenCXX/pragma-visibility.cpp
@@ -17,14 +17,6 @@ int x2::y = 10;
#pragma GCC visibility pop
#pragma GCC visibility push(hidden)
-struct x3 {
- static int y;
-} __attribute((visibility("default")));
-int x3::y = 10;
-// CHECK: @_ZN2x31yE = global
-#pragma GCC visibility pop
-
-#pragma GCC visibility push(hidden)
template<class T> struct x4 {
static int y;
};
@@ -60,3 +52,23 @@ namespace n __attribute((visibility("default"))) {
// CHECK: define hidden void @_ZN1n1gEv
#pragma GCC visibility pop
}
+
+namespace test2 {
+#pragma GCC visibility push(default)
+#pragma GCC visibility push(hidden)
+ struct foo { // foo is hidden
+ };
+#pragma GCC visibility pop
+ struct foo; // declaration is ok, we ignore the default in the stack
+ template<typename T>
+ struct bar { // bar is default
+ static void f(){}
+ };
+#pragma GCC visibility pop
+ void zed() {
+ bar<foo>::f();
+ bar<int>::f();
+ }
+ // CHECK: define linkonce_odr hidden void @_ZN5test23barINS_3fooEE1fEv
+ // CHECK: define linkonce_odr void @_ZN5test23barIiE1fEv
+}
diff --git a/test/CodeGenCXX/rvalue-references.cpp b/test/CodeGenCXX/rvalue-references.cpp
index 1c25543..b8d47dc 100644
--- a/test/CodeGenCXX/rvalue-references.cpp
+++ b/test/CodeGenCXX/rvalue-references.cpp
@@ -10,7 +10,7 @@ B &getB();
// CHECK: define %struct.A* @_Z4getAv()
// CHECK: call %struct.B* @_Z4getBv()
// CHECK-NEXT: bitcast %struct.B*
-// CHECK-NEXT: getelementptr i8*
+// CHECK-NEXT: getelementptr inbounds i8*
// CHECK-NEXT: bitcast i8* {{.*}} to %struct.A*
// CHECK-NEXT: ret %struct.A*
A &&getA() { return static_cast<A&&>(getB()); }
diff --git a/test/CodeGenCXX/template-instantiation.cpp b/test/CodeGenCXX/template-instantiation.cpp
index 09b3a4f..b90716b 100644
--- a/test/CodeGenCXX/template-instantiation.cpp
+++ b/test/CodeGenCXX/template-instantiation.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -O1 -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -O1 -disable-llvm-optzns -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
// CHECK: @_ZN7PR100011xE = global
// CHECK-NOT: @_ZN7PR100014kBarE = external global i32
@@ -13,7 +13,7 @@
// CHECK-NOT: _ZTVN5test31SIiEE
// CHECK-NOT: _ZTSN5test31SIiEE
-// CHECK: define linkonce_odr void @_ZN5test21CIiEC1Ev(%"class.test2::C"* nocapture %this) unnamed_addr
+// CHECK: define linkonce_odr void @_ZN5test21CIiEC1Ev(%"class.test2::C"* %this) unnamed_addr
// CHECK: define linkonce_odr void @_ZN5test21CIiE6foobarIdEEvT_(
// CHECK: define available_externally void @_ZN5test21CIiE6zedbarEd(
diff --git a/test/CodeGenCXX/throw-expression-cleanup.cpp b/test/CodeGenCXX/throw-expression-cleanup.cpp
new file mode 100644
index 0000000..0c41bc6
--- /dev/null
+++ b/test/CodeGenCXX/throw-expression-cleanup.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 %s -emit-llvm -fcxx-exceptions -fexceptions -std=c++11 -o - | FileCheck %s
+// PR13359
+
+struct X {
+ ~X();
+};
+struct Error {
+ Error(const X&) noexcept;
+};
+
+void f() {
+ try {
+ throw Error(X());
+ } catch (...) { }
+}
+
+// CHECK: define void @_Z1fv
+// CHECK: call void @_ZN5ErrorC1ERK1X
+// CHECK: invoke void @__cxa_throw
+// CHECK: landingpad
+// CHECK: call void @_ZN1XD1Ev
+// CHECK-NOT: __cxa_free_exception
diff --git a/test/CodeGenCXX/virt-call-offsets.cpp b/test/CodeGenCXX/virt-call-offsets.cpp
deleted file mode 100644
index 5eef6fe..0000000
--- a/test/CodeGenCXX/virt-call-offsets.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
-
-struct A { virtual void a(); };
-struct B : A {};
-struct C : B { virtual void a(); };
-void (C::*x)() = &C::a;
-
-// CHECK: @x = global { i{{[0-9]+}}, i{{[0-9]+}} } { i{{[0-9]+}} 1, i{{[0-9]+}} 0 }
diff --git a/test/CodeGenCXX/virt-template-vtable.cpp b/test/CodeGenCXX/virt-template-vtable.cpp
index 25736fd..a6067d6 100644
--- a/test/CodeGenCXX/virt-template-vtable.cpp
+++ b/test/CodeGenCXX/virt-template-vtable.cpp
@@ -20,3 +20,13 @@ template class A<short>;
// CHECK: @_ZTV1AIlE = weak_odr unnamed_addr constant
// CHECK: @_ZTV1AIsE = weak_odr unnamed_addr constant
// CHECK: @_ZTV1AIiE = linkonce_odr unnamed_addr constant
+
+template<class T> struct C {
+ virtual void c() {}
+};
+struct D : C<int> {
+ virtual void d();
+};
+void D::d() {}
+
+// CHECK: define {{.*}}@_ZN1CIiE1cEv(
diff --git a/test/CodeGenCXX/virtual-destructor-calls.cpp b/test/CodeGenCXX/virtual-destructor-calls.cpp
index 1cc8bcc..7ef50b2 100644
--- a/test/CodeGenCXX/virtual-destructor-calls.cpp
+++ b/test/CodeGenCXX/virtual-destructor-calls.cpp
@@ -46,3 +46,14 @@ C::~C() { }
// CHECK: call void @_ZdlPv
// Base dtor: just an alias to B's base dtor.
+
+namespace PR12798 {
+ // A qualified call to a base class destructor should not undergo virtual
+ // dispatch. Template instantiation used to lose the qualifier.
+ struct A { virtual ~A(); };
+ template<typename T> void f(T *p) { p->A::~A(); }
+
+ // CHECK: define {{.*}} @_ZN7PR127981fINS_1AEEEvPT_(
+ // CHECK: call void @_ZN7PR127981AD1Ev(
+ template void f(A*);
+}
diff --git a/test/CodeGenCXX/visibility-inlines-hidden.cpp b/test/CodeGenCXX/visibility-inlines-hidden.cpp
index d660b1b..2bb1348 100644
--- a/test/CodeGenCXX/visibility-inlines-hidden.cpp
+++ b/test/CodeGenCXX/visibility-inlines-hidden.cpp
@@ -108,3 +108,21 @@ namespace PR11642 {
template class Foo<int>;
// CHECK: define weak_odr i32 @_ZN7PR116423FooIiE3fooEi
}
+
+// Test that clang implements the new gcc behaviour for inline functions.
+// GCC PR30066.
+namespace test3 {
+ inline void foo(void) {
+ }
+ template<typename T>
+ inline void zed() {
+ }
+ template void zed<float>();
+ void bar(void) {
+ foo();
+ zed<int>();
+ }
+ // CHECK: define weak_odr void @_ZN5test33zedIfEEvv
+ // CHECK: define linkonce_odr hidden void @_ZN5test33fooEv
+ // CHECK: define linkonce_odr hidden void @_ZN5test33zedIiEEvv
+}
diff --git a/test/CodeGenCXX/visibility.cpp b/test/CodeGenCXX/visibility.cpp
index 59fd7c2..0145039 100644
--- a/test/CodeGenCXX/visibility.cpp
+++ b/test/CodeGenCXX/visibility.cpp
@@ -1,10 +1,24 @@
-// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
-// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -fvisibility hidden -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-HIDDEN
+// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -fvisibility hidden -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-HIDDEN
#define HIDDEN __attribute__((visibility("hidden")))
#define PROTECTED __attribute__((visibility("protected")))
#define DEFAULT __attribute__((visibility("default")))
+namespace test30 {
+ // When H is hidden, it should make X hidden, even if the template argument
+ // is not.
+ struct H {
+ };
+ template<H *T>
+ struct X {
+ };
+ H DEFAULT a;
+ X<&a> b;
+ // CHECK: _ZN6test301bE = global
+ // CHECK-HIDDEN: _ZN6test301bE = hidden global
+}
+
namespace test25 {
template<typename T>
struct X {
@@ -28,6 +42,62 @@ namespace test28 {
// CHECK-HIDDEN: @_ZN6test285myvecE = hidden global
}
+namespace test29 {
+#pragma GCC visibility push(hidden)
+ struct RECT {
+ int top;
+ };
+ __attribute__ ((visibility ("default"))) extern RECT data_rect;
+ RECT data_rect = { -1};
+#pragma GCC visibility pop
+ // CHECK: @_ZN6test299data_rectE = global
+ // CHECK-HIDDEN: @_ZN6test299data_rectE = global
+}
+
+namespace test40 {
+ template<typename T>
+ struct foo {
+ DEFAULT static int bar;
+ };
+ template<typename T>
+ int foo<T>::bar;
+ template struct foo<int>;
+ // CHECK: _ZN6test403fooIiE3barE = weak_odr global
+ // CHECK-HIDDEN: _ZN6test403fooIiE3barE = weak_odr global
+}
+
+namespace test41 {
+ // Unlike gcc we propagate the information that foo not only is hidden, but
+ // has been explicitly marked as so. This lets us produce a hidden undefined
+ // reference to bar.
+ struct __attribute__((visibility("hidden"))) foo {};
+ extern foo bar;
+ foo *zed() {
+ return &bar;
+ }
+ // CHECK: @_ZN6test413barE = external hidden global
+ // CHECK-HIDDEN: @_ZN6test413barE = external hidden global
+}
+
+namespace test48 {
+ // Test that we use the visibility of struct foo when instantiating the
+ // template. Note that is a case where we disagree with gcc, it produces
+ // a default symbol.
+ struct HIDDEN foo {
+ };
+ DEFAULT foo x;
+
+ struct bar {
+ template<foo *z>
+ struct zed {
+ };
+ };
+
+ bar::zed<&x> y;
+ // CHECK: _ZN6test481yE = hidden global
+ // CHECK-HIDDEN: _ZN6test481yE = hidden global
+}
+
// CHECK: @_ZN5Test425VariableInHiddenNamespaceE = hidden global i32 10
// CHECK: @_ZN5Test71aE = hidden global
// CHECK: @_ZN5Test71bE = global
@@ -510,6 +580,8 @@ namespace PR10113 {
};
template class foo::bar<zed>;
// CHECK: define weak_odr void @_ZN7PR101133foo3barINS_3zedEE3zedEv
+
+ // FIXME: This should be hidden as zed is hidden.
// CHECK-HIDDEN: define weak_odr void @_ZN7PR101133foo3barINS_3zedEE3zedEv
}
@@ -541,6 +613,8 @@ namespace PR11690_2 {
};
template class foo::zed<baz>;
// CHECK: define weak_odr void @_ZN9PR11690_23foo3zedINS_3bazENS0_3barEE3barEv
+
+ // FIXME: This should be hidden as baz is hidden.
// CHECK-HIDDEN: define weak_odr void @_ZN9PR11690_23foo3zedINS_3bazENS0_3barEE3barEv
}
@@ -605,3 +679,436 @@ namespace test26 {
// CHECK: define void @_ZN6test261CIiE1fEv
// CHECK-HIDDEN: define void @_ZN6test261CIiE1fEv
}
+
+namespace test31 {
+ struct A {
+ struct HIDDEN B {
+ static void DEFAULT baz();
+ };
+ };
+ void f() {
+ A::B::baz();
+ }
+ // CHECK: declare void @_ZN6test311A1B3bazEv()
+ // CHECK-HIDDEN: declare void @_ZN6test311A1B3bazEv()
+}
+
+namespace test32 {
+ struct HIDDEN A {
+ struct DEFAULT B {
+ void DEFAULT baz();
+ };
+ };
+ void A::B::baz() {
+ }
+ // CHECK: define void @_ZN6test321A1B3bazEv
+ // CHECK-HIDDEN: define void @_ZN6test321A1B3bazEv
+}
+
+namespace test33 {
+ template<typename T>
+ class foo {
+ void bar() {}
+ };
+ struct HIDDEN zed {
+ };
+ template class DEFAULT foo<zed>;
+ // CHECK: define weak_odr void @_ZN6test333fooINS_3zedEE3barEv
+ // CHECK-HIDDEN: define weak_odr void @_ZN6test333fooINS_3zedEE3barEv
+}
+
+namespace test34 {
+ struct foo {
+ };
+ template<class T>
+ void bar() {}
+ template DEFAULT void bar<foo>();
+ // CHECK: define weak_odr void @_ZN6test343barINS_3fooEEEvv
+ // CHECK-HIDDEN: define weak_odr void @_ZN6test343barINS_3fooEEEvv
+}
+
+namespace test35 {
+ // This is a really ugly testcase. GCC propagates the DEFAULT in zed's
+ // definition. What we do instead is be conservative about merging
+ // implicit visibilities.
+ // FIXME: Maybe the best thing to do here is error? The test at least
+ // makes sure we don't produce a hidden symbol for foo<zed>::bar.
+ template<typename T>
+ struct DEFAULT foo {
+ void bar() {}
+ };
+ class zed;
+ template class foo<zed>;
+ class DEFAULT zed {
+ };
+ // CHECK: define weak_odr void @_ZN6test353fooINS_3zedEE3barEv
+ // CHECK-HIDDEN: define weak_odr void @_ZN6test353fooINS_3zedEE3barEv
+}
+
+namespace test36 {
+ template<typename T1, typename T2>
+ class foo {
+ void bar() {}
+ };
+ class DEFAULT S1 {};
+ struct HIDDEN S2 {};
+ template class foo<S1, S2>;
+ // CHECK: define weak_odr hidden void @_ZN6test363fooINS_2S1ENS_2S2EE3barEv
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test363fooINS_2S1ENS_2S2EE3barEv
+}
+
+namespace test37 {
+ struct HIDDEN foo {
+ };
+ template<class T>
+ DEFAULT void bar() {}
+ template DEFAULT void bar<foo>();
+ // CHECK: define weak_odr void @_ZN6test373barINS_3fooEEEvv
+ // CHECK-HIDDEN: define weak_odr void @_ZN6test373barINS_3fooEEEvv
+}
+
+namespace test38 {
+ template<typename T>
+ class DEFAULT foo {
+ void bar() {}
+ };
+ struct HIDDEN zed {
+ };
+ template class foo<zed>;
+ // CHECK: define weak_odr hidden void @_ZN6test383fooINS_3zedEE3barEv
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test383fooINS_3zedEE3barEv
+}
+
+namespace test39 {
+ class DEFAULT default_t;
+ class HIDDEN hidden_t;
+ template <class T> class A {
+ template <class U> class B {
+ HIDDEN void hidden() {}
+ void noattr() {}
+ template <class V> void temp() {}
+ };
+ };
+ template class DEFAULT A<hidden_t>;
+ template class DEFAULT A<hidden_t>::B<hidden_t>;
+ template void A<hidden_t>::B<hidden_t>::temp<default_t>();
+ template void A<hidden_t>::B<hidden_t>::temp<hidden_t>();
+
+ // CHECK: define weak_odr hidden void @_ZN6test391AINS_8hidden_tEE1BIS1_E6hiddenEv
+ // CHECK: define weak_odr void @_ZN6test391AINS_8hidden_tEE1BIS1_E6noattrEv
+ // CHECK: define weak_odr void @_ZN6test391AINS_8hidden_tEE1BIS1_E4tempINS_9default_tEEEvv
+
+ // GCC produces a default for this one. Why?
+ // CHECK: define weak_odr hidden void @_ZN6test391AINS_8hidden_tEE1BIS1_E4tempIS1_EEvv
+
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test391AINS_8hidden_tEE1BIS1_E6hiddenEv
+ // CHECK-HIDDEN: define weak_odr void @_ZN6test391AINS_8hidden_tEE1BIS1_E6noattrEv
+ // CHECK-HIDDEN: define weak_odr void @_ZN6test391AINS_8hidden_tEE1BIS1_E4tempINS_9default_tEEEvv
+
+ // GCC produces a default for this one. Why?
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test391AINS_8hidden_tEE1BIS1_E4tempIS1_EEvv
+}
+
+namespace test42 {
+ struct HIDDEN foo {
+ };
+ template <class P>
+ struct bar {
+ };
+ template <>
+ struct HIDDEN bar<foo> {
+ DEFAULT static void zed();
+ };
+ void bar<foo>::zed() {
+ }
+ // CHECK: define hidden void @_ZN6test423barINS_3fooEE3zedEv
+ // CHECK-HIDDEN: define hidden void @_ZN6test423barINS_3fooEE3zedEv
+}
+
+namespace test43 {
+ struct HIDDEN foo {
+ };
+ template <class P>
+ void bar() {
+ }
+ template <>
+ DEFAULT void bar<foo>() {
+ }
+ // CHECK: define hidden void @_ZN6test433barINS_3fooEEEvv
+ // CHECK-HIDDEN: define hidden void @_ZN6test433barINS_3fooEEEvv
+}
+
+namespace test44 {
+ template <typename T>
+ struct foo {
+ foo() {}
+ };
+ namespace {
+ struct bar;
+ }
+ template struct DEFAULT foo<bar>;
+ foo<bar> x;
+ // CHECK: define internal void @_ZN6test443fooINS_12_GLOBAL__N_13barEEC1Ev
+ // CHECK-HIDDEN: define internal void @_ZN6test443fooINS_12_GLOBAL__N_13barEEC1Ev
+}
+
+namespace test45 {
+ template <typename T>
+ struct foo {
+ template <typename T2>
+ struct bar {
+ bar() {};
+ };
+ };
+ namespace {
+ struct zed;
+ }
+ template struct DEFAULT foo<int>::bar<zed>;
+ foo<int>::bar<zed> x;
+ // CHECK: define internal void @_ZN6test453fooIiE3barINS_12_GLOBAL__N_13zedEEC1Ev
+ // CHECK-HIDDEN: define internal void @_ZN6test453fooIiE3barINS_12_GLOBAL__N_13zedEEC1Ev
+}
+
+namespace test46 {
+ template <typename T>
+ void foo() {
+ }
+ namespace {
+ struct bar;
+ }
+ template DEFAULT void foo<bar>();
+ void zed() {
+ foo<bar>();
+ }
+ // CHECK: define internal void @_ZN6test463fooINS_12_GLOBAL__N_13barEEEvv
+ // CHECK-HIDDEN: define internal void @_ZN6test463fooINS_12_GLOBAL__N_13barEEEvv
+}
+
+namespace test47 {
+ struct foo {
+ template <typename T>
+ static void bar() {
+ }
+ };
+ namespace {
+ struct zed;
+ }
+ template __attribute__((visibility("default"))) void foo::bar<zed>();
+ void baz() {
+ foo::bar<zed>();
+ }
+ // CHECK: define internal void @_ZN6test473foo3barINS_12_GLOBAL__N_13zedEEEvv
+ // CHECK-HIDDEN: define internal void @_ZN6test473foo3barINS_12_GLOBAL__N_13zedEEEvv
+}
+
+namespace test49 {
+ // Test that we use the visibility of struct foo when instantiating the
+ // template. Note that is a case where we disagree with gcc, it produces
+ // a default symbol.
+
+ struct HIDDEN foo {
+ };
+
+ DEFAULT foo x;
+
+ struct bar {
+ template<foo *z>
+ void zed() {
+ }
+ };
+
+ template void bar::zed<&x>();
+ // CHECK: define weak_odr hidden void @_ZN6test493bar3zedIXadL_ZNS_1xEEEEEvv
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test493bar3zedIXadL_ZNS_1xEEEEEvv
+}
+
+namespace test50 {
+ // Test that we use the visibility of struct foo when instantiating the
+ // template. Note that is a case where we disagree with gcc, it produces
+ // a default symbol.
+
+ struct HIDDEN foo {
+ };
+ DEFAULT foo x;
+ template<foo *z>
+ struct DEFAULT bar {
+ void zed() {
+ }
+ };
+ template void bar<&x>::zed();
+ // CHECK: define weak_odr hidden void @_ZN6test503barIXadL_ZNS_1xEEEE3zedEv
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test503barIXadL_ZNS_1xEEEE3zedEv
+}
+
+namespace test51 {
+ // Test that we use the visibility of struct foo when instantiating the
+ // template. Note that is a case where we disagree with gcc, it produces
+ // a default symbol.
+
+ struct HIDDEN foo {
+ };
+ DEFAULT foo x;
+ template<foo *z>
+ void DEFAULT zed() {
+ }
+ template void zed<&x>();
+ // CHECK: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_1xEEEEEvv
+ // CHECK-HIDDEN: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_1xEEEEEvv
+}
+
+namespace test52 {
+ // Test that we use the linkage of struct foo when instantiating the
+ // template. Note that is a case where we disagree with gcc, it produces
+ // an external symbol.
+
+ namespace {
+ struct foo {
+ };
+ }
+ template<foo *x>
+ void zed() {
+ }
+ void f() {
+ zed<nullptr>();
+ }
+ // CHECK: define internal void @_ZN6test523zedILPNS_12_GLOBAL__N_13fooE0EEEvv
+ // CHECK-HIDDEN: define internal void @_ZN6test523zedILPNS_12_GLOBAL__N_13fooE0EEEvv
+}
+
+namespace test53 {
+ template<typename _Tp > struct vector {
+ static void _M_fill_insert();
+ };
+#pragma GCC visibility push(hidden)
+ // GCC doesn't seem to use the visibility of enums at all, we do.
+ enum zed {v1};
+
+ // GCC fails to mark this specialization hidden, we mark it.
+ template<>
+ struct vector<int> {
+ static void _M_fill_insert();
+ };
+ void foo() {
+ vector<unsigned>::_M_fill_insert();
+ vector<int>::_M_fill_insert();
+ vector<zed>::_M_fill_insert();
+ }
+#pragma GCC visibility pop
+ // CHECK: declare void @_ZN6test536vectorIjE14_M_fill_insertEv
+ // CHECK-HIDDEN: declare void @_ZN6test536vectorIjE14_M_fill_insertEv
+ // CHECK: declare hidden void @_ZN6test536vectorIiE14_M_fill_insertEv
+ // CHECK-HIDDEN: declare hidden void @_ZN6test536vectorIiE14_M_fill_insertEv
+ // CHECK: declare hidden void @_ZN6test536vectorINS_3zedEE14_M_fill_insertEv
+ // CHECK-HIDDEN: declare hidden void @_ZN6test536vectorINS_3zedEE14_M_fill_insertEv
+}
+
+namespace test54 {
+ template <class T>
+ struct foo {
+ static void bar();
+ };
+#pragma GCC visibility push(hidden)
+ class zed {
+ zed(const zed &);
+ };
+ void bah() {
+ foo<zed>::bar();
+ }
+#pragma GCC visibility pop
+ // CHECK: declare hidden void @_ZN6test543fooINS_3zedEE3barEv
+ // CHECK-HIDDEN: declare hidden void @_ZN6test543fooINS_3zedEE3barEv
+}
+
+namespace test55 {
+ template <class T>
+ struct __attribute__((visibility("hidden"))) foo {
+ static void bar();
+ };
+ template <class T> struct foo;
+ void foobar() {
+ foo<int>::bar();
+ }
+ // CHECK: declare hidden void @_ZN6test553fooIiE3barEv
+ // CHECK-HIDDEN: declare hidden void @_ZN6test553fooIiE3barEv
+}
+
+namespace test56 {
+ template <class T> struct foo;
+ template <class T>
+ struct __attribute__((visibility("hidden"))) foo {
+ static void bar();
+ };
+ void foobar() {
+ foo<int>::bar();
+ }
+ // CHECK: declare hidden void @_ZN6test563fooIiE3barEv
+ // CHECK-HIDDEN: declare hidden void @_ZN6test563fooIiE3barEv
+}
+
+namespace test57 {
+#pragma GCC visibility push(hidden)
+ template <class T>
+ struct foo;
+ void bar(foo<int>*);
+ template <class T>
+ struct foo {
+ static void zed();
+ };
+ void bah() {
+ foo<int>::zed();
+ }
+#pragma GCC visibility pop
+ // CHECK: declare hidden void @_ZN6test573fooIiE3zedEv
+ // CHECK-HIDDEN: declare hidden void @_ZN6test573fooIiE3zedEv
+}
+
+namespace test58 {
+#pragma GCC visibility push(hidden)
+ struct foo;
+ template<typename T>
+ struct __attribute__((visibility("default"))) bar {
+ static void zed() {
+ }
+ };
+ void bah() {
+ bar<foo>::zed();
+ }
+#pragma GCC visibility pop
+ // CHECK: define linkonce_odr hidden void @_ZN6test583barINS_3fooEE3zedEv
+ // CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test583barINS_3fooEE3zedEv
+}
+
+namespace test59 {
+ DEFAULT int f();
+ HIDDEN int g();
+ typedef int (*foo)();
+ template<foo x, foo y>
+ void test() {}
+ void use() {
+ test<&g, &f>();
+ // CHECK: define linkonce_odr hidden void @_ZN6test594testIXadL_ZNS_1gEvEEXadL_ZNS_1fEvEEEEvv
+ // CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test594testIXadL_ZNS_1gEvEEXadL_ZNS_1fEvEEEEvv
+
+ test<&f, &g>();
+ // CHECK: define linkonce_odr hidden void @_ZN6test594testIXadL_ZNS_1fEvEEXadL_ZNS_1gEvEEEEvv
+ // CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test594testIXadL_ZNS_1fEvEEXadL_ZNS_1gEvEEEEvv
+ }
+}
+
+namespace test60 {
+ template<int i>
+ class __attribute__((visibility("hidden"))) a {};
+ template<int i>
+ class __attribute__((visibility("default"))) b {};
+ template<template<int> class x, template<int> class y>
+ void test() {}
+ void use() {
+ test<a, b>();
+ // CHECK: define linkonce_odr hidden void @_ZN6test604testINS_1aENS_1bEEEvv
+ // CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test604testINS_1aENS_1bEEEvv
+
+ test<b, a>();
+ // CHECK: define linkonce_odr hidden void @_ZN6test604testINS_1bENS_1aEEEvv
+ // CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test604testINS_1bENS_1aEEEvv
+ }
+}
diff --git a/test/CodeGenObjC/2008-11-12-Metadata.m b/test/CodeGenObjC/2008-11-12-Metadata.m
index afd7ce0..afd340a 100644
--- a/test/CodeGenObjC/2008-11-12-Metadata.m
+++ b/test/CodeGenObjC/2008-11-12-Metadata.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -fobjc-fragile-abi %s -o /dev/null
+// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 %s -o /dev/null
@interface A
@end
diff --git a/test/CodeGenObjC/2008-11-24-ConstCFStrings.m b/test/CodeGenObjC/2008-11-24-ConstCFStrings.m
index b37f66c..53eec2a 100644
--- a/test/CodeGenObjC/2008-11-24-ConstCFStrings.m
+++ b/test/CodeGenObjC/2008-11-24-ConstCFStrings.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -fobjc-fragile-abi %s -o - | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 %s -o - | FileCheck %s
// CHECK: _unnamed_cfstring_
diff --git a/test/CodeGenObjC/2010-03-17-StructRef.m b/test/CodeGenObjC/2010-03-17-StructRef.m
index fd0e646..ce24c8d 100644
--- a/test/CodeGenObjC/2010-03-17-StructRef.m
+++ b/test/CodeGenObjC/2010-03-17-StructRef.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-apple-darwin -fobjc-fragile-abi -o - | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -o - | FileCheck %s
// Bitfield references must not touch memory outside of the enclosing
// struct. Radar 7639995
typedef signed char BOOL;
diff --git a/test/CodeGenObjC/arc-blocks.m b/test/CodeGenObjC/arc-blocks.m
index 06acf01..2326bce 100644
--- a/test/CodeGenObjC/arc-blocks.m
+++ b/test/CodeGenObjC/arc-blocks.m
@@ -124,7 +124,7 @@ void test4(void) {
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]]
// CHECK-NEXT: call void @objc_release(i8* [[T1]])
- // CHECK: define internal void @__test4_block_invoke_
+ // CHECK: define internal void @__test4_block_invoke
// CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6
// CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]], align 8
// CHECK-NEXT: store i8* null, i8** [[SLOT]],
@@ -200,7 +200,7 @@ void test6(void) {
// CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]])
- // CHECK: define internal void @__test6_block_invoke_
+ // CHECK: define internal void @__test6_block_invoke
// CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6
// CHECK-NEXT: call i8* @objc_storeWeak(i8** [[SLOT]], i8* null)
// CHECK-NEXT: ret void
@@ -238,7 +238,7 @@ void test7(void) {
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[VAR]])
// CHECK-NEXT: ret void
- // CHECK: define internal void @__test7_block_invoke_
+ // CHECK: define internal void @__test7_block_invoke
// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* {{%.*}}, i32 0, i32 5
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[SLOT]])
// CHECK-NEXT: call void @test7_consume(i8* [[T0]])
@@ -521,3 +521,14 @@ void test15_helper(void (^block)(void), int x);
void test15(int a) {
test15_helper(^{ (void) a; }, ({ a; }));
}
+
+// rdar://11016025
+void test16() {
+ void (^BLKVAR)(void) = ^{ BLKVAR(); };
+
+ // CHECK: define void @test16(
+ // CHECK: [[BLKVAR:%.*]] = alloca void ()*, align 8
+ // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
+ // CHECK-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
+ // CHECK-NEXT: store void ()* null, void ()** [[BLKVAR]], align 8
+}
diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m
index 2a98b10..66a6a2f 100644
--- a/test/CodeGenObjC/arc.m
+++ b/test/CodeGenObjC/arc.m
@@ -9,6 +9,9 @@ void test0(id x) {
// CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]]
// CHECK-NEXT: call void @objc_release(i8* [[TMP]])
// CHECK-NEXT: ret void
+// rdar://12040837
+ // CHECK: declare extern_weak i8* @objc_retain(i8*) nonlazybind
+ // CHECK: declare extern_weak void @objc_release(i8*) nonlazybind
}
// CHECK: define i8* @test1(i8*
diff --git a/test/CodeGenObjC/assign.m b/test/CodeGenObjC/assign.m
index 82da800..bdc99c6 100644
--- a/test/CodeGenObjC/assign.m
+++ b/test/CodeGenObjC/assign.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
struct s0 {
int x;
diff --git a/test/CodeGenObjC/autorelease.m b/test/CodeGenObjC/autorelease.m
index 9260c3f..830929a 100644
--- a/test/CodeGenObjC/autorelease.m
+++ b/test/CodeGenObjC/autorelease.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-runtime-has-arc -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-runtime-has-arc -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-runtime=macosx-10.7 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-runtime=macosx-10.7 -o - %s | FileCheck %s
// rdar://8881826
// rdar://9412038
diff --git a/test/CodeGenObjC/bitfield-1.m b/test/CodeGenObjC/bitfield-1.m
index 648ab2a..ad52d8f 100644
--- a/test/CodeGenObjC/bitfield-1.m
+++ b/test/CodeGenObjC/bitfield-1.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
-// RUN: %clang_cc1 -triple i386-pc-linux-gnu -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-pc-linux-gnu -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
@interface Object
- (id) alloc;
diff --git a/test/CodeGenObjC/bitfield-access.m b/test/CodeGenObjC/bitfield-access.m
index 521d2e5..6d4c82a 100644
--- a/test/CodeGenObjC/bitfield-access.m
+++ b/test/CodeGenObjC/bitfield-access.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o %t1 %s
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t1 %s
// RUN: FileCheck -check-prefix=CHECK-I386 < %t1 %s
-// RUN: %clang_cc1 -triple armv6-apple-darwin10 -fobjc-fragile-abi -target-abi apcs-gnu -emit-llvm -o %t2 %s
+// RUN: %clang_cc1 -triple armv6-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -target-abi apcs-gnu -emit-llvm -o %t2 %s
// RUN: FileCheck -check-prefix=CHECK-ARM < %t2 %s
@interface I0 {
diff --git a/test/CodeGenObjC/bitfield-gnu.m b/test/CodeGenObjC/bitfield-gnu.m
index 7935bda..383907f 100644
--- a/test/CodeGenObjC/bitfield-gnu.m
+++ b/test/CodeGenObjC/bitfield-gnu.m
@@ -1,4 +1,4 @@
-// RUN: %clang -S -emit-llvm -fgnu-runtime -o %t %s
+// RUN: %clang -S -emit-llvm -fobjc-runtime=gcc -o %t %s
typedef enum { A1, A2 } A;
typedef struct { A a : 1; } B;
@interface Obj { B *b; } @end
diff --git a/test/CodeGenObjC/bitfield_encoding.m b/test/CodeGenObjC/bitfield_encoding.m
index 17fd4a4..1a516aa 100644
--- a/test/CodeGenObjC/bitfield_encoding.m
+++ b/test/CodeGenObjC/bitfield_encoding.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: grep "ib1b14" %t | count 1
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-fragile-abi -fgnu-runtime -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -fobjc-runtime=gcc -emit-llvm -o %t %s
// RUN: grep "ib32i1b33i14" %t | count 1
struct foo{
diff --git a/test/CodeGenObjC/block-6.m b/test/CodeGenObjC/block-6.m
index 140fa88..57b9ea3 100644
--- a/test/CodeGenObjC/block-6.m
+++ b/test/CodeGenObjC/block-6.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - -fblocks -triple x86_64-apple-darwin10 -fobjc-fragile-abi | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -fblocks -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s
// rdar://8893785
void MYFUNC() {
diff --git a/test/CodeGenObjC/block-var-layout.m b/test/CodeGenObjC/block-var-layout.m
index 1d0ce2d..c8065be 100644
--- a/test/CodeGenObjC/block-var-layout.m
+++ b/test/CodeGenObjC/block-var-layout.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -O0 -emit-llvm %s -o %t-64.s
+// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -O0 -emit-llvm %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
struct S {
diff --git a/test/CodeGenObjC/blocks-1.m b/test/CodeGenObjC/blocks-1.m
index 64da359..99a11f9 100644
--- a/test/CodeGenObjC/blocks-1.m
+++ b/test/CodeGenObjC/blocks-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-fragile-abi
+// RUN: %clang_cc1 %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5
// RUN: grep "_Block_object_dispose" %t | count 6
// RUN: grep "__copy_helper_block_" %t | count 4
// RUN: grep "__destroy_helper_block_" %t | count 4
@@ -8,7 +8,7 @@
// RUN: grep "_Block_object_assign" %t | count 4
// RUN: grep "objc_read_weak" %t | count 2
// RUN: grep "objc_assign_weak" %t | count 3
-// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-fragile-abi
+// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5
// RUN: grep "_Block_object_dispose" %t | count 6
// RUN: grep "__copy_helper_block_" %t | count 4
// RUN: grep "__destroy_helper_block_" %t | count 4
diff --git a/test/CodeGenObjC/blocks-2.m b/test/CodeGenObjC/blocks-2.m
index 591d63b..b04fa00 100644
--- a/test/CodeGenObjC/blocks-2.m
+++ b/test/CodeGenObjC/blocks-2.m
@@ -1,10 +1,10 @@
// We run this twice, once as Objective-C and once as Objective-C++.
-// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -fexceptions -triple i386-apple-darwin10 -fobjc-fragile-abi | FileCheck %s
-// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -fexceptions -triple i386-apple-darwin10 -fobjc-fragile-abi -x objective-c++ | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -fexceptions -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -fexceptions -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -x objective-c++ | FileCheck %s
// CHECK: define i8* @{{.*}}test0
-// CHECK: define internal void @__test0_block_invoke_0(
+// CHECK: define internal void @{{.*}}_block_invoke(
// CHECK: call i8* @objc_assign_strongCast(
// CHECK-NEXT: ret void
id test0(id x) {
diff --git a/test/CodeGenObjC/blocks-3.m b/test/CodeGenObjC/blocks-3.m
index 55e215c..a7edc12 100644
--- a/test/CodeGenObjC/blocks-3.m
+++ b/test/CodeGenObjC/blocks-3.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -fblocks -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fblocks -o %t %s
// 1x for the declaration
// 1x for the object-pointer byref copy helper
diff --git a/test/CodeGenObjC/blocks-4.m b/test/CodeGenObjC/blocks-4.m
index b3d0998..f5af9bc 100644
--- a/test/CodeGenObjC/blocks-4.m
+++ b/test/CodeGenObjC/blocks-4.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -fobjc-exceptions -fblocks -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fobjc-exceptions -fblocks -o %t %s
// rdar://7590273
void EXIT(id e);
diff --git a/test/CodeGenObjC/blocks-5.m b/test/CodeGenObjC/blocks-5.m
index caa8d66..18adb0f 100644
--- a/test/CodeGenObjC/blocks-5.m
+++ b/test/CodeGenObjC/blocks-5.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -fblocks -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fblocks -o %t %s
// rdar: // 8064140
diff --git a/test/CodeGenObjC/blocks.m b/test/CodeGenObjC/blocks.m
index f478c07..6c85da9 100644
--- a/test/CodeGenObjC/blocks.m
+++ b/test/CodeGenObjC/blocks.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -fblocks -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fblocks -o - %s | FileCheck %s
// test1. All of this is somehow testing rdar://6676764
struct S {
@@ -18,7 +18,7 @@ void foo(T *P) {
-(void) im0;
@end
-// CHECK: define internal i32 @"__8-[A im0]_block_invoke_0"(
+// CHECK: define internal i32 @"__8-[A im0]_block_invoke"(
@implementation A
-(void) im0 {
(void) ^{ return 1; }();
@@ -91,7 +91,7 @@ void test2(Test2 *x) {
// rdar://problem/9124263
// In the test above, check that the use in the invocation function
// doesn't require a read barrier.
-// CHECK: define internal void @__test2_block_invoke_
+// CHECK: define internal void @__test2_block_invoke
// CHECK: [[BLOCK:%.*]] = bitcast i8* {{%.*}} to [[BLOCK_T]]*
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]]
diff --git a/test/CodeGenObjC/boxing.m b/test/CodeGenObjC/boxing.m
new file mode 100644
index 0000000..9664298
--- /dev/null
+++ b/test/CodeGenObjC/boxing.m
@@ -0,0 +1,95 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
+
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+typedef signed char BOOL;
+#define nil ((void*) 0)
+
+@interface NSObject
++ (id)alloc;
+@end
+
+@interface NSNumber : NSObject
+@end
+
+@interface NSNumber (NSNumberCreation)
+- (id)initWithChar:(char)value;
+- (id)initWithUnsignedChar:(unsigned char)value;
+- (id)initWithShort:(short)value;
+- (id)initWithUnsignedShort:(unsigned short)value;
+- (id)initWithInt:(int)value;
+- (id)initWithUnsignedInt:(unsigned int)value;
+- (id)initWithLong:(long)value;
+- (id)initWithUnsignedLong:(unsigned long)value;
+- (id)initWithLongLong:(long long)value;
+- (id)initWithUnsignedLongLong:(unsigned long long)value;
+- (id)initWithFloat:(float)value;
+- (id)initWithDouble:(double)value;
+- (id)initWithBool:(BOOL)value;
+- (id)initWithInteger:(NSInteger)value;
+- (id)initWithUnsignedInteger:(NSUInteger)value;
+
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
+@end
+
+@interface NSString : NSObject
+@end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
+@end
+
+// CHECK: [[WithIntMeth:@".*"]] = internal global [15 x i8] c"numberWithInt:\00"
+// CHECK: [[WithIntSEL:@".*"]] = internal global i8* getelementptr inbounds ([15 x i8]* [[WithIntMeth]]
+// CHECK: [[WithCharMeth:@".*"]] = internal global [16 x i8] c"numberWithChar:\00"
+// CHECK: [[WithCharSEL:@".*"]] = internal global i8* getelementptr inbounds ([16 x i8]* [[WithCharMeth]]
+// CHECK: [[WithBoolMeth:@".*"]] = internal global [16 x i8] c"numberWithBool:\00"
+// CHECK: [[WithBoolSEL:@".*"]] = internal global i8* getelementptr inbounds ([16 x i8]* [[WithBoolMeth]]
+// CHECK: [[WithIntegerMeth:@".*"]] = internal global [19 x i8] c"numberWithInteger:\00"
+// CHECK: [[WithIntegerSEL:@".*"]] = internal global i8* getelementptr inbounds ([19 x i8]* [[WithIntegerMeth]]
+// CHECK: [[WithUnsignedIntegerMeth:@".*"]] = internal global [27 x i8] c"numberWithUnsignedInteger:\00"
+// CHECK: [[WithUnsignedIntegerSEL:@".*"]] = internal global i8* getelementptr inbounds ([27 x i8]* [[WithUnsignedIntegerMeth]]
+// CHECK: [[stringWithUTF8StringMeth:@".*"]] = internal global [22 x i8] c"stringWithUTF8String:\00"
+// CHECK: [[stringWithUTF8StringSEL:@".*"]] = internal global i8* getelementptr inbounds ([22 x i8]* [[stringWithUTF8StringMeth]]
+
+int main() {
+ // CHECK: load i8** [[WithIntSEL]]
+ int i; @(i);
+ // CHECK: load i8** [[WithCharSEL]]
+ signed char sc; @(sc);
+ // CHECK: load i8** [[WithBoolSEL]]
+ BOOL b; @(b);
+ // CHECK: load i8** [[WithBoolSEL]]
+ typeof(b) b2; @(b2);
+ // CHECK: load i8** [[WithBoolSEL]]
+ typedef const typeof(b) MyBOOL; MyBOOL b3; @(b3);
+ // CHECK: load i8** [[WithBoolSEL]]
+ @((BOOL)i);
+ // CHECK: load i8** [[WithIntegerSEL]]
+ @((NSInteger)i);
+ // CHECK: load i8** [[WithUnsignedIntegerSEL]]
+ @((NSUInteger)i);
+ // CHECK: load i8** [[stringWithUTF8StringSEL]]
+ const char *s; @(s);
+
+ typedef enum : NSInteger { Red, Green, Blue } Color;
+ // CHECK: load i8** [[WithIntegerSEL]]
+ @(Red);
+ Color col = Red;
+ // CHECK: load i8** [[WithIntegerSEL]]
+ @(col);
+}
diff --git a/test/CodeGenObjC/builtins.m b/test/CodeGenObjC/builtins.m
index cb2995d..0c57448 100644
--- a/test/CodeGenObjC/builtins.m
+++ b/test/CodeGenObjC/builtins.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
void test0(id receiver, SEL sel, const char *str) {
short s = ((short (*)(id, SEL, const char*)) objc_msgSend)(receiver, sel, str);
diff --git a/test/CodeGenObjC/category-class.m b/test/CodeGenObjC/category-class.m
index 5a82c14..92fd36c 100644
--- a/test/CodeGenObjC/category-class.m
+++ b/test/CodeGenObjC/category-class.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// PR7431
// CHECK: module asm "\09.lazy_reference .objc_class_name_A"
diff --git a/test/CodeGenObjC/class-type.m b/test/CodeGenObjC/class-type.m
index 45aae25..4eae1ae 100644
--- a/test/CodeGenObjC/class-type.m
+++ b/test/CodeGenObjC/class-type.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-fragile-abi -emit-llvm -o - %s
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
@interface I0 {
diff --git a/test/CodeGenObjC/constant-string-class.m b/test/CodeGenObjC/constant-string-class.m
index ea049a5..adad6b0 100644
--- a/test/CodeGenObjC/constant-string-class.m
+++ b/test/CodeGenObjC/constant-string-class.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -fno-constant-cfstrings -fconstant-string-class Foo -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fno-constant-cfstrings -fconstant-string-class Foo -emit-llvm -o %t %s
// RUN: FileCheck --check-prefix CHECK-FRAGILE < %t %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fno-constant-cfstrings -fconstant-string-class Foo -emit-llvm -o %t %s
diff --git a/test/CodeGenObjC/constant-strings.m b/test/CodeGenObjC/constant-strings.m
index c308d7a..cad634a 100644
--- a/test/CodeGenObjC/constant-strings.m
+++ b/test/CodeGenObjC/constant-strings.m
@@ -5,11 +5,11 @@
//
// CHECK-NEXT: @.str = {{.*}}constant [13 x i8] c"Hello World!\00", align 1
-// RUN: %clang_cc1 -fgnu-runtime -emit-llvm -o %t %s
+// RUN: %clang_cc1 -fobjc-runtime=gcc -emit-llvm -o %t %s
// RUN: FileCheck --check-prefix=CHECK-GNU < %t %s
// CHECK-GNU: NXConstantString
-// RUN: %clang_cc1 -fgnu-runtime -fconstant-string-class NSConstantString -emit-llvm -o %t %s
+// RUN: %clang_cc1 -fobjc-runtime=gcc -fconstant-string-class NSConstantString -emit-llvm -o %t %s
// RUN: FileCheck --check-prefix=CHECK-GNU-WITH-CLASS < %t %s
// CHECK-GNU-WITH-CLASS: NSConstantString
id a = @"Hello World!";
diff --git a/test/CodeGenObjC/deadcode_strip_used_var.m b/test/CodeGenObjC/deadcode_strip_used_var.m
index 3137ceb..b8a7034 100644
--- a/test/CodeGenObjC/deadcode_strip_used_var.m
+++ b/test/CodeGenObjC/deadcode_strip_used_var.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 %s -emit-llvm -o %t -triple i386-apple-darwin10 -fobjc-fragile-abi
+// RUN: %clang_cc1 %s -emit-llvm -o %t -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5
// RUN: grep "llvm.used" %t | count 1
-// RUN: %clang_cc1 %s -emit-llvm -o %t -triple x86_64-apple-darwin10 -fobjc-fragile-abi
+// RUN: %clang_cc1 %s -emit-llvm -o %t -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5
// RUN: grep "llvm.used" %t | count 1
diff --git a/test/CodeGenObjC/debug-info-block-helper.m b/test/CodeGenObjC/debug-info-block-helper.m
index 83db0c9..cf8c2a2 100644
--- a/test/CodeGenObjC/debug-info-block-helper.m
+++ b/test/CodeGenObjC/debug-info-block-helper.m
@@ -1,8 +1,8 @@
// REQUIRES: x86-64-registered-target
-// RUN: %clang_cc1 -emit-llvm -fblocks -g -triple x86_64-apple-darwin10 -fobjc-fragile-abi %s -o - | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -fblocks -g -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 %s -o - | FileCheck %s
extern void foo(void(^)(void));
-// CHECK: metadata !{i32 786478, i32 0, metadata !27, metadata !"__destroy_helper_block_", metadata !"__destroy_helper_block_", metadata !"", metadata !27, i32 24, metadata !43, i1 true, i1 true, i32 0, i32 0, null, i32 0, i1 false, void (i8*)* @__destroy_helper_block_, null, null, metadata !45, i32 24} ; [ DW_TAG_subprogram ]
+// CHECK: metadata !{i32 786478, i32 0, metadata !27, metadata !"__destroy_helper_block_", metadata !"__destroy_helper_block_", metadata !"", metadata !27, i32 24, metadata !37, i1 true, i1 true, i32 0, i32 0, null, i32 0, i1 false, void (i8*)* @__destroy_helper_block_, null, null, metadata !5, i32 24} ; [ DW_TAG_subprogram ]
@interface NSObject {
struct objc_object *isa;
diff --git a/test/CodeGenObjC/debug-info-crash.m b/test/CodeGenObjC/debug-info-crash.m
index 5504356..abbe2eb 100644
--- a/test/CodeGenObjC/debug-info-crash.m
+++ b/test/CodeGenObjC/debug-info-crash.m
@@ -1,5 +1,5 @@
// REQUIRES: x86-registered-target
-// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-fragile-abi -fblocks -g -S %s -o -
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fblocks -g -S %s -o -
// rdar://7556129
@implementation test
diff --git a/test/CodeGenObjC/debug-info-static-var.m b/test/CodeGenObjC/debug-info-static-var.m
index c65e77c..8602ffb 100644
--- a/test/CodeGenObjC/debug-info-static-var.m
+++ b/test/CodeGenObjC/debug-info-static-var.m
@@ -1,5 +1,5 @@
// REQUIRES: x86-64-registered-target
-// RUN: %clang_cc1 -g -triple x86_64-apple-darwin10 -fobjc-fragile-abi -S -masm-verbose -o - %s | FileCheck %s
+// RUN: %clang_cc1 -g -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -S -masm-verbose -o - %s | FileCheck %s
// Radar 8801045
// Do not emit AT_MIPS_linkage_name for static variable i
diff --git a/test/CodeGenObjC/debug-info-synthesis.m b/test/CodeGenObjC/debug-info-synthesis.m
index 7e263cf..bf8e6d1 100644
--- a/test/CodeGenObjC/debug-info-synthesis.m
+++ b/test/CodeGenObjC/debug-info-synthesis.m
@@ -31,4 +31,4 @@ int main(int argc, char *argv[]) {
}
// CHECK: !7 = metadata !{i32 {{.*}}, metadata !"./foo.h"
-// CHECK: !31 = metadata !{i32 {{.*}}, i32 0, metadata !7, metadata !"-[Foo dict]", metadata !"-[Foo dict]", metadata !"", metadata !7, i32 8, metadata !32, i1 true, i1 true, i32 0, i32 0, null, i32 320, i1 false, %1* (%0*, i8*)* @"\01-[Foo dict]", null, null, metadata !34, i32 8} ; [ DW_TAG_subprogram ]
+// CHECK: !29 = metadata !{i32 {{.*}}, i32 0, metadata !7, metadata !"-[Foo dict]", metadata !"-[Foo dict]", metadata !"", metadata !7, i32 8, metadata !30, i1 true, i1 true, i32 0, i32 0, null, i32 320, i1 false, %1* (%0*, i8*)* @"\01-[Foo dict]", null, null, metadata !1, i32 8} ; [ DW_TAG_subprogram ]
diff --git a/test/CodeGenObjC/encode-cstyle-method.m b/test/CodeGenObjC/encode-cstyle-method.m
index ea63023..f3243a3 100644
--- a/test/CodeGenObjC/encode-cstyle-method.m
+++ b/test/CodeGenObjC/encode-cstyle-method.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck -check-prefix LP64 %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck -check-prefix LP64 %s
// rdar: // 7445205
@interface Foo
diff --git a/test/CodeGenObjC/encode-test-6.m b/test/CodeGenObjC/encode-test-6.m
new file mode 100644
index 0000000..10681db
--- /dev/null
+++ b/test/CodeGenObjC/encode-test-6.m
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o %t %s
+// RUN: FileCheck < %t %s
+// rdar://11777609
+
+typedef struct {} Z;
+
+@interface A
+-(void)bar:(Z)a;
+-(void)foo:(Z)a : (char*)b : (Z)c : (double) d;
+@end
+
+@implementation A
+-(void)bar:(Z)a {}
+-(void)foo:(Z)a: (char*)b : (Z)c : (double) d {}
+@end
+
+// CHECK: internal global [14 x i8] c"v16@0:8{?=}16
+// CHECK: internal global [26 x i8] c"v32@0:8{?=}16*16{?=}24d24
+
diff --git a/test/CodeGenObjC/encode-test.m b/test/CodeGenObjC/encode-test.m
index 02af5da..3780068 100644
--- a/test/CodeGenObjC/encode-test.m
+++ b/test/CodeGenObjC/encode-test.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i686-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i686-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: FileCheck < %t %s
//
// CHECK: @"\01L_OBJC_METH_VAR_TYPE_34" = internal global [16 x i8] c"v12@0:4[3[4@]]8\00"
diff --git a/test/CodeGenObjC/exceptions.m b/test/CodeGenObjC/exceptions.m
index 24fb657..25780fd 100644
--- a/test/CodeGenObjC/exceptions.m
+++ b/test/CodeGenObjC/exceptions.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -fexceptions -fobjc-exceptions -O2 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fexceptions -fobjc-exceptions -O2 -o - %s | FileCheck %s
//
// <rdar://problem/7471679> [irgen] [eh] Exception code built with clang (x86_64) crashes
diff --git a/test/CodeGenObjC/fp2ret.m b/test/CodeGenObjC/fp2ret.m
index 9c956ae..2e65332 100644
--- a/test/CodeGenObjC/fp2ret.m
+++ b/test/CodeGenObjC/fp2ret.m
@@ -1,10 +1,10 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | \
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | \
// RUN: FileCheck --check-prefix=CHECK-X86_32 %s
//
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | \
// RUN: FileCheck --check-prefix=CHECK-X86_64 %s
//
-// RUN: %clang_cc1 -triple armv7-apple-darwin10 -fobjc-fragile-abi -emit-llvm -target-abi apcs-gnu -o - %s | \
+// RUN: %clang_cc1 -triple armv7-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -target-abi apcs-gnu -o - %s | \
// RUN: FileCheck --check-prefix=CHECK-ARMV7 %s
@interface A
diff --git a/test/CodeGenObjC/fpret.m b/test/CodeGenObjC/fpret.m
index bf111e0..cabef10 100644
--- a/test/CodeGenObjC/fpret.m
+++ b/test/CodeGenObjC/fpret.m
@@ -1,10 +1,10 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | \
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | \
// RUN: FileCheck --check-prefix=CHECK-X86_32 %s
//
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | \
// RUN: FileCheck --check-prefix=CHECK-X86_64 %s
//
-// RUN: %clang_cc1 -triple armv7-apple-darwin10 -fobjc-fragile-abi -emit-llvm -target-abi apcs-gnu -o - %s | \
+// RUN: %clang_cc1 -triple armv7-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -target-abi apcs-gnu -o - %s | \
// RUN: FileCheck --check-prefix=CHECK-ARMV7 %s
@interface A
diff --git a/test/CodeGenObjC/getter-property-mismatch.m b/test/CodeGenObjC/getter-property-mismatch.m
new file mode 100644
index 0000000..21ed6ee
--- /dev/null
+++ b/test/CodeGenObjC/getter-property-mismatch.m
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-apple-darwin -o - | FileCheck %s
+// rdar://11323676
+
+@interface NSDictionary @end
+@interface NSMutableDictionary : NSDictionary@end@interface CalDAVAddManagedAttachmentsTaskGroup {
+ NSMutableDictionary *_filenamesToServerLocation;
+}
+- (NSDictionary *)filenamesToServerLocation;
+@property (readwrite, retain) NSMutableDictionary *filenamesToServerLocation;
+@end
+
+@implementation CalDAVAddManagedAttachmentsTaskGroup
+@synthesize filenamesToServerLocation=_filenamesToServerLocation;
+@end
+
+// CHECK: [[CALL:%.*]] = call i8* @objc_getProperty
+// CHECK: [[ONE:%.*]] = bitcast i8* [[CALL:%.*]] to [[T1:%.*]]*
+// CHECK: [[TWO:%.*]] = bitcast [[T1]]* [[ONE]] to [[T2:%.*]]*
+// CHECK: ret [[T2]]* [[TWO]]
+
diff --git a/test/CodeGenObjC/getter-property-type-mismatch.m b/test/CodeGenObjC/getter-property-type-mismatch.m
new file mode 100644
index 0000000..c7e1c1a
--- /dev/null
+++ b/test/CodeGenObjC/getter-property-type-mismatch.m
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
+// rdar://11515196
+
+@interface NSArray @end
+
+@interface NSMutableArray : NSArray
+- (void) addObject;
+@end
+
+@interface BPXLAppDelegate
+
+- (NSArray *)arrayOfThings;
+
+@end
+
+
+@interface BPXLAppDelegate ()
+@property (retain, nonatomic) NSMutableArray *arrayOfThings;
+@end
+
+@implementation BPXLAppDelegate
+
+@synthesize arrayOfThings=_arrayOfThings;
+
+- (void)applicationDidFinishLaunching
+{
+ [self.arrayOfThings addObject];
+}
+
+@end
+
+// CHECK: define internal [[RET:%.*]]* @"\01-[BPXLAppDelegate arrayOfThings
+// CHECK: [[THREE:%.*]] = bitcast [[OPQ:%.*]]* [[TWO:%.*]] to [[RET]]*
+// CHECK: ret [[RET]]* [[THREE]]
+
diff --git a/test/CodeGenObjC/gnu-exceptions.m b/test/CodeGenObjC/gnu-exceptions.m
index 8917bf3..141747e 100644
--- a/test/CodeGenObjC/gnu-exceptions.m
+++ b/test/CodeGenObjC/gnu-exceptions.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -fexceptions -fobjc-exceptions -fgnu-runtime -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -fexceptions -fobjc-exceptions -fobjc-runtime=gcc -o - %s | FileCheck %s
void opaque(void);
void log(int i);
diff --git a/test/CodeGenObjC/id-isa-codegen.m b/test/CodeGenObjC/id-isa-codegen.m
index 8cac750..8717ce2 100644
--- a/test/CodeGenObjC/id-isa-codegen.m
+++ b/test/CodeGenObjC/id-isa-codegen.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck -check-prefix LP64 %s
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck -check-prefix LP32 %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck -check-prefix LP64 %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck -check-prefix LP32 %s
typedef struct objc_class *Class;
diff --git a/test/CodeGenObjC/image-info.m b/test/CodeGenObjC/image-info.m
index 613b272..030bcae 100644
--- a/test/CodeGenObjC/image-info.m
+++ b/test/CodeGenObjC/image-info.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: FileCheck --check-prefix CHECK-FRAGILE < %t %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o %t %s
diff --git a/test/CodeGenObjC/implicit-objc_msgSend.m b/test/CodeGenObjC/implicit-objc_msgSend.m
index aff0fe4..6fc3d46 100644
--- a/test/CodeGenObjC/implicit-objc_msgSend.m
+++ b/test/CodeGenObjC/implicit-objc_msgSend.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: grep -F 'declare i8* @objc_msgSend(i8*, i8*, ...)' %t
typedef struct objc_selector *SEL;
diff --git a/test/CodeGenObjC/interface-layout-64.m b/test/CodeGenObjC/interface-layout-64.m
index 4fdda45..16361a2 100644
--- a/test/CodeGenObjC/interface-layout-64.m
+++ b/test/CodeGenObjC/interface-layout-64.m
@@ -1,33 +1,32 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
// RUNX: llvm-gcc -m64 -emit-llvm -S -o %t %s &&
-// RUN: grep '@"OBJC_IVAR_$_I3._iv2" = global i64 8, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I3._iv3" = global i64 12, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I4._iv4" = global i64 13, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I5._iv5" = global i64 14, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I5._iv6_synth" = hidden global i64 16, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I5._iv7_synth" = hidden global i64 20, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I6.iv0" = global i64 0, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I8.b" = global i64 8, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I9.iv0" = global i64 0, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I10.iv1" = global i64 4, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '@"OBJC_IVAR_$_I12.iv2" = global i64 8, section "__DATA, __objc_ivar", align 8' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I3" = internal global .* { i32 0, i32 8, i32 13, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I4" = internal global .* { i32 0, i32 13, i32 14, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I5" = internal global .* { i32 0, i32 14, i32 24, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I6" = internal global .* { i32 2, i32 0, i32 1, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I8" = internal global .* { i32 0, i32 8, i32 16, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I9" = internal global .* { i32 2, i32 0, i32 4, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I10" = internal global .* { i32 0, i32 4, i32 5, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I11" = internal global .* { i32 0, i32 5, i32 5, .*' %t
-// RUN: grep '_OBJC_CLASS_RO_$_I12" = internal global .* { i32 0, i32 8, i32 12, .*' %t
-
+// CHECK: @"OBJC_IVAR_$_I3._iv2" = global i64 8, section "__DATA, __objc_ivar", align 8
+// CHECK: @"OBJC_IVAR_$_I3._iv3" = global i64 12, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I3" = internal global {{.*}} { i32 0, i32 8, i32 13
+// CHECK: @"OBJC_IVAR_$_I4._iv4" = global i64 13, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I4" = internal global {{.*}} { i32 0, i32 13, i32 14, {{.*}}
+// CHECK: @"OBJC_IVAR_$_I5._iv6_synth" = hidden global i64 16, section "__DATA, __objc_ivar", align 8
+// CHECK: @"OBJC_IVAR_$_I5._iv7_synth" = hidden global i64 20, section "__DATA, __objc_ivar", align 8
+// CHECK: @"OBJC_IVAR_$_I5._iv5" = global i64 14, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I5" = internal global {{.*}} { i32 0, i32 14, i32 24, {{.*}}
+// CHECK: @"OBJC_IVAR_$_I6.iv0" = global i64 0, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I6" = internal global {{.*}} { i32 2, i32 0, i32 1, {{.*}}
+// CHECK: @"OBJC_IVAR_$_I8.b" = global i64 8, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I8" = internal global {{.*}} { i32 0, i32 8, i32 16, {{.*}}
+// CHECK: @"OBJC_IVAR_$_I9.iv0" = global i64 0, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I9" = internal global {{.*}} { i32 2, i32 0, i32 4, {{.*}}
+// CHECK: @"OBJC_IVAR_$_I10.iv1" = global i64 4, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I10" = internal global {{.*}} { i32 0, i32 4, i32 5, {{.*}}
+// CHECK: _OBJC_CLASS_RO_$_I11" = internal global {{.*}} { i32 0, i32 5, i32 5, {{.*}}
+// CHECK: @"OBJC_IVAR_$_I12.iv2" = global i64 8, section "__DATA, __objc_ivar", align 8
+// CHECK: _OBJC_CLASS_RO_$_I12" = internal global {{.*}} { i32 0, i32 8, i32 12, {{.*}}
/*
Compare to:
- gcc -m64 -S -o - interface-layout-64.m | grep '^_OBJC_IVAR_$_*.*' -A 1
+ gcc -m64 -S -o - interface-layout-64.m | grep '^_OBJC_IVAR_$_*{{.*}}' -A 1
and
- gcc -m64 -S -o - interface-layout-64.m | grep '^l.*_CLASS_RO_$_I[0-9]*' -A 3
+ gcc -m64 -S -o - interface-layout-64.m | grep '^l{{.*}}_CLASS_RO_$_I[0-9]*' -A 3
*/
struct s0 {
diff --git a/test/CodeGenObjC/interface.m b/test/CodeGenObjC/interface.m
index 0ca64ec..4adc926 100644
--- a/test/CodeGenObjC/interface.m
+++ b/test/CodeGenObjC/interface.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -O3 -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -O3 -emit-llvm -o %t %s
// RUN: grep 'ret i32 385' %t
void *alloca();
diff --git a/test/CodeGenObjC/ivar-layout-64-bitfields.m b/test/CodeGenObjC/ivar-layout-64-bitfields.m
index acc734a..f2f0593 100644
--- a/test/CodeGenObjC/ivar-layout-64-bitfields.m
+++ b/test/CodeGenObjC/ivar-layout-64-bitfields.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
#ifdef __cplusplus
typedef bool _Bool;
diff --git a/test/CodeGenObjC/ivar-layout-array0-struct.m b/test/CodeGenObjC/ivar-layout-array0-struct.m
index 7ef32f6..267f947 100644
--- a/test/CodeGenObjC/ivar-layout-array0-struct.m
+++ b/test/CodeGenObjC/ivar-layout-array0-struct.m
@@ -1,5 +1,5 @@
// REQUIRES: x86-64-registered-target
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -O0 -S %s -o %t-64.s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -O0 -S %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
// rdar://8800513
diff --git a/test/CodeGenObjC/ivar-layout-no-optimize.m b/test/CodeGenObjC/ivar-layout-no-optimize.m
index 85bba8a..46a7034 100644
--- a/test/CodeGenObjC/ivar-layout-no-optimize.m
+++ b/test/CodeGenObjC/ivar-layout-no-optimize.m
@@ -1,7 +1,7 @@
// REQUIRES: x86-64-registered-target
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -O0 -S %s -o %t-64.s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -O0 -S %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
-// RUN: %clang_cc1 -x objective-c++ -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -O0 -S %s -o %t-64.s
+// RUN: %clang_cc1 -x objective-c++ -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -O0 -S %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
@interface NSObject {
diff --git a/test/CodeGenObjC/ivars.m b/test/CodeGenObjC/ivars.m
index 6c8a72d..1ccfa36 100644
--- a/test/CodeGenObjC/ivars.m
+++ b/test/CodeGenObjC/ivars.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
// RUN: %clang_cc1 -fobjc-gc -emit-llvm -o - %s
// rdar://6800926
diff --git a/test/CodeGenObjC/layout-bitfield-crash.m b/test/CodeGenObjC/layout-bitfield-crash.m
new file mode 100644
index 0000000..5d0e7bf
--- /dev/null
+++ b/test/CodeGenObjC/layout-bitfield-crash.m
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-gc -emit-llvm -g -o - %s
+// Check that this doesn't crash when compiled with debugging on.
+@class Foo;
+typedef struct Bar *BarRef;
+
+@interface Baz
+@end
+
+@interface Foo
+- (void) setFlag;
+@end
+
+@implementation Baz
+
+- (void) a:(BarRef)b
+{
+ Foo* view = (Foo*)self;
+ [view setFlag];
+}
+
+@end
+
+
+@implementation Foo
+{
+ int flag : 1;
+}
+
+- (void) setFlag
+{
+ if (!flag)
+ flag = 1;
+}
+
+@end
diff --git a/test/CodeGenObjC/link-errors.m b/test/CodeGenObjC/link-errors.m
index 0d19681..f2d9ddb 100644
--- a/test/CodeGenObjC/link-errors.m
+++ b/test/CodeGenObjC/link-errors.m
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: grep '.lazy_reference .objc_class_name_A' %t | count 1
// RUN: grep '.lazy_reference .objc_class_name_Unknown' %t | count 1
// RUN: grep '.lazy_reference .objc_class_name_Protocol' %t | count 1
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -DWITH_IMPL -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -DWITH_IMPL -emit-llvm -o %t %s
// RUN: grep '.lazy_reference .objc_class_name_Root' %t | count 1
@interface Root
diff --git a/test/CodeGenObjC/local-static-block.m b/test/CodeGenObjC/local-static-block.m
index 7a7b6f6..deea8ba 100644
--- a/test/CodeGenObjC/local-static-block.m
+++ b/test/CodeGenObjC/local-static-block.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin -fobjc-fragile-abi -emit-llvm %s -o %t-64.ll
+// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o %t-64.ll
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.ll %s
// rdar: // 8390455
diff --git a/test/CodeGenObjC/messages-2.m b/test/CodeGenObjC/messages-2.m
index 7c9d81c..ce6624a 100644
--- a/test/CodeGenObjC/messages-2.m
+++ b/test/CodeGenObjC/messages-2.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NF
// Most of this test is apparently just verifying that we don't crash.
diff --git a/test/CodeGenObjC/messages.m b/test/CodeGenObjC/messages.m
index 6f39602..a42677e 100644
--- a/test/CodeGenObjC/messages.m
+++ b/test/CodeGenObjC/messages.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-MAC
+// RUN: %clang_cc1 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-MAC
// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-MAC-NF
-// RUN: %clang_cc1 -fobjc-fragile-abi -fgnu-runtime -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-GNU
-// RUN: %clang_cc1 -fgnu-runtime -emit-llvm -o - %s | FileCheck %s -check-prefix CHECK-GNU-NF
+// RUN: %clang_cc1 -fobjc-runtime=gcc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-GNU
+// RUN: %clang_cc1 -fobjc-runtime=gnustep -emit-llvm -o - %s | FileCheck %s -check-prefix CHECK-GNU-NF
typedef struct {
int x;
diff --git a/test/CodeGenObjC/metadata-symbols-32.m b/test/CodeGenObjC/metadata-symbols-32.m
index a7bcf01..1df1560 100644
--- a/test/CodeGenObjC/metadata-symbols-32.m
+++ b/test/CodeGenObjC/metadata-symbols-32.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUNX: llvm-gcc -m32 -emit-llvm -S -o %t %s &&
// RUN: grep '@"\\01L_OBJC_CATEGORY_A_Cat" = internal global .*section "__OBJC,__category,regular,no_dead_strip", align 4' %t
diff --git a/test/CodeGenObjC/misc-atomic-property.m b/test/CodeGenObjC/misc-atomic-property.m
index f2645dc..4c8cc66 100644
--- a/test/CodeGenObjC/misc-atomic-property.m
+++ b/test/CodeGenObjC/misc-atomic-property.m
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar: //8808439
typedef struct {
diff --git a/test/CodeGenObjC/mrr-autorelease.m b/test/CodeGenObjC/mrr-autorelease.m
index f7a13fd..773fdbc 100644
--- a/test/CodeGenObjC/mrr-autorelease.m
+++ b/test/CodeGenObjC/mrr-autorelease.m
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar://8881826
// rdar://9423507
diff --git a/test/CodeGenObjC/nested-rethrow.m b/test/CodeGenObjC/nested-rethrow.m
index 5576c16..bff52c3 100644
--- a/test/CodeGenObjC/nested-rethrow.m
+++ b/test/CodeGenObjC/nested-rethrow.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -fobjc-exceptions %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fobjc-exceptions %s -o - | FileCheck %s
extern int printf(const char*, ...);
diff --git a/test/CodeGenObjC/next-objc-dispatch.m b/test/CodeGenObjC/next-objc-dispatch.m
index 4288b2d..6afd658 100644
--- a/test/CodeGenObjC/next-objc-dispatch.m
+++ b/test/CodeGenObjC/next-objc-dispatch.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s \
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s \
// RUN: -fobjc-dispatch-method=legacy | \
// RUN: FileCheck -check-prefix CHECK-FRAGILE_LEGACY %s
//
diff --git a/test/CodeGenObjC/no-category-class.m b/test/CodeGenObjC/no-category-class.m
index 3969f91..0c672f7 100644
--- a/test/CodeGenObjC/no-category-class.m
+++ b/test/CodeGenObjC/no-category-class.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
@interface NSObject
@end
diff --git a/test/CodeGenObjC/nonlazy-msgSend.m b/test/CodeGenObjC/nonlazy-msgSend.m
index 73157c7..7c349b2 100644
--- a/test/CodeGenObjC/nonlazy-msgSend.m
+++ b/test/CodeGenObjC/nonlazy-msgSend.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: grep -F 'declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind' %t
void f0(id x) {
diff --git a/test/CodeGenObjC/ns-constant-strings.m b/test/CodeGenObjC/ns-constant-strings.m
index d04793c..ccaacaf 100644
--- a/test/CodeGenObjC/ns-constant-strings.m
+++ b/test/CodeGenObjC/ns-constant-strings.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -fno-constant-cfstrings -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fno-constant-cfstrings -emit-llvm -o %t %s
// RUN: FileCheck --check-prefix CHECK-FRAGILE < %t %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fno-constant-cfstrings -emit-llvm -o %t %s
diff --git a/test/CodeGenObjC/objc-align.m b/test/CodeGenObjC/objc-align.m
index f3c586e..324740c 100644
--- a/test/CodeGenObjC/objc-align.m
+++ b/test/CodeGenObjC/objc-align.m
@@ -1,7 +1,7 @@
// 32-bit
// RUNX: llvm-gcc -m32 -emit-llvm -S -o %t %s &&
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
// RUN: grep '@"\\01L_OBJC_CATEGORY_A_Cat" = internal global .*, section "__OBJC,__category,regular,no_dead_strip", align 4' %t
// RUN: grep '@"\\01L_OBJC_CLASS_A" = internal global .*, section "__OBJC,__class,regular,no_dead_strip", align 4' %t
// RUN: grep '@"\\01L_OBJC_CLASS_C" = internal global .*, section "__OBJC,__class,regular,no_dead_strip", align 4' %t
diff --git a/test/CodeGenObjC/objc-assign-ivar.m b/test/CodeGenObjC/objc-assign-ivar.m
index d0a1a0f..0dc9ec2 100644
--- a/test/CodeGenObjC/objc-assign-ivar.m
+++ b/test/CodeGenObjC/objc-assign-ivar.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_ivar' %t | count 14
typedef struct {
diff --git a/test/CodeGenObjC/objc-gc-aggr-assign.m b/test/CodeGenObjC/objc-gc-aggr-assign.m
index dfdf02e..6c2baac 100644
--- a/test/CodeGenObjC/objc-gc-aggr-assign.m
+++ b/test/CodeGenObjC/objc-gc-aggr-assign.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o - %s | FileCheck -check-prefix C %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o - %s | FileCheck -check-prefix CP %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o - %s | FileCheck -check-prefix C %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o - %s | FileCheck -check-prefix CP %s
static int count;
diff --git a/test/CodeGenObjC/objc-literal-debugger-test.m b/test/CodeGenObjC/objc-literal-debugger-test.m
index 389ef22..5f69fd5 100644
--- a/test/CodeGenObjC/objc-literal-debugger-test.m
+++ b/test/CodeGenObjC/objc-literal-debugger-test.m
@@ -1,7 +1,29 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdebugger-objc-literal -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdebugger-objc-literal -emit-llvm -o - %s -DINCLUDE_INTERFACES=1 | FileCheck %s
+
+// We need two different RUN lines here because the first time a class/method is absent,
+// it will be added for -fdebugger-objc-literal.
+
+#ifdef INCLUDE_INTERFACES
+@interface NSObject
+@end
+
+@interface NSNumber : NSObject
+@end
+
+@interface NSArray : NSObject
+@end
+
+@interface NSDictionary : NSObject
+@end
+
+@interface NSString : NSObject
+@end
+#endif
int main() {
- id l = @'a';
+ // object literals.
+ id l;
l = @'a';
l = @42;
l = @-42;
@@ -11,6 +33,21 @@ int main() {
l = @__objc_no;
l = @{ @"name":@666 };
l = @[ @"foo", @"bar" ];
+
+#if __has_feature(objc_boxed_expressions)
+ // boxed expressions.
+ id b;
+ b = @('a');
+ b = @(42);
+ b = @(-42);
+ b = @(42u);
+ b = @(3.141592654f);
+ b = @(__objc_yes);
+ b = @(__objc_no);
+ b = @("hello");
+#else
+#error "boxed expressions not supported"
+#endif
}
// CHECK: declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind
diff --git a/test/CodeGenObjC/objc-read-weak-byref.m b/test/CodeGenObjC/objc-read-weak-byref.m
index 8fe1436..94eca28 100644
--- a/test/CodeGenObjC/objc-read-weak-byref.m
+++ b/test/CodeGenObjC/objc-read-weak-byref.m
@@ -1,7 +1,7 @@
// REQUIRES: x86-registered-target,x86-64-registered-target
-// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -S %s -o %t-64.s
+// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -S %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
-// RUN: %clang_cc1 -fblocks -fobjc-gc -triple i386-apple-darwin -fobjc-fragile-abi -S %s -o %t-32.s
+// RUN: %clang_cc1 -fblocks -fobjc-gc -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -S %s -o %t-32.s
// RUN: FileCheck -check-prefix LP32 --input-file=%t-32.s %s
@interface NSObject
diff --git a/test/CodeGenObjC/objc2-assign-global.m b/test/CodeGenObjC/objc2-assign-global.m
index 36c95f7..147aa3d 100644
--- a/test/CodeGenObjC/objc2-assign-global.m
+++ b/test/CodeGenObjC/objc2-assign-global.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_global' %t | count 26
@class NSObject;
diff --git a/test/CodeGenObjC/objc2-new-gc-api-strongcast.m b/test/CodeGenObjC/objc2-new-gc-api-strongcast.m
index 1044ba5..5619ab3 100644
--- a/test/CodeGenObjC/objc2-new-gc-api-strongcast.m
+++ b/test/CodeGenObjC/objc2-new-gc-api-strongcast.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fblocks -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fblocks -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_strongCast' %t | count 4
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fblocks -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fblocks -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_strongCast' %t | count 4
@interface DSATextSearch @end
diff --git a/test/CodeGenObjC/objc2-no-write-barrier.m b/test/CodeGenObjC/objc2-no-write-barrier.m
index d439368..ece6b9d 100644
--- a/test/CodeGenObjC/objc2-no-write-barrier.m
+++ b/test/CodeGenObjC/objc2-no-write-barrier.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep 'objc_assign' %t | count 0
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep 'objc_assign' %t | count 0
typedef struct {
diff --git a/test/CodeGenObjC/objc2-retain-codegen.m b/test/CodeGenObjC/objc2-retain-codegen.m
index d5b473e..594e3df 100644
--- a/test/CodeGenObjC/objc2-retain-codegen.m
+++ b/test/CodeGenObjC/objc2-retain-codegen.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-fragile-abi -fobjc-gc-only -emit-llvm -o %t %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-unknown-unknown -fobjc-fragile-abi -fobjc-gc-only -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc-only -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc-only -emit-llvm -o %t %s
@interface I0 {
I0 *_f0;
diff --git a/test/CodeGenObjC/objc2-strong-cast-1.m b/test/CodeGenObjC/objc2-strong-cast-1.m
index 9bb750f..4417084 100644
--- a/test/CodeGenObjC/objc2-strong-cast-1.m
+++ b/test/CodeGenObjC/objc2-strong-cast-1.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-unknown-unknown -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
@interface I {
__attribute__((objc_gc(strong))) int *i_IdocumentIDs;
diff --git a/test/CodeGenObjC/objc2-weak-assign.m b/test/CodeGenObjC/objc2-weak-assign.m
index e5c67c5..13ce338 100644
--- a/test/CodeGenObjC/objc2-weak-assign.m
+++ b/test/CodeGenObjC/objc2-weak-assign.m
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -e "objc_assign_weak" %t | grep -e "call" | count 6
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -e "objc_assign_weak" %t | grep -e "call" | count 6
__weak id* x;
diff --git a/test/CodeGenObjC/objc2-weak-block-call.m b/test/CodeGenObjC/objc2-weak-block-call.m
index 94c54e7..2543494 100644
--- a/test/CodeGenObjC/objc2-weak-block-call.m
+++ b/test/CodeGenObjC/objc2-weak-block-call.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -emit-llvm %s -o - | FileCheck -check-prefix LP64 %s
-// RUN: %clang_cc1 -fblocks -fobjc-gc -triple i386-apple-darwin -fobjc-fragile-abi -emit-llvm %s -o - | FileCheck -check-prefix LP64 %s
+// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o - | FileCheck -check-prefix LP64 %s
+// RUN: %clang_cc1 -fblocks -fobjc-gc -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o - | FileCheck -check-prefix LP64 %s
@interface MyView
- (void)MyView_sharedInit;
diff --git a/test/CodeGenObjC/objc2-weak-compare.m b/test/CodeGenObjC/objc2-weak-compare.m
index 75cf689..d8d5459 100644
--- a/test/CodeGenObjC/objc2-weak-compare.m
+++ b/test/CodeGenObjC/objc2-weak-compare.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
-// RUN: %clang_cc1 -x objective-c++ -triple i386-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
@interface PBXTarget
{
diff --git a/test/CodeGenObjC/objc2-weak-ivar-debug.m b/test/CodeGenObjC/objc2-weak-ivar-debug.m
index 83262a8..8c323b7 100644
--- a/test/CodeGenObjC/objc2-weak-ivar-debug.m
+++ b/test/CodeGenObjC/objc2-weak-ivar-debug.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -g -emit-llvm -o - %s
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -g -emit-llvm -o - %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -g -emit-llvm -o - %s
-// RUN: %clang_cc1 -x objective-c++ -triple i386-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -g -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -g -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -g -emit-llvm -o - %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -g -emit-llvm -o - %s
+// RUN: %clang_cc1 -x objective-c++ -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -g -emit-llvm -o - %s
// rdar://7252252
@interface Loop {
diff --git a/test/CodeGenObjC/objc2-weak-ivar.m b/test/CodeGenObjC/objc2-weak-ivar.m
index 78ccdf8..0c8503e 100644
--- a/test/CodeGenObjC/objc2-weak-ivar.m
+++ b/test/CodeGenObjC/objc2-weak-ivar.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
@class NSObject;
@interface Foo {
diff --git a/test/CodeGenObjC/objc2-write-barrier-2.m b/test/CodeGenObjC/objc2-write-barrier-2.m
index eae2551..6bc2f50 100644
--- a/test/CodeGenObjC/objc2-write-barrier-2.m
+++ b/test/CodeGenObjC/objc2-write-barrier-2.m
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_global' %t | count 7
// RUN: grep -F '@objc_assign_ivar' %t | count 5
// RUN: grep -F '@objc_assign_strongCast' %t | count 8
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_global' %t | count 7
// RUN: grep -F '@objc_assign_ivar' %t | count 5
// RUN: grep -F '@objc_assign_strongCast' %t | count 8
diff --git a/test/CodeGenObjC/objc2-write-barrier-4.m b/test/CodeGenObjC/objc2-write-barrier-4.m
index 4089920..d01ed19 100644
--- a/test/CodeGenObjC/objc2-write-barrier-4.m
+++ b/test/CodeGenObjC/objc2-write-barrier-4.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep objc_assign_global %t | count 3
// RUN: grep objc_assign_strongCast %t | count 2
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep objc_assign_global %t | count 3
// RUN: grep objc_assign_strongCast %t | count 2
diff --git a/test/CodeGenObjC/objc2-write-barrier-5.m b/test/CodeGenObjC/objc2-write-barrier-5.m
index 122fa9f..65a71a5 100644
--- a/test/CodeGenObjC/objc2-write-barrier-5.m
+++ b/test/CodeGenObjC/objc2-write-barrier-5.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep objc_assign_ivar %t | count 0
// RUN: grep objc_assign_strongCast %t | count 8
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep objc_assign_ivar %t | count 0
// RUN: grep objc_assign_strongCast %t | count 8
diff --git a/test/CodeGenObjC/objc2-write-barrier.m b/test/CodeGenObjC/objc2-write-barrier.m
index bf2dfb9..7634dc9 100644
--- a/test/CodeGenObjC/objc2-write-barrier.m
+++ b/test/CodeGenObjC/objc2-write-barrier.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_global' %t | count 21
// RUN: grep -F '@objc_assign_ivar' %t | count 11
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o %t %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o %t %s
// RUN: grep -F '@objc_assign_global' %t | count 21
// RUN: grep -F '@objc_assign_ivar' %t | count 11
diff --git a/test/CodeGenObjC/object-incr-decr-1.m b/test/CodeGenObjC/object-incr-decr-1.m
index 19c12cb..c1181a2 100644
--- a/test/CodeGenObjC/object-incr-decr-1.m
+++ b/test/CodeGenObjC/object-incr-decr-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm %s -o %t
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o %t
@interface Foo
{
diff --git a/test/CodeGenObjC/objfw.m b/test/CodeGenObjC/objfw.m
new file mode 100644
index 0000000..98e3fb0
--- /dev/null
+++ b/test/CodeGenObjC/objfw.m
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fobjc-runtime=objfw -emit-llvm -o - %s | FileCheck %s
+
+// Test the ObjFW runtime.
+
+@interface Test0
++ (void) test;
+@end
+void test0(void) {
+ [Test0 test];
+}
+// CHECK: define void @test0()
+// CHECK: [[T0:%.*]] = call i8* (i8*, i8*, ...)* (i8*, i8*)* @objc_msg_lookup(i8* bitcast (i64* @_OBJC_CLASS_Test0 to i8*),
+// CHECK-NEXT: [[T1:%.*]] = bitcast i8* (i8*, i8*, ...)* [[T0]] to void (i8*, i8*)*
+// CHECK-NEXT: call void [[T1]](i8* bitcast (i64* @_OBJC_CLASS_Test0 to i8*),
+// CHECK-NEXT: ret void
diff --git a/test/CodeGenObjC/predefined-expr.m b/test/CodeGenObjC/predefined-expr.m
index 009bbcd..d5866bb 100644
--- a/test/CodeGenObjC/predefined-expr.m
+++ b/test/CodeGenObjC/predefined-expr.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 %s -emit-llvm -o - | FileCheck %s
// CHECK: @"__func__.-[Foo instanceTest1]" = private unnamed_addr constant [21 x i8] c"-[Foo instanceTest1]\00"
// CHECK: @"__func__.-[Foo instanceTest2:]" = private unnamed_addr constant [22 x i8] c"-[Foo instanceTest2:]\00"
diff --git a/test/CodeGenObjC/property-complex.m b/test/CodeGenObjC/property-complex.m
index 3cdd2ec..027f6d6 100644
--- a/test/CodeGenObjC/property-complex.m
+++ b/test/CodeGenObjC/property-complex.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s
@interface I0 {
@public
diff --git a/test/CodeGenObjC/property-ref-cast-to-void.m b/test/CodeGenObjC/property-ref-cast-to-void.m
index ad1689f..ae2a458 100644
--- a/test/CodeGenObjC/property-ref-cast-to-void.m
+++ b/test/CodeGenObjC/property-ref-cast-to-void.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar: // 8399655
@interface TestClass
diff --git a/test/CodeGenObjC/protocol-in-extended-class.m b/test/CodeGenObjC/protocol-in-extended-class.m
index a924084..b919d5f 100644
--- a/test/CodeGenObjC/protocol-in-extended-class.m
+++ b/test/CodeGenObjC/protocol-in-extended-class.m
@@ -1,7 +1,7 @@
// REQUIRES: x86-registered-target,x86-64-registered-target
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -S %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
-// RUN: %clang_cc1 -triple i386-apple-darwin -fobjc-fragile-abi -S %s -o %t-32.s
+// RUN: %clang_cc1 -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -S %s -o %t-32.s
// RUN: FileCheck -check-prefix LP32 --input-file=%t-32.s %s
@protocol MyProtocol
diff --git a/test/CodeGenObjC/protocols-lazy.m b/test/CodeGenObjC/protocols-lazy.m
index 1c551fb..877d492 100644
--- a/test/CodeGenObjC/protocols-lazy.m
+++ b/test/CodeGenObjC/protocols-lazy.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin8 -fobjc-fragile-abi -o %t %s
+// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin8 -fobjc-runtime=macosx-fragile-10.5 -o %t %s
// RUNX: llvm-gcc -S -emit-llvm -o %t %s &&
// No object generated
diff --git a/test/CodeGenObjC/rdr-6732143-dangling-block-reference.m b/test/CodeGenObjC/rdr-6732143-dangling-block-reference.m
index a93ca03..fb808b3 100644
--- a/test/CodeGenObjC/rdr-6732143-dangling-block-reference.m
+++ b/test/CodeGenObjC/rdr-6732143-dangling-block-reference.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-fragile-abi -emit-llvm -fobjc-exceptions %s -o -
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fobjc-exceptions %s -o -
void f0(id x) {
@synchronized (x) {
diff --git a/test/CodeGenObjC/super-message-fragileabi.m b/test/CodeGenObjC/super-message-fragileabi.m
index 0135919..b6c9086 100644
--- a/test/CodeGenObjC/super-message-fragileabi.m
+++ b/test/CodeGenObjC/super-message-fragileabi.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o - | FileCheck %s
@class Some;
diff --git a/test/CodeGenObjC/synchronized.m b/test/CodeGenObjC/synchronized.m
index 4997bb7..1f01282 100644
--- a/test/CodeGenObjC/synchronized.m
+++ b/test/CodeGenObjC/synchronized.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin9 -fobjc-fragile-abi -o - %s -O2 | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -o - %s -O2 | FileCheck %s
@interface MyClass
{
diff --git a/test/CodeGenObjC/terminate.m b/test/CodeGenObjC/terminate.m
index a862058..8728ec4 100644
--- a/test/CodeGenObjC/terminate.m
+++ b/test/CodeGenObjC/terminate.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -fexceptions -fobjc-exceptions -fobjc-runtime-has-terminate -o - %s | FileCheck %s -check-prefix=CHECK-WITH
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s -check-prefix=CHECK-WITHOUT
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.8 -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s -check-prefix=CHECK-WITH
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.7 -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s -check-prefix=CHECK-WITHOUT
void destroy(void**);
diff --git a/test/CodeGenObjC/undefined-protocol.m b/test/CodeGenObjC/undefined-protocol.m
index d87a5c9..e5a72ab 100644
--- a/test/CodeGenObjC/undefined-protocol.m
+++ b/test/CodeGenObjC/undefined-protocol.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm-only -fgnu-runtime %s
+// RUN: %clang_cc1 -emit-llvm-only -fobjc-runtime=gcc %s
@protocol MadeUpProtocol;
diff --git a/test/CodeGenObjC/variadic-sends.m b/test/CodeGenObjC/variadic-sends.m
index 94d7baf..726b497 100644
--- a/test/CodeGenObjC/variadic-sends.m
+++ b/test/CodeGenObjC/variadic-sends.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-X86-32 %s
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-X86-64 %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-X86-32 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-X86-64 %s
@interface A
-(void) im0;
diff --git a/test/CodeGenObjC/x86_64-struct-return-gc.m b/test/CodeGenObjC/x86_64-struct-return-gc.m
index 76407d6..dab5b15 100644
--- a/test/CodeGenObjC/x86_64-struct-return-gc.m
+++ b/test/CodeGenObjC/x86_64-struct-return-gc.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -fobjc-gc -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -fobjc-gc -emit-llvm -o - %s | FileCheck %s
struct Coerce {
id a;
};
diff --git a/test/CodeGenObjCXX/arc.mm b/test/CodeGenObjCXX/arc.mm
index 45211a2..f31b993 100644
--- a/test/CodeGenObjCXX/arc.mm
+++ b/test/CodeGenObjCXX/arc.mm
@@ -252,3 +252,19 @@ template <class T> class Test38 {
};
// CHECK: define weak_odr void @_ZN6Test38IiE4testEi(
template class Test38<int>;
+
+// rdar://problem/11964832
+class Test39_base1 {
+ virtual void foo();
+};
+class Test39_base2 {
+ virtual id bar();
+};
+class Test39 : Test39_base1, Test39_base2 { // base2 is at non-zero offset
+ virtual id bar();
+};
+id Test39::bar() { return 0; }
+// Note lack of autorelease.
+// CHECK: define i8* @_ZThn8_N6Test393barEv(
+// CHECK: call i8* @_ZN6Test393barEv(
+// CHECK-NEXT: ret i8*
diff --git a/test/CodeGenObjCXX/block-in-template-inst.mm b/test/CodeGenObjCXX/block-in-template-inst.mm
index 93a0e49..c280874 100644
--- a/test/CodeGenObjCXX/block-in-template-inst.mm
+++ b/test/CodeGenObjCXX/block-in-template-inst.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm-only -std=c++11 -fblocks -o - -triple x86_64-apple-darwin10 -fobjc-fragile-abi %s
+// RUN: %clang_cc1 -emit-llvm-only -std=c++11 -fblocks -o - -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 %s
// rdar://9362021
@class DYFuture;
diff --git a/test/CodeGenObjCXX/block-var-layout.mm b/test/CodeGenObjCXX/block-var-layout.mm
index a8f8be0..00dd2c0 100644
--- a/test/CodeGenObjCXX/block-var-layout.mm
+++ b/test/CodeGenObjCXX/block-var-layout.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-fragile-abi -emit-llvm %s -o %t-64.ll
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fobjc-gc -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o %t-64.ll
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.ll %s
// See commentary in test/CodeGenObjC/block-var-layout.m, from which
diff --git a/test/CodeGenObjCXX/blocks.mm b/test/CodeGenObjCXX/blocks.mm
index 126931d..62ae428 100644
--- a/test/CodeGenObjCXX/blocks.mm
+++ b/test/CodeGenObjCXX/blocks.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin -fobjc-fragile-abi %s -verify -emit-llvm -o %t
+// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 %s -verify -std=c++11 -emit-llvm -o %t
// rdar://8979379
@interface A
@@ -30,7 +30,7 @@ void foo(id <NSObject>(^objectCreationBlock)(void)) {
// Test4
struct S {
- S *(^a)() = ^{ // expected-warning {{C++11}}
+ S *(^a)() = ^{
return this;
};
};
@@ -40,7 +40,22 @@ S s;
struct X {
void f() {
^ {
- struct Nested { Nested *ptr = this; }; // expected-warning {{C++11}}
+ struct Nested { Nested *ptr = this; };
} ();
};
};
+
+// Regression test for PR13314
+class FooClass { };
+void fun() {
+ FooClass foovar;
+ ^() { // expected-warning {{expression result unused}}
+ return foovar;
+ };
+}
+void gun() {
+ FooClass foovar;
+ [=]() { // expected-warning {{expression result unused}}
+ return foovar;
+ };
+}
diff --git a/test/CodeGenObjCXX/catch-id-type.mm b/test/CodeGenObjCXX/catch-id-type.mm
index a5fa3e7..2d6cccc 100644
--- a/test/CodeGenObjCXX/catch-id-type.mm
+++ b/test/CodeGenObjCXX/catch-id-type.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-macosx10.6.6 -fobjc-fragile-abi -emit-llvm -fobjc-exceptions -fcxx-exceptions -fexceptions -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-apple-macosx10.6.6 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fobjc-exceptions -fcxx-exceptions -fexceptions -o - %s | FileCheck %s
// rdar://8940528
@interface ns_array
diff --git a/test/CodeGenObjCXX/copy.mm b/test/CodeGenObjCXX/copy.mm
index 9382ee8..9e41bf0 100644
--- a/test/CodeGenObjCXX/copy.mm
+++ b/test/CodeGenObjCXX/copy.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar://problem/9158302
// This should not use a memmove_collectable in non-GC mode.
diff --git a/test/CodeGenObjCXX/copyable-property-object.mm b/test/CodeGenObjCXX/copyable-property-object.mm
index 03c0c06..b2ca85c 100644
--- a/test/CodeGenObjCXX/copyable-property-object.mm
+++ b/test/CodeGenObjCXX/copyable-property-object.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
struct POD {
int array[3][4];
diff --git a/test/CodeGenObjCXX/encode.mm b/test/CodeGenObjCXX/encode.mm
index 8391c04..1b6a241 100644
--- a/test/CodeGenObjCXX/encode.mm
+++ b/test/CodeGenObjCXX/encode.mm
@@ -87,8 +87,8 @@ namespace rdar9357400 {
typedef vector< float, fixed<4> > vector4f;
- // CHECK: @_ZN11rdar9357400L2ggE = internal constant [49 x i8] c"{vector<float, rdar9357400::fixed<4, -1> >=[4f]}\00"
- const char gg[] = @encode(vector4f);
+ // CHECK: @_ZN11rdar93574002ggE = constant [49 x i8] c"{vector<float, rdar9357400::fixed<4, -1> >=[4f]}\00"
+ extern const char gg[] = @encode(vector4f);
}
// rdar://9624314
@@ -97,12 +97,12 @@ namespace rdar9624314 {
struct B3 {};
struct S : B2, B3 {};
- // CHECK: @_ZN11rdar9624314L2ggE = internal constant [6 x i8] c"{S=i}\00"
- const char gg[] = @encode(S);
+ // CHECK: @_ZN11rdar96243142ggE = constant [6 x i8] c"{S=i}\00"
+ extern const char gg[] = @encode(S);
struct S2 { unsigned : 0; int x; unsigned : 0; };
- // CHECK: @_ZN11rdar9624314L2g2E = internal constant [11 x i8] c"{S2=b0ib0}\00"
- const char g2[] = @encode(S2);
+ // CHECK: @_ZN11rdar96243142g2E = constant [11 x i8] c"{S2=b0ib0}\00"
+ extern const char g2[] = @encode(S2);
}
namespace test {
@@ -122,8 +122,8 @@ namespace test {
int y;
};
- // CHECK: @_ZN4testL3ecdE = internal constant [15 x i8] c"{Zoo=^^?ii^^?}\00"
- const char ecd[] = @encode(Zoo);
+ // CHECK: @_ZN4test3ecdE = constant [15 x i8] c"{Zoo=^^?ii^^?}\00"
+ extern const char ecd[] = @encode(Zoo);
}
struct Base1 {
@@ -143,17 +143,17 @@ struct Sub2 : public Sub_with_virt, public Base1, virtual DBase {
float x;
};
-// CHECK: @_ZL2g1 = internal constant [10 x i8] c"{Base1=c}\00"
-const char g1[] = @encode(Base1);
+// CHECK: @g1 = constant [10 x i8] c"{Base1=c}\00"
+extern const char g1[] = @encode(Base1);
-// CHECK: @_ZL2g2 = internal constant [14 x i8] c"{DBase=^^?cd}\00"
-const char g2[] = @encode(DBase);
+// CHECK: @g2 = constant [14 x i8] c"{DBase=^^?cd}\00"
+extern const char g2[] = @encode(DBase);
-// CHECK: @_ZL2g3 = internal constant [26 x i8] c"{Sub_with_virt=^^?q^^?cd}\00"
-const char g3[] = @encode(Sub_with_virt);
+// CHECK: @g3 = constant [26 x i8] c"{Sub_with_virt=^^?q^^?cd}\00"
+extern const char g3[] = @encode(Sub_with_virt);
-// CHECK: @_ZL2g4 = internal constant [19 x i8] c"{Sub2=^^?qcf^^?cd}\00"
-const char g4[] = @encode(Sub2);
+// CHECK: @g4 = constant [19 x i8] c"{Sub2=^^?qcf^^?cd}\00"
+extern const char g4[] = @encode(Sub2);
// http://llvm.org/PR9927
class allocator {
@@ -165,8 +165,8 @@ char* _M_p;
_Alloc_hider _M_dataplus;
};
-// CHECK: @_ZL2g5 = internal constant [32 x i8] c"{basic_string={_Alloc_hider=*}}\00"
-const char g5[] = @encode(basic_string);
+// CHECK: @g5 = constant [32 x i8] c"{basic_string={_Alloc_hider=*}}\00"
+extern const char g5[] = @encode(basic_string);
// PR10990
@@ -175,8 +175,8 @@ class CefBase {
};
class CefBrowser : public virtual CefBase {};
class CefBrowserImpl : public CefBrowser {};
-// CHECK: @_ZL2g6 = internal constant [21 x i8] c"{CefBrowserImpl=^^?}\00"
-const char g6[] = @encode(CefBrowserImpl);
+// CHECK: @g6 = constant [21 x i8] c"{CefBrowserImpl=^^?}\00"
+extern const char g6[] = @encode(CefBrowserImpl);
// PR10990_2
class CefBase2 {
@@ -185,5 +185,19 @@ class CefBase2 {
};
class CefBrowser2 : public virtual CefBase2 {};
class CefBrowserImpl2 : public CefBrowser2 {};
-// CHECK: @_ZL2g7 = internal constant [26 x i8] c"{CefBrowserImpl2=^^?^^?i}\00"
-const char g7[] = @encode(CefBrowserImpl2);
+// CHECK: @g7 = constant [26 x i8] c"{CefBrowserImpl2=^^?^^?i}\00"
+extern const char g7[] = @encode(CefBrowserImpl2);
+
+// <rdar://problem/11324167>
+struct Empty {};
+
+struct X : Empty {
+ int array[10];
+};
+
+struct Y : Empty {
+ X vec;
+};
+
+// CHECK: @g8 = constant [14 x i8] c"{Y={X=[10i]}}\00"
+extern const char g8[] = @encode(Y);
diff --git a/test/CodeGenObjCXX/gc.mm b/test/CodeGenObjCXX/gc.mm
index 1e9fe00..a504892 100644
--- a/test/CodeGenObjCXX/gc.mm
+++ b/test/CodeGenObjCXX/gc.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
namespace test0 {
extern id x;
diff --git a/test/CodeGenObjCXX/implicit-copy-assign-operator.mm b/test/CodeGenObjCXX/implicit-copy-assign-operator.mm
index 0a6e08e..29ec9ac 100644
--- a/test/CodeGenObjCXX/implicit-copy-assign-operator.mm
+++ b/test/CodeGenObjCXX/implicit-copy-assign-operator.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -emit-llvm -triple x86_64-apple-darwin10.0.0 -fobjc-fragile-abi -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fobjc-gc -emit-llvm -triple x86_64-apple-darwin10.0.0 -fobjc-runtime=macosx-fragile-10.5 -o - %s | FileCheck %s
struct A {
A &operator=(const A&);
A &operator=(A&);
diff --git a/test/CodeGenObjCXX/implicit-copy-constructor.mm b/test/CodeGenObjCXX/implicit-copy-constructor.mm
index 63dd4f0..5cd6f42 100644
--- a/test/CodeGenObjCXX/implicit-copy-constructor.mm
+++ b/test/CodeGenObjCXX/implicit-copy-constructor.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
struct A {
A();
diff --git a/test/CodeGenObjCXX/lambda-expressions.mm b/test/CodeGenObjCXX/lambda-expressions.mm
index 858cb74..ec3eb1f 100644
--- a/test/CodeGenObjCXX/lambda-expressions.mm
+++ b/test/CodeGenObjCXX/lambda-expressions.mm
@@ -9,7 +9,7 @@ fp f() { auto x = []{ return 3; }; return x; }
// MRC: define i32 ()* @_Z1fv(
// MRC: define internal i32 ()* @"_ZZ1fvENK3$_0cvU13block_pointerFivEEv"
// MRC: store i8* bitcast (i8** @_NSConcreteStackBlock to i8*)
-// MRC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke_0" to i8*)
+// MRC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke" to i8*)
// MRC: call i32 ()* (i8*, i8*)* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 ()* (i8*, i8*)*)
// MRC: call i32 ()* (i8*, i8*)* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 ()* (i8*, i8*)*)
// MRC: ret i32 ()*
@@ -17,7 +17,7 @@ fp f() { auto x = []{ return 3; }; return x; }
// ARC: define i32 ()* @_Z1fv(
// ARC: define internal i32 ()* @"_ZZ1fvENK3$_0cvU13block_pointerFivEEv"
// ARC: store i8* bitcast (i8** @_NSConcreteStackBlock to i8*)
-// ARC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke_0" to i8*)
+// ARC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke" to i8*)
// ARC: call i8* @objc_retainBlock
// ARC: call i8* @objc_autoreleaseReturnValue
@@ -26,14 +26,14 @@ fp global;
void f2() { global = []{ return 3; }; }
// MRC: define void @_Z2f2v() nounwind {
-// MRC: store i8* bitcast (i32 (i8*)* @__f2_block_invoke_0 to i8*),
+// MRC: store i8* bitcast (i32 (i8*)* @___Z2f2v_block_invoke to i8*),
// MRC-NOT: call
// MRC: ret void
// ("global" contains a dangling pointer after this function runs.)
// ARC: define void @_Z2f2v() nounwind {
-// ARC: store i8* bitcast (i32 (i8*)* @__f2_block_invoke_0 to i8*),
+// ARC: store i8* bitcast (i32 (i8*)* @___Z2f2v_block_invoke to i8*),
// ARC: call i8* @objc_retainBlock
// ARC: call void @objc_release
-// ARC: define internal i32 @__f2_block_invoke_0
+// ARC: define internal i32 @___Z2f2v_block_invoke
// ARC: call i32 @"_ZZ2f2vENK3$_1clEv
diff --git a/test/CodeGenObjCXX/mangle-blocks.mm b/test/CodeGenObjCXX/mangle-blocks.mm
index fcbc608..892c8af 100644
--- a/test/CodeGenObjCXX/mangle-blocks.mm
+++ b/test/CodeGenObjCXX/mangle-blocks.mm
@@ -1,19 +1,19 @@
-// RUN: %clang_cc1 -emit-llvm -fblocks -o - -triple x86_64-apple-darwin10 -fobjc-fragile-abi %s | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -fblocks -o - -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 %s | FileCheck %s
-// CHECK: @_ZGVN3foo20__foo_block_invoke_05valueE = internal global i64 0
+// CHECK: @_ZGVN3foo22___Z3foov_block_invoke5valueE = internal global i64 0
int f();
void foo() {
- // CHECK: define internal i32 @__foo_block_invoke_0
- // CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVN3foo20__foo_block_invoke_05value
+ // CHECK: define internal i32 @___Z3foov_block_invoke
+ // CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVN3foo22___Z3foov_block_invoke5valueE
(void)^(int x) {
static int value = f();
return x + value;
};
}
-// CHECK: define internal i32 @__block_global_0
+// CHECK: define internal i32 @i_block_invoke
int i = ^(int x) { return x;}(i);
@interface A
@@ -22,9 +22,9 @@ int i = ^(int x) { return x;}(i);
@implementation A
- (void)method {
- // CHECK: define internal signext i8 @"__11-[A method]_block_invoke_0"
+ // CHECK: define internal signext i8 @"__11-[A method]_block_invoke"
(void)^(int x) {
- // CHECK: @"_ZN11-[A method]30__11-[A method]_block_invoke_04nameE"
+ // CHECK: @"_ZN11-[A method]28__11-[A method]_block_invoke4nameE"
static const char *name = "hello";
return name[x];
};
@@ -39,10 +39,10 @@ void foo(int) {
}
namespace N {
- // CHECK: define internal signext i8 @__bar_block_invoke_0
+ // CHECK: define internal signext i8 @___Z3fooi_block_invoke
void bar() {
(void)^(int x) {
- // CHECK: @_ZN1N3bar20__bar_block_invoke_04nameE
+ // CHECK: @_ZN1N3bar26___ZN1N3barEv_block_invoke4nameE
static const char *name = "hello";
return name[x];
};
diff --git a/test/CodeGenObjCXX/message-reference.mm b/test/CodeGenObjCXX/message-reference.mm
index fa41fef..0d1bbc7 100644
--- a/test/CodeGenObjCXX/message-reference.mm
+++ b/test/CodeGenObjCXX/message-reference.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar://8604515
@interface I {}
diff --git a/test/CodeGenObjCXX/nrvo.mm b/test/CodeGenObjCXX/nrvo.mm
index ef5052e..47460c2 100644
--- a/test/CodeGenObjCXX/nrvo.mm
+++ b/test/CodeGenObjCXX/nrvo.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - -fblocks %s -O1 -triple x86_64-apple-darwin10.0.0 -fobjc-fragile-abi | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -o - -fblocks %s -O1 -triple x86_64-apple-darwin10.0.0 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s
// PR10835 / <rdar://problem/10050178>
struct X {
@@ -22,7 +22,7 @@ struct X {
X blocksNRVO() {
return ^{
- // CHECK: define internal void @__blocksNRVO_block_invoke_0
+ // CHECK: define internal void @___Z10blocksNRVOv_block_invoke
X x;
// CHECK: tail call void @_ZN1XC1Ev
// CHECK-NEXT: ret void
diff --git a/test/CodeGenObjCXX/property-derived-to-base-conv.mm b/test/CodeGenObjCXX/property-derived-to-base-conv.mm
index ddca857..5784f2b 100644
--- a/test/CodeGenObjCXX/property-derived-to-base-conv.mm
+++ b/test/CodeGenObjCXX/property-derived-to-base-conv.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
// rdar: // 7501812
struct A {
diff --git a/test/CodeGenObjCXX/property-object-conditional-exp.mm b/test/CodeGenObjCXX/property-object-conditional-exp.mm
index 281076e..899c2c8 100644
--- a/test/CodeGenObjCXX/property-object-conditional-exp.mm
+++ b/test/CodeGenObjCXX/property-object-conditional-exp.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
struct CGRect {
char* origin;
diff --git a/test/CodeGenObjCXX/property-object-reference-2.mm b/test/CodeGenObjCXX/property-object-reference-2.mm
index b150a3e..2a38038 100644
--- a/test/CodeGenObjCXX/property-object-reference-2.mm
+++ b/test/CodeGenObjCXX/property-object-reference-2.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-10.7 -emit-llvm -o - | FileCheck %s
// rdar://6137845
extern int DEFAULT();
diff --git a/test/CodeGenObjCXX/property-object-reference.mm b/test/CodeGenObjCXX/property-object-reference.mm
index 0bd8fb8..ec311f1 100644
--- a/test/CodeGenObjCXX/property-object-reference.mm
+++ b/test/CodeGenObjCXX/property-object-reference.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - | FileCheck %s
// rdar://10188258
struct Foo {int i;};
diff --git a/test/CodeGenObjCXX/property-reference.mm b/test/CodeGenObjCXX/property-reference.mm
index 4897f6d..0e4897b 100644
--- a/test/CodeGenObjCXX/property-reference.mm
+++ b/test/CodeGenObjCXX/property-reference.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - | FileCheck %s
// rdar://9208606
struct MyStruct {
diff --git a/test/CodeGenObjCXX/refence-assign-write-barrier.mm b/test/CodeGenObjCXX/refence-assign-write-barrier.mm
index 206ecb0..ad277ef 100644
--- a/test/CodeGenObjCXX/refence-assign-write-barrier.mm
+++ b/test/CodeGenObjCXX/refence-assign-write-barrier.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar://8681766
@interface NSArray
diff --git a/test/CodeGenObjCXX/selector-expr-lvalue.mm b/test/CodeGenObjCXX/selector-expr-lvalue.mm
index 3e3bf4e..508ea83 100644
--- a/test/CodeGenObjCXX/selector-expr-lvalue.mm
+++ b/test/CodeGenObjCXX/selector-expr-lvalue.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s
// PR7390
@interface NSObject {}
diff --git a/test/CodeGenObjCXX/write-barrier-global-assign.mm b/test/CodeGenObjCXX/write-barrier-global-assign.mm
index cb563f3..539e21f 100644
--- a/test/CodeGenObjCXX/write-barrier-global-assign.mm
+++ b/test/CodeGenObjCXX/write-barrier-global-assign.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-fragile-abi -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s
// rdar://8761767
@class CPDestUser;
diff --git a/test/CodeGenOpenCL/kernel-arg-info.cl b/test/CodeGenOpenCL/kernel-arg-info.cl
new file mode 100644
index 0000000..9d52736
--- /dev/null
+++ b/test/CodeGenOpenCL/kernel-arg-info.cl
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 %s -cl-kernel-arg-info -emit-llvm -o - | FileCheck %s
+
+kernel void foo(int *X, int Y, int anotherArg) {
+ *X = Y + anotherArg;
+}
+
+// CHECK: metadata !{metadata !"kernel_arg_name", metadata !"X", metadata !"Y", metadata !"anotherArg"}
diff --git a/test/CodeGenOpenCL/kernel-attributes.cl b/test/CodeGenOpenCL/kernel-attributes.cl
new file mode 100644
index 0000000..de16a41
--- /dev/null
+++ b/test/CodeGenOpenCL/kernel-attributes.cl
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -emit-llvm -O0 -o - %s | FileCheck %s
+
+kernel __attribute__((reqd_work_group_size(1,2,4))) void kernel1(int a) {}
+
+kernel __attribute__((work_group_size_hint(8,16,32))) void kernel2(int a) {}
+
+// CHECK: opencl.kernels = !{[[MDNODE0:![0-9]+]], [[MDNODE3:![0-9]+]]}
+
+// CHECK: [[MDNODE0]] = metadata !{void (i32)* @kernel1, metadata [[MDNODE2:![0-9]+]]}
+// CHECK: [[MDNODE2]] = metadata !{metadata !"reqd_work_group_size", i32 1, i32 2, i32 4}
+// CHECK: [[MDNODE3]] = metadata !{void (i32)* @kernel2, metadata [[MDNODE5:![0-9]+]]}
+// CHECK: [[MDNODE5]] = metadata !{metadata !"work_group_size_hint", i32 8, i32 16, i32 32}
diff --git a/test/CodeGenOpenCL/ptx-calls.cl b/test/CodeGenOpenCL/ptx-calls.cl
index 6f33640..34a21c6 100644
--- a/test/CodeGenOpenCL/ptx-calls.cl
+++ b/test/CodeGenOpenCL/ptx-calls.cl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple ptx32-unknown-unknown -emit-llvm -O0 -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple nvptx-unknown-unknown -emit-llvm -O0 -o - | FileCheck %s
void device_function() {
}
diff --git a/test/CodeGenOpenCL/ptx-kernels.cl b/test/CodeGenOpenCL/ptx-kernels.cl
index 4d6fa10..1d7e497 100644
--- a/test/CodeGenOpenCL/ptx-kernels.cl
+++ b/test/CodeGenOpenCL/ptx-kernels.cl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple ptx32-unknown-unknown -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple nvptx-unknown-unknown -emit-llvm -o - | FileCheck %s
void device_function() {
}
diff --git a/test/Coverage/codegen-gnu.m b/test/Coverage/codegen-gnu.m
index 432637c..67f9906 100644
--- a/test/Coverage/codegen-gnu.m
+++ b/test/Coverage/codegen-gnu.m
@@ -1,3 +1,3 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-exceptions -fgnu-runtime -emit-llvm -o %t %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-exceptions -fobjc-runtime=gcc -emit-llvm -o %t %s
#include "objc-language-features.inc"
diff --git a/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_dynamic.o b/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_dynamic.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_dynamic.o
diff --git a/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_so.o b/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_so.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_so.o
diff --git a/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_static.o b/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_static.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/basic_android_tree/usr/lib/crtbegin_static.o
diff --git a/test/Driver/Inputs/basic_android_tree/usr/lib/crtend_android.o b/test/Driver/Inputs/basic_android_tree/usr/lib/crtend_android.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/basic_android_tree/usr/lib/crtend_android.o
diff --git a/test/Driver/Inputs/basic_android_tree/usr/lib/crtend_so.o b/test/Driver/Inputs/basic_android_tree/usr/lib/crtend_so.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/basic_android_tree/usr/lib/crtend_so.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/lib/mips-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/lib/mips-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/lib/mips-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/lib/mipsel-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/lib/mipsel-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/lib/mipsel-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mips-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mips-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mips-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mipsel-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mipsel-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/include/c++/4.5/mipsel-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/include/mips-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/usr/include/mips-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/include/mips-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/include/mipsel-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/usr/include/mipsel-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/include/mipsel-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/64/crtbegin.o b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/64/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/64/crtbegin.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/crtbegin.o b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/crtbegin.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/n32/crtbegin.o b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/n32/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mips-linux-gnu/4.5/n32/crtbegin.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/64/crtbegin.o b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/64/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/64/crtbegin.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/crtbegin.o b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/crtbegin.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/n32/crtbegin.o b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/n32/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/gcc/mipsel-linux-gnu/4.5/n32/crtbegin.o
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/mips-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/mips-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/mips-linux-gnu/.keep
diff --git a/test/Driver/Inputs/debian_multiarch_tree/usr/lib/mipsel-linux-gnu/.keep b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/mipsel-linux-gnu/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/debian_multiarch_tree/usr/lib/mipsel-linux-gnu/.keep
diff --git a/test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/crtbegin.o b/test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/crtbegin.o
new file mode 100644
index 0000000..26d251d
--- /dev/null
+++ b/test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/crtbegin.o
@@ -0,0 +1 @@
+dummy file for gcc toolchain detection (crtbegin.o)
diff --git a/test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/libgcc.a b/test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/libgcc.a
new file mode 100644
index 0000000..d796928
--- /dev/null
+++ b/test/Driver/Inputs/montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/libgcc.a
@@ -0,0 +1 @@
+dummy file for gcc toolchain detection (libgcc.a)
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabi/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabi/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabi/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabihf/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabihf/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/lib/arm-linux-gnueabihf/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabi/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabi/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabi/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabihf/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabihf/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/include/arm-linux-gnueabihf/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/.keep b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/.keep
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crt1.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crt1.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crt1.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crti.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crti.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crti.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crtn.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crtn.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabi/crtn.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crt1.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crt1.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crt1.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crti.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crti.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crti.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crtn.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crtn.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/arm-linux-gnueabihf/crtn.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o
diff --git a/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/ubuntu_12.04_LTS_multiarch_tree/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o
diff --git a/test/Driver/altivec.cpp b/test/Driver/altivec.cpp
index 6059ad0..a893636 100644
--- a/test/Driver/altivec.cpp
+++ b/test/Driver/altivec.cpp
@@ -1,7 +1,8 @@
// Check that we error when -faltivec is specified on non-ppc platforms.
-// RUN: %clang -ccc-clang-archs powerpc -target powerpc-apple-darwin -faltivec -fsyntax-only %s
+// RUN: %clang -ccc-clang-archs powerpc -target powerpc-unk-unk -faltivec -fsyntax-only %s
// RUN: %clang -ccc-clang-archs powerpc64 -target powerpc64-linux-gnu -faltivec -fsyntax-only %s
+// RUN: %clang -ccc-clang-archs powerpc64 -target powerpc64-linux-gnu -maltivec -fsyntax-only %s
// RUN: %clang -target i386-pc-win32 -faltivec -fsyntax-only %s 2>&1 | FileCheck %s
// RUN: %clang -target x86_64-unknown-freebsd -faltivec -fsyntax-only %s 2>&1 | FileCheck %s
diff --git a/test/Driver/apple-kext-i386.cpp b/test/Driver/apple-kext-i386.cpp
index c11a136..eb9f957 100644
--- a/test/Driver/apple-kext-i386.cpp
+++ b/test/Driver/apple-kext-i386.cpp
@@ -41,6 +41,11 @@
// CHECK-UNSUPPORTED2: cc1plus"
// CHECK-UNSUPPORTED2-NOT: "-Wconstant-logical-operand"
+// Check that -serialize-diagnostics does not cause an "argument unused" error.
+// RUN: %clang -target i386-apple-darwin10 \
+// RUN: -Wall -fapple-kext -### -serialize-diagnostics %t.dia -c %s 2>&1 | \
+// RUN: FileCheck --check-prefix=CHECK-UNUSED %s
+
// Check that --serialize-diagnostics does not cause an "argument unused" error.
// RUN: %clang -target i386-apple-darwin10 \
// RUN: -Wall -fapple-kext -### --serialize-diagnostics %t.dia -c %s 2>&1 | \
diff --git a/test/Driver/arc.c b/test/Driver/arc.c
index f2c1127..5a5720c 100644
--- a/test/Driver/arc.c
+++ b/test/Driver/arc.c
@@ -8,10 +8,10 @@
// Just to test clang is working.
# foo
-// CHECK: error: -fobjc-arc is not supported with fragile abi
+// CHECK: error: -fobjc-arc is not supported with legacy abi
// CHECK-NOT: invalid preprocessing directive
-// NOTOBJC-NOT: error: -fobjc-arc is not supported with fragile abi
+// NOTOBJC-NOT: error: -fobjc-arc is not supported with legacy abi
// NOTOBJC: invalid preprocessing directive
// UNSUPPORTED: error: -fobjc-arc is not supported on current deployment target
diff --git a/test/Driver/arclite-link.c b/test/Driver/arclite-link.c
index 9cf1efe..3471bf6 100644
--- a/test/Driver/arclite-link.c
+++ b/test/Driver/arclite-link.c
@@ -1,8 +1,17 @@
// RUN: touch %t.o
-// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -mmacosx-version-min=10.7 %t.o 2>&1 | FileCheck -check-prefix=CHECK-ARCLITE-OSX %s
+// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -lfoo -mmacosx-version-min=10.7 %t.o 2>&1 | FileCheck -check-prefix=CHECK-ARCLITE-OSX %s
// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -mmacosx-version-min=10.8 %t.o 2>&1 | FileCheck -check-prefix=CHECK-NOARCLITE %s
// RUN: %clang -### -target i386-apple-darwin10 -fobjc-link-runtime -mmacosx-version-min=10.7 %t.o 2>&1 | FileCheck -check-prefix=CHECK-NOARCLITE %s
+// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -nostdlib %t.o 2>&1 | FileCheck -check-prefix=CHECK-NOSTDLIB %s
+// CHECK-ARCLITE-OSX: -lfoo
// CHECK-ARCLITE-OSX: libarclite_macosx.a
+// CHECK-ARCLITE-OSX: -framework
+// CHECK-ARCLITE-OSX: Foundation
// CHECK-ARCLITE-OSX: -lobjc
// CHECK-NOARCLITE-NOT: libarclite
+// CHECK-NOSTDLIB-NOT: -lobjc
+
+// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -fobjc-arc -mmacosx-version-min=10.7 %s 2>&1 | FileCheck -check-prefix=CHECK-UNUSED %s
+
+// CHECK-UNUSED-NOT: warning: argument unused during compilation: '-fobjc-link-runtime'
diff --git a/test/Driver/asan-ld.c b/test/Driver/asan-ld.c
new file mode 100644
index 0000000..daf046b
--- /dev/null
+++ b/test/Driver/asan-ld.c
@@ -0,0 +1,31 @@
+// Test AddressSanitizer ld flags.
+
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target i386-unknown-linux -faddress-sanitizer \
+// RUN: --sysroot=%S/Inputs/basic_linux_tree \
+// RUN: | FileCheck --check-prefix=CHECK-LINUX %s
+// CHECK-LINUX: "{{.*}}ld{{(.exe)?}}"
+// CHECK-LINUX-NOT: "-lc"
+// CHECK-LINUX: libclang_rt.asan-i386.a"
+// CHECK-LINUX: "-lpthread"
+// CHECK-LINUX: "-ldl"
+// CHECK-LINUX: "-export-dynamic"
+
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-androideabi -faddress-sanitizer \
+// RUN: --sysroot=%S/Inputs/basic_android_tree \
+// RUN: | FileCheck --check-prefix=CHECK-ANDROID %s
+// CHECK-ANDROID: "{{.*}}ld{{(.exe)?}}"
+// CHECK-ANDROID-NOT: "-lc"
+// CHECK-ANDROID: "-u" "__asan_preinit" "-lasan"
+// CHECK-ANDROID: "-lasan_preload" "-ldl"
+
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-androideabi -faddress-sanitizer \
+// RUN: --sysroot=%S/Inputs/basic_android_tree \
+// RUN: -shared \
+// RUN: | FileCheck --check-prefix=CHECK-ANDROID-SHARED %s
+// CHECK-ANDROID-SHARED: "{{.*}}ld{{(.exe)?}}"
+// CHECK-ANDROID-SHARED-NOT: "-lc"
+// CHECK-ANDROID-SHARED-NOT: "-lasan"
+// CHECK-ANDROID-SHARED: "-lasan_preload" "-ldl"
diff --git a/test/Driver/bounds-checking.c b/test/Driver/bounds-checking.c
new file mode 100644
index 0000000..95bb8af
--- /dev/null
+++ b/test/Driver/bounds-checking.c
@@ -0,0 +1,7 @@
+// RUN: %clang -target x86_64-apple-darwin10 -fbounds-checking -### -fsyntax-only %s 2> %t
+// RUN: FileCheck < %t %s
+// RUN: %clang -target x86_64-apple-darwin10 -fbounds-checking=3 -### -fsyntax-only %s 2> %t
+// RUN: FileCheck -check-prefix=CHECK2 < %t %s
+
+// CHECK: "-fbounds-checking=1"
+// CHECK2: "-fbounds-checking=3"
diff --git a/test/Driver/ccc-as-cpp.c b/test/Driver/ccc-as-cpp.c
new file mode 100644
index 0000000..feead51
--- /dev/null
+++ b/test/Driver/ccc-as-cpp.c
@@ -0,0 +1,6 @@
+// REQUIRES: shell
+// RUN: ln -sf %clang %T/clang-cpp
+
+// PR13529: Don't crash.
+// RUN: %T/clang-cpp -lfoo -M %s 2>&1 | FileCheck --check-prefix=CHECK-PR13529 %s
+// CHECK-PR13529: warning: -lfoo: 'linker' input unused in cpp mode
diff --git a/test/Driver/clang-translation.c b/test/Driver/clang-translation.c
index 0e82de4..76196da 100644
--- a/test/Driver/clang-translation.c
+++ b/test/Driver/clang-translation.c
@@ -44,3 +44,33 @@
// ARMV7_HARDFLOAT: "-mfloat-abi" "hard"
// ARMV7_HARDFLOAT-NOT: "-msoft-float"
// ARMV7_HARDFLOAT: "-x" "c"
+
+// RUN: %clang -target arm-linux -### -S %s 2> %t.log \
+// RUN: -march=armv5e
+// RUN: FileCheck -check-prefix=ARMV5E %s < %t.log
+// ARMV5E: clang
+// ARMV5E: "-cc1"
+// ARMV5E: "-target-cpu" "arm1022e"
+
+// RUN: %clang -ccc-clang-archs powerpc64 \
+// RUN: -target powerpc64-unknown-linux-gnu -### -S %s 2> %t.log \
+// RUN: -mcpu=G5
+// RUN: FileCheck -check-prefix=PPCG5 %s < %t.log
+// PPCG5: clang
+// PPCG5: "-cc1"
+// PPCG5: "-target-cpu" "g5"
+
+// RUN: %clang -ccc-clang-archs powerpc64 \
+// RUN: -target powerpc64-unknown-linux-gnu -### -S %s 2> %t.log \
+// RUN: -mcpu=power7
+// RUN: FileCheck -check-prefix=PPCPWR7 %s < %t.log
+// PPCPWR7: clang
+// PPCPWR7: "-cc1"
+// PPCPWR7: "-target-cpu" "pwr7"
+
+// RUN: %clang -ccc-clang-archs powerpc64 \
+// RUN: -target powerpc64-unknown-linux-gnu -### -S %s 2> %t.log
+// RUN: FileCheck -check-prefix=PPC64NS %s < %t.log
+// PPC64NS: clang
+// PPC64NS: "-cc1"
+// PPC64NS: "-target-cpu" "ppc64"
diff --git a/test/Driver/clang_f_opts.c b/test/Driver/clang_f_opts.c
index 4eed4aa..0ee7d2da 100644
--- a/test/Driver/clang_f_opts.c
+++ b/test/Driver/clang_f_opts.c
@@ -29,3 +29,12 @@
// RUN: %clang -### -c -Wdeprecated %s 2>&1 | FileCheck -check-prefix=DEPRECATED-OFF-CHECK %s
// DEPRECATED-ON-CHECK: -fdeprecated-macro
// DEPRECATED-OFF-CHECK-NOT: -fdeprecated-macro
+
+// RUN: %clang -### -S -ffp-contract=fast %s 2>&1 | FileCheck -check-prefix=FP-CONTRACT-FAST-CHECK %s
+// RUN: %clang -### -S -ffast-math %s 2>&1 | FileCheck -check-prefix=FP-CONTRACT-FAST-CHECK %s
+// RUN: %clang -### -S -ffp-contract=off %s 2>&1 | FileCheck -check-prefix=FP-CONTRACT-OFF-CHECK %s
+// FP-CONTRACT-FAST-CHECK: -ffp-contract=fast
+// FP-CONTRACT-OFF-CHECK: -ffp-contract=off
+
+// RUN: %clang -fms-extensions -fenable-experimental-ms-inline-asm %s -### 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS3 %s
+// CHECK-OPTIONS3: -fenable-experimental-ms-inline-asm
diff --git a/test/Driver/clang_f_opts.h b/test/Driver/clang_f_opts.h
new file mode 100644
index 0000000..e48d0cf
--- /dev/null
+++ b/test/Driver/clang_f_opts.h
@@ -0,0 +1,2 @@
+// RUN: %clang -### -fsyntax-only %s 2>&1 | FileCheck %s
+// CHECK: -fsyntax-only
diff --git a/test/Driver/constructors.c b/test/Driver/constructors.c
new file mode 100644
index 0000000..ca2cac2
--- /dev/null
+++ b/test/Driver/constructors.c
@@ -0,0 +1,14 @@
+// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \
+// RUN: -target i386-unknown-linux \
+// RUN: --sysroot=%S/Inputs/fake_install_tree \
+// RUN: | FileCheck --check-prefix=CHECK-GCC-4-7 %s
+
+// CHECK-GCC-4-7: -fuse-init-array
+
+// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \
+// RUN: -target i386-unknown-linux \
+// RUN: --sysroot=%S/Inputs/basic_linux_tree \
+// RUN: | FileCheck --check-prefix=CHECK-GCC-4-6 %s
+
+
+// CHECK-GCC-4-6-NOT: -fuse-init-array
diff --git a/test/Driver/crash-report.c b/test/Driver/crash-report.c
new file mode 100644
index 0000000..7adaf42
--- /dev/null
+++ b/test/Driver/crash-report.c
@@ -0,0 +1,27 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: env TMPDIR=%t TEMP=%t TMP=%t %clang -fsyntax-only %s \
+// RUN: -F/tmp/ -I /tmp/ -idirafter /tmp/ -iquote /tmp/ -isystem /tmp/ \
+// RUN: -iprefix /the/prefix -iwithprefix /tmp -iwithprefixbefore /tmp/ \
+// RUN: -internal-isystem /tmp/ -internal-externc-isystem /tmp/ \
+// RUN: -DFOO=BAR 2>&1 | FileCheck %s
+// RUN: cat %t/crash-report-*.c | FileCheck --check-prefix=CHECKSRC %s
+// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH %s
+// REQUIRES: crash-recovery
+
+#pragma clang __debug parser_crash
+// CHECK: Preprocessed source(s) and associated run script(s) are located at:
+// CHECK-NEXT: note: diagnostic msg: {{.*}}.c
+FOO
+// CHECKSRC: FOO
+// CHECKSH: -D "FOO=BAR"
+// CHECKSH-NOT: -F/tmp/
+// CHECKSH-NOT: -I /tmp/
+// CHECKSH-NOT: -idirafter /tmp/
+// CHECKSH-NOT: -iquote /tmp/
+// CHECKSH-NOT: -isystem /tmp/
+// CHECKSH-NOT: -iprefix /the/prefix
+// CHECKSH-NOT: -iwithprefix /tmp/
+// CHECKSH-NOT: -iwithprefixbefore /tmp/
+// CHECKSH-NOT: -internal-isystem /tmp/
+// CHECKSH-NOT: -internal-externc-isystem /tmp/
diff --git a/test/Driver/darwin-ld.c b/test/Driver/darwin-ld.c
index 3206f65..4cda37f 100644
--- a/test/Driver/darwin-ld.c
+++ b/test/Driver/darwin-ld.c
@@ -121,3 +121,8 @@
// RUN: %clang -target x86_64-apple-darwin12 -### %t.o 2> %t.log
// RUN: FileCheck -check-prefix=LINK_NO_CRT1 %s < %t.log
// LINK_NO_CRT1-NOT: crt
+
+// RUN: %clang -target i386-apple-darwin12 -pg -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=LINK_PG %s < %t.log
+// LINK_PG: -lgcrt1.o
+// LINK_PG: -no_new_main
diff --git a/test/Driver/darwin-objc-defaults.m b/test/Driver/darwin-objc-defaults.m
index 49fe8f9..6265cfb 100644
--- a/test/Driver/darwin-objc-defaults.m
+++ b/test/Driver/darwin-objc-defaults.m
@@ -7,7 +7,7 @@
// RUN: FileCheck --check-prefix CHECK-I386_OSX10_5 < %t %s
// CHECK-CHECK-I386_OSX10_5: "-cc1"
-// CHECK-CHECK-I386_OSX10_5: -fobjc-fragile-abi
+// CHECK-CHECK-I386_OSX10_5: -fobjc-runtime=macosx-fragile-10.5
// CHECK-CHECK-I386_OSX10_5-NOT: -fobjc-dispatch-method
// CHECK-CHECK-I386_OSX10_5: darwin-objc-defaults
@@ -16,7 +16,7 @@
// RUN: FileCheck --check-prefix CHECK-I386_OSX10_6 < %t %s
// CHECK-CHECK-I386_OSX10_6: "-cc1"
-// CHECK-CHECK-I386_OSX10_6: -fobjc-fragile-abi
+// CHECK-CHECK-I386_OSX10_6: -fobjc-runtime=macosx-fragile-10.6
// CHECK-CHECK-I386_OSX10_6-NOT: -fobjc-dispatch-method
// CHECK-CHECK-I386_OSX10_6: darwin-objc-defaults
@@ -25,7 +25,7 @@
// RUN: FileCheck --check-prefix CHECK-I386_IPHONE3_0 < %t %s
// CHECK-CHECK-I386_IPHONE3_0: "-cc1"
-// CHECK-CHECK-I386_IPHONE3_0: -fobjc-fragile-abi
+// CHECK-CHECK-I386_IPHONE3_0: -fobjc-runtime=ios-3.0
// CHECK-CHECK-I386_IPHONE3_0-NOT: -fobjc-dispatch-method
// CHECK-CHECK-I386_IPHONE3_0: darwin-objc-defaults
@@ -36,7 +36,7 @@
// RUN: FileCheck --check-prefix CHECK-X86_64_OSX10_5 < %t %s
// CHECK-CHECK-X86_64_OSX10_5: "-cc1"
-// CHECK-CHECK-X86_64_OSX10_5-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-X86_64_OSX10_5: -fobjc-runtime=macosx-10.5
// CHECK-CHECK-X86_64_OSX10_5: -fobjc-dispatch-method=non-legacy
// CHECK-CHECK-X86_64_OSX10_5: darwin-objc-defaults
@@ -45,7 +45,7 @@
// RUN: FileCheck --check-prefix CHECK-X86_64_OSX10_6 < %t %s
// CHECK-CHECK-X86_64_OSX10_6: "-cc1"
-// CHECK-CHECK-X86_64_OSX10_6-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-X86_64_OSX10_6: -fobjc-runtime=macosx-10.6
// CHECK-CHECK-X86_64_OSX10_6: -fobjc-dispatch-method=mixed
// CHECK-CHECK-X86_64_OSX10_6: darwin-objc-defaults
@@ -54,7 +54,7 @@
// RUN: FileCheck --check-prefix CHECK-X86_64_IPHONE3_0 < %t %s
// CHECK-CHECK-X86_64_IPHONE3_0: "-cc1"
-// CHECK-CHECK-X86_64_IPHONE3_0-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-X86_64_IPHONE3_0: -fobjc-runtime=ios-3.0
// CHECK-CHECK-X86_64_IPHONE3_0: -fobjc-dispatch-method=mixed
// CHECK-CHECK-X86_64_IPHONE3_0: darwin-objc-defaults
@@ -65,7 +65,7 @@
// RUN: FileCheck --check-prefix CHECK-ARMV7_OSX10_5 < %t %s
// CHECK-CHECK-ARMV7_OSX10_5: "-cc1"
-// CHECK-CHECK-ARMV7_OSX10_5-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-ARMV7_OSX10_5: -fobjc-runtime=macosx-10.5
// CHECK-CHECK-ARMV7_OSX10_5-NOT: -fobjc-dispatch-method
// CHECK-CHECK-ARMV7_OSX10_5: darwin-objc-defaults
@@ -74,7 +74,7 @@
// RUN: FileCheck --check-prefix CHECK-ARMV7_OSX10_6 < %t %s
// CHECK-CHECK-ARMV7_OSX10_6: "-cc1"
-// CHECK-CHECK-ARMV7_OSX10_6-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-ARMV7_OSX10_6: -fobjc-runtime=macosx-10.6
// CHECK-CHECK-ARMV7_OSX10_6-NOT: -fobjc-dispatch-method
// CHECK-CHECK-ARMV7_OSX10_6: darwin-objc-defaults
@@ -83,6 +83,6 @@
// RUN: FileCheck --check-prefix CHECK-ARMV7_IPHONE3_0 < %t %s
// CHECK-CHECK-ARMV7_IPHONE3_0: "-cc1"
-// CHECK-CHECK-ARMV7_IPHONE3_0-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-ARMV7_IPHONE3_0: -fobjc-runtime=ios-3.0
// CHECK-CHECK-ARMV7_IPHONE3_0-NOT: -fobjc-dispatch-method
// CHECK-CHECK-ARMV7_IPHONE3_0: darwin-objc-defaults
diff --git a/test/Driver/darwin-objc-options.m b/test/Driver/darwin-objc-options.m
index 5b421d8..77501ab 100644
--- a/test/Driver/darwin-objc-options.m
+++ b/test/Driver/darwin-objc-options.m
@@ -5,7 +5,7 @@
// RUN: FileCheck --check-prefix CHECK-X86_64_ABI1 < %t %s
// CHECK-CHECK-X86_64_ABI1: "-cc1"
-// CHECK-CHECK-X86_64_ABI1: -fobjc-fragile-abi
+// CHECK-CHECK-X86_64_ABI1: -fobjc-runtime=macosx-fragile-10.6.0
// CHECK-CHECK-X86_64_ABI1-NOT: -fobjc-dispatch-method
// CHECK-CHECK-X86_64_ABI1: darwin-objc-options
@@ -14,8 +14,19 @@
// RUN: FileCheck --check-prefix CHECK-I386_ABI2 < %t %s
// CHECK-CHECK-I386_ABI2: "-cc1"
-// CHECK-CHECK-I386_ABI2-NOT: -fobjc-fragile-abi
+// CHECK-CHECK-I386_ABI2: -fobjc-runtime=macosx-10.6.0
// CHECK-CHECK-I386_ABI2: -fobjc-exceptions
// CHECK-CHECK-I386_ABI2: -fexceptions
// CHECK-CHECK-I386_ABI2-NOT: -fobjc-dispatch-method
// CHECK-CHECK-I386_ABI2: darwin-objc-options
+
+// RUN: %clang -target x86_64-apple-darwin10 -S -### %s \
+// RUN: -arch i386 -fobjc-runtime=ios-5.0 2> %t
+// RUN: FileCheck --check-prefix CHECK-I386_IOS < %t %s
+
+// CHECK-CHECK-I386_IOS: "-cc1"
+// CHECK-CHECK-I386_IOS: -fobjc-runtime=ios-5.0
+// CHECK-CHECK-I386_IOS: -fobjc-exceptions
+// CHECK-CHECK-I386_IOS: -fexceptions
+// CHECK-CHECK-I386_IOS-NOT: -fobjc-dispatch-method
+// CHECK-CHECK-I386_IOS: darwin-objc-options
diff --git a/test/Driver/debug-options-as.c b/test/Driver/debug-options-as.c
index a1dbe2e..57036e4 100644
--- a/test/Driver/debug-options-as.c
+++ b/test/Driver/debug-options-as.c
@@ -4,7 +4,8 @@
// Check to make sure clang is somewhat picky about -g options.
// (Delived from debug-options.c)
// rdar://10383444
-// RUN: %clang -### -c -save-temps -g %s 2>&1 | FileCheck -check-prefix=SAVE %s
+// RUN: %clang -### -c -save-temps -integrated-as -g %s 2>&1 \
+// RUN: | FileCheck -check-prefix=SAVE %s
//
// SAVE: "-cc1as"
// SAVE-NOT: "-g"
@@ -12,7 +13,8 @@
// Check to make sure clang with -g on a .s file gets passed.
// rdar://9275556
// RUN: touch %t.s
-// RUN: %clang -### -c -g %t.s 2>&1 | FileCheck -check-prefix=S %s
+// RUN: %clang -### -c -integrated-as -g %t.s 2>&1 \
+// RUN: | FileCheck -check-prefix=S %s
//
// S: "-cc1as"
// S: "-g"
diff --git a/test/Driver/debug-options.c b/test/Driver/debug-options.c
index 5dad8e9..ca77abf 100644
--- a/test/Driver/debug-options.c
+++ b/test/Driver/debug-options.c
@@ -2,26 +2,43 @@
// rdar://10383444
// RUN: %clang -### -c -g %s 2>&1 | FileCheck -check-prefix=G %s
-// RUN: %clang -### -c -g2 %s 2>&1 | FileCheck -check-prefix=G2 %s
-// RUN: %clang -### -c -g3 %s 2>&1 | FileCheck -check-prefix=G3 %s
-// RUN: %clang -### -c -ganything %s 2>&1 | FileCheck -check-prefix=GANY %s
-// RUN: %clang -### -c -ggdb %s 2>&1 | FileCheck -check-prefix=GGDB %s
-// RUN: %clang -### -c -gfoo %s 2>&1 | FileCheck -check-prefix=GFOO %s
+// RUN: %clang -### -c -g2 %s 2>&1 | FileCheck -check-prefix=G %s
+// RUN: %clang -### -c -g3 %s 2>&1 | FileCheck -check-prefix=G %s
+// RUN: %clang -### -c -ggdb %s 2>&1 | FileCheck -check-prefix=G %s
+// RUN: %clang -### -c -ggdb1 %s 2>&1 | FileCheck -check-prefix=G %s
+// RUN: %clang -### -c -ggdb3 %s 2>&1 | FileCheck -check-prefix=G %s
+// RUN: %clang -### -c -gdwarf-2 %s 2>&1 | FileCheck -check-prefix=G %s
+//
+// RUN: %clang -### -c -gfoo %s 2>&1 | FileCheck -check-prefix=G_NO %s
+// RUN: %clang -### -c -g -g0 %s 2>&1 | FileCheck -check-prefix=G_NO %s
+// RUN: %clang -### -c -ggdb0 %s 2>&1 | FileCheck -check-prefix=G_NO %s
+//
+// RUN: %clang -### -c -gline-tables-only %s 2>&1 \
+// RUN: | FileCheck -check-prefix=GLTO_ONLY %s
+// RUN: %clang -### -c -gline-tables-only -g %s 2>&1 \
+// RUN: | FileCheck -check-prefix=G_ONLY %s
+// RUN: %clang -### -c -gline-tables-only -g0 %s 2>&1 \
+// RUN: | FileCheck -check-prefix=GLTO_NO %s
+//
+// RUN: %clang -### -c -grecord-gcc-switches -gno-record-gcc-switches \
+// RUN: -gstrict-dwarf -gno-strict-dwarf %s 2>&1 \
+// RUN: | not grep "argument unused during compilation"
//
// G: "-cc1"
// G: "-g"
//
-// G2: "-cc1"
-// G2: "-g"
-//
-// G3: "-cc1"
-// G3: "-g"
+// G_NO: "-cc1"
+// G_NO-NOT: "-g"
//
-// GANY: "-cc1"
-// GANY-NOT: "-g"
+// GLTO_ONLY: "-cc1"
+// GLTO_ONLY-NOT: "-g"
+// GLTO_ONLY: "-gline-tables-only"
+// GLTO_ONLY-NOT: "-g"
//
-// GGDB: "-cc1"
-// GGDB: "-g"
+// G_ONLY: "-cc1"
+// G_ONLY-NOT: "-gline-tables-only"
+// G_ONLY: "-g"
+// G_ONLY-NOT: "-gline-tables-only"
//
-// GFOO: "-cc1"
-// GFOO-NOT: "-g"
+// GLTO_NO: "-cc1"
+// GLTO_NO-NOT: "-gline-tables-only"
diff --git a/test/Driver/debug-unsupported.c b/test/Driver/debug-unsupported.c
new file mode 100644
index 0000000..acbd767
--- /dev/null
+++ b/test/Driver/debug-unsupported.c
@@ -0,0 +1,13 @@
+// RUN: %clang -c -gstabs %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gstabs+ %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gcoff %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gxcoff %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gxcoff+ %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gvms %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gstabs1 %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gcoff2 %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gxcoff3 %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gvms0 %s 2>&1 | FileCheck %s
+// RUN: %clang -c -gtoggle %s 2>&1 | FileCheck %s
+//
+// CHECK: error: unsupported option
diff --git a/test/Driver/fast-math.c b/test/Driver/fast-math.c
index aef7cc3..8426f09 100644
--- a/test/Driver/fast-math.c
+++ b/test/Driver/fast-math.c
@@ -4,6 +4,9 @@
// LLVM only supports three switches which is more coarse grained than GCC's
// support.
//
+// Both of them use gcc driver for as.
+// XFAIL: cygwin,mingw32
+//
// RUN: %clang -### -fno-honor-infinities -c %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-NO-INFS %s
// CHECK-NO-INFS: "-cc1"
@@ -19,8 +22,23 @@
// CHECK-MATH-ERRNO: "-cc1"
// CHECK-MATH-ERRNO: "-fmath-errno"
//
-// RUN: %clang -### -fassociative-math -freciprocal-math -fno-signed-zeros \
-// RUN: -fno-trapping-math -c %s 2>&1 \
+// RUN: %clang -### -fmath-errno -fno-math-errno -c %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NO-MATH-ERRNO %s
+// RUN: %clang -### -target i686-apple-darwin -c %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NO-MATH-ERRNO %s
+// RUN: %clang -### -target x86_64-unknown-freebsd -c %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NO-MATH-ERRNO %s
+// RUN: %clang -### -target x86_64-unknown-netbsd -c %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NO-MATH-ERRNO %s
+// RUN: %clang -### -target x86_64-unknown-openbsd -c %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NO-MATH-ERRNO %s
+// RUN: %clang -### -target x86_64-unknown-dragonfly -c %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NO-MATH-ERRNO %s
+// CHECK-NO-MATH-ERRNO: "-cc1"
+// CHECK-NO-MATH-ERRNO-NOT: "-fmath-errno"
+//
+// RUN: %clang -### -fno-math-errno -fassociative-math -freciprocal-math \
+// RUN: -fno-signed-zeros -fno-trapping-math -c %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-UNSAFE-MATH %s
// CHECK-UNSAFE-MATH: "-cc1"
// CHECK-UNSAFE-MATH: "-menable-unsafe-fp-math"
@@ -36,7 +54,7 @@
// RUN: | FileCheck --check-prefix=CHECK-NO-INFS %s
// RUN: %clang -### -ffinite-math-only -c %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-NO-NANS %s
-// RUN: %clang -### -funsafe-math-optimizations -c %s 2>&1 \
+// RUN: %clang -### -funsafe-math-optimizations -fno-math-errno -c %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-UNSAFE-MATH %s
//
// One umbrella flag is *really* weird and also changes the semantics of the
diff --git a/test/Driver/flags.c b/test/Driver/flags.c
index 7a885b6..698a54e 100644
--- a/test/Driver/flags.c
+++ b/test/Driver/flags.c
@@ -7,3 +7,5 @@
// RUN: %clang -target i386-apple-darwin9 -### -S -mno-soft-float %s -msoft-float 2> %t.log
// RUN: grep '"-no-implicit-float"' %t.log
+// RUN: %clang -target armv7-apple-darwin10 -### -S -mno-implicit-float %s 2> %t.log
+// RUN: grep '"-no-implicit-float"' %t.log | count 1
diff --git a/test/Driver/fpack-struct.c b/test/Driver/fpack-struct.c
new file mode 100644
index 0000000..cc75da5
--- /dev/null
+++ b/test/Driver/fpack-struct.c
@@ -0,0 +1,10 @@
+// RUN: %clang -fpack-struct -### %s 2> %t
+// RUN: FileCheck < %t %s
+// RUN: %clang -fpack-struct=8 -### %s 2> %t
+// RUN: FileCheck < %t %s --check-prefix=EQ
+
+// CHECK: "-cc1"
+// CHECK: "-fpack-struct=1"
+
+// CHECK-EQ: "-cc1"
+// CHECK-EQ: "-fpack-struct=8"
diff --git a/test/Driver/freebsd.c b/test/Driver/freebsd.c
index 1fb84a0..642c60c 100644
--- a/test/Driver/freebsd.c
+++ b/test/Driver/freebsd.c
@@ -28,3 +28,19 @@
// RUN: --sysroot=%S/Inputs/multiarch_freebsd64_tree -print-search-dirs 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-LIB32PATHS %s
// CHECK-LIB32PATHS: libraries: ={{.*:?}}/usr/lib32
+//
+// Check that the new linker flags are passed to FreeBSD
+// RUN: %clang -no-canonical-prefixes -target x86_64-pc-freebsd8 -m32 %s \
+// RUN: --sysroot=%S/Inputs/multiarch_freebsd64_tree -### 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-LDFLAGS8 %s
+// RUN: %clang -no-canonical-prefixes -target x86_64-pc-freebsd9 -m32 %s \
+// RUN: --sysroot=%S/Inputs/multiarch_freebsd64_tree -### 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-LDFLAGS9 %s
+// RUN: %clang -no-canonical-prefixes -target x86_64-pc-freebsd10.0 -m32 %s \
+// RUN: --sysroot=%S/Inputs/multiarch_freebsd64_tree -### 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-LDFLAGS9 %s
+// CHECK-LDFLAGS8-NOT: --hash-style=both
+// CHECK-LDFLAGS8: --enable-new-dtags
+// CHECK-LDFLAGS9: --hash-style=both
+// CHECK-LDFLAGS9: --enable-new-dtags
+
diff --git a/test/Driver/gnu-runtime.m b/test/Driver/gnu-runtime.m
index 12426d9..1d7a40a 100644
--- a/test/Driver/gnu-runtime.m
+++ b/test/Driver/gnu-runtime.m
@@ -1,5 +1,4 @@
// RUN: %clang -target i386-apple-darwin10 -### -fsyntax-only -fgnu-runtime %s 2>&1 | FileCheck %s
// RUN: %clang -target i386-apple-darwin10 -### -x objective-c++ -fsyntax-only -fgnu-runtime %s 2>&1 | FileCheck %s
-// CHECK: -fgnu-runtime
-// CHECK: -fobjc-runtime-has-arc
-// CHECK: -fobjc-runtime-has-weak
+// CHECK: -fobjc-runtime=gcc
+// CHECK-NOT: fragile
diff --git a/test/Driver/ios-simulator-arcruntime.c b/test/Driver/ios-simulator-arcruntime.c
index 9bf2091..33d3492 100644
--- a/test/Driver/ios-simulator-arcruntime.c
+++ b/test/Driver/ios-simulator-arcruntime.c
@@ -1,6 +1,8 @@
// RUN: %clang -### -x objective-c -target i386-apple-darwin10 -arch i386 -mmacosx-version-min=10.6 -D__IPHONE_OS_VERSION_MIN_REQUIRED=40201 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS1 %s
-// RUN: %clang -### -x objective-c -target i386-apple-darwin10 -arch i386 -mmacosx-version-min=10.6 -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS2 %s
+// RUN: %clang -### -x objective-c -target i386-apple-darwin10 -arch i386 -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS2 %s
//
-// CHECK-OPTIONS1-NOT: -fobjc-runtime-has-weak
-// CHECK-OPTIONS2: -fobjc-runtime-has-weak
+// CHECK-OPTIONS1: i386-apple-macosx10.6.0
+// CHECK-OPTIONS1: -fobjc-runtime=ios-4.2.1
+// CHECK-OPTIONS2: i386-apple-macosx10.6.0
+// CHECK-OPTIONS2: -fobjc-runtime=ios-5.0.0
diff --git a/test/Driver/linker-opts.c b/test/Driver/linker-opts.c
index d036dfd..85e180c 100644
--- a/test/Driver/linker-opts.c
+++ b/test/Driver/linker-opts.c
@@ -2,4 +2,4 @@
// CHECK: "-L" "{{.*}}/test1"
// GCC driver is used as linker on cygming. It should be aware of LIBRARY_PATH.
-// XFAIL: cygwin,mingw32
+// XFAIL: cygwin,mingw32,win32
diff --git a/test/Driver/linux-as.c b/test/Driver/linux-as.c
index 22eac0d..b3265da 100644
--- a/test/Driver/linux-as.c
+++ b/test/Driver/linux-as.c
@@ -3,29 +3,54 @@
// RUN: %clang -target arm-linux -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ARM %s
-// CHECK-ARM: as{{(.exe)?}}"
+// CHECK-ARM: as{{(.exe)?}}" "-mfloat-abi=soft"
//
// RUN: %clang -target arm-linux -mcpu=cortex-a8 -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ARM-MCPU %s
-// CHECK-ARM-MCPU: as{{(.exe)?}}" "-mcpu=cortex-a8"
+// CHECK-ARM-MCPU: as{{(.exe)?}}" "-mfloat-abi=soft" "-mcpu=cortex-a8"
//
// RUN: %clang -target arm-linux -mfpu=neon -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ARM-MFPU %s
-// CHECK-ARM-MFPU: as{{(.exe)?}}" "-mfpu=neon"
+// CHECK-ARM-MFPU: as{{(.exe)?}}" "-mfloat-abi=soft" "-mfpu=neon"
//
// RUN: %clang -target arm-linux -march=armv7-a -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ARM-MARCH %s
-// CHECK-ARM-MARCH: as{{(.exe)?}}" "-march=armv7-a"
+// CHECK-ARM-MARCH: as{{(.exe)?}}" "-mfloat-abi=soft" "-march=armv7-a"
//
// RUN: %clang -target arm-linux -mcpu=cortex-a8 -mfpu=neon -march=armv7-a -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ARM-ALL %s
-// CHECK-ARM-ALL: as{{(.exe)?}}" "-march=armv7-a" "-mcpu=cortex-a8" "-mfpu=neon"
+// CHECK-ARM-ALL: as{{(.exe)?}}" "-mfloat-abi=soft" "-march=armv7-a" "-mcpu=cortex-a8" "-mfpu=neon"
//
// RUN: %clang -target armv7-linux -mcpu=cortex-a8 -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=ARM-TARGET %s
-// CHECK-ARM-TARGET: as{{(.exe)?}}" "-mfpu=neon" "-mcpu=cortex-a8"
+// CHECK-ARM-TARGET: as{{(.exe)?}}" "-mfpu=neon" "-mfloat-abi=soft" "-mcpu=cortex-a8"
+//
+// RUN: %clang -target arm-linux -mfloat-abi=hard -### \
+// RUN: -no-integrated-as -c %s 2>&1 \
+// RUN: | FileCheck -check-prefix=ARM-MFLOAT-ABI %s
+// CHECK-ARM-MFLOAT-ABI: as{{(.exe)?}}" "-mfloat-abi=hard"
+//
+// RUN: %clang -target arm-linux-androideabi -### \
+// RUN: -no-integrated-as -c %s 2>&1 \
+// RUN: | FileCheck -check-prefix=ARM-ANDROID %s
+// CHECK-ARM-ANDROID: as{{(.exe)?}}" "-mfloat-abi=soft"
+//
+// RUN: %clang -target arm-linux-androideabi -march=armv7-a -### \
+// RUN: -no-integrated-as -c %s 2>&1 \
+// RUN: | FileCheck -check-prefix=ARM-ANDROID-SOFTFP %s
+// CHECK-ARM-ANDROID-SOFTFP: as{{(.exe)?}}" "-mfloat-abi=softfp" "-march=armv7-a"
+//
+// RUN: %clang -target arm-linux-eabi -mhard-float -### \
+// RUN: -no-integrated-as -c %s 2>&1 \
+// RUN: | FileCheck -check-prefix=ARM-HARDFP %s
+// CHECK-ARM-HARDFP: as{{(.exe)?}}" "-mfloat-abi=hard"
+//
+// RUN: %clang -target ppc-linux -mcpu=invalid-cpu -### \
+// RUN: -no-integrated-as -c %s 2>&1 \
+// RUN: | FileCheck -check-prefix=PPC-NO-MCPU %s
+// CHECK-PPC-NO-MCPU-NOT: as{{.*}} "-mcpu=invalid-cpu"
diff --git a/test/Driver/linux-ld.c b/test/Driver/linux-ld.c
index 9a35d5d..a6831b6 100644
--- a/test/Driver/linux-ld.c
+++ b/test/Driver/linux-ld.c
@@ -174,6 +174,39 @@
// CHECK-UBUNTU-11-04: "-L[[SYSROOT]]/lib"
// CHECK-UBUNTU-11-04: "-L[[SYSROOT]]/usr/lib"
//
+// Check multi arch support on Ubuntu 12.04 LTS.
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-unknown-linux-gnueabihf \
+// RUN: --sysroot=%S/Inputs/ubuntu_12.04_LTS_multiarch_tree \
+// RUN: | FileCheck --check-prefix=CHECK-UBUNTU-12-04-ARM-HF %s
+// CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/../../../arm-linux-gnueabihf/crt1.o"
+// CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/../../../arm-linux-gnueabihf/crti.o"
+// CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtbegin.o"
+// CHECK-UBUNTU-12-04-ARM-HF: "-L[[SYSROOT]]/usr/lib/gcc/arm-linux-gnueabihf/4.6.3"
+// CHECK-UBUNTU-12-04-ARM-HF: "-L[[SYSROOT]]/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/../../../arm-linux-gnueabihf"
+// CHECK-UBUNTU-12-04-ARM-HF: "-L[[SYSROOT]]/lib/arm-linux-gnueabihf"
+// CHECK-UBUNTU-12-04-ARM-HF: "-L[[SYSROOT]]/usr/lib/arm-linux-gnueabihf"
+// CHECK-UBUNTU-12-04-ARM-HF: "-L[[SYSROOT]]/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/../../.."
+// CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o"
+// CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/../../../arm-linux-gnueabihf/crtn.o"
+//
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-unknown-linux-gnueabi \
+// RUN: --sysroot=%S/Inputs/ubuntu_12.04_LTS_multiarch_tree \
+// RUN: | FileCheck --check-prefix=CHECK-UBUNTU-12-04-ARM %s
+// CHECK-UBUNTU-12-04-ARM: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-UBUNTU-12-04-ARM: "{{.*}}/usr/lib/gcc/arm-linux-gnueabi/4.6.1/../../../arm-linux-gnueabi/crt1.o"
+// CHECK-UBUNTU-12-04-ARM: "{{.*}}/usr/lib/gcc/arm-linux-gnueabi/4.6.1/../../../arm-linux-gnueabi/crti.o"
+// CHECK-UBUNTU-12-04-ARM: "{{.*}}/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtbegin.o"
+// CHECK-UBUNTU-12-04-ARM: "-L[[SYSROOT]]/usr/lib/gcc/arm-linux-gnueabi/4.6.1"
+// CHECK-UBUNTU-12-04-ARM: "-L[[SYSROOT]]/usr/lib/gcc/arm-linux-gnueabi/4.6.1/../../../arm-linux-gnueabi"
+// CHECK-UBUNTU-12-04-ARM: "-L[[SYSROOT]]/lib/arm-linux-gnueabi"
+// CHECK-UBUNTU-12-04-ARM: "-L[[SYSROOT]]/usr/lib/arm-linux-gnueabi"
+// CHECK-UBUNTU-12-04-ARM: "-L[[SYSROOT]]/usr/lib/gcc/arm-linux-gnueabi/4.6.1/../../.."
+// CHECK-UBUNTU-12-04-ARM: "{{.*}}/usr/lib/gcc/arm-linux-gnueabi/4.6.1/crtend.o"
+// CHECK-UBUNTU-12-04-ARM: "{{.*}}/usr/lib/gcc/arm-linux-gnueabi/4.6.1/../../../arm-linux-gnueabi/crtn.o"
+//
// Test the setup that shipped in SUSE 10.3 on ppc64.
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
// RUN: -target powerpc64-suse-linux \
@@ -186,6 +219,21 @@
// CHECK-SUSE-10-3-PPC64: "-L[[SYSROOT]]/lib/../lib64"
// CHECK-SUSE-10-3-PPC64: "-L[[SYSROOT]]/usr/lib/../lib64"
//
+// Check dynamic-linker for different archs
+// RUN: %clang %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-gnueabi \
+// RUN: | FileCheck --check-prefix=CHECK-ARM %s
+// CHECK-ARM: "{{.*}}ld{{(.exe)?}}"
+// CHECK-ARM: "-m" "armelf_linux_eabi"
+// CHECK-ARM: "-dynamic-linker" "{{.*}}/lib/ld-linux.so.3"
+//
+// RUN: %clang %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-gnueabihf \
+// RUN: | FileCheck --check-prefix=CHECK-ARM-HF %s
+// CHECK-ARM-HF: "{{.*}}ld{{(.exe)?}}"
+// CHECK-ARM-HF: "-m" "armelf_linux_eabi"
+// CHECK-ARM-HF: "-dynamic-linker" "{{.*}}/lib/ld-linux-armhf.so.3"
+//
// Check that we do not pass --hash-style=gnu and --hash-style=both to linker
// and provide correct path to the dynamic linker and emulation mode when build
// for MIPS platforms.
@@ -267,4 +315,86 @@
// CHECK-DEBIAN-PPC64: "-L[[SYSROOT]]/usr/lib/gcc/powerpc64-linux-gnu/4.5/../../.."
// CHECK-DEBIAN-PPC64: "-L[[SYSROOT]]/lib"
// CHECK-DEBIAN-PPC64: "-L[[SYSROOT]]/usr/lib"
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target mips-linux-gnu \
+// RUN: --sysroot=%S/Inputs/debian_multiarch_tree \
+// RUN: | FileCheck --check-prefix=CHECK-DEBIAN-MIPS %s
+// CHECK-DEBIAN-MIPS: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-DEBIAN-MIPS: "{{.*}}/usr/lib/gcc/mips-linux-gnu/4.5/crtbegin.o"
+// CHECK-DEBIAN-MIPS: "-L[[SYSROOT]]/usr/lib/gcc/mips-linux-gnu/4.5"
+// CHECK-DEBIAN-MIPS: "-L[[SYSROOT]]/usr/lib/gcc/mips-linux-gnu/4.5/../../../mips-linux-gnu"
+// CHECK-DEBIAN-MIPS: "-L[[SYSROOT]]/usr/lib/mips-linux-gnu"
+// CHECK-DEBIAN-MIPS: "-L[[SYSROOT]]/usr/lib/gcc/mips-linux-gnu/4.5/../../.."
+// CHECK-DEBIAN-MIPS: "-L[[SYSROOT]]/lib"
+// CHECK-DEBIAN-MIPS: "-L[[SYSROOT]]/usr/lib"
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target mipsel-linux-gnu \
+// RUN: --sysroot=%S/Inputs/debian_multiarch_tree \
+// RUN: | FileCheck --check-prefix=CHECK-DEBIAN-MIPSEL %s
+// CHECK-DEBIAN-MIPSEL: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-DEBIAN-MIPSEL: "{{.*}}/usr/lib/gcc/mipsel-linux-gnu/4.5/crtbegin.o"
+// CHECK-DEBIAN-MIPSEL: "-L[[SYSROOT]]/usr/lib/gcc/mipsel-linux-gnu/4.5"
+// CHECK-DEBIAN-MIPSEL: "-L[[SYSROOT]]/usr/lib/gcc/mipsel-linux-gnu/4.5/../../../mipsel-linux-gnu"
+// CHECK-DEBIAN-MIPSEL: "-L[[SYSROOT]]/usr/lib/mipsel-linux-gnu"
+// CHECK-DEBIAN-MIPSEL: "-L[[SYSROOT]]/usr/lib/gcc/mipsel-linux-gnu/4.5/../../.."
+// CHECK-DEBIAN-MIPSEL: "-L[[SYSROOT]]/lib"
+// CHECK-DEBIAN-MIPSEL: "-L[[SYSROOT]]/usr/lib"
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target mips64-linux-gnu \
+// RUN: --sysroot=%S/Inputs/debian_multiarch_tree \
+// RUN: | FileCheck --check-prefix=CHECK-DEBIAN-MIPS64 %s
+// CHECK-DEBIAN-MIPS64: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-DEBIAN-MIPS64: "{{.*}}/usr/lib/gcc/mips-linux-gnu/4.5/64/crtbegin.o"
+// CHECK-DEBIAN-MIPS64: "-L[[SYSROOT]]/usr/lib/gcc/mips-linux-gnu/4.5/64"
+// CHECK-DEBIAN-MIPS64: "-L[[SYSROOT]]/usr/lib/gcc/mips-linux-gnu/4.5"
+// CHECK-DEBIAN-MIPS64: "-L[[SYSROOT]]/usr/lib/gcc/mips-linux-gnu/4.5/../../.."
+// CHECK-DEBIAN-MIPS64: "-L[[SYSROOT]]/lib"
+// CHECK-DEBIAN-MIPS64: "-L[[SYSROOT]]/usr/lib"
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target mips64el-linux-gnu \
+// RUN: --sysroot=%S/Inputs/debian_multiarch_tree \
+// RUN: | FileCheck --check-prefix=CHECK-DEBIAN-MIPS64EL %s
+// CHECK-DEBIAN-MIPS64EL: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-DEBIAN-MIPS64EL: "{{.*}}/usr/lib/gcc/mipsel-linux-gnu/4.5/64/crtbegin.o"
+// CHECK-DEBIAN-MIPS64EL: "-L[[SYSROOT]]/usr/lib/gcc/mipsel-linux-gnu/4.5/64"
+// CHECK-DEBIAN-MIPS64EL: "-L[[SYSROOT]]/usr/lib/gcc/mipsel-linux-gnu/4.5"
+// CHECK-DEBIAN-MIPS64EL: "-L[[SYSROOT]]/usr/lib/gcc/mipsel-linux-gnu/4.5/../../.."
+// CHECK-DEBIAN-MIPS64EL: "-L[[SYSROOT]]/lib"
+// CHECK-DEBIAN-MIPS64EL: "-L[[SYSROOT]]/usr/lib"
//
+// Test linker invocation on Android.
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-androideabi \
+// RUN: --sysroot=%S/Inputs/basic_android_tree \
+// RUN: | FileCheck --check-prefix=CHECK-ANDROID %s
+// CHECK-ANDROID: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-ANDROID: "{{.*}}/crtbegin_dynamic.o"
+// CHECK-ANDROID: "-L[[SYSROOT]]/usr/lib"
+// CHECK-ANDROID-NOT: "gcc_s"
+// CHECK-ANDROID: "-lgcc"
+// CHECK-ANDROID-NOT: "gcc_s"
+// CHECK-ANDROID: "{{.*}}/crtend_android.o"
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-androideabi \
+// RUN: --sysroot=%S/Inputs/basic_android_tree \
+// RUN: -shared \
+// RUN: | FileCheck --check-prefix=CHECK-ANDROID-SO %s
+// CHECK-ANDROID-SO: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-ANDROID-SO: "{{.*}}/crtbegin_so.o"
+// CHECK-ANDROID-SO: "-L[[SYSROOT]]/usr/lib"
+// CHECK-ANDROID-SO-NOT: "gcc_s"
+// CHECK-ANDROID-SO: "-lgcc"
+// CHECK-ANDROID-SO-NOT: "gcc_s"
+// CHECK-ANDROID-SO: "{{.*}}/crtend_so.o"
+// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
+// RUN: -target arm-linux-androideabi \
+// RUN: --sysroot=%S/Inputs/basic_android_tree \
+// RUN: -static \
+// RUN: | FileCheck --check-prefix=CHECK-ANDROID-STATIC %s
+// CHECK-ANDROID-STATIC: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]"
+// CHECK-ANDROID-STATIC: "{{.*}}/crtbegin_static.o"
+// CHECK-ANDROID-STATIC: "-L[[SYSROOT]]/usr/lib"
+// CHECK-ANDROID-STATIC-NOT: "gcc_s"
+// CHECK-ANDROID-STATIC: "-lgcc"
+// CHECK-ANDROID-STATIC-NOT: "gcc_s"
+// CHECK-ANDROID-STATIC: "{{.*}}/crtend_android.o"
diff --git a/test/Driver/mips-as.c b/test/Driver/mips-as.c
index 44b8d8d..0ace4dd 100644
--- a/test/Driver/mips-as.c
+++ b/test/Driver/mips-as.c
@@ -3,29 +3,36 @@
// RUN: %clang -target mips-linux-gnu -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=MIPS32-EB-AS %s
-// CHECK-MIPS32-EB-AS: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB"
+// MIPS32-EB-AS: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB"
+// MIPS32-EB-AS-NOT: "-KPIC"
+//
+// RUN: %clang -target mips-linux-gnu -### \
+// RUN: -no-integrated-as -fPIC -c %s 2>&1 \
+// RUN: | FileCheck -check-prefix=MIPS32-EB-PIC %s
+// MIPS32-EB-PIC: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB"
+// MIPS32-EB-PIC: "-KPIC"
//
// RUN: %clang -target mipsel-linux-gnu -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=MIPS32-EL-AS %s
-// CHECK-MIPS32-EL-AS: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EL"
+// MIPS32-EL-AS: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EL"
//
// RUN: %clang -target mips64-linux-gnu -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=MIPS64-EB-AS %s
-// CHECK-MIPS64-EB-AS: as{{(.exe)?}}" "-march" "mips64" "-mabi" "64" "-EB"
+// MIPS64-EB-AS: as{{(.exe)?}}" "-march" "mips64" "-mabi" "64" "-EB"
//
// RUN: %clang -target mips64el-linux-gnu -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=MIPS64-EL-AS %s
-// CHECK-MIPS64-EL-AS: as{{(.exe)?}}" "-march" "mips64" "-mabi" "64" "-EL"
+// MIPS64-EL-AS: as{{(.exe)?}}" "-march" "mips64" "-mabi" "64" "-EL"
//
// RUN: %clang -target mips-linux-gnu -mabi=eabi -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=MIPS-EABI %s
-// CHECK-MIPS-EABI: as{{(.exe)?}}" "-march" "mips32" "-mabi" "eabi" "-EB"
+// MIPS-EABI: as{{(.exe)?}}" "-march" "mips32" "-mabi" "eabi" "-EB"
//
// RUN: %clang -target mips64-linux-gnu -mabi=n32 -### \
// RUN: -no-integrated-as -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=MIPS-N32 %s
-// CHECK-MIPS-N32: as{{(.exe)?}}" "-march" "mips64" "-mabi" "n32" "-EB"
+// MIPS-N32: as{{(.exe)?}}" "-march" "mips64" "-mabi" "n32" "-EB"
diff --git a/test/Driver/mips-features.c b/test/Driver/mips-features.c
new file mode 100644
index 0000000..5be2683
--- /dev/null
+++ b/test/Driver/mips-features.c
@@ -0,0 +1,39 @@
+// REQUIRES: mips-registered-target
+//
+// Check handling MIPS specific features options.
+//
+// -mips16
+// RUN: %clang -target mips-linux-gnu -### -c %s \
+// RUN: -mno-mips16 -mips16 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-MIPS16 %s
+// CHECK-MIPS16: "-target-feature" "+mips16"
+//
+// -mno-mips16
+// RUN: %clang -target mips-linux-gnu -### -c %s \
+// RUN: -mips16 -mno-mips16 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NOMIPS16 %s
+// CHECK-NOMIPS16: "-target-feature" "-mips16"
+//
+// -mdsp
+// RUN: %clang -target mips-linux-gnu -### -c %s \
+// RUN: -mno-dsp -mdsp 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-MDSP %s
+// CHECK-MDSP: "-target-feature" "+dsp"
+//
+// -mno-dsp
+// RUN: %clang -target mips-linux-gnu -### -c %s \
+// RUN: -mdsp -mno-dsp 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NOMDSP %s
+// CHECK-NOMDSP: "-target-feature" "-dsp"
+//
+// -mdspr2
+// RUN: %clang -target mips-linux-gnu -### -c %s \
+// RUN: -mno-dspr2 -mdspr2 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-MDSPR2 %s
+// CHECK-MDSPR2: "-target-feature" "+dspr2"
+//
+// -mno-dspr2
+// RUN: %clang -target mips-linux-gnu -### -c %s \
+// RUN: -mdspr2 -mno-dspr2 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NOMDSPR2 %s
+// CHECK-NOMDSPR2: "-target-feature" "-dspr2"
diff --git a/test/Driver/mips-float.c b/test/Driver/mips-float.c
index c9e107f..95eb002 100644
--- a/test/Driver/mips-float.c
+++ b/test/Driver/mips-float.c
@@ -1,3 +1,4 @@
+// REQUIRES: mips-registered-target
// Check handling -mhard-float / -msoft-float / -mfloat-abi options
// when build for MIPS platforms.
//
diff --git a/test/Driver/montavista-gcc-toolchain.c b/test/Driver/montavista-gcc-toolchain.c
new file mode 100644
index 0000000..b9ab729
--- /dev/null
+++ b/test/Driver/montavista-gcc-toolchain.c
@@ -0,0 +1,9 @@
+// Test that the montavista gcc-toolchain is correctly detected
+//
+// RUN: %clang -print-libgcc-file-name 2>&1 \
+// RUN: -target i686-montavista-linux \
+// RUN: -gcc-toolchain %S/Inputs/montavista_i686_tree/usr \
+// RUN: | FileCheck %s
+
+// Test for header search toolchain detection.
+// CHECK: montavista_i686_tree/usr/lib/gcc/i686-montavista-linux/4.2.0/libgcc.a
diff --git a/test/Driver/msvc_forward.c b/test/Driver/msvc_forward.c
new file mode 100644
index 0000000..fe0ae84
--- /dev/null
+++ b/test/Driver/msvc_forward.c
@@ -0,0 +1,5 @@
+// RUN: %clang -target i686-pc-win32 -lkernel32.lib -luser32.lib -### %s 2>&1 | FileCheck %s
+// CHECK-NOT: "-lkernel32.lib"
+// CHECK-NOT: "-luser32.lib"
+// CHECK: "kernel32.lib"
+// CHECK: "user32.lib"
diff --git a/test/Driver/nodefaultlib.c b/test/Driver/nodefaultlib.c
new file mode 100644
index 0000000..518928a
--- /dev/null
+++ b/test/Driver/nodefaultlib.c
@@ -0,0 +1,8 @@
+// RUN: %clang -target i686-pc-linux-gnu -### -nodefaultlibs %s 2> %t
+// RUN: FileCheck < %t %s
+//
+// CHECK-NOT: start-group
+// CHECK-NOT: -lgcc
+// CHECK-NOT: -lc
+// CHECK: crtbegin
+// CHECK: crtend
diff --git a/test/Driver/rewrite-legacy-objc.m b/test/Driver/rewrite-legacy-objc.m
index af59ff6..d243c7a 100644
--- a/test/Driver/rewrite-legacy-objc.m
+++ b/test/Driver/rewrite-legacy-objc.m
@@ -3,7 +3,7 @@
// TEST0: clang{{.*}}" "-cc1"
// TEST0: "-rewrite-objc"
// FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead.
-// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-mstackrealign" "-fblocks" "-fobjc-runtime-has-arc" "-fobjc-runtime-has-weak" "-fobjc-fragile-abi" "-fobjc-default-synthesize-properties" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fdiagnostics-show-option"
+// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-mstackrealign" "-fblocks" "-fobjc-runtime=macosx-fragile" "-fobjc-default-synthesize-properties" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fdiagnostics-show-option"
// TEST0: rewrite-legacy-objc.m"
// RUN: not %clang -ccc-no-clang -target unknown -rewrite-legacy-objc %s -o - -### 2>&1 | \
diff --git a/test/Driver/rewrite-objc.m b/test/Driver/rewrite-objc.m
index 7b080fc..6696797 100644
--- a/test/Driver/rewrite-objc.m
+++ b/test/Driver/rewrite-objc.m
@@ -3,7 +3,7 @@
// TEST0: clang{{.*}}" "-cc1"
// TEST0: "-rewrite-objc"
// FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead.
-// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-mstackrealign" "-fblocks" "-fobjc-runtime-has-arc" "-fobjc-runtime-has-weak" "-fobjc-dispatch-method=mixed" "-fobjc-default-synthesize-properties" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fdiagnostics-show-option"
+// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-mstackrealign" "-fblocks" "-fobjc-runtime=macosx" "-fobjc-dispatch-method=mixed" "-fobjc-default-synthesize-properties" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fdiagnostics-show-option"
// TEST0: rewrite-objc.m"
// RUN: not %clang -ccc-no-clang -target unknown -rewrite-objc %s -o - -### 2>&1 | \
diff --git a/test/Driver/target-triple-deployment.c b/test/Driver/target-triple-deployment.c
new file mode 100644
index 0000000..8e4824a7fe
--- /dev/null
+++ b/test/Driver/target-triple-deployment.c
@@ -0,0 +1,33 @@
+// RUN: touch %t.o
+// RUN: %clang -target x86_64-apple-macosx -### %t.o 2> %t.log
+// RUN: %clang -target x86_64-apple-darwin9 -### %t.o 2>> %t.log
+// RUN: %clang -target x86_64-apple-macosx10.7 -### %t.o 2>> %t.log
+//
+// RUN: %clang -target armv7-apple-ios -### %t.o 2>> %t.log
+// RUN: %clang -target armv7-apple-ios0.0 -### %t.o 2>> %t.log
+// RUN: %clang -target armv7-apple-ios1.2.3 -### %t.o 2>> %t.log
+// RUN: %clang -target armv7-apple-ios5.0 -### %t.o 2>> %t.log
+//
+// RUN: FileCheck %s < %t.log
+
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -macosx_version_min
+// CHECK: 10.4.0
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -macosx_version_min
+// CHECK: 10.5.0
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -macosx_version_min
+// CHECK: 10.7.0
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -iphoneos_version_min
+// CHECK: 3.0.0
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -iphoneos_version_min
+// CHECK: 3.0.0
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -iphoneos_version_min
+// CHECK: 1.2.3
+// CHECK: {{ld(.exe)?"}}
+// CHECK: -iphoneos_version_min
+// CHECK: 5.0.0
diff --git a/test/Driver/warning-options.cpp b/test/Driver/warning-options.cpp
new file mode 100644
index 0000000..ab0da42
--- /dev/null
+++ b/test/Driver/warning-options.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang -### -Wlarge-by-value-copy %s 2>&1 | FileCheck -check-prefix=LARGE_VALUE_COPY_DEFAULT %s
+// LARGE_VALUE_COPY_DEFAULT: -Wlarge-by-value-copy=64
+// RUN: %clang -### -Wlarge-by-value-copy=128 %s 2>&1 | FileCheck -check-prefix=LARGE_VALUE_COPY_JOINED %s
+// LARGE_VALUE_COPY_JOINED: -Wlarge-by-value-copy=128
+
+// RUN: %clang -### -c -Wmonkey -Wno-monkey -Wno-unused-command-line-arguments \
+// RUN: -Wno-unused-command-line-argument %s 2>&1 | FileCheck %s
+// CHECK: unknown warning option '-Wmonkey'
+// CHECK: unknown warning option '-Wno-monkey'
+// CHECK: unknown warning option '-Wno-unused-command-line-arguments'; did you mean '-Wno-unused-command-line-argument'?
diff --git a/test/Driver/warning-options_pedantic.cpp b/test/Driver/warning-options_pedantic.cpp
new file mode 100644
index 0000000..c6d11be
--- /dev/null
+++ b/test/Driver/warning-options_pedantic.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang -### -pedantic -no-pedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC %s
+// RUN: %clang -### -pedantic -Wno-pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC %s
+// NO_PEDANTIC-NOT: -pedantic
+// RUN: %clang -### -pedantic -pedantic -no-pedantic -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC %s
+// RUN: %clang -### -pedantic -pedantic -no-pedantic -Wpedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC %s
+// PEDANTIC: -pedantic
+// XFAIL: cygwin,mingw32
diff --git a/test/FixIt/fixit-autoreleasepool.m b/test/FixIt/fixit-autoreleasepool.m
new file mode 100644
index 0000000..ba1ad13
--- /dev/null
+++ b/test/FixIt/fixit-autoreleasepool.m
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdiagnostics-parseable-fixits -x objective-c %s 2>&1 | FileCheck %s
+// rdar://10723084
+
+void f0() {
+ @autorelease {
+ }
+}
+
+// CHECK: {5:4-5:15}:"autoreleasepool"
diff --git a/test/FixIt/fixit-cxx0x.cpp b/test/FixIt/fixit-cxx0x.cpp
index b6cc2c0..0c837b4 100644
--- a/test/FixIt/fixit-cxx0x.cpp
+++ b/test/FixIt/fixit-cxx0x.cpp
@@ -108,3 +108,17 @@ void func();
template<int *ip> struct IP { }; // expected-note{{declared here}}
IP<0> ip0; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}}
+namespace MissingSemi {
+ struct a // expected-error {{expected ';' after struct}}
+ struct b // expected-error {{expected ';' after struct}}
+ enum x : int { x1, x2, x3 } // expected-error {{expected ';' after enum}}
+ struct c // expected-error {{expected ';' after struct}}
+ enum x : int // expected-error {{expected ';' after enum}}
+ // FIXME: The following gives a poor diagnostic (we parse the 'int' and the
+ // 'struct' as part of the same enum-base.
+ // enum x : int
+ // struct y
+ namespace N {
+ struct d // expected-error {{expected ';' after struct}}
+ }
+}
diff --git a/test/FixIt/fixit-include.c b/test/FixIt/fixit-include.c
new file mode 100644
index 0000000..51bd9b0
--- /dev/null
+++ b/test/FixIt/fixit-include.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -Wall -pedantic -verify %s
+// RUN: cp %s %t
+// RUN: cp %S/fixit-include.h %T
+// RUN: not %clang_cc1 -fsyntax-only -fixit %t
+// RUN: %clang_cc1 -Wall -pedantic %t
+
+#include <fixit-include.h> // expected-error {{'fixit-include.h' file not found with <angled> include; use "quotes" instead}}
+
+#pragma does_not_exist // expected-warning {{unknown pragma ignored}}
+
+int main( void ) {
+ return 0;
+}
diff --git a/test/FixIt/fixit-include.h b/test/FixIt/fixit-include.h
new file mode 100644
index 0000000..6a22d2e
--- /dev/null
+++ b/test/FixIt/fixit-include.h
@@ -0,0 +1 @@
+// This file is purposefully left empty
diff --git a/test/FixIt/fixit-interface-as-param.m b/test/FixIt/fixit-interface-as-param.m
new file mode 100644
index 0000000..3295c82
--- /dev/null
+++ b/test/FixIt/fixit-interface-as-param.m
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdiagnostics-parseable-fixits -x objective-c %s 2>&1 | FileCheck %s
+// rdar://11311333
+
+@interface NSView @end
+
+@interface INTF
+- (void) drawRect : inView:(NSView)view;
+@end
+
+// CHECK: {7:35-7:35}:"*"
+
diff --git a/test/FixIt/fixit-objc-message-comma-separator.m b/test/FixIt/fixit-objc-message-comma-separator.m
new file mode 100644
index 0000000..0caa33e
--- /dev/null
+++ b/test/FixIt/fixit-objc-message-comma-separator.m
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdiagnostics-parseable-fixits -x objective-c %s 2>&1 | FileCheck %s
+// rdar://11376372
+
+@class NSObject;
+
+@interface TestObj {
+}
+-(void)aMethodWithArg1:(NSObject*)arg1 arg2:(NSObject*)arg2;
+@end
+
+int main(int argc, char *argv[])
+{
+ TestObj *obj;
+ [obj aMethodWithArg1:@"Arg 1 Good", arg2:@"Arg 2 Good"];
+}
+
+// CHECK: {14:39-14:40}:""
diff --git a/test/FixIt/fixit-unicode.c b/test/FixIt/fixit-unicode.c
new file mode 100644
index 0000000..2af5e08
--- /dev/null
+++ b/test/FixIt/fixit-unicode.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only %s 2>&1 | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck -check-prefix=CHECK-MACHINE %s
+
+struct Foo {
+ int bar;
+};
+
+// PR13312
+void test1() {
+ struct Foo foo;
+ (&foo)☃>bar = 42;
+// CHECK: error: expected ';' after expression
+// Make sure we emit the fixit right in front of the snowman.
+// CHECK: {{^ \^}}
+// CHECK: {{^ ;}}
+
+// CHECK-MACHINE: fix-it:"{{.*}}fixit-unicode.c":{11:9-11:9}:";"
+}
+
+
+int printf(const char *, ...);
+void test2() {
+ printf("∆: %d", 1L);
+// CHECK: warning: format specifies type 'int' but the argument has type 'long'
+// Don't crash emitting a fixit after the delta.
+// CHECK: printf("
+// CHECK: : %d", 1L);
+// Unfortunately, we can't actually check the location of the printed fixit,
+// because different systems will render the delta differently (either as a
+// character, or as <U+2206>.) The fixit should line up with the %d regardless.
+
+// CHECK-MACHINE: fix-it:"{{.*}}fixit-unicode.c":{23:16-23:18}:"%ld"
+}
diff --git a/test/FixIt/fixit-vexing-parse.cpp b/test/FixIt/fixit-vexing-parse.cpp
index 8450590..dd0f873 100644
--- a/test/FixIt/fixit-vexing-parse.cpp
+++ b/test/FixIt/fixit-vexing-parse.cpp
@@ -7,6 +7,7 @@ struct S {
struct T {
T();
+ T(S, S);
int n;
};
@@ -30,26 +31,44 @@ S F2();
namespace N {
void test() {
- // CHECK: fix-it:"{{.*}}":{34:9-34:11}:" = {}"
+ // CHECK: fix-it:"{{.*}}":{35:9-35:11}:" = {}"
S s1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{38:9-38:10}:";"
- // CHECK: fix-it:"{{.*}}":{39:7-39:9}:" = {}"
+ // CHECK: fix-it:"{{.*}}":{39:9-39:10}:";"
+ // CHECK: fix-it:"{{.*}}":{40:7-40:9}:" = {}"
S s2, // expected-note {{change this ',' to a ';' to call 'F2'}}
F2(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{43:9-43:11}:""
// CHECK: fix-it:"{{.*}}":{44:9-44:11}:""
+ // CHECK: fix-it:"{{.*}}":{45:9-45:11}:""
T t1(), // expected-warning {{function declaration}} expected-note {{remove parentheses}}
t2(); // expected-warning {{function declaration}} expected-note {{remove parentheses}}
- // CHECK: fix-it:"{{.*}}":{47:8-47:10}:" = {}"
+ // Suggest parentheses only around the first argument.
+ // CHECK: fix-it:"{{.*}}":{50:10-50:10}:"("
+ // CHECK: fix-it:"{{.*}}":{50:13-50:13}:")"
+ T t3(S(), S()); // expected-warning {{disambiguated as a function declaration}} expected-note {{add a pair of parentheses}}
+
+ // Check fixit position for pathological case
+ // CHECK: fix-it:"{{.*}}":{56:11-56:11}:"("
+ // CHECK: fix-it:"{{.*}}":{56:20-56:20}:")"
+ float k[1];
+ int l(int(k[0])); // expected-warning {{disambiguated as a function declaration}} expected-note {{add a pair of parentheses}}
+
+ // Don't emit warning and fixit because this must be a function declaration due to void return type.
+ typedef void VO;
+ VO m(int (*p)[4]);
+
+ // Don't emit warning and fixit because direct initializer is not permitted here.
+ if (int n(int())){} // expected-error {{function type is not allowed here}} expected-error {{condition must have an initializer}}
+
+ // CHECK: fix-it:"{{.*}}":{66:8-66:10}:" = {}"
U u(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{50:8-50:10}:""
+ // CHECK: fix-it:"{{.*}}":{69:8-69:10}:""
V v(); // expected-warning {{function declaration}} expected-note {{remove parentheses}}
- // CHECK: fix-it:"{{.*}}":{53:8-53:10}:""
+ // CHECK: fix-it:"{{.*}}":{72:8-72:10}:""
W w(); // expected-warning {{function declaration}} expected-note {{remove parentheses}}
// TODO: Removing the parens here would not initialize U::n.
@@ -57,33 +76,33 @@ namespace N {
// Maybe suggest removing the parens anyway?
X x(); // expected-warning {{function declaration}}
- // CHECK: fix-it:"{{.*}}":{61:11-61:13}:" = 0"
+ // CHECK: fix-it:"{{.*}}":{80:11-80:13}:" = 0"
int n1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{65:11-65:12}:";"
- // CHECK: fix-it:"{{.*}}":{66:7-66:9}:" = 0"
+ // CHECK: fix-it:"{{.*}}":{84:11-84:12}:";"
+ // CHECK: fix-it:"{{.*}}":{85:7-85:9}:" = 0"
int n2, // expected-note {{change this ',' to a ';' to call 'F1'}}
F1(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{69:13-69:15}:" = 0.0"
+ // CHECK: fix-it:"{{.*}}":{88:13-88:15}:" = 0.0"
double d(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
typedef void *Ptr;
- // CHECK: fix-it:"{{.*}}":{74:10-74:12}:" = 0"
+ // CHECK: fix-it:"{{.*}}":{93:10-93:12}:" = 0"
Ptr p(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
#define NULL 0
- // CHECK: fix-it:"{{.*}}":{78:10-78:12}:" = NULL"
+ // CHECK: fix-it:"{{.*}}":{97:10-97:12}:" = NULL"
Ptr p(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{81:11-81:13}:" = false"
+ // CHECK: fix-it:"{{.*}}":{100:11-100:13}:" = false"
bool b(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{84:11-84:13}:" = '\\0'"
+ // CHECK: fix-it:"{{.*}}":{103:11-103:13}:" = '\\0'"
char c(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
- // CHECK: fix-it:"{{.*}}":{87:15-87:17}:" = L'\\0'"
+ // CHECK: fix-it:"{{.*}}":{106:15-106:17}:" = L'\\0'"
wchar_t wc(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
}
}
diff --git a/test/FixIt/fixit.c b/test/FixIt/fixit.c
index 5e4947b..ce6f109 100644
--- a/test/FixIt/fixit.c
+++ b/test/FixIt/fixit.c
@@ -100,3 +100,11 @@ int noSemiAfterLabel(int n) {
}
return 1;
}
+
+struct noSemiAfterStruct // expected-error {{expected ';' after struct}}
+struct noSemiAfterStruct {
+ int n // expected-warning {{';'}}
+} // expected-error {{expected ';' after struct}}
+enum noSemiAfterEnum {
+ e1
+} // expected-error {{expected ';' after enum}}
diff --git a/test/FixIt/fixit.cpp b/test/FixIt/fixit.cpp
index 7d531a5..3eac434 100644
--- a/test/FixIt/fixit.cpp
+++ b/test/FixIt/fixit.cpp
@@ -54,7 +54,7 @@ namespace rdar7853795 {
}
namespace rdar7796492 {
- class A { int x, y; A(); };
+ struct A { int x, y; A(); };
A::A()
: x(1) y(2) { // expected-error{{missing ',' between base or member initializers}}
@@ -204,3 +204,91 @@ template<template<typename> Foo, // expected-error {{template template parameter
template<typename> typename Bar, // expected-error {{template template parameter requires 'class' after the parameter list}}
template<typename> struct Baz> // expected-error {{template template parameter requires 'class' after the parameter list}}
void func();
+
+namespace ShadowedTagType {
+class Foo {
+ public:
+ enum Bar { X, Y };
+ void SetBar(Bar bar);
+ Bar Bar(); // expected-note 2 {{enum 'Bar' is hidden by a non-type declaration of 'Bar' here}}
+ private:
+ Bar bar_; // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}}
+};
+void Foo::SetBar(Bar bar) { bar_ = bar; } // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}}
+}
+
+#define NULL __null
+char c = NULL; // expected-warning {{implicit conversion of NULL constant to 'char'}}
+double dbl = NULL; // expected-warning {{implicit conversion of NULL constant to 'double'}}
+
+namespace arrow_suggest {
+
+template <typename T>
+class wrapped_ptr {
+ public:
+ wrapped_ptr(T* ptr) : ptr_(ptr) {}
+ T* operator->() { return ptr_; }
+ private:
+ T *ptr_;
+};
+
+class Worker {
+ public:
+ void DoSomething();
+};
+
+void test() {
+ wrapped_ptr<Worker> worker(new Worker);
+ worker.DoSomething(); // expected-error {{no member named 'DoSomething' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'; did you mean to use '->' instead of '.'?}}
+}
+
+} // namespace arrow_suggest
+
+// Make sure fixing namespace-qualified identifiers functions properly with
+// namespace-aware typo correction/
+namespace redecl_typo {
+namespace Foo {
+ void BeEvil(); // expected-note {{'BeEvil' declared here}}
+}
+namespace Bar {
+ namespace Foo {
+ bool isGood(); // expected-note {{'Bar::Foo::isGood' declared here}}
+ void beEvil();
+ }
+}
+bool Foo::isGood() { // expected-error {{out-of-line definition of 'isGood' does not match any declaration in namespace 'redecl_typo::Foo'; did you mean 'Bar::Foo::isGood'?}}
+ return true;
+}
+void Foo::beEvil() {} // expected-error {{out-of-line definition of 'beEvil' does not match any declaration in namespace 'redecl_typo::Foo'; did you mean 'BeEvil'?}}
+}
+
+// Test behavior when a template-id is ended by a token which starts with '>'.
+namespace greatergreater {
+ template<typename T> struct S { S(); S(T); };
+ void f(S<int>=0); // expected-error {{a space is required between a right angle bracket and an equals sign (use '> =')}}
+
+ // FIXME: The fix-its here overlap so -fixit mode can't apply the second one.
+ //void f(S<S<int>>=S<int>());
+
+ struct Shr {
+ template<typename T> Shr(T);
+ template<typename T> void operator >>=(T);
+ };
+
+ template<template<typename>> struct TemplateTemplateParam; // expected-error {{requires 'class'}}
+
+ template<typename T> void t();
+ void g() {
+ void (*p)() = &t<int>;
+ (void)(&t<int>==p); // expected-error {{use '> ='}}
+ (void)(&t<int>>=p); // expected-error {{use '> >'}}
+ (void)(&t<S<int>>>=p); // expected-error {{use '> >'}}
+ (Shr)&t<S<int>>>>=p; // expected-error {{use '> >'}}
+
+ // FIXME: We correct this to '&t<int> > >= p;' not '&t<int> >>= p;'
+ //(Shr)&t<int>>>=p;
+
+ // FIXME: The fix-its here overlap.
+ //(void)(&t<S<int>>==p);
+ }
+}
diff --git a/test/FixIt/format-no-fixit.m b/test/FixIt/format-no-fixit.m
new file mode 100644
index 0000000..36ecd3f
--- /dev/null
+++ b/test/FixIt/format-no-fixit.m
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -fsyntax-only %s 2>&1 | FileCheck %s
+
+// CHECK-NOT: fix-it:
+
+@class NSString;
+extern void NSLog(NSString *format, ...);
+int printf(const char * restrict, ...) ;
+
+
+void test_object_correction (id x) {
+ printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'id'}}
+ printf("%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'id'}}
+ printf("%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'id'}}
+}
+
+
+// Old-style Core Foundation types do not have __attribute__((NSObject)).
+// This is okay, but we won't suggest a fixit; arbitrary structure pointers may
+// not be objects.
+typedef const struct __CFString * CFStringRef;
+
+void test_cf_object_correction (CFStringRef x) {
+ NSLog(@"%@", x); // no-warning
+
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'CFStringRef'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'CFStringRef'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'CFStringRef'}}
+}
+
diff --git a/test/FixIt/format.m b/test/FixIt/format.m
new file mode 100644
index 0000000..c474701
--- /dev/null
+++ b/test/FixIt/format.m
@@ -0,0 +1,95 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
+// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -fblocks %s 2>&1 | FileCheck %s
+
+@class NSString;
+extern void NSLog(NSString *, ...);
+int printf(const char * restrict, ...) ;
+
+void test_integer_correction (int x) {
+ printf("%d", x); // no-warning
+ printf("%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+ printf("%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'int'}}
+ // CHECK: fix-it:"{{.*}}":{10:11-10:13}:"%d"
+ // CHECK: fix-it:"{{.*}}":{11:11-11:14}:"%d"
+
+ NSLog(@"%d", x); // no-warning
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'int'}}
+ NSLog(@"%@", x); // expected-warning{{format specifies type 'id' but the argument has type 'int'}}
+ // CHECK: fix-it:"{{.*}}":{16:11-16:13}:"%d"
+ // CHECK: fix-it:"{{.*}}":{17:11-17:14}:"%d"
+ // CHECK: fix-it:"{{.*}}":{18:11-18:13}:"%d"
+}
+
+void test_string_correction (char *x) {
+ printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'char *'}}
+ printf("%s", x); // no-warning
+ printf("%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'char *'}}
+ // CHECK: fix-it:"{{.*}}":{25:11-25:13}:"%s"
+ // CHECK: fix-it:"{{.*}}":{27:11-27:14}:"%s"
+
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'char *'}}
+ NSLog(@"%s", x); // no-warning
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'char *'}}
+ NSLog(@"%@", x); // expected-warning{{format specifies type 'id' but the argument has type 'char *'}}
+ // CHECK: fix-it:"{{.*}}":{31:11-31:13}:"%s"
+ // CHECK: fix-it:"{{.*}}":{33:11-33:14}:"%s"
+ // CHECK: fix-it:"{{.*}}":{34:11-34:13}:"%s"
+}
+
+void test_object_correction (id x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'id'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'id'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'id'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{41:11-41:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{42:11-42:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{43:11-43:14}:"%@"
+}
+
+typedef const struct __CFString * __attribute__((NSObject)) CFStringRef;
+void test_cf_object_correction (CFStringRef x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'CFStringRef'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'CFStringRef'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'CFStringRef'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{52:11-52:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{53:11-53:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{54:11-54:14}:"%@"
+}
+
+typedef void (^block_t)(void);
+void test_block_correction (block_t x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'block_t'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'block_t'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'block_t'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{63:11-63:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{64:11-64:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{65:11-65:14}:"%@"
+}
+
+void test_class_correction (Class x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'Class'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'Class'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'Class'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{73:11-73:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{74:11-74:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{75:11-75:14}:"%@"
+}
+
+
+typedef enum : int { NSUTF8StringEncoding = 8 } NSStringEncoding;
+void test_fixed_enum_correction(NSStringEncoding x) {
+ NSLog(@"%@", x); // expected-warning{{format specifies type 'id' but the argument has type 'NSStringEncoding'}}
+ // CHECK: fix-it:"{{.*}}":{85:11-85:13}:"%d"
+}
+
+typedef __SIZE_TYPE__ size_t;
+enum SomeSize : size_t { IntegerSize = sizeof(int) };
+void test_named_fixed_enum_correction(enum SomeSize x) {
+ NSLog(@"%@", x); // expected-warning{{format specifies type 'id' but the argument has type 'enum SomeSize'}}
+ // CHECK: fix-it:"{{.*}}":{92:11-92:13}:"%zu"
+}
+
diff --git a/test/FixIt/messages.cpp b/test/FixIt/messages.cpp
new file mode 100644
index 0000000..b939144
--- /dev/null
+++ b/test/FixIt/messages.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 2>&1 %s | FileCheck -strict-whitespace %s
+
+struct A {
+ unsigned int a;
+};
+
+// PR10696
+void testOverlappingInsertions(int b) {
+ A var = { b };
+ // CHECK: A var = { b };
+ // CHECK: ^
+ // CHECK: static_cast<unsigned int>( )
+}
diff --git a/test/FixIt/objc-literals.m b/test/FixIt/objc-literals.m
index 03d64b1..549cfde 100644
--- a/test/FixIt/objc-literals.m
+++ b/test/FixIt/objc-literals.m
@@ -2,13 +2,13 @@
// RUN: cp %s %t
// RUN: not %clang_cc1 -fsyntax-only -fixit -x objective-c %t
// RUN: %clang_cc1 -fsyntax-only -pedantic -Werror -x objective-c %t
-// RUN: grep arrayWithObjects %t
typedef unsigned char BOOL;
-@interface NSNumber @end
+@interface NSObject
+@end
-@interface NSNumber (NSNumberCreation)
+@interface NSNumber : NSObject
+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
@@ -24,17 +24,17 @@ typedef unsigned char BOOL;
+ (NSNumber *)numberWithBool:(BOOL)value;
@end
-@interface NSArray
-@end
-
-@interface NSArray (NSArrayCreation)
+@interface NSArray : NSObject
+ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
@end
-@interface NSDictionary
+@interface NSDictionary : NSObject
+ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
@end
+@interface NSString : NSObject
+@end
+
void fixes() {
id arr = @[
17, // expected-error{{numeric literal must be prefixed by '@' in a collection}}
diff --git a/test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/all.h b/test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/all.h
new file mode 100644
index 0000000..0f3f0be
--- /dev/null
+++ b/test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/all.h
@@ -0,0 +1 @@
+#include "warn.h"
diff --git a/test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/warn.h b/test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/warn.h
new file mode 100644
index 0000000..def881a
--- /dev/null
+++ b/test/Frontend/Inputs/SystemHeaderPrefix/libs/boost/warn.h
@@ -0,0 +1,2 @@
+#if BOOST
+#endif
diff --git a/test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/all.h b/test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/all.h
new file mode 100644
index 0000000..0f3f0be
--- /dev/null
+++ b/test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/all.h
@@ -0,0 +1 @@
+#include "warn.h"
diff --git a/test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/warn.h b/test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/warn.h
new file mode 100644
index 0000000..6a0c102
--- /dev/null
+++ b/test/Frontend/Inputs/SystemHeaderPrefix/libs/mylib/warn.h
@@ -0,0 +1,2 @@
+#if MYLIB
+#endif
diff --git a/test/Frontend/Inputs/SystemHeaderPrefix/src/all.h b/test/Frontend/Inputs/SystemHeaderPrefix/src/all.h
new file mode 100644
index 0000000..ace9699
--- /dev/null
+++ b/test/Frontend/Inputs/SystemHeaderPrefix/src/all.h
@@ -0,0 +1,6 @@
+#include "libs/boost/all.h"
+#include "libs/mylib/all.h"
+
+#include "libs/boost/warn.h"
+#include "libs/mylib/warn.h"
+#include "src/warn.h"
diff --git a/test/Frontend/Inputs/SystemHeaderPrefix/src/warn.h b/test/Frontend/Inputs/SystemHeaderPrefix/src/warn.h
new file mode 100644
index 0000000..91e2591
--- /dev/null
+++ b/test/Frontend/Inputs/SystemHeaderPrefix/src/warn.h
@@ -0,0 +1,2 @@
+#if SRC
+#endif
diff --git a/test/Frontend/Inputs/rewrite-includes1.h b/test/Frontend/Inputs/rewrite-includes1.h
new file mode 100644
index 0000000..1b6c80d
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes1.h
@@ -0,0 +1,3 @@
+#pragma clang system_header
+included_line1
+#include "rewrite-includes2.h"
diff --git a/test/Frontend/Inputs/rewrite-includes2.h b/test/Frontend/Inputs/rewrite-includes2.h
new file mode 100644
index 0000000..1114e51
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes2.h
@@ -0,0 +1 @@
+included_line2
diff --git a/test/Frontend/Inputs/rewrite-includes3.h b/test/Frontend/Inputs/rewrite-includes3.h
new file mode 100644
index 0000000..3757bc8
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes3.h
@@ -0,0 +1 @@
+included_line3
diff --git a/test/Frontend/Inputs/rewrite-includes4.h b/test/Frontend/Inputs/rewrite-includes4.h
new file mode 100644
index 0000000..b4e25d2
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes4.h
@@ -0,0 +1 @@
+included_line4
diff --git a/test/Frontend/Inputs/rewrite-includes5.h b/test/Frontend/Inputs/rewrite-includes5.h
new file mode 100644
index 0000000..934bf41
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes5.h
@@ -0,0 +1 @@
+included_line5
diff --git a/test/Frontend/Inputs/rewrite-includes6.h b/test/Frontend/Inputs/rewrite-includes6.h
new file mode 100644
index 0000000..c18e501
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes6.h
@@ -0,0 +1,2 @@
+#pragma once
+included_line6
diff --git a/test/Frontend/Inputs/rewrite-includes7.h b/test/Frontend/Inputs/rewrite-includes7.h
new file mode 100644
index 0000000..da00d4f
--- /dev/null
+++ b/test/Frontend/Inputs/rewrite-includes7.h
@@ -0,0 +1,4 @@
+#ifndef REWRITE_INCLUDES_7
+#define REWRITE_INCLUDES_7
+included_line7
+#endif
diff --git a/test/Frontend/rewrite-includes-missing.c b/test/Frontend/rewrite-includes-missing.c
new file mode 100644
index 0000000..b79bbd9
--- /dev/null
+++ b/test/Frontend/rewrite-includes-missing.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -verify -E -frewrite-includes %s -o - | FileCheck -strict-whitespace %s
+
+#include "foobar.h" // expected-error {{'foobar.h' file not found}}
+// CHECK: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "foobar.h"
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 4 "{{.*}}rewrite-includes-missing.c" 2{{$}}
diff --git a/test/Frontend/rewrite-includes.c b/test/Frontend/rewrite-includes.c
new file mode 100644
index 0000000..546a2c4
--- /dev/null
+++ b/test/Frontend/rewrite-includes.c
@@ -0,0 +1,145 @@
+// RUN: %clang_cc1 -verify -E -frewrite-includes -DFIRST -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -verify -E -frewrite-includes -P -DFIRST -I %S/Inputs %s -o - | FileCheck -check-prefix=CHECKNL -strict-whitespace %s
+// STARTCOMPARE
+#define A(a,b) a ## b
+A(1,2)
+#include "rewrite-includes1.h"
+#ifdef FIRST
+#define HEADER "rewrite-includes3.h"
+#include HEADER
+#else
+#include "rewrite-includes4.h"
+#endif
+#/**/include /**/ "rewrite-includes5.h" /**/ \
+
+#include "rewrite-includes6.h" // comment
+
+#include "rewrite-includes6.h" /* comment
+ continues */
+#include "rewrite-includes7.h"
+#include "rewrite-includes7.h"
+// ENDCOMPARE
+// CHECK: {{^}}// STARTCOMPARE{{$}}
+// CHECK-NEXT: {{^}}#define A(a,b) a ## b{{$}}
+// CHECK-NEXT: {{^}}A(1,2){{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes1.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes1.h" 1{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#pragma clang system_header{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs[/\\]}}rewrite-includes1.h" 3{{$}}
+// CHECK-NEXT: {{^}}included_line1{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes2.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes2.h" 1 3{{$}}
+// CHECK-NEXT: {{^}}included_line2{{$}}
+// CHECK-NEXT: {{^}}# 4 "{{.*[/\\]Inputs[/\\]}}rewrite-includes1.h" 2 3{{$}}
+// CHECK-NEXT: {{^}}# 7 "{{.*}}rewrite-includes.c" 2{{$}}
+// CHECK-NEXT: {{^}}#ifdef FIRST{{$}}
+// CHECK-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include HEADER{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes3.h" 1{{$}}
+// CHECK-NEXT: {{^}}included_line3{{$}}
+// CHECK-NEXT: {{^}}# 10 "{{.*}}rewrite-includes.c" 2{{$}}
+// CHECK-NEXT: {{^}}#else{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes4.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 12 "{{.*}}rewrite-includes.c"{{$}}
+// CHECK-NEXT: {{^}}#endif{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}}
+// CHECK-NEXT: {{^}} {{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes5.h" 1{{$}}
+// CHECK-NEXT: {{^}}included_line5{{$}}
+// CHECK-NEXT: {{^}}# 15 "{{.*}}rewrite-includes.c" 2{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes6.h" 1{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#pragma once{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 2 "{{.*[/\\]Inputs[/\\]}}rewrite-includes6.h"{{$}}
+// CHECK-NEXT: {{^}}included_line6{{$}}
+// CHECK-NEXT: {{^}}# 16 "{{.*}}rewrite-includes.c" 2{{$}}
+// CHECK-NEXT: {{^}} {{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}}
+// CHECK-NEXT: {{^}} continues */{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 19 "{{.*}}rewrite-includes.c"{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes7.h" 1{{$}}
+// CHECK-NEXT: {{^}}#ifndef REWRITE_INCLUDES_7{{$}}
+// CHECK-NEXT: {{^}}#define REWRITE_INCLUDES_7{{$}}
+// CHECK-NEXT: {{^}}included_line7{{$}}
+// CHECK-NEXT: {{^}}#endif{{$}}
+// CHECK-NEXT: {{^}}# 20 "{{.*}}rewrite-includes.c" 2{{$}}
+// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}}
+// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}}
+// CHECK-NEXT: {{^}}// ENDCOMPARE{{$}}
+
+// CHECKNL: {{^}}// STARTCOMPARE{{$}}
+// CHECKNL-NEXT: {{^}}#define A(a,b) a ## b{{$}}
+// CHECKNL-NEXT: {{^}}A(1,2){{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes1.h"{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#pragma clang system_header{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}included_line1{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes2.h"{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}included_line2{{$}}
+// CHECKNL-NEXT: {{^}}#ifdef FIRST{{$}}
+// CHECKNL-NEXT: {{^}}#define HEADER "rewrite-includes3.h"{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include HEADER{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}included_line3{{$}}
+// CHECKNL-NEXT: {{^}}#else{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes4.h"{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#endif{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#/**/include /**/ "rewrite-includes5.h" /**/ {{\\}}{{$}}
+// CHECKNL-NEXT: {{^}} {{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}included_line5{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes6.h" // comment{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#pragma once{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}included_line6{{$}}
+// CHECKNL-NEXT: {{^}} {{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes6.h" /* comment{{$}}
+// CHECKNL-NEXT: {{^}} continues */{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes7.h"{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#ifndef REWRITE_INCLUDES_7{{$}}
+// CHECKNL-NEXT: {{^}}#define REWRITE_INCLUDES_7{{$}}
+// CHECKNL-NEXT: {{^}}included_line7{{$}}
+// CHECKNL-NEXT: {{^}}#endif{{$}}
+// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}#include "rewrite-includes7.h"{{$}}
+// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}}
+// CHECKNL-NEXT: {{^}}// ENDCOMPARE{{$}}
diff --git a/test/Frontend/system-header-prefix.c b/test/Frontend/system-header-prefix.c
new file mode 100644
index 0000000..31194d9
--- /dev/null
+++ b/test/Frontend/system-header-prefix.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -isystem-prefix libs/ -ino-system-prefix libs/mylib/ -I%S/Inputs/SystemHeaderPrefix -Wundef -E %s 2>&1 | FileCheck %s
+
+#include "src/all.h"
+
+// CHECK-NOT: BOOST
+// CHECK: libs/mylib/warn.h:1:5: warning: 'MYLIB' is not defined, evaluates to 0
+// CHECK-NOT: BOOST
+// CHECK: libs/mylib/warn.h:1:5: warning: 'MYLIB' is not defined, evaluates to 0
+// CHECK-NOT: BOOST
+// CHECK: src/warn.h:1:5: warning: 'SRC' is not defined, evaluates to 0
+// CHECK-NOT: BOOST
diff --git a/test/Frontend/verify-directive.h b/test/Frontend/verify-directive.h
new file mode 100644
index 0000000..cb40586
--- /dev/null
+++ b/test/Frontend/verify-directive.h
@@ -0,0 +1,2 @@
+// Check that directives inside includes are included!
+// expected-error@1 {{include file test}}
diff --git a/test/Frontend/verify-fatal.c b/test/Frontend/verify-fatal.c
new file mode 100644
index 0000000..0e74032
--- /dev/null
+++ b/test/Frontend/verify-fatal.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -Wfatal-errors -verify %s 2>&1 | FileCheck %s
+
+#error first fatal
+// expected-error@-1 {{first fatal}}
+
+#error second fatal
+// expected-error@-1 {{second fatal}}
+
+
+// CHECK: error: 'error' diagnostics expected but not seen:
+// CHECK-NEXT: Line 6 (directive at {{.*}}verify-fatal.c:7): second fatal
+// CHECK-NEXT: 1 error generated.
diff --git a/test/Frontend/verify.c b/test/Frontend/verify.c
new file mode 100644
index 0000000..f8d0f42
--- /dev/null
+++ b/test/Frontend/verify.c
@@ -0,0 +1,125 @@
+// RUN: %clang_cc1 -DTEST1 -verify %s
+// RUN: %clang_cc1 -DTEST2 -verify %s 2>&1 | FileCheck -check-prefix=CHECK2 %s
+// RUN: %clang_cc1 -DTEST3 -verify %s 2>&1 | FileCheck -check-prefix=CHECK3 %s
+// RUN: %clang_cc1 -DTEST4 -verify %s 2>&1 | FileCheck -check-prefix=CHECK4 %s
+// RUN: %clang_cc1 -DTEST5 -verify %s 2>&1 | FileCheck -check-prefix=CHECK5 %s
+
+// expected-warning@ malformed
+// expected-error@7 1 {{missing or invalid line number}}
+
+// expected-warning@0 malformed
+// expected-error@10 {{missing or invalid line number}}
+
+// expected-warning@-50 malformed
+// expected-error@13 {{missing or invalid line number}}
+
+// expected-warning malformed
+// expected-error@16 {{cannot find start}}
+
+// expected-error 0+ {{should also be ignored}}
+
+#ifdef TEST1
+#if 0
+// expected-error {{should be ignored}}
+#endif
+
+#error should not be ignored
+// expected-error@-1 1+ {{should not be ignored}}
+
+#line 90
+unexpected a; // expected-error@+0 + {{unknown type}}
+
+#line 60
+unexpected b; // expected-error@33 1-1 {{unknown type}}
+
+// expected-error@+2 {{file not found}} check that multi-line handled correctly: \
+
+#include "missing_header_file.include"
+#endif
+
+#ifdef TEST2
+#define MACRO some_value // expected-error {{define_error}}
+#undef MACRO extra_token // expected-warning {{undef_error}}
+#line -2 // expected-error {{line_error}}
+#error AAA // expected-error {{BBB}} <- this shall be part of diagnostic
+#warning CCC // expected-warning {{DDD}} <- this shall be part of diagnostic
+
+#if 0
+// This is encapsulated in "#if 0" so that the expected-* checks below
+// are not inadvertently included in the diagnostic checking!
+
+// CHECK2: error: 'error' diagnostics expected but not seen:
+// CHECK2-NEXT: Line 41: define_error
+// CHECK2-NEXT: Line 43: line_error
+// CHECK2-NEXT: error: 'error' diagnostics seen but not expected:
+// CHECK2-NEXT: Line 43: #line directive requires a positive integer argument
+// CHECK2-NEXT: Line 44: AAA // expected-error {{[{][{]BBB[}][}]}} <- this shall be part of diagnostic
+// CHECK2-NEXT: error: 'warning' diagnostics expected but not seen:
+// CHECK2-NEXT: Line 42: undef_error
+// CHECK2-NEXT: error: 'warning' diagnostics seen but not expected:
+// CHECK2-NEXT: Line 42: extra tokens at end of #undef directive
+// CHECK2-NEXT: Line 45: CCC // expected-warning {{[{][{]DDD[}][}]}} <- this shall be part of diagnostic
+// CHECK2-NEXT: 7 errors generated.
+#endif
+#endif
+
+#ifdef TEST3
+#ifndef TEST3 // expected-note {{line_67}}
+ // expected-note {{line_68_ignored}}
+# ifdef UNDEFINED // expected-note {{line_69_ignored}}
+# endif // expected-note {{line_70_ignored}}
+#elif defined(TEST3) // expected-note {{line_71}}
+# if 1 // expected-note {{line_72}}
+ // expected-note {{line_73}}
+# else // expected-note {{line_74}}
+ // expected-note {{line_75_ignored}}
+# ifndef TEST3 // expected-note {{line_76_ignored}}
+# endif // expected-note {{line_77_ignored}}
+# endif // expected-note {{line_78}}
+#endif
+
+// CHECK3: error: 'note' diagnostics expected but not seen:
+// CHECK3-NEXT: Line 67: line_67
+// CHECK3-NEXT: Line 71: line_71
+// CHECK3-NEXT: Line 72: line_72
+// CHECK3-NEXT: Line 73: line_73
+// CHECK3-NEXT: Line 74: line_74
+// CHECK3-NEXT: Line 78: line_78
+// CHECK3-NEXT: 6 errors generated.
+#endif
+
+#ifdef TEST4
+#include "missing_header_file.include" // expected-error {{include_error}}
+
+// CHECK4: error: 'error' diagnostics expected but not seen:
+// CHECK4-NEXT: Line 92: include_error
+// CHECK4-NEXT: error: 'error' diagnostics seen but not expected:
+// CHECK4-NEXT: Line 92: 'missing_header_file.include' file not found
+// CHECK4-NEXT: 2 errors generated.
+#endif
+
+#ifdef TEST5
+#include "verify-directive.h"
+// expected-error@50 {{source file test}}
+
+// CHECK5: error: 'error' diagnostics expected but not seen:
+// CHECK5-NEXT: Line 1 (directive at {{.*}}verify-directive.h:2): include file test
+// CHECK5-NEXT: Line 50 (directive at {{.*}}verify.c:103): source file test
+// CHECK5-NEXT: 2 errors generated.
+#endif
+
+#if 0
+// RUN: %clang_cc1 -verify %t.invalid 2>&1 | FileCheck -check-prefix=CHECK6 %s
+
+// CHECK6: error: 'error' diagnostics seen but not expected:
+// CHECK6-NEXT: (frontend): error reading '{{.*}}verify.c.tmp.invalid'
+// CHECK6-NEXT: 1 error generated.
+
+// RUN: echo -e '//expected-error@2{{1}}\n#error 2' | %clang_cc1 -verify 2>&1 | FileCheck -check-prefix=CHECK7 %s
+
+// CHECK7: error: 'error' diagnostics expected but not seen:
+// CHECK7-NEXT: Line 2 (directive at <stdin>:1): 1
+// CHECK7-NEXT: error: 'error' diagnostics seen but not expected:
+// CHECK7-NEXT: Line 2: 2
+// CHECK7-NEXT: 2 errors generated.
+#endif
diff --git a/test/Frontend/verify2.c b/test/Frontend/verify2.c
new file mode 100644
index 0000000..a1c7975
--- /dev/null
+++ b/test/Frontend/verify2.c
@@ -0,0 +1,19 @@
+#if 0
+// RUN: %clang_cc1 -verify %s 2>&1 | FileCheck %s
+
+// Please note that all comments are inside "#if 0" blocks so that
+// VerifyDiagnosticConsumer sees no comments while processing this
+// test-case.
+#endif
+
+#include "verify2.h"
+#error source
+
+#if 0
+// expected-error {{should be ignored}}
+
+// CHECK: error: 'error' diagnostics seen but not expected:
+// CHECK-NEXT: Line 1: header
+// CHECK-NEXT: Line 10: source
+// CHECK-NEXT: 2 errors generated.
+#endif
diff --git a/test/Frontend/verify2.h b/test/Frontend/verify2.h
new file mode 100644
index 0000000..8acbf6e
--- /dev/null
+++ b/test/Frontend/verify2.h
@@ -0,0 +1,5 @@
+#error header
+
+#if 0
+// expected-error {{should be ignored}}
+#endif
diff --git a/test/Headers/ms-null-ms-header-vs-stddef.cpp b/test/Headers/ms-null-ms-header-vs-stddef.cpp
new file mode 100644
index 0000000..7efa871
--- /dev/null
+++ b/test/Headers/ms-null-ms-header-vs-stddef.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang -fsyntax-only -target i686-pc-win32 %s
+// RUN: %clang -fsyntax-only -target i386-mingw32 %s
+
+// Something in MSVC's headers (pulled in e.g. by <crtdefs.h>) defines __null
+// to something, mimick that.
+#define __null
+
+#include <stddef.h>
+
+// __null is used as a type annotation in MS headers, with __null defined to
+// nothing in regular builds. This should continue to work even with stddef.h
+// included.
+void f(__null void* p) { }
+
+// NULL should work fine even with __null defined to nothing.
+void* p = NULL;
diff --git a/test/Index/Inputs/CommentXML/invalid-function-01.xml b/test/Index/Inputs/CommentXML/invalid-function-01.xml
new file mode 100644
index 0000000..85f0669
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-01.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-02.xml b/test/Index/Inputs/CommentXML/invalid-function-02.xml
new file mode 100644
index 0000000..700711b
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-02.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-03.xml b/test/Index/Inputs/CommentXML/invalid-function-03.xml
new file mode 100644
index 0000000..0c4618f
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-03.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Term></Term>
+ <Definition><Para>Bbb.</Para></Definition>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-04.xml b/test/Index/Inputs/CommentXML/invalid-function-04.xml
new file mode 100644
index 0000000..88dd5a8
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-04.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Term> </Term>
+ <Definition><Para>Bbb.</Para></Definition>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-05.xml b/test/Index/Inputs/CommentXML/invalid-function-05.xml
new file mode 100644
index 0000000..ce96b7d
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-05.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Term>x1</Term>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-06.xml b/test/Index/Inputs/CommentXML/invalid-function-06.xml
new file mode 100644
index 0000000..5419c67
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-06.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa <monospaced></monospaced>.</Para></Abstract>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-07.xml b/test/Index/Inputs/CommentXML/invalid-function-07.xml
new file mode 100644
index 0000000..ce7ecce
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-07.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>-1</Index>
+ <Direction isExplicit="0">in</Direction>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-08.xml b/test/Index/Inputs/CommentXML/invalid-function-08.xml
new file mode 100644
index 0000000..66e69e8
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-08.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>0</Index>
+ <Direction isExplicit="aaa">in</Direction>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-09.xml b/test/Index/Inputs/CommentXML/invalid-function-09.xml
new file mode 100644
index 0000000..39617b6
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-09.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>0</Index>
+ <Direction isExplicit="0">aaa</Direction>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/invalid-function-10.xml b/test/Index/Inputs/CommentXML/invalid-function-10.xml
new file mode 100644
index 0000000..ccce4bb
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-10.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<TemplateParameters>
+ <Parameter>
+ <Name>x1</Name>
+ </Parameter>
+</TemplateParameters>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/invalid-function-11.xml b/test/Index/Inputs/CommentXML/invalid-function-11.xml
new file mode 100644
index 0000000..167911e
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-11.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<TemplateParameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>aaa</Index>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</TemplateParameters>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/invalid-function-12.xml b/test/Index/Inputs/CommentXML/invalid-function-12.xml
new file mode 100644
index 0000000..f5b5e03
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/invalid-function-12.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function templateKind="aaa">
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/valid-class-01.xml b/test/Index/Inputs/CommentXML/valid-class-01.xml
new file mode 100644
index 0000000..bd893e6
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-class-01.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Class>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Class>
diff --git a/test/Index/Inputs/CommentXML/valid-class-02.xml b/test/Index/Inputs/CommentXML/valid-class-02.xml
new file mode 100644
index 0000000..2e20a92
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-class-02.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Class templateKind="template">
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Class>
diff --git a/test/Index/Inputs/CommentXML/valid-class-03.xml b/test/Index/Inputs/CommentXML/valid-class-03.xml
new file mode 100644
index 0000000..2ce1a2c
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-class-03.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Class templateKind="specialization">
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Class>
diff --git a/test/Index/Inputs/CommentXML/valid-class-04.xml b/test/Index/Inputs/CommentXML/valid-class-04.xml
new file mode 100644
index 0000000..da1522d
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-class-04.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Class templateKind="partialSpecialization">
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Class>
diff --git a/test/Index/Inputs/CommentXML/valid-enum-01.xml b/test/Index/Inputs/CommentXML/valid-enum-01.xml
new file mode 100644
index 0000000..e346d73
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-enum-01.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Enum>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Enum>
+
diff --git a/test/Index/Inputs/CommentXML/valid-function-01.xml b/test/Index/Inputs/CommentXML/valid-function-01.xml
new file mode 100644
index 0000000..02060e7
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-01.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/valid-function-02.xml b/test/Index/Inputs/CommentXML/valid-function-02.xml
new file mode 100644
index 0000000..989d6a7
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-02.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa <bold>bbb</bold> <monospaced>ccc</monospaced> <emphasized>ddd</emphasized>.</Para></Abstract>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/valid-function-03.xml b/test/Index/Inputs/CommentXML/valid-function-03.xml
new file mode 100644
index 0000000..891211d
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-03.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Direction isExplicit="0">in</Direction>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/valid-function-04.xml b/test/Index/Inputs/CommentXML/valid-function-04.xml
new file mode 100644
index 0000000..b65b3e9
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-04.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>0</Index>
+ <Direction isExplicit="0">in</Direction>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</Parameters>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/valid-function-05.xml b/test/Index/Inputs/CommentXML/valid-function-05.xml
new file mode 100644
index 0000000..2dddbd7
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-05.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Discussion>
+ <Para>Ccc</Para>
+</Discussion>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/valid-function-06.xml b/test/Index/Inputs/CommentXML/valid-function-06.xml
new file mode 100644
index 0000000..1df3aa4
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-06.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<ResultDiscussion><Para>Ccc.</Para></ResultDiscussion>
+</Function>
diff --git a/test/Index/Inputs/CommentXML/valid-function-07.xml b/test/Index/Inputs/CommentXML/valid-function-07.xml
new file mode 100644
index 0000000..89b8a0c
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-07.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<Parameters>
+ <Parameter>
+ <Name>x2</Name>
+ <Index>0</Index>
+ <Direction isExplicit="0">in</Direction>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+ <Parameter>
+ <Name>x3</Name>
+ <Index>2</Index>
+ <Direction isExplicit="1">out</Direction>
+ <Discussion><Para>Ccc</Para></Discussion>
+ </Parameter>
+ <Parameter>
+ <Name>x1</Name>
+ <Direction isExplicit="1">in,out</Direction>
+ <Discussion><Para>Ddd</Para></Discussion>
+ </Parameter>
+</Parameters>
+<ResultDiscussion><Para>Eee.</Para></ResultDiscussion>
+<Discussion>
+ <Para>Fff</Para>
+ <Verbatim xml:space="preserve" kind="verbatim">Ggg</Verbatim>
+</Discussion>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/valid-function-08.xml b/test/Index/Inputs/CommentXML/valid-function-08.xml
new file mode 100644
index 0000000..481a6c0
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-08.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<TemplateParameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>0</Index>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+ <Parameter>
+ <Name>x2</Name>
+ <Discussion><Para>Ccc</Para></Discussion>
+ </Parameter>
+</TemplateParameters>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/valid-function-09.xml b/test/Index/Inputs/CommentXML/valid-function-09.xml
new file mode 100644
index 0000000..cf4cc8f
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-09.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function templateKind="template">
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/valid-function-10.xml b/test/Index/Inputs/CommentXML/valid-function-10.xml
new file mode 100644
index 0000000..4fadf30
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-function-10.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Function templateKind="specialization">
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Function>
+
diff --git a/test/Index/Inputs/CommentXML/valid-namespace-01.xml b/test/Index/Inputs/CommentXML/valid-namespace-01.xml
new file mode 100644
index 0000000..a73aad5
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-namespace-01.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Namespace>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Namespace>
+
diff --git a/test/Index/Inputs/CommentXML/valid-other-01.xml b/test/Index/Inputs/CommentXML/valid-other-01.xml
new file mode 100644
index 0000000..46b8a46
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-other-01.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Other>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Other>
diff --git a/test/Index/Inputs/CommentXML/valid-typedef-01.xml b/test/Index/Inputs/CommentXML/valid-typedef-01.xml
new file mode 100644
index 0000000..1b7da8d
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-typedef-01.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Typedef>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Typedef>
+
diff --git a/test/Index/Inputs/CommentXML/valid-typedef-02.xml b/test/Index/Inputs/CommentXML/valid-typedef-02.xml
new file mode 100644
index 0000000..2a32189
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-typedef-02.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Typedef>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+<TemplateParameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>0</Index>
+ <Discussion><Para>Bbb</Para></Discussion>
+ </Parameter>
+</TemplateParameters>
+<Parameters>
+ <Parameter>
+ <Name>x1</Name>
+ <Index>0</Index>
+ <Direction isExplicit="0">in</Direction>
+ <Discussion><Para>Ccc</Para></Discussion>
+ </Parameter>
+</Parameters>
+<ResultDiscussion><Para>Ddd.</Para></ResultDiscussion>
+<Discussion>
+ <Para>Eee.</Para>
+</Discussion>
+</Typedef>
+
diff --git a/test/Index/Inputs/CommentXML/valid-variable-01.xml b/test/Index/Inputs/CommentXML/valid-variable-01.xml
new file mode 100644
index 0000000..e17da91
--- /dev/null
+++ b/test/Index/Inputs/CommentXML/valid-variable-01.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Variable>
+<Name>aaa</Name>
+<Abstract><Para>Aaa.</Para></Abstract>
+</Variable>
+
diff --git a/test/Index/Inputs/annotate-comments-preprocessor.h b/test/Index/Inputs/annotate-comments-preprocessor.h
new file mode 100644
index 0000000..220f36e
--- /dev/null
+++ b/test/Index/Inputs/annotate-comments-preprocessor.h
@@ -0,0 +1,2 @@
+/* Meow */
+
diff --git a/test/Index/annotate-comments-preprocessor.c b/test/Index/annotate-comments-preprocessor.c
new file mode 100644
index 0000000..202847f
--- /dev/null
+++ b/test/Index/annotate-comments-preprocessor.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fsyntax-only -I%S/Inputs %s
+
+// As long as none of this crashes, we don't care about comments in
+// preprocessor directives.
+
+#include "annotate-comments-preprocessor.h" /* Aaa. */ /* Bbb. */
+#include "annotate-comments-preprocessor.h" /* Aaa. */
+#include "annotate-comments-preprocessor.h" /** Aaa. */
+#include "annotate-comments-preprocessor.h" /**< Aaa. */
+#include "annotate-comments-preprocessor.h" // Aaa.
+#include "annotate-comments-preprocessor.h" /// Aaa.
+#include "annotate-comments-preprocessor.h" ///< Aaa.
+
+#define A0 0
+#define A1 1 /* Aaa. */
+#define A2 1 /** Aaa. */
+#define A3 1 /**< Aaa. */
+#define A4 1 // Aaa.
+#define A5 1 /// Aaa.
+#define A6 1 ///< Aaa.
+
+int A[] = { A0, A1, A2, A3, A4, A5, A6 };
+
+#if A0 /** Aaa. */
+int f(int a1[A1], int a2[A2], int a3[A3], int a4[A4], int a5[A5], int a6[A6]);
+#endif /** Aaa. */
+
+#if A1 /** Aaa. */
+int g(int a1[A1], int a2[A2], int a3[A3], int a4[A4], int a5[A5], int a6[A6]);
+#endif /* Aaa. */
+
+#pragma once /** Aaa. */
+
+#define FOO \
+ do { \
+ /* Aaa. */ \
+ /** Aaa. */ \
+ /**< Aaa. */ \
+ ; \
+ } while(0)
+
+void h(void) {
+ FOO;
+}
+
diff --git a/test/Index/annotate-comments-unterminated.c b/test/Index/annotate-comments-unterminated.c
new file mode 100644
index 0000000..6bba911
--- /dev/null
+++ b/test/Index/annotate-comments-unterminated.c
@@ -0,0 +1,13 @@
+// RUN: c-index-test -test-load-source all %s | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only %s 2>&1 | FileCheck -check-prefix=ERR %s
+
+// CHECK: annotate-comments-unterminated.c:9:5: VarDecl=x:{{.*}} RawComment=[/** Aaa. */]{{.*}} BriefComment=[Aaa.]
+// CHECK: annotate-comments-unterminated.c:11:5: VarDecl=y:{{.*}} RawComment=[/**< Bbb. */]{{.*}} BriefComment=[Bbb.]
+// CHECK-ERR: error: unterminated
+
+/** Aaa. */
+int x;
+
+int y; /**< Bbb. */
+/**< Ccc.
+ * Ddd.
diff --git a/test/Index/annotate-comments.cpp b/test/Index/annotate-comments.cpp
new file mode 100644
index 0000000..22724d0
--- /dev/null
+++ b/test/Index/annotate-comments.cpp
@@ -0,0 +1,926 @@
+// Run lines are sensitive to line numbers and come below the code.
+
+#ifndef HEADER
+#define HEADER
+
+// Not a Doxygen comment. NOT_DOXYGEN
+void notdoxy1(void);
+
+/* Not a Doxygen comment. NOT_DOXYGEN */
+void notdoxy2(void);
+
+/*/ Not a Doxygen comment. NOT_DOXYGEN */
+void notdoxy3(void);
+
+/** Doxygen comment. isdoxy4 IS_DOXYGEN_SINGLE */
+void isdoxy4(void);
+
+/**
+ * Doxygen comment. isdoxy5 IS_DOXYGEN_SINGLE */
+void isdoxy5(void);
+
+/**
+ * Doxygen comment.
+ * isdoxy6 IS_DOXYGEN_SINGLE */
+void isdoxy6(void);
+
+/**
+ * Doxygen comment.
+ * isdoxy7 IS_DOXYGEN_SINGLE
+ */
+void isdoxy7(void);
+
+/*! Doxygen comment. isdoxy8 IS_DOXYGEN_SINGLE */
+void isdoxy8(void);
+
+/// Doxygen comment. isdoxy9 IS_DOXYGEN_SINGLE
+void isdoxy9(void);
+
+// Not a Doxygen comment. NOT_DOXYGEN
+/// Doxygen comment. isdoxy10 IS_DOXYGEN_SINGLE
+void isdoxy10(void);
+
+/// Doxygen comment. isdoxy11 IS_DOXYGEN_SINGLE
+// Not a Doxygen comment. NOT_DOXYGEN
+void isdoxy11(void);
+
+/** Doxygen comment. isdoxy12 IS_DOXYGEN_SINGLE */
+/* Not a Doxygen comment. NOT_DOXYGEN */
+void isdoxy12(void);
+
+/// Doxygen comment. isdoxy13 IS_DOXYGEN_START
+/// Doxygen comment. IS_DOXYGEN_END
+void isdoxy13(void);
+
+/// Doxygen comment. isdoxy14 IS_DOXYGEN_START
+/// Blah-blah-blah.
+/// Doxygen comment. IS_DOXYGEN_END
+void isdoxy14(void);
+
+/// Doxygen comment. isdoxy15 IS_DOXYGEN_START
+/** Blah-blah-blah */
+/// Doxygen comment. IS_DOXYGEN_END
+void isdoxy15(void);
+
+/** Blah-blah-blah. isdoxy16 IS_DOXYGEN_START *//** Blah */
+/// Doxygen comment. IS_DOXYGEN_END
+void isdoxy16(void);
+
+/// isdoxy17 IS_DOXYGEN_START
+// Not a Doxygen comment, but still picked up.
+/// IS_DOXYGEN_END
+void isdoxy17(void);
+
+unsigned
+// NOT_DOXYGEN
+/// isdoxy18 IS_DOXYGEN_START
+// Not a Doxygen comment, but still picked up.
+/// IS_DOXYGEN_END
+// NOT_DOXYGEN
+int isdoxy18(void);
+
+//! It all starts here. isdoxy19 IS_DOXYGEN_START
+/*! It's a little odd to continue line this,
+ *
+ * but we need more multi-line comments. */
+/// This comment comes before my other comments
+/** This is a block comment that is associated with the function f. It
+ * runs for three lines. IS_DOXYGEN_END
+ */
+void isdoxy19(int, int);
+
+// NOT IN THE COMMENT NOT_DOXYGEN
+/// This is a BCPL comment. isdoxy20 IS_DOXYGEN_START
+/// It has only two lines.
+/** But there are other blocks that are part of the comment, too. IS_DOXYGEN_END */
+void isdoxy20(int);
+
+void notdoxy21(int); ///< This is a member comment. isdoxy21 IS_DOXYGEN_NOT_ATTACHED
+
+void notdoxy22(int); /*!< This is a member comment. isdoxy22 IS_DOXYGEN_NOT_ATTACHED */
+
+void notdoxy23(int); /**< This is a member comment. isdoxy23 IS_DOXYGEN_NOT_ATTACHED */
+
+void notdoxy24(int); // NOT_DOXYGEN
+
+/// IS_DOXYGEN_SINGLE
+struct isdoxy25 {
+};
+
+struct test26 {
+ /// IS_DOXYGEN_SINGLE
+ int isdoxy26;
+};
+
+struct test27 {
+ int isdoxy27; ///< IS_DOXYGEN_SINGLE
+};
+
+struct notdoxy28 {
+}; ///< IS_DOXYGEN_NOT_ATTACHED
+
+/// IS_DOXYGEN_SINGLE
+enum isdoxy29 {
+};
+
+enum notdoxy30 {
+}; ///< IS_DOXYGEN_NOT_ATTACHED
+
+/// IS_DOXYGEN_SINGLE
+namespace isdoxy31 {
+};
+
+namespace notdoxy32 {
+}; ///< IS_DOXYGEN_NOT_ATTACHED
+
+class test33 {
+ ///< IS_DOXYGEN_NOT_ATTACHED
+ int isdoxy33; ///< isdoxy33 IS_DOXYGEN_SINGLE
+ int isdoxy34; ///< isdoxy34 IS_DOXYGEN_SINGLE
+
+ ///< IS_DOXYGEN_NOT_ATTACHED
+ int isdoxy35, ///< isdoxy35 IS_DOXYGEN_SINGLE
+ isdoxy36; ///< isdoxy36 IS_DOXYGEN_SINGLE
+
+ ///< IS_DOXYGEN_NOT_ATTACHED
+ int isdoxy37 ///< isdoxy37 IS_DOXYGEN_SINGLE
+ , isdoxy38 ///< isdoxy38 IS_DOXYGEN_SINGLE
+ , isdoxy39; ///< isdoxy39 IS_DOXYGEN_SINGLE
+};
+
+// Verified that Doxygen attaches these.
+
+/// isdoxy40 IS_DOXYGEN_SINGLE
+// NOT_DOXYGEN
+void isdoxy40(int);
+
+unsigned
+/// isdoxy41 IS_DOXYGEN_SINGLE
+// NOT_DOXYGEN
+int isdoxy41(int);
+
+class test42 {
+ int isdoxy42; /* NOT_DOXYGEN */ ///< isdoxy42 IS_DOXYGEN_SINGLE
+};
+
+/// IS_DOXYGEN_START
+/// It is fine to have a command at the end of comment.
+///\brief
+///
+/// Some malformed command.
+/* \*/
+/**
+ * \brief Aaa aaaaaaa aaaa.
+ * IS_DOXYGEN_END
+ */
+void isdoxy43(void);
+
+/// IS_DOXYGEN_START Aaa bbb
+/// ccc.
+///
+/// Ddd eee.
+/// Fff.
+///
+/// Ggg. IS_DOXYGEN_END
+void isdoxy44(void);
+
+/// IS_DOXYGEN_START Aaa bbb
+/// ccc.
+///
+/// \brief
+/// Ddd eee.
+/// Fff.
+///
+/// Ggg. IS_DOXYGEN_END
+void isdoxy45(void);
+
+/// IS_DOXYGEN_START Aaa bbb
+/// ccc.
+///
+/// \short
+/// Ddd eee.
+/// Fff.
+///
+/// Ggg. IS_DOXYGEN_END
+void isdoxy46(void);
+
+/// IS_DOXYGEN_NOT_ATTACHED
+#define FOO
+void notdoxy47(void);
+
+/// IS_DOXYGEN_START Aaa bbb
+/// \param ccc
+/// \returns ddd IS_DOXYGEN_END
+void isdoxy48(int);
+
+/// \brief IS_DOXYGEN_START Aaa
+/// \returns bbb IS_DOXYGEN_END
+void isdoxy49(void);
+
+/// \param ccc IS_DOXYGEN_START
+/// \returns ddd IS_DOXYGEN_END
+void isdoxy50(int);
+
+/// Aaa.
+void comment_to_html_conversion_1();
+
+/// \brief Aaa.
+void comment_to_html_conversion_2();
+
+/// \short Aaa.
+void comment_to_html_conversion_3();
+
+/// Aaa.
+///
+/// \brief Bbb.
+void comment_to_html_conversion_4();
+
+/// Aaa.
+///
+/// \brief Bbb.
+///
+/// Ccc.
+void comment_to_html_conversion_5();
+
+/// \brief Aaa.
+/// \brief Bbb.
+void comment_to_html_conversion_6();
+
+/// Aaa.
+///
+/// \return Bbb.
+void comment_to_html_conversion_7();
+
+/// Aaa.
+///
+/// \returns Bbb.
+void comment_to_html_conversion_8();
+
+/// Aaa.
+///
+/// \result Bbb.
+void comment_to_html_conversion_9();
+
+/// \returns Aaa.
+/// \returns Bbb.
+void comment_to_html_conversion_10();
+
+/// Aaa.
+///
+/// Bbb.
+///
+/// \returns Ccc.
+void comment_to_html_conversion_11();
+
+/// \param
+void comment_to_html_conversion_12(int x1);
+
+/// \param x1 Aaa.
+void comment_to_html_conversion_13(int x1);
+
+/// \param zzz Aaa.
+void comment_to_html_conversion_14(int x1);
+
+/// \param x2 Bbb.
+/// \param x1 Aaa.
+void comment_to_html_conversion_15(int x1, int x2);
+
+/// \param x2 Bbb.
+/// \param zzz Aaa.
+/// \param x1 Aaa.
+void comment_to_html_conversion_16(int x1, int x2);
+
+/// \tparam
+/// \param aaa Blah blah
+template<typename T>
+void comment_to_html_conversion_17(T aaa);
+
+/// \tparam T
+/// \param aaa Blah blah
+template<typename T>
+void comment_to_html_conversion_18(T aaa);
+
+/// \tparam T2 Bbb
+/// \tparam T1 Aaa
+template<typename T1, typename T2>
+void comment_to_html_conversion_19(T1 aaa, T2 bbb);
+
+/// \tparam T2 Bbb
+/// \tparam U Zzz
+/// \tparam V Ccc
+/// \tparam T1 Aaa
+template<typename T1, typename T2, int V>
+void comment_to_html_conversion_20(T1 aaa, T2 bbb);
+
+/// \tparam TTT Ddd
+/// \tparam C Ccc
+/// \tparam T Aaa
+/// \tparam TT Bbb
+template<template<template<typename T> class TT, class C> class TTT>
+void comment_to_html_conversion_21();
+
+/// \brief Aaa.
+///
+/// Bbb.
+///
+/// \param x2 Ddd.
+/// \param x1 Ccc.
+/// \returns Eee.
+void comment_to_html_conversion_22(int x1, int x2);
+
+/// <br><a href="http://example.com/">Aaa</a>
+void comment_to_html_conversion_23();
+
+/// \verbatim
+/// <a href="http://example.com/">Aaa</a>
+/// <a href='http://example.com/'>Aaa</a>
+/// \endverbatim
+void comment_to_html_conversion_24();
+
+/// \function foo
+/// \class foo
+/// \method foo
+/// \interface foo
+/// Blah blah.
+void comment_to_html_conversion_25();
+
+/// \b Aaa
+void comment_to_html_conversion_26();
+
+/// \c Aaa \p Bbb
+void comment_to_html_conversion_27();
+
+/// \a Aaa \e Bbb \em Ccc
+void comment_to_html_conversion_28();
+
+/// \a 1<2 \e 3<4 \em 5<6 \param 7<8 aaa \tparam 9<10 bbb
+void comment_to_html_conversion_29();
+
+/// \\ \@ \& \$ \# \< \> \% \" \. \::
+void comment_to_html_conversion_30();
+
+/// &amp; &lt; &gt; &quot;
+void comment_to_html_conversion_31();
+
+/// <em>0&lt;i</em>
+void comment_to_html_conversion_32();
+
+/// Aaa.
+class comment_to_xml_conversion_01 {
+ /// \param aaa Blah blah.
+ comment_to_xml_conversion_01(int aaa);
+
+ /// Aaa.
+ ~comment_to_xml_conversion_01();
+
+ /// \param aaa Blah blah.
+ int comment_to_xml_conversion_02(int aaa);
+
+ /// \param aaa Blah blah.
+ static int comment_to_xml_conversion_03(int aaa);
+
+ /// Aaa.
+ int comment_to_xml_conversion_04;
+
+ /// Aaa.
+ static int comment_to_xml_conversion_05;
+
+ /// \param aaa Blah blah.
+ void operator()(int aaa);
+
+ /// Aaa.
+ operator bool();
+
+ /// Aaa.
+ typedef int comment_to_xml_conversion_06;
+
+ /// Aaa.
+ using comment_to_xml_conversion_07 = int;
+
+ template<typename T, typename U>
+ class comment_to_xml_conversion_08 { };
+
+ /// Aaa.
+ template<typename T>
+ using comment_to_xml_conversion_09 = comment_to_xml_conversion_08<T, int>;
+};
+
+/// Aaa.
+template<typename T, typename U>
+void comment_to_xml_conversion_10(T aaa, U bbb);
+
+/// Aaa.
+template<>
+void comment_to_xml_conversion_10(int aaa, int bbb);
+
+/// Aaa.
+template<typename T, typename U>
+class comment_to_xml_conversion_11 { };
+
+/// Aaa.
+template<typename T>
+class comment_to_xml_conversion_11<T, int> { };
+
+/// Aaa.
+template<>
+class comment_to_xml_conversion_11<int, int> { };
+
+/// Aaa.
+int comment_to_xml_conversion_12;
+
+/// Aaa.
+namespace comment_to_xml_conversion_13 {
+ /// Aaa.
+ namespace comment_to_xml_conversion_14 {
+ }
+}
+
+/// Aaa.
+enum comment_to_xml_conversion_15 {
+ /// Aaa.
+ comment_to_xml_conversion_16
+};
+
+/// Aaa.
+enum class comment_to_xml_conversion_17 {
+ /// Aaa.
+ comment_to_xml_conversion_18
+};
+
+#endif
+
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -x c++ -std=c++11 -emit-pch -o %t/out.pch %s
+// RUN: %clang_cc1 -x c++ -std=c++11 -include-pch %t/out.pch -fsyntax-only %s
+
+// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s -std=c++11 > %t/out.c-index-direct
+// RUN: c-index-test -test-load-tu %t/out.pch all > %t/out.c-index-pch
+
+// RUN: FileCheck %s -check-prefix=WRONG < %t/out.c-index-direct
+// RUN: FileCheck %s -check-prefix=WRONG < %t/out.c-index-pch
+
+// Declarations without Doxygen comments should not pick up some Doxygen comments.
+// WRONG-NOT: notdoxy{{.*}}Comment=
+// WRONG-NOT: test{{.*}}Comment=
+
+// Non-Doxygen comments should not be attached to anything.
+// WRONG-NOT: NOT_DOXYGEN
+
+// Some Doxygen comments are not attached to anything.
+// WRONG-NOT: IS_DOXYGEN_NOT_ATTACHED
+
+// Ensure we don't pick up extra comments.
+// WRONG-NOT: IS_DOXYGEN_START{{.*}}IS_DOXYGEN_START{{.*}}BriefComment=
+// WRONG-NOT: IS_DOXYGEN_END{{.*}}IS_DOXYGEN_END{{.*}}BriefComment=
+//
+// Ensure that XML is not invalid
+// WRONG-NOT: CommentXMLInvalid
+
+// RUN: FileCheck %s < %t/out.c-index-direct
+// RUN: FileCheck %s < %t/out.c-index-pch
+
+// CHECK: annotate-comments.cpp:16:6: FunctionDecl=isdoxy4:{{.*}} isdoxy4 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:20:6: FunctionDecl=isdoxy5:{{.*}} isdoxy5 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:25:6: FunctionDecl=isdoxy6:{{.*}} isdoxy6 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:31:6: FunctionDecl=isdoxy7:{{.*}} isdoxy7 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:34:6: FunctionDecl=isdoxy8:{{.*}} isdoxy8 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:37:6: FunctionDecl=isdoxy9:{{.*}} isdoxy9 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:41:6: FunctionDecl=isdoxy10:{{.*}} isdoxy10 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:53:6: FunctionDecl=isdoxy13:{{.*}} isdoxy13 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:58:6: FunctionDecl=isdoxy14:{{.*}} isdoxy14 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:63:6: FunctionDecl=isdoxy15:{{.*}} isdoxy15 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:67:6: FunctionDecl=isdoxy16:{{.*}} isdoxy16 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:72:6: FunctionDecl=isdoxy17:{{.*}} isdoxy17 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:80:5: FunctionDecl=isdoxy18:{{.*}} isdoxy18 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:90:6: FunctionDecl=isdoxy19:{{.*}} isdoxy19 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:96:6: FunctionDecl=isdoxy20:{{.*}} isdoxy20 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:107:8: StructDecl=isdoxy25:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:112:7: FieldDecl=isdoxy26:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:116:7: FieldDecl=isdoxy27:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:123:6: EnumDecl=isdoxy29:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:130:11: Namespace=isdoxy31:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:138:7: FieldDecl=isdoxy33:{{.*}} isdoxy33 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:139:7: FieldDecl=isdoxy34:{{.*}} isdoxy34 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:142:7: FieldDecl=isdoxy35:{{.*}} isdoxy35 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:143:7: FieldDecl=isdoxy36:{{.*}} isdoxy36 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:146:7: FieldDecl=isdoxy37:{{.*}} isdoxy37 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:147:7: FieldDecl=isdoxy38:{{.*}} isdoxy38 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:148:7: FieldDecl=isdoxy39:{{.*}} isdoxy39 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:155:6: FunctionDecl=isdoxy40:{{.*}} isdoxy40 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:160:5: FunctionDecl=isdoxy41:{{.*}} isdoxy41 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:163:7: FieldDecl=isdoxy42:{{.*}} isdoxy42 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:176:6: FunctionDecl=isdoxy43:{{.*}} IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+
+// CHECK: annotate-comments.cpp:185:6: FunctionDecl=isdoxy44:{{.*}} BriefComment=[IS_DOXYGEN_START Aaa bbb ccc.]
+// CHECK: annotate-comments.cpp:195:6: FunctionDecl=isdoxy45:{{.*}} BriefComment=[Ddd eee. Fff.]
+// CHECK: annotate-comments.cpp:205:6: FunctionDecl=isdoxy46:{{.*}} BriefComment=[Ddd eee. Fff.]
+// CHECK: annotate-comments.cpp:214:6: FunctionDecl=isdoxy48:{{.*}} BriefComment=[IS_DOXYGEN_START Aaa bbb]
+// CHECK: annotate-comments.cpp:218:6: FunctionDecl=isdoxy49:{{.*}} BriefComment=[IS_DOXYGEN_START Aaa]
+// CHECK: annotate-comments.cpp:222:6: FunctionDecl=isdoxy50:{{.*}} BriefComment=[Returns ddd IS_DOXYGEN_END]
+
+// CHECK: annotate-comments.cpp:225:6: FunctionDecl=comment_to_html_conversion_1:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="225" column="6"><Name>comment_to_html_conversion_1</Name><USR>c:@F@comment_to_html_conversion_1#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])))]
+// CHECK: annotate-comments.cpp:228:6: FunctionDecl=comment_to_html_conversion_2:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="228" column="6"><Name>comment_to_html_conversion_2</Name><USR>c:@F@comment_to_html_conversion_2#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
+// CHECK: annotate-comments.cpp:231:6: FunctionDecl=comment_to_html_conversion_3:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="231" column="6"><Name>comment_to_html_conversion_3</Name><USR>c:@F@comment_to_html_conversion_3#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[short]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
+// CHECK: annotate-comments.cpp:236:6: FunctionDecl=comment_to_html_conversion_4:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Bbb.</p><p> Aaa.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="236" column="6"><Name>comment_to_html_conversion_4</Name><USR>c:@F@comment_to_html_conversion_4#</USR><Abstract><Para> Bbb.</Para></Abstract><Discussion><Para> Aaa.</Para></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
+// CHECK: annotate-comments.cpp:243:6: FunctionDecl=comment_to_html_conversion_5:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Bbb.</p><p> Aaa.</p><p> Ccc.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="243" column="6"><Name>comment_to_html_conversion_5</Name><USR>c:@F@comment_to_html_conversion_5#</USR><Abstract><Para> Bbb.</Para></Abstract><Discussion><Para> Aaa.</Para><Para> Ccc.</Para></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.])))
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ccc.])))]
+// CHECK: annotate-comments.cpp:247:6: FunctionDecl=comment_to_html_conversion_6:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa. </p><p class="para-brief"> Bbb.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="247" column="6"><Name>comment_to_html_conversion_6</Name><USR>c:@F@comment_to_html_conversion_6#</USR><Abstract><Para> Aaa. </Para></Abstract><Discussion><Para> Bbb.</Para></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
+// CHECK: annotate-comments.cpp:252:6: FunctionDecl=comment_to_html_conversion_7:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p class="para-returns"><span class="word-returns">Returns</span> Bbb.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="252" column="6"><Name>comment_to_html_conversion_7</Name><USR>c:@F@comment_to_html_conversion_7#</USR><Abstract><Para> Aaa.</Para></Abstract><ResultDiscussion><Para> Bbb.</Para></ResultDiscussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[return]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
+// CHECK: annotate-comments.cpp:257:6: FunctionDecl=comment_to_html_conversion_8:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p class="para-returns"><span class="word-returns">Returns</span> Bbb.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="257" column="6"><Name>comment_to_html_conversion_8</Name><USR>c:@F@comment_to_html_conversion_8#</USR><Abstract><Para> Aaa.</Para></Abstract><ResultDiscussion><Para> Bbb.</Para></ResultDiscussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
+// CHECK: annotate-comments.cpp:262:6: FunctionDecl=comment_to_html_conversion_9:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p class="para-returns"><span class="word-returns">Returns</span> Bbb.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="262" column="6"><Name>comment_to_html_conversion_9</Name><USR>c:@F@comment_to_html_conversion_9#</USR><Abstract><Para> Aaa.</Para></Abstract><ResultDiscussion><Para> Bbb.</Para></ResultDiscussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[result]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
+// CHECK: annotate-comments.cpp:266:6: FunctionDecl=comment_to_html_conversion_10:{{.*}} FullCommentAsHTML=[<p class="para-returns"><span class="word-returns">Returns</span> Bbb.</p><p class="para-returns"><span class="word-returns">Returns</span> Aaa. </p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="266" column="6"><Name>comment_to_html_conversion_10</Name><USR>c:@F@comment_to_html_conversion_10#</USR><ResultDiscussion><Para> Aaa. </Para></ResultDiscussion><Discussion><Para> Bbb.</Para></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))))]
+// CHECK: annotate-comments.cpp:273:6: FunctionDecl=comment_to_html_conversion_11:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p> Bbb.</p><p class="para-returns"><span class="word-returns">Returns</span> Ccc.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="273" column="6"><Name>comment_to_html_conversion_11</Name><USR>c:@F@comment_to_html_conversion_11#</USR><Abstract><Para> Aaa.</Para></Abstract><ResultDiscussion><Para> Ccc.</Para></ResultDiscussion><Discussion><Para> Bbb.</Para></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ccc.]))))]
+// CHECK: annotate-comments.cpp:276:6: FunctionDecl=comment_to_html_conversion_12:{{.*}} FullCommentAsHTML=[] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="276" column="6"><Name>comment_to_html_conversion_12</Name><USR>c:@F@comment_to_html_conversion_12#I#</USR></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[] ParamIndex=Invalid
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace)))]
+// CHECK: annotate-comments.cpp:279:6: FunctionDecl=comment_to_html_conversion_13:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Aaa.</dd></dl>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="279" column="6"><Name>comment_to_html_conversion_13</Name><USR>c:@F@comment_to_html_conversion_13#I#</USR><Parameters><Parameter><Name>x1</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Aaa.</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
+// CHECK: annotate-comments.cpp:282:6: FunctionDecl=comment_to_html_conversion_14:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-invalid">zzz</dt><dd class="param-descr-index-invalid"> Aaa.</dd></dl>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="282" column="6"><Name>comment_to_html_conversion_14</Name><USR>c:@F@comment_to_html_conversion_14#I#</USR><Parameters><Parameter><Name>zzz</Name><Direction isExplicit="0">in</Direction><Discussion><Para> Aaa.</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[zzz] ParamIndex=Invalid
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
+// CHECK: annotate-comments.cpp:286:6: FunctionDecl=comment_to_html_conversion_15:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Aaa.</dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Bbb. </dd></dl>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="286" column="6"><Name>comment_to_html_conversion_15</Name><USR>c:@F@comment_to_html_conversion_15#I#I#</USR><Parameters><Parameter><Name>x1</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Aaa.</Para></Discussion></Parameter><Parameter><Name>x2</Name><Index>1</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Bbb. </Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x2] ParamIndex=1
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
+// CHECK: annotate-comments.cpp:291:6: FunctionDecl=comment_to_html_conversion_16:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Aaa.</dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Bbb. </dd><dt class="param-name-index-invalid">zzz</dt><dd class="param-descr-index-invalid"> Aaa. </dd></dl>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="291" column="6"><Name>comment_to_html_conversion_16</Name><USR>c:@F@comment_to_html_conversion_16#I#I#</USR><Parameters><Parameter><Name>x1</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Aaa.</Para></Discussion></Parameter><Parameter><Name>x2</Name><Index>1</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Bbb. </Para></Discussion></Parameter><Parameter><Name>zzz</Name><Direction isExplicit="0">in</Direction><Discussion><Para> Aaa. </Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x2] ParamIndex=1
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[zzz] ParamIndex=Invalid
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.]))))]
+// CHECK: annotate-comments.cpp:296:6: FunctionTemplate=comment_to_html_conversion_17:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">aaa</dt><dd class="param-descr-index-0"> Blah blah</dd></dl>] FullCommentAsXML=[<Function templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="296" column="6"><Name>comment_to_html_conversion_17</Name><USR>c:@FT@&gt;1#Tcomment_to_html_conversion_17#t0.0#</USR><Parameters><Parameter><Name>aaa</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Blah blah</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[] ParamPosition=Invalid
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[aaa] ParamIndex=0
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Blah blah]))))]
+// CHECK: annotate-comments.cpp:301:6: FunctionTemplate=comment_to_html_conversion_18:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">aaa</dt><dd class="param-descr-index-0"> Blah blah</dd></dl>] FullCommentAsXML=[<Function templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="301" column="6"><Name>comment_to_html_conversion_18</Name><USR>c:@FT@&gt;1#Tcomment_to_html_conversion_18#t0.0#</USR><Parameters><Parameter><Name>aaa</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Blah blah</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T] ParamPosition={0}
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[aaa] ParamIndex=0
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Blah blah]))))]
+// CHECK: annotate-comments.cpp:306:6: FunctionTemplate=comment_to_html_conversion_19:{{.*}} FullCommentAsHTML=[<dl><dt class="tparam-name-index-0">T1</dt><dd class="tparam-descr-index-0"> Aaa</dd><dt class="tparam-name-index-1">T2</dt><dd class="tparam-descr-index-1"> Bbb </dd></dl>] FullCommentAsXML=[<Function templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="306" column="6"><Name>comment_to_html_conversion_19</Name><USR>c:@FT@&gt;2#T#Tcomment_to_html_conversion_19#t0.0#t0.1#</USR><TemplateParameters><Parameter><Name>T1</Name><Index>0</Index><Discussion><Para> Aaa</Para></Discussion></Parameter><Parameter><Name>T2</Name><Index>1</Index><Discussion><Para> Bbb </Para></Discussion></Parameter></TemplateParameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T2] ParamPosition={1}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T1] ParamPosition={0}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa]))))]
+// CHECK: annotate-comments.cpp:313:6: FunctionTemplate=comment_to_html_conversion_20:{{.*}} FullCommentAsHTML=[<dl><dt class="tparam-name-index-0">T1</dt><dd class="tparam-descr-index-0"> Aaa</dd><dt class="tparam-name-index-1">T2</dt><dd class="tparam-descr-index-1"> Bbb </dd><dt class="tparam-name-index-2">V</dt><dd class="tparam-descr-index-2"> Ccc </dd><dt class="tparam-name-index-invalid">U</dt><dd class="tparam-descr-index-invalid"> Zzz </dd></dl>] FullCommentAsXML=[<Function templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="313" column="6"><Name>comment_to_html_conversion_20</Name><USR>c:@FT@&gt;3#T#T#NIcomment_to_html_conversion_20#t0.0#t0.1#</USR><TemplateParameters><Parameter><Name>T1</Name><Index>0</Index><Discussion><Para> Aaa</Para></Discussion></Parameter><Parameter><Name>T2</Name><Index>1</Index><Discussion><Para> Bbb </Para></Discussion></Parameter><Parameter><Name>V</Name><Index>2</Index><Discussion><Para> Ccc </Para></Discussion></Parameter><Parameter><Name>U</Name><Discussion><Para> Zzz </Para></Discussion></Parameter></TemplateParameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T2] ParamPosition={1}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[U] ParamPosition=Invalid
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Zzz] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[V] ParamPosition={2}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ccc] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T1] ParamPosition={0}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa]))))]
+// CHECK: annotate-comments.cpp:320:6: FunctionTemplate=comment_to_html_conversion_21:{{.*}} FullCommentAsHTML=[<dl><dt class="tparam-name-index-0">TTT</dt><dd class="tparam-descr-index-0"> Ddd </dd><dt class="tparam-name-index-other">C</dt><dd class="tparam-descr-index-other"> Ccc </dd><dt class="tparam-name-index-other">T</dt><dd class="tparam-descr-index-other"> Aaa </dd><dt class="tparam-name-index-other">TT</dt><dd class="tparam-descr-index-other"> Bbb</dd></dl>] FullCommentAsXML=[<Function templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="320" column="6"><Name>comment_to_html_conversion_21</Name><USR>c:@FT@&gt;1#t&gt;2#t&gt;1#T#Tcomment_to_html_conversion_21#</USR><TemplateParameters><Parameter><Name>TTT</Name><Index>0</Index><Discussion><Para> Ddd </Para></Discussion></Parameter><Parameter><Name>C</Name><Discussion><Para> Ccc </Para></Discussion></Parameter><Parameter><Name>T</Name><Discussion><Para> Aaa </Para></Discussion></Parameter><Parameter><Name>TT</Name><Discussion><Para> Bbb</Para></Discussion></Parameter></TemplateParameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[TTT] ParamPosition={0}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ddd] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[C] ParamPosition={0, 1}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ccc] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[T] ParamPosition={0, 0, 0}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[TT] ParamPosition={0, 0}
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb]))))]
+// CHECK: annotate-comments.cpp:329:6: FunctionDecl=comment_to_html_conversion_22:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p> Bbb.</p><dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Ccc. </dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Ddd. </dd></dl><p class="para-returns"><span class="word-returns">Returns</span> Eee.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="329" column="6"><Name>comment_to_html_conversion_22</Name><USR>c:@F@comment_to_html_conversion_22#I#I#</USR><Abstract><Para> Aaa.</Para></Abstract><Parameters><Parameter><Name>x1</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Ccc. </Para></Discussion></Parameter><Parameter><Name>x2</Name><Index>1</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Ddd. </Para></Discussion></Parameter></Parameters><ResultDiscussion><Para> Eee.</Para></ResultDiscussion><Discussion><Para> Bbb.</Para></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[brief]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Aaa.])))
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Bbb.]))
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x2] ParamIndex=1
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ddd.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Ccc.] HasTrailingNewline)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT: (CXComment_BlockCommand CommandName=[returns]
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ Eee.]))))]
+// CHECK: annotate-comments.cpp:332:6: FunctionDecl=comment_to_html_conversion_23:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="332" column="6"><Name>comment_to_html_conversion_23</Name><USR>c:@F@comment_to_html_conversion_23#</USR><Abstract><Para> <rawHTML><![CDATA[<br>]]></rawHTML><rawHTML><![CDATA[<a href="http://example.com/">]]></rawHTML>Aaa<rawHTML>&lt;/a&gt;</rawHTML></Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_HTMLStartTag Name=[br])
+// CHECK-NEXT: (CXComment_HTMLStartTag Name=[a] Attrs: href=http://example.com/)
+// CHECK-NEXT: (CXComment_Text Text=[Aaa])
+// CHECK-NEXT: (CXComment_HTMLEndTag Name=[a])))]
+// CHECK: annotate-comments.cpp:338:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<pre> &lt;a href=&quot;http:&#47;&#47;example.com&#47;&quot;&gt;Aaa&lt;&#47;a&gt;\n &lt;a href=&#39;http:&#47;&#47;example.com&#47;&#39;&gt;Aaa&lt;&#47;a&gt;</pre>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="338" column="6"><Name>comment_to_html_conversion_24</Name><USR>c:@F@comment_to_html_conversion_24#</USR><Discussion><Verbatim xml:space="preserve" kind="verbatim"> &lt;a href=&quot;http://example.com/&quot;&gt;Aaa&lt;/a&gt;\n &lt;a href=&apos;http://example.com/&apos;&gt;Aaa&lt;/a&gt;</Verbatim></Discussion></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_VerbatimBlockCommand CommandName=[verbatim]
+// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ <a href="http://example.com/">Aaa</a>])
+// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ <a href='http://example.com/'>Aaa</a>])))]
+// CHECK: annotate-comments.cpp:345:6: FunctionDecl=comment_to_html_conversion_25:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Blah blah.</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="345" column="6"><Name>comment_to_html_conversion_25</Name><USR>c:@F@comment_to_html_conversion_25#</USR><Abstract><Para> Blah blah.</Para></Abstract></Function>]
+// CHECK: CommentAST=[
+// CHECK: (CXComment_FullComment
+// CHECK: (CXComment_Paragraph IsWhitespace
+// CHECK: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK: (CXComment_VerbatimLine Text=[ foo])
+// CHECK: (CXComment_Paragraph IsWhitespace
+// CHECK: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK: (CXComment_VerbatimLine Text=[ foo])
+// CHECK: (CXComment_Paragraph IsWhitespace
+// CHECK: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK: (CXComment_VerbatimLine Text=[ foo])
+// CHECK: (CXComment_Paragraph IsWhitespace
+// CHECK: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK: (CXComment_VerbatimLine Text=[ foo])
+// CHECK: (CXComment_Paragraph
+// CHECK: (CXComment_Text Text=[ Blah blah.])))]
+// CHECK: annotate-comments.cpp:348:6: FunctionDecl=comment_to_html_conversion_26:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <b>Aaa</b></p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="348" column="6"><Name>comment_to_html_conversion_26</Name><USR>c:@F@comment_to_html_conversion_26#</USR><Abstract><Para> <bold>Aaa</bold></Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[b] RenderBold Arg[0]=Aaa)))]
+// CHECK: annotate-comments.cpp:351:6: FunctionDecl=comment_to_html_conversion_27:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <tt>Aaa</tt> <tt>Bbb</tt></p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="351" column="6"><Name>comment_to_html_conversion_27</Name><USR>c:@F@comment_to_html_conversion_27#</USR><Abstract><Para> <monospaced>Aaa</monospaced> <monospaced>Bbb</monospaced></Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[c] RenderMonospaced Arg[0]=Aaa)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[p] RenderMonospaced Arg[0]=Bbb)))]
+// CHECK: annotate-comments.cpp:354:6: FunctionDecl=comment_to_html_conversion_28:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>Aaa</em> <em>Bbb</em> <em>Ccc</em></p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="354" column="6"><Name>comment_to_html_conversion_28</Name><USR>c:@F@comment_to_html_conversion_28#</USR><Abstract><Para> <emphasized>Aaa</emphasized> <emphasized>Bbb</emphasized> <emphasized>Ccc</emphasized></Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[a] RenderEmphasized Arg[0]=Aaa)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[e] RenderEmphasized Arg[0]=Bbb)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[em] RenderEmphasized Arg[0]=Ccc)))]
+// CHECK: annotate-comments.cpp:357:6: FunctionDecl=comment_to_html_conversion_29:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>1&lt;2</em> <em>3&lt;4</em> <em>5&lt;6</em> </p><dl><dt class="tparam-name-index-invalid">9&lt;10</dt><dd class="tparam-descr-index-invalid"> bbb</dd></dl><dl><dt class="param-name-index-invalid">7&lt;8</dt><dd class="param-descr-index-invalid"> aaa </dd></dl>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="357" column="6"><Name>comment_to_html_conversion_29</Name><USR>c:@F@comment_to_html_conversion_29#</USR><Abstract><Para> <emphasized>1&lt;2</emphasized> <emphasized>3&lt;4</emphasized> <emphasized>5&lt;6</emphasized> </Para></Abstract><TemplateParameters><Parameter><Name>9&lt;10</Name><Discussion><Para> bbb</Para></Discussion></Parameter></TemplateParameters><Parameters><Parameter><Name>7&lt;8</Name><Direction isExplicit="0">in</Direction><Discussion><Para> aaa </Para></Discussion></Parameter></Parameters></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[a] RenderEmphasized Arg[0]=1<2)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[e] RenderEmphasized Arg[0]=3<4)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_InlineCommand CommandName=[em] RenderEmphasized Arg[0]=5<6)
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT: (CXComment_ParamCommand in implicitly ParamName=[7<8] ParamIndex=Invalid
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ aaa ])))
+// CHECK-NEXT: (CXComment_TParamCommand ParamName=[9<10] ParamPosition=Invalid
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ bbb]))))]
+// CHECK: annotate-comments.cpp:360:6: FunctionDecl=comment_to_html_conversion_30:{{.*}} FullCommentAsHTML=[<p class="para-brief"> \ @ &amp; $ # &lt; &gt; % &quot; . ::</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="360" column="6"><Name>comment_to_html_conversion_30</Name><USR>c:@F@comment_to_html_conversion_30#</USR><Abstract><Para> \ @ &amp; $ # &lt; &gt; % &quot; . ::</Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[\])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[@])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[&])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[$])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[#])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[<])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[>])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[%])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=["])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[.])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[::])))]
+// CHECK: annotate-comments.cpp:363:6: FunctionDecl=comment_to_html_conversion_31:{{.*}} FullCommentAsHTML=[<p class="para-brief"> &amp; &lt; &gt; &quot;</p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="363" column="6"><Name>comment_to_html_conversion_31</Name><USR>c:@F@comment_to_html_conversion_31#</USR><Abstract><Para> &amp; &lt; &gt; &quot;</Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[&])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[<])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=[>])
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_Text Text=["])))]
+// CHECK: annotate-comments.cpp:366:6: FunctionDecl=comment_to_html_conversion_32:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0&lt;i</em></p>] FullCommentAsXML=[<Function file="{{[^"]+}}annotate-comments.cpp" line="366" column="6"><Name>comment_to_html_conversion_32</Name><USR>c:@F@comment_to_html_conversion_32#</USR><Abstract><Para> <rawHTML><![CDATA[<em>]]></rawHTML>0&lt;i<rawHTML>&lt;/em&gt;</rawHTML></Para></Abstract></Function>]
+// CHECK-NEXT: CommentAST=[
+// CHECK-NEXT: (CXComment_FullComment
+// CHECK-NEXT: (CXComment_Paragraph
+// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)
+// CHECK-NEXT: (CXComment_HTMLStartTag Name=[em])
+// CHECK-NEXT: (CXComment_Text Text=[0])
+// CHECK-NEXT: (CXComment_Text Text=[<])
+// CHECK-NEXT: (CXComment_Text Text=[i])
+// CHECK-NEXT: (CXComment_HTMLEndTag Name=[em])))]
+
+// CHECK: annotate-comments.cpp:369:7: ClassDecl=comment_to_xml_conversion_01:{{.*}} FullCommentAsXML=[<Class file="{{[^"]+}}annotate-comments.cpp" line="369" column="7"><Name>comment_to_xml_conversion_01</Name><USR>c:@C@comment_to_xml_conversion_01</USR><Abstract><Para> Aaa.</Para></Abstract></Class>]
+// CHECK: annotate-comments.cpp:371:3: CXXConstructor=comment_to_xml_conversion_01:{{.*}} FullCommentAsXML=[<Function isInstanceMethod="1" file="{{[^"]+}}annotate-comments.cpp" line="371" column="3"><Name>comment_to_xml_conversion_01</Name><USR>c:@C@comment_to_xml_conversion_01@F@comment_to_xml_conversion_01#I#</USR><Parameters><Parameter><Name>aaa</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Blah blah.</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK: annotate-comments.cpp:374:3: CXXDestructor=~comment_to_xml_conversion_01:{{.*}} FullCommentAsXML=[<Function isInstanceMethod="1" file="{{[^"]+}}annotate-comments.cpp" line="374" column="3"><Name>~comment_to_xml_conversion_01</Name><USR>c:@C@comment_to_xml_conversion_01@F@~comment_to_xml_conversion_01#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK: annotate-comments.cpp:377:7: CXXMethod=comment_to_xml_conversion_02:{{.*}} FullCommentAsXML=[<Function isInstanceMethod="1" file="{{[^"]+}}annotate-comments.cpp" line="377" column="7"><Name>comment_to_xml_conversion_02</Name><USR>c:@C@comment_to_xml_conversion_01@F@comment_to_xml_conversion_02#I#</USR><Parameters><Parameter><Name>aaa</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Blah blah.</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK: annotate-comments.cpp:380:14: CXXMethod=comment_to_xml_conversion_03:{{.*}} FullCommentAsXML=[<Function isClassMethod="1" file="{{[^"]+}}annotate-comments.cpp" line="380" column="14"><Name>comment_to_xml_conversion_03</Name><USR>c:@C@comment_to_xml_conversion_01@F@comment_to_xml_conversion_03#I#S</USR><Parameters><Parameter><Name>aaa</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Blah blah.</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK: annotate-comments.cpp:383:7: FieldDecl=comment_to_xml_conversion_04:{{.*}} FullCommentAsXML=[<Variable file="{{[^"]+}}annotate-comments.cpp" line="383" column="7"><Name>comment_to_xml_conversion_04</Name><USR>c:@C@comment_to_xml_conversion_01@FI@comment_to_xml_conversion_04</USR><Abstract><Para> Aaa.</Para></Abstract></Variable>]
+// CHECK: annotate-comments.cpp:386:14: VarDecl=comment_to_xml_conversion_05:{{.*}} FullCommentAsXML=[<Variable file="{{[^"]+}}annotate-comments.cpp" line="386" column="14"><Name>comment_to_xml_conversion_05</Name><USR>c:@C@comment_to_xml_conversion_01@comment_to_xml_conversion_05</USR><Abstract><Para> Aaa.</Para></Abstract></Variable>]
+// CHECK: annotate-comments.cpp:389:8: CXXMethod=operator():{{.*}} FullCommentAsXML=[<Function isInstanceMethod="1" file="{{[^"]+}}annotate-comments.cpp" line="389" column="8"><Name>operator()</Name><USR>c:@C@comment_to_xml_conversion_01@F@operator()#I#</USR><Parameters><Parameter><Name>aaa</Name><Index>0</Index><Direction isExplicit="0">in</Direction><Discussion><Para> Blah blah.</Para></Discussion></Parameter></Parameters></Function>]
+// CHECK: annotate-comments.cpp:392:3: CXXConversion=operator _Bool:{{.*}} FullCommentAsXML=[<Function isInstanceMethod="1" file="{{[^"]+}}annotate-comments.cpp" line="392" column="3"><Name>operator _Bool</Name><USR>c:@C@comment_to_xml_conversion_01@F@operator _Bool#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK: annotate-comments.cpp:395:15: TypedefDecl=comment_to_xml_conversion_06:{{.*}} FullCommentAsXML=[<Typedef file="{{[^"]+}}annotate-comments.cpp" line="395" column="15"><Name>comment_to_xml_conversion_06</Name><USR>c:annotate-comments.cpp@8055@C@comment_to_xml_conversion_01@T@comment_to_xml_conversion_06</USR><Abstract><Para> Aaa.</Para></Abstract></Typedef>]
+// CHECK: annotate-comments.cpp:398:9: TypeAliasDecl=comment_to_xml_conversion_07:{{.*}} FullCommentAsXML=[<Typedef file="{{[^"]+}}annotate-comments.cpp" line="398" column="9"><Name>comment_to_xml_conversion_07</Name><USR>c:@C@comment_to_xml_conversion_01@comment_to_xml_conversion_07</USR><Abstract><Para> Aaa.</Para></Abstract></Typedef>]
+// CHECK: annotate-comments.cpp:405:3: UnexposedDecl=comment_to_xml_conversion_09:{{.*}} FullCommentAsXML=[<Typedef file="{{[^"]+}}annotate-comments.cpp" line="405" column="3"><Name>comment_to_xml_conversion_09</Name><USR>c:@C@comment_to_xml_conversion_01@comment_to_xml_conversion_09</USR><Abstract><Para> Aaa.</Para></Abstract></Typedef>]
+// CHECK: annotate-comments.cpp:410:6: FunctionTemplate=comment_to_xml_conversion_10:{{.*}} FullCommentAsXML=[<Function templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="410" column="6"><Name>comment_to_xml_conversion_10</Name><USR>c:@FT@&gt;2#T#Tcomment_to_xml_conversion_10#t0.0#t0.1#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK: annotate-comments.cpp:414:6: FunctionDecl=comment_to_xml_conversion_10:{{.*}} FullCommentAsXML=[<Function templateKind="specialization" file="{{[^"]+}}annotate-comments.cpp" line="414" column="6"><Name>comment_to_xml_conversion_10</Name><USR>c:@F@comment_to_xml_conversion_10&lt;#I#I&gt;#I#I#</USR><Abstract><Para> Aaa.</Para></Abstract></Function>]
+// CHECK: annotate-comments.cpp:418:7: ClassTemplate=comment_to_xml_conversion_11:{{.*}} FullCommentAsXML=[<Class templateKind="template" file="{{[^"]+}}annotate-comments.cpp" line="418" column="7"><Name>comment_to_xml_conversion_11</Name><USR>c:@CT&gt;2#T#T@comment_to_xml_conversion_11</USR><Abstract><Para> Aaa.</Para></Abstract></Class>]
+// CHECK: annotate-comments.cpp:422:7: ClassTemplatePartialSpecialization=comment_to_xml_conversion_11:{{.*}} FullCommentAsXML=[<Class templateKind="partialSpecialization" file="{{[^"]+}}annotate-comments.cpp" line="422" column="7"><Name>comment_to_xml_conversion_11</Name><USR>c:@CP&gt;1#T@comment_to_xml_conversion_11&gt;#t0.0#I</USR><Abstract><Para> Aaa.</Para></Abstract></Class>]
+// CHECK: annotate-comments.cpp:426:7: ClassDecl=comment_to_xml_conversion_11:{{.*}} FullCommentAsXML=[<Class templateKind="specialization" file="{{[^"]+}}annotate-comments.cpp" line="426" column="7"><Name>comment_to_xml_conversion_11</Name><USR>c:@C@comment_to_xml_conversion_11&gt;#I#I</USR><Abstract><Para> Aaa.</Para></Abstract></Class>]
+// CHECK: annotate-comments.cpp:429:5: VarDecl=comment_to_xml_conversion_12:{{.*}} FullCommentAsXML=[<Variable file="{{[^"]+}}annotate-comments.cpp" line="429" column="5"><Name>comment_to_xml_conversion_12</Name><USR>c:@comment_to_xml_conversion_12</USR><Abstract><Para> Aaa.</Para></Abstract></Variable>]
+// CHECK: annotate-comments.cpp:432:11: Namespace=comment_to_xml_conversion_13:{{.*}} FullCommentAsXML=[<Namespace file="{{[^"]+}}annotate-comments.cpp" line="432" column="11"><Name>comment_to_xml_conversion_13</Name><USR>c:@N@comment_to_xml_conversion_13</USR><Abstract><Para> Aaa.</Para></Abstract></Namespace>]
+// CHECK: annotate-comments.cpp:434:13: Namespace=comment_to_xml_conversion_14:{{.*}} FullCommentAsXML=[<Namespace file="{{[^"]+}}annotate-comments.cpp" line="434" column="13"><Name>comment_to_xml_conversion_14</Name><USR>c:@N@comment_to_xml_conversion_13@N@comment_to_xml_conversion_14</USR><Abstract><Para> Aaa.</Para></Abstract></Namespace>]
+// CHECK: annotate-comments.cpp:439:6: EnumDecl=comment_to_xml_conversion_15:{{.*}} FullCommentAsXML=[<Enum file="{{[^"]+}}annotate-comments.cpp" line="439" column="6"><Name>comment_to_xml_conversion_15</Name><USR>c:@E@comment_to_xml_conversion_15</USR><Abstract><Para> Aaa.</Para></Abstract></Enum>]
+// CHECK: annotate-comments.cpp:441:3: EnumConstantDecl=comment_to_xml_conversion_16:{{.*}} FullCommentAsXML=[<Variable file="{{[^"]+}}annotate-comments.cpp" line="441" column="3"><Name>comment_to_xml_conversion_16</Name><USR>c:@E@comment_to_xml_conversion_15@comment_to_xml_conversion_16</USR><Abstract><Para> Aaa.</Para></Abstract></Variable>]
+// CHECK: annotate-comments.cpp:445:12: EnumDecl=comment_to_xml_conversion_17:{{.*}} FullCommentAsXML=[<Enum file="{{[^"]+}}annotate-comments.cpp" line="445" column="12"><Name>comment_to_xml_conversion_17</Name><USR>c:@E@comment_to_xml_conversion_17</USR><Abstract><Para> Aaa.</Para></Abstract></Enum>]
+// CHECK: annotate-comments.cpp:447:3: EnumConstantDecl=comment_to_xml_conversion_18:{{.*}} FullCommentAsXML=[<Variable file="{{[^"]+}}annotate-comments.cpp" line="447" column="3"><Name>comment_to_xml_conversion_18</Name><USR>c:@E@comment_to_xml_conversion_17@comment_to_xml_conversion_18</USR><Abstract><Para> Aaa.</Para></Abstract></Variable>]
diff --git a/test/Index/availability.c b/test/Index/availability.c
new file mode 100644
index 0000000..e6b1273
--- /dev/null
+++ b/test/Index/availability.c
@@ -0,0 +1,10 @@
+// Run lines below; this test is line- and column-sensitive.
+
+void foo(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5,obsoleted=10.7), availability(ios,introduced=3.2,deprecated=4.1)));
+
+// RUN: c-index-test -test-load-source all %s > %t
+// RUN: FileCheck -check-prefix=CHECK-1 %s < %t
+// RUN: FileCheck -check-prefix=CHECK-2 %s < %t
+// CHECK-1: (ios, introduced=3.2, deprecated=4.1)
+// CHECK-2: (macosx, introduced=10.4, deprecated=10.5, obsoleted=10.7)
+
diff --git a/test/Index/boxed-exprs.h b/test/Index/boxed-exprs.h
new file mode 100644
index 0000000..bdb8038
--- /dev/null
+++ b/test/Index/boxed-exprs.h
@@ -0,0 +1,10 @@
+
+@interface NSString @end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
+@end
+
+static inline void infoo(const char *cs) {
+ NSString *s = @(cs);
+}
diff --git a/test/Index/boxed-exprs.m b/test/Index/boxed-exprs.m
new file mode 100644
index 0000000..0a63b3f
--- /dev/null
+++ b/test/Index/boxed-exprs.m
@@ -0,0 +1,19 @@
+
+#include "boxed-exprs.h"
+
+const char *glob_str;
+
+void foo() {
+ NSString *s = @(glob_str);
+}
+
+// RUN: c-index-test -index-file -target x86_64-apple-macosx10.7 %s | FileCheck -check-prefix=CHECK-INDEX %s
+// CHECK-INDEX: [indexEntityReference]: kind: variable | name: glob_str | {{.*}} | loc: 7:19
+
+// RUN: c-index-test -cursor-at=%s:7:24 -target x86_64-apple-macosx10.7 %s | FileCheck -check-prefix=CHECK-CURSOR %s
+// RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:7:24 -target x86_64-apple-macosx10.7 %s | FileCheck -check-prefix=CHECK-CURSOR %s
+// CHECK-CURSOR: 7:19 DeclRefExpr=glob_str:4:13 Extent=[7:19 - 7:27] Spelling=glob_str ([7:19 - 7:27])
+
+// RUN: c-index-test -cursor-at=%S/boxed-exprs.h:9:19 -target x86_64-apple-macosx10.7 %s | FileCheck -check-prefix=CHECK-CURSOR2 %s
+// RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%S/boxed-exprs.h:9:19 -target x86_64-apple-macosx10.7 %s | FileCheck -check-prefix=CHECK-CURSOR2 %s
+// CHECK-CURSOR2: 9:19 DeclRefExpr=cs:8:38 Extent=[9:19 - 9:21] Spelling=cs ([9:19 - 9:21])
diff --git a/test/Index/c-index-api-loadTU-test.m b/test/Index/c-index-api-loadTU-test.m
index 251d73b..7520330 100644
--- a/test/Index/c-index-api-loadTU-test.m
+++ b/test/Index/c-index-api-loadTU-test.m
@@ -83,7 +83,7 @@ struct X0 {};
// CHECK: <invalid loc>:0:0: attribute(ibaction)=
// CHECK: c-index-api-loadTU-test.m:8:50: ParmDecl=msg:8:50 (Definition) Extent=[8:47 - 8:53]
// CHECK: c-index-api-loadTU-test.m:8:47: TypeRef=id:0:0 Extent=[8:47 - 8:49]
-// CHECK: c-index-api-loadTU-test.m:9:3: ObjCInstanceMethodDecl=foo:9:3 (deprecated) Extent=[9:1 - 9:35]
+// CHECK: c-index-api-loadTU-test.m:9:3: ObjCInstanceMethodDecl=foo:9:3 (deprecated) (always deprecated: "") Extent=[9:1 - 9:35]
// CHECK: c-index-api-loadTU-test.m:10:3: ObjCClassMethodDecl=fooC:10:3 Extent=[10:1 - 10:8]
// CHECK: c-index-api-loadTU-test.m:14:12: ObjCInterfaceDecl=Bar:14:12 Extent=[14:1 - 18:5]
// CHECK: c-index-api-loadTU-test.m:14:18: ObjCSuperClassRef=Foo:4:12 Extent=[14:18 - 14:21]
diff --git a/test/Index/comment-xml-schema.c b/test/Index/comment-xml-schema.c
new file mode 100644
index 0000000..1166e78
--- /dev/null
+++ b/test/Index/comment-xml-schema.c
@@ -0,0 +1,43 @@
+// REQUIRES: xmllint
+
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-other-01.xml
+//
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-01.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-02.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-03.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-04.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-05.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-06.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-07.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-08.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-function-09.xml
+//
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-class-01.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-class-02.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-class-03.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-class-04.xml
+//
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-variable-01.xml
+//
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-namespace-01.xml
+//
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-typedef-01.xml
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-typedef-02.xml
+//
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/valid-enum-01.xml
+
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-01.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-02.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-03.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-04.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-05.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-06.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-07.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-08.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-09.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-10.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-11.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+// RUN: xmllint --noout --relaxng %S/../../bindings/xml/comment-xml-schema.rng %S/Inputs/CommentXML/invalid-function-12.xml 2>&1 | FileCheck %s -check-prefix=INVALID
+
+// CHECK-INVALID: fails to validate
+
diff --git a/test/Index/compile_commands.json b/test/Index/compile_commands.json
new file mode 100644
index 0000000..89d9f0d
--- /dev/null
+++ b/test/Index/compile_commands.json
@@ -0,0 +1,27 @@
+[
+{
+ "directory": "/home/john.doe/MyProject",
+ "command": "clang++ -o project.o -c /home/john.doe/MyProject/project.cpp",
+ "file": "/home/john.doe/MyProject/project.cpp"
+},
+{
+ "directory": "/home/john.doe/MyProjectA",
+ "command": "clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp",
+ "file": "/home/john.doe/MyProject/project2.cpp"
+},
+{
+ "directory": "/home/john.doe/MyProjectB",
+ "command": "clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp",
+ "file": "/home/john.doe/MyProject/project2.cpp"
+}
+]
+# RUN: c-index-test -compilation-db %s
+# RUN: c-index-test -compilation-db lookup file_does_not_exists.cpp %s | FileCheck -check-prefix=FILE-NOT-FOUND %s
+# FILE-NOT-FOUND: file file_does_not_exists.cpp not found in compilation db
+
+# RUN: c-index-test -compilation-db lookup /home/john.doe/MyProject/project.cpp %s | FileCheck -check-prefix=FILE-1-CMD %s
+# FILE-1-CMD: workdir:'/home/john.doe/MyProject' cmdline:'clang++ -o project.o -c /home/john.doe/MyProject/project.cpp'
+
+# RUN: c-index-test -compilation-db lookup /home/john.doe/MyProject/project2.cpp %s | FileCheck -check-prefix=FILE-2-CMD %s
+# FILE-2-CMD: workdir:'/home/john.doe/MyProjectA' cmdline:'clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp'
+# FILE-2-CMD: workdir:'/home/john.doe/MyProjectB' cmdline:'clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp'
diff --git a/test/Index/complete-documentation.cpp b/test/Index/complete-documentation.cpp
new file mode 100644
index 0000000..a64e62f
--- /dev/null
+++ b/test/Index/complete-documentation.cpp
@@ -0,0 +1,51 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
+
+/// Aaa.
+void T1(float x, float y);
+
+/// Bbb.
+class T2 {
+public:
+ /// Ccc.
+ void T3();
+
+ int T4; ///< Ddd.
+};
+
+/// Eee.
+namespace T5 {
+}
+
+struct T6 {
+ /// \brief Fff.
+ void T7();
+
+ /// \brief Ggg.
+ void T8();
+};
+
+void T6::T7() {
+}
+
+void test1() {
+
+ T2 t2;
+ t2.T4;
+
+ T6 t6;
+ t6.T8();
+}
+
+// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:32:1 %s | FileCheck -check-prefix=CC1 %s
+// CHECK-CC1: FunctionDecl:{ResultType void}{TypedText T1}{{.*}}(brief comment: Aaa.)
+// CHECK-CC1: ClassDecl:{TypedText T2}{{.*}}(brief comment: Bbb.)
+// CHECK-CC1: Namespace:{TypedText T5}{{.*}}(brief comment: Eee.)
+
+// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:34:6 %s | FileCheck -check-prefix=CC2 %s
+// CHECK-CC2: CXXMethod:{ResultType void}{TypedText T3}{{.*}}(brief comment: Ccc.)
+// CHECK-CC2: FieldDecl:{ResultType int}{TypedText T4}{{.*}}(brief comment: Ddd.)
+
+// RUN: env CINDEXTEST_COMPLETION_BRIEF_COMMENTS=1 c-index-test -code-completion-at=%s:37:6 %s | FileCheck -check-prefix=CC3 %s
+// CHECK-CC3: CXXMethod:{ResultType void}{TypedText T7}{LeftParen (}{RightParen )} (34) (parent: StructDecl 'T6')(brief comment: Fff.)
+// CHECK-CC3: CXXMethod:{ResultType void}{TypedText T8}{LeftParen (}{RightParen )} (34) (parent: StructDecl 'T6')(brief comment: Ggg.)
diff --git a/test/Index/complete-exprs.m b/test/Index/complete-exprs.m
index c3ff63b..3fb1540 100644
--- a/test/Index/complete-exprs.m
+++ b/test/Index/complete-exprs.m
@@ -18,8 +18,10 @@ __strong id global;
@end
// RUN: c-index-test -code-completion-at=%s:13:2 %s | FileCheck -check-prefix=CHECK-CC1 %s
-// CHECK-CC1: NotImplemented:{TypedText @[}{HorizontalSpace }{Placeholder objects, ...}{HorizontalSpace }{RightBracket ]} (40)
-// CHECK-CC1: NotImplemented:{TypedText @{}{HorizontalSpace }{Placeholder key}{HorizontalSpace }{Colon :}{HorizontalSpace }{Placeholder object, ...}{HorizontalSpace }{RightBrace }} (40)
+// CHECK-CC1: NotImplemented:{ResultType NSString *}{TypedText @"}{Placeholder string}{Text "} (40)
+// CHECK-CC1: NotImplemented:{ResultType id}{TypedText @(}{Placeholder expression}{RightParen )} (40)
+// CHECK-CC1: NotImplemented:{ResultType NSArray *}{TypedText @[}{Placeholder objects, ...}{RightBracket ]} (40)
+// CHECK-CC1: NotImplemented:{ResultType NSDictionary *}{TypedText @{}{Placeholder key}{HorizontalSpace }{Colon :}{HorizontalSpace }{Placeholder object, ...}{RightBrace }} (40)
// CHECK-CC1: NotImplemented:{ResultType SEL}{TypedText _cmd} (80)
// CHECK-CC1: TypedefDecl:{TypedText BOOL} (50)
// CHECK-CC1: macro definition:{TypedText bool} (51)
@@ -40,8 +42,8 @@ __strong id global;
// RUN: c-index-test -code-completion-at=%s:15:5 %s | FileCheck -check-prefix=CHECK-CC4 %s
// RUN: c-index-test -code-completion-at=%s:16:5 %s | FileCheck -check-prefix=CHECK-CC4 %s
// RUN: c-index-test -code-completion-at=%s:16:14 %s | FileCheck -check-prefix=CHECK-CC4 %s
-// CHECK-CC4: NotImplemented:{TypedText @[}{HorizontalSpace }{Placeholder objects, ...}{HorizontalSpace }{RightBracket ]} (40)
-// CHECK-CC4: NotImplemented:{TypedText @{}{HorizontalSpace }{Placeholder key}{HorizontalSpace }{Colon :}{HorizontalSpace }{Placeholder object, ...}{HorizontalSpace }{RightBrace }} (40)
+// CHECK-CC4: NotImplemented:{ResultType NSArray *}{TypedText @[}{Placeholder objects, ...}{RightBracket ]} (40)
+// CHECK-CC4: NotImplemented:{ResultType NSDictionary *}{TypedText @{}{Placeholder key}{HorizontalSpace }{Colon :}{HorizontalSpace }{Placeholder object, ...}{RightBrace }} (40)
// CHECK-CC4: NotImplemented:{ResultType SEL}{TypedText _cmd} (80)
// CHECK-CC4: macro definition:{TypedText bool} (51)
// CHECK-CC4: macro definition:{TypedText NO} (65)
diff --git a/test/Index/complete-lambdas.mm b/test/Index/complete-lambdas.mm
new file mode 100644
index 0000000..3f77dd2
--- /dev/null
+++ b/test/Index/complete-lambdas.mm
@@ -0,0 +1,51 @@
+// This test is line- and column-sensitive. See below for run lines.
+
+
+@interface A
+- instanceMethod:(int)value withOther:(int)other;
++ classMethod;
+@end
+
+@interface B : A
+@end
+
+@implementation B
+- someMethod:(A*)a {
+ [a classMethod];
+ [A classMethod];
+ [a instanceMethod:0 withOther:1];
+ [self someMethod:a];
+ [super instanceMethod];
+ [&,a ]{};
+ [a,self instanceMethod:0 withOther:1]{};
+}
+
+@end
+
+// RUN: c-index-test -code-completion-at=%s:14:6 -std=c++11 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: ObjCInstanceMethodDecl:{ResultType id}{TypedText instanceMethod:}{Placeholder (int)}{HorizontalSpace }{TypedText withOther:}{Placeholder (int)} (35) (parent: ObjCInterfaceDecl 'A')
+
+// RUN: c-index-test -code-completion-at=%s:15:6 -std=c++11 %s | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: ObjCClassMethodDecl:{ResultType id}{TypedText classMethod} (35) (parent: ObjCInterfaceDecl 'A')
+
+// RUN: c-index-test -code-completion-at=%s:16:4 -std=c++11 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: ObjCInterfaceDecl:{TypedText A} (50) (parent: TranslationUnit '(null)')
+// CHECK-CC3: ParmDecl:{ResultType A *}{TypedText a} (34)
+// CHECK-CC3: ObjCInterfaceDecl:{TypedText B} (50) (parent: TranslationUnit '(null)')
+// CHECK-CC3: TypedefDecl:{TypedText Class} (50) (parent: TranslationUnit '(null)')
+
+
+// RUN: c-index-test -code-completion-at=%s:16:21 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC4 %s
+// CHECK-CC4: NotImplemented:{ResultType B *}{TypedText self} (34)
+// CHECK-CC4: NotImplemented:{ResultType A *}{TypedText super} (40)
+
+// RUN: c-index-test -code-completion-at=%s:18:10 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC1 %s
+
+// RUN: c-index-test -code-completion-at=%s:19:8 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC5 %s
+// CHECK-CC5: NotImplemented:{ResultType SEL}{TypedText _cmd} (80)
+// CHECK-CC5-NEXT: NotImplemented:{ResultType B *}{TypedText self} (34)
+
+// RUN: c-index-test -code-completion-at=%s:20:11 -x objective-c++ -std=c++11 %s | FileCheck -check-prefix=CHECK-CC6 %s
+// CHECK-CC6: ObjCInstanceMethodDecl:{ResultType id}{TypedText instanceMethod:}{Placeholder (int)}{HorizontalSpace }{TypedText withOther:}{Placeholder (int)} (37) (parent: ObjCInterfaceDecl 'A')
+// CHECK-CC6-NEXT: ObjCInstanceMethodDecl:{ResultType id}{TypedText someMethod:}{Placeholder (A *)} (32) (parent: ObjCImplementationDecl 'B')
+
diff --git a/test/Index/complete-method-decls.m b/test/Index/complete-method-decls.m
index becb7de..ce48246 100644
--- a/test/Index/complete-method-decls.m
+++ b/test/Index/complete-method-decls.m
@@ -8,7 +8,7 @@
- (int)getInt;
- (id)getSelf;
@end
-
+@protocol P1;
@protocol P2<P1>
+ (id)alloc;
@end
diff --git a/test/Index/complete-properties.m b/test/Index/complete-properties.m
index ce1870e..a445056 100644
--- a/test/Index/complete-properties.m
+++ b/test/Index/complete-properties.m
@@ -45,6 +45,17 @@ id test(I3 *i3) {
@synthesize Prop2 = Prop2_;
@end
+@protocol P1
+@property int Prop5;
+@end
+
+@class P1;
+
+@interface I5<P1>
+@end
+@implementation I5
+@synthesize Prop5;
+@end
// RUN: c-index-test -code-completion-at=%s:20:13 %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText Prop0}
// CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText Prop1}
@@ -80,3 +91,6 @@ id test(I3 *i3) {
// CHECK-CC7-NOT: ObjCIvarDecl:{ResultType id}{TypedText _Prop2}
// CHECK-CC7: ObjCIvarDecl:{ResultType I4 *}{TypedText Prop1} (17)
// CHECK-CC7: ObjCIvarDecl:{ResultType id}{TypedText Prop2_} (7)
+
+// RUN: c-index-test -code-completion-at=%s:57:13 -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC8 %s
+// CHECK-CC8: ObjCPropertyDecl:{ResultType int}{TypedText Prop5} (35)
diff --git a/test/Index/create-tu-fail.c b/test/Index/create-tu-fail.c
new file mode 100644
index 0000000..f259cca
--- /dev/null
+++ b/test/Index/create-tu-fail.c
@@ -0,0 +1,10 @@
+// RUN: rm -f %t.c
+// RUN: touch %t.c
+// RUN: c-index-test -write-pch %t.pch %t.c
+// RUN: cp %s %t.c
+// RUN: c-index-test -test-load-tu %t.pch local 2>&1 | FileCheck %s
+
+// rdar://11558355
+// Unfortunately this would crash reliably only via valgrind.
+
+// CHECK: Unable to load translation unit
diff --git a/test/Index/cursor-dynamic-call.mm b/test/Index/cursor-dynamic-call.mm
new file mode 100644
index 0000000..f9d6a87
--- /dev/null
+++ b/test/Index/cursor-dynamic-call.mm
@@ -0,0 +1,59 @@
+
+struct SB {
+ virtual void meth();
+};
+
+struct SS : public SB {
+ void submeth() {
+ this->meth();
+ SB::meth();
+ }
+};
+
+@interface IB
+-(void)meth;
++(void)ClsMeth;
+@end
+
+@interface IS : IB
+-(void)submeth;
++(void)ClsMeth;
+@end
+
+@implementation IS
+-(void)submeth {
+ [self meth];
+ [super meth];
+}
++(void)ClsMeth {
+ [super ClsMeth];
+}
+@end
+
+void foo(SS *ss, IS* is, Class cls) {
+ ss->meth();
+ [is meth];
+ [IB ClsMeth];
+ [cls ClsMeth];
+}
+
+// RUN: c-index-test -cursor-at=%s:8:11 \
+// RUN: -cursor-at=%s:9:11 \
+// RUN: -cursor-at=%s:25:11 \
+// RUN: -cursor-at=%s:26:11 \
+// RUN: -cursor-at=%s:29:11 \
+// RUN: -cursor-at=%s:34:9 \
+// RUN: -cursor-at=%s:35:9 \
+// RUN: -cursor-at=%s:36:9 \
+// RUN: -cursor-at=%s:37:9 \
+// RUN: %s | FileCheck %s
+
+// CHECK: 8:11 MemberRefExpr=meth:3:16 {{.*}} Dynamic-call
+// CHECK-NOT: 9:9 {{.*}} Dynamic-call
+// CHECK: 25:3 ObjCMessageExpr=meth:14:8 {{.*}} Dynamic-call
+// CHECK-NOT: 26:3 {{.*}} Dynamic-call
+// CHECK-NOT: 29:3 {{.*}} Dynamic-call
+// CHECK: 34:7 MemberRefExpr=meth:3:16 {{.*}} Dynamic-call
+// CHECK: 35:3 ObjCMessageExpr=meth:14:8 {{.*}} Dynamic-call
+// CHECK-NOT: 36:3 {{.*}} Dynamic-call
+// CHECK: 37:3 ObjCMessageExpr=ClsMeth:15:8 {{.*}} Dynamic-call
diff --git a/test/Index/get-cursor.c b/test/Index/get-cursor.c
index 23a4b5c..c0614af 100644
--- a/test/Index/get-cursor.c
+++ b/test/Index/get-cursor.c
@@ -4,11 +4,17 @@ struct _MyS {
struct _MyS ww;
+int x, y;
+
// RUN: c-index-test -cursor-at=%s:1:9 \
// RUN: -cursor-at=%s:2:9 \
// RUN: -cursor-at=%s:5:9 \
+// RUN: -cursor-at=%s:7:5 \
+// RUN: -cursor-at=%s:7:8 \
// RUN: %s | FileCheck %s
// CHECK: StructDecl=_MyS:1:8 (Definition)
// CHECK: FieldDecl=foo:2:7 (Definition)
// CHECK: TypeRef=struct _MyS:1:8
+// CHECK: VarDecl=x:7:5
+// CHECK: VarDecl=y:7:8
diff --git a/test/Index/get-cursor.cpp b/test/Index/get-cursor.cpp
index e1e6835..8b70216 100644
--- a/test/Index/get-cursor.cpp
+++ b/test/Index/get-cursor.cpp
@@ -38,6 +38,13 @@ void test() {
} catch (X e) {
X x;
}
+
+ struct LocalS {
+ void meth() {
+ int x;
+ ++x;
+ }
+ };
}
// RUN: c-index-test -cursor-at=%s:6:4 %s | FileCheck -check-prefix=CHECK-COMPLETION-1 %s
@@ -93,3 +100,6 @@ void test() {
// RUN: c-index-test -test-load-source-usrs local %s | FileCheck -check-prefix=CHECK-USR %s
// CHECK-USR: get-cursor.cpp c:get-cursor.cpp@472@F@test#@e Extent=[38:12 - 38:15]
// CHECK-USR: get-cursor.cpp c:get-cursor.cpp@483@F@test#@x Extent=[39:5 - 39:8]
+
+// RUN: c-index-test -cursor-at=%s:45:9 %s | FileCheck -check-prefix=CHECK-LOCALCLASS %s
+// CHECK-LOCALCLASS: 45:9 DeclRefExpr=x:44:11 Extent=[45:9 - 45:10] Spelling=x ([45:9 - 45:10])
diff --git a/test/Index/get-cursor.m b/test/Index/get-cursor.m
index 5ac3375..d3da9ec 100644
--- a/test/Index/get-cursor.m
+++ b/test/Index/get-cursor.m
@@ -69,6 +69,28 @@ void foo3(Test3 *test3) {
@class Forw1, Forw2, Forw3;
+@interface Test5 {
+ id prop1;
+ id prop2;
+}
+@property (assign) id prop1;
+@property (assign) id prop2;
+@property (assign) id prop3;
+@property (assign) id prop4;
+@end
+
+@implementation Test5
+@synthesize prop1, prop2;
+@dynamic prop3, prop4;
+
+-(id)meth1 {
+ return 0;
+}
+-(id)meth2{
+ return 0;
+}
+@end
+
// RUN: c-index-test -cursor-at=%s:4:28 -cursor-at=%s:5:28 %s | FileCheck -check-prefix=CHECK-PROP %s
// CHECK-PROP: ObjCPropertyDecl=foo1:4:26
// CHECK-PROP: ObjCPropertyDecl=foo2:5:27
@@ -102,3 +124,14 @@ void foo3(Test3 *test3) {
// CHECK-SPELLRANGE: 70:8 ObjCClassRef=Forw1:70:8 Extent=[70:8 - 70:13] Spelling=Forw1 ([70:8 - 70:13])
// CHECK-SPELLRANGE: 70:15 ObjCClassRef=Forw2:70:15 Extent=[70:15 - 70:20] Spelling=Forw2 ([70:15 - 70:20])
// CHECK-SPELLRANGE: 70:22 ObjCClassRef=Forw3:70:22 Extent=[70:22 - 70:27] Spelling=Forw3 ([70:22 - 70:27])
+
+// RUN: c-index-test -cursor-at=%s:83:15 -cursor-at=%s:83:21 \
+// RUN: -cursor-at=%s:84:12 -cursor-at=%s:84:20 %s | FileCheck -check-prefix=CHECK-MULTISYNTH %s
+// CHECK-MULTISYNTH: 83:13 ObjCSynthesizeDecl=prop1:76:23 (Definition) Extent=[83:1 - 83:18] Spelling=prop1 ([83:13 - 83:18])
+// CHECK-MULTISYNTH: 83:20 ObjCSynthesizeDecl=prop2:77:23 (Definition) Extent=[83:1 - 83:25] Spelling=prop2 ([83:20 - 83:25])
+// CHECK-MULTISYNTH: 84:10 ObjCDynamicDecl=prop3:78:23 (Definition) Extent=[84:1 - 84:15] Spelling=prop3 ([84:10 - 84:15])
+// CHECK-MULTISYNTH: 84:17 ObjCDynamicDecl=prop4:79:23 (Definition) Extent=[84:1 - 84:22] Spelling=prop4 ([84:17 - 84:22])
+
+// RUN: c-index-test -cursor-at=%s:86:7 -cursor-at=%s:89:7 %s | FileCheck -check-prefix=CHECK-SELECTORLOC %s
+// CHECK-SELECTORLOC: 86:6 ObjCInstanceMethodDecl=meth1:86:6 (Definition) Extent=[86:1 - 88:2] Spelling=meth1 ([86:6 - 86:11]) Selector index=0
+// CHECK-SELECTORLOC: 89:6 ObjCInstanceMethodDecl=meth2:89:6 (Definition) Extent=[89:1 - 91:2] Spelling=meth2 ([89:6 - 89:11]) Selector index=0
diff --git a/test/Index/index-decls.m b/test/Index/index-decls.m
index 9e4e620..46d37c4 100644
--- a/test/Index/index-decls.m
+++ b/test/Index/index-decls.m
@@ -11,12 +11,22 @@
@synthesize prop = _prop;
@end
-rdar://11015325
+// rdar://11015325
@interface I1
__attribute__((something)) @interface I2 @end
@end
-// RUN: c-index-test -index-file %s > %t
+@interface I3
+@property (assign,readwrite) id auto_prop;
+@end
+
+@implementation I3
+-(void)meth {
+ _auto_prop = 0;
+}
+@end
+
+// RUN: c-index-test -index-file %s -target x86_64-apple-macosx10.7 > %t
// RUN: FileCheck %s -input-file=%t
// CHECK: [indexDeclaration]: kind: objc-class | name: I | {{.*}} | loc: 1:12
// CHECK: [indexDeclaration]: kind: objc-instance-method | name: prop | {{.*}} | loc: 3:2
@@ -28,3 +38,6 @@ __attribute__((something)) @interface I2 @end
// CHECK: [indexDeclaration]: kind: objc-ivar | name: _prop | {{.*}} | loc: 11:20
// CHECK: [indexDeclaration]: kind: objc-instance-method | name: prop | {{.*}} | loc: 11:13 | {{.*}} | lexical-container: [I:10:17]
// CHECK: [indexDeclaration]: kind: objc-instance-method | name: setProp: | {{.*}} | loc: 11:13 | {{.*}} | lexical-container: [I:10:17]
+
+// CHECK: [indexDeclaration]: kind: objc-ivar | name: _auto_prop | {{.*}} | loc: 20:33
+// CHECK: [indexEntityReference]: kind: objc-ivar | name: _auto_prop | {{.*}} | loc: 25:3
diff --git a/test/Index/index-kernel-invocation.cpp b/test/Index/index-kernel-invocation.cpp
new file mode 100644
index 0000000..dba8e6b
--- /dev/null
+++ b/test/Index/index-kernel-invocation.cpp
@@ -0,0 +1,4 @@
+// RUN: c-index-test -index-file -arch i386 -mkernel %s | FileCheck %s
+
+// CHECK: [indexDeclaration]: kind: function | name: foobar
+void foobar(void);
diff --git a/test/Index/index-many-call-ops.cpp b/test/Index/index-many-call-ops.cpp
new file mode 100644
index 0000000..e732b5f
--- /dev/null
+++ b/test/Index/index-many-call-ops.cpp
@@ -0,0 +1,47 @@
+// RUN: c-index-test -index-file %s | FileCheck %s
+
+// rdar://11289247
+// Check that we don't get stack overflow trying to index a huge number of
+// call operators.
+
+struct S {
+ S &operator()();
+};
+
+// CHECK: [indexDeclaration]: kind: function | name: foo
+void foo() {
+ S s;
+ s()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
+ ;
+}
+
+// CHECK: [indexDeclaration]: kind: function | name: bar
+void bar();
diff --git a/test/Index/index-refs.m b/test/Index/index-refs.m
new file mode 100644
index 0000000..b82345f
--- /dev/null
+++ b/test/Index/index-refs.m
@@ -0,0 +1,18 @@
+
+@class Protocol;
+
+@protocol Prot
+@end
+
+struct FooS {
+ int x;
+};
+
+void foo() {
+ Protocol *p = @protocol(Prot);
+ @encode(struct FooS);
+}
+
+// RUN: c-index-test -index-file %s | FileCheck %s
+// CHECK: [indexEntityReference]: kind: objc-protocol | name: Prot | {{.*}} | loc: 12:27
+// CHECK: [indexEntityReference]: kind: struct | name: FooS | {{.*}} | loc: 13:18
diff --git a/test/Index/overrides.m b/test/Index/overrides.m
index 6908754..d0447b7 100644
--- a/test/Index/overrides.m
+++ b/test/Index/overrides.m
@@ -50,6 +50,52 @@
-(void)meth { }
@end
+@protocol P5
+-(void)kol;
+-(void)kol;
+@end
+
+@protocol P6
+@property (readonly) id prop1;
+@property (readonly) id prop2;
+-(void)meth;
+@end
+
+@interface I3 <P6>
+@property (readwrite) id prop1;
+@property (readonly) id bar;
+@end
+
+@interface I3()
+@property (readwrite) id prop2;
+@property (readwrite) id bar;
+-(void)meth;
+@end
+
+@interface B4
+-(id)prop;
+-(void)setProp:(id)prop;
+@end
+
+@interface I4 : B4
+@property (assign) id prop;
+@end
+
+@interface B5
+@end
+
+@interface I5 : B5
+-(void)meth;
+@end
+
+@interface B5(cat)
+-(void)meth;
+@end
+
+@implementation I5
+-(void)meth{}
+@end
+
// RUN: c-index-test -test-load-source local %s | FileCheck %s
// CHECK: overrides.m:12:9: ObjCInstanceMethodDecl=protoMethod:12:9 [Overrides @3:9]
// CHECK: overrides.m:22:9: ObjCInstanceMethodDecl=method:22:9 [Overrides @16:9]
@@ -59,3 +105,15 @@
// CHECK: overrides.m:32:9: ObjCInstanceMethodDecl=protoMethod:32:9 [Overrides @8:9]
// CHECK: overrides.m:36:9: ObjCInstanceMethodDecl=protoMethod:36:9 [Overrides @12:9, @8:9, @32:9, @17:9]
// CHECK: overrides.m:50:8: ObjCInstanceMethodDecl=meth:50:8 (Definition) [Overrides @43:8]
+// CHECK: overrides.m:55:8: ObjCInstanceMethodDecl=kol:55:8 Extent=[55:1 - 55:12]
+// CHECK: overrides.m:65:26: ObjCInstanceMethodDecl=prop1:65:26 [Overrides @59:25] Extent=[65:26 - 65:31]
+// CHECK: overrides.m:65:26: ObjCInstanceMethodDecl=setProp1::65:26 Extent=[65:26 - 65:31]
+// CHECK: overrides.m:70:26: ObjCInstanceMethodDecl=prop2:70:26 [Overrides @60:25] Extent=[70:26 - 70:31]
+// CHECK: overrides.m:70:26: ObjCInstanceMethodDecl=setProp2::70:26 Extent=[70:26 - 70:31]
+// CHECK: overrides.m:71:26: ObjCInstanceMethodDecl=setBar::71:26 Extent=[71:26 - 71:29]
+// CHECK: overrides.m:72:8: ObjCInstanceMethodDecl=meth:72:8 [Overrides @61:8] Extent=[72:1 - 72:13]
+// CHECK: overrides.m:81:23: ObjCInstanceMethodDecl=prop:81:23 [Overrides @76:6] Extent=[81:23 - 81:27]
+// CHECK: overrides.m:81:23: ObjCInstanceMethodDecl=setProp::81:23 [Overrides @77:8] Extent=[81:23 - 81:27]
+// CHECK: overrides.m:92:8: ObjCInstanceMethodDecl=meth:92:8 Extent=[92:1 - 92:13]
+// CHECK: overrides.m:95:17: ObjCImplementationDecl=I5:95:17 (Definition) Extent=[95:1 - 97:2]
+// CHECK: overrides.m:96:8: ObjCInstanceMethodDecl=meth:96:8 (Definition) [Overrides @92:8] Extent=[96:1 - 96:14]
diff --git a/test/Index/pch-with-errors.c b/test/Index/pch-with-errors.c
index be8728e..2d39613 100644
--- a/test/Index/pch-with-errors.c
+++ b/test/Index/pch-with-errors.c
@@ -38,5 +38,7 @@ void foo(void) {
// CHECK-INDEX: [indexEntityReference]: kind: function | name: erroneous
// RUN: %clang -fsyntax-only %s -include %t.h 2>&1 | FileCheck -check-prefix=PCH-ERR %s
-
// PCH-ERR: error: PCH file contains compiler errors
+
+// RUN: c-index-test -write-pch %t.pch foobar.c 2>&1 | FileCheck -check-prefix=NONEXISTENT %s
+// NONEXISTENT: Unable to load translation unit
diff --git a/test/Index/print-typekind.m b/test/Index/print-typekind.m
index 9db1929..565c5e3 100644
--- a/test/Index/print-typekind.m
+++ b/test/Index/print-typekind.m
@@ -1,10 +1,10 @@
@interface Foo
@property (readonly) id x;
-(int) mymethod;
--(int) mymethod2:(int)x blah:(float)y;
+-(const id) mymethod2:(id)x blah:(Class)y boo:(SEL)z;
@end
// RUN: c-index-test -test-print-typekind %s | FileCheck %s
-// CHECK: ObjCPropertyDecl=x:2:25 typekind=Typedef [canonical=ObjCObjectPointer]
+// CHECK: ObjCPropertyDecl=x:2:25 typekind=ObjCId [canonical=ObjCObjectPointer]
// CHECK: ObjCInstanceMethodDecl=mymethod:3:8 typekind=Invalid [result=Int]
-// CHECK: ObjCInstanceMethodDecl=mymethod2:blah::4:8 typekind=Invalid [result=Int] [args= Int Float]
+// CHECK: ObjCInstanceMethodDecl=mymethod2:blah:boo::4:13 typekind=Invalid [result=ObjCId] [args= ObjCId ObjCClass ObjCSel]
diff --git a/test/Index/recursive-cxx-member-calls.cpp b/test/Index/recursive-cxx-member-calls.cpp
index b80cbf4..9b93872 100644
--- a/test/Index/recursive-cxx-member-calls.cpp
+++ b/test/Index/recursive-cxx-member-calls.cpp
@@ -961,7 +961,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
// CHECK-tokens: Punctuation: ">" [105:51 - 105:52] CallExpr=StringSwitch:87:12
// CHECK-tokens: Punctuation: "(" [105:53 - 105:54] CallExpr=StringSwitch:87:12
// CHECK-tokens: Identifier: "AttrName" [105:54 - 105:62] DeclRefExpr=AttrName:101:19
-// CHECK-tokens: Punctuation: ")" [105:62 - 105:63] CXXFunctionalCastExpr=
+// CHECK-tokens: Punctuation: ")" [105:62 - 105:63] CallExpr=StringSwitch:87:12
// CHECK-tokens: Punctuation: "." [106:5 - 106:6] MemberRefExpr=Case:88:42
// CHECK-tokens: Identifier: "Case" [106:6 - 106:10] MemberRefExpr=Case:88:42
// CHECK-tokens: Punctuation: "(" [106:10 - 106:11] CallExpr=Case:88:42
@@ -2081,7 +2081,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
// CHECK: 106:6: MemberRefExpr=Case:88:42 SingleRefName=[106:6 - 106:10] RefName=[106:6 - 106:10] Extent=[105:10 - 106:10]
// CHECK: 105:10: CXXFunctionalCastExpr= Extent=[105:10 - 105:63]
// CHECK: 105:16: TemplateRef=StringSwitch:83:47 Extent=[105:16 - 105:28]
-// CHECK: 105:10: CallExpr=StringSwitch:87:12 Extent=[105:10 - 105:62]
+// CHECK: 105:10: CallExpr=StringSwitch:87:12 Extent=[105:10 - 105:63]
// CHECK: 105:54: CallExpr=StringRef:38:7 Extent=[105:54 - 105:62]
// CHECK: 105:54: UnexposedExpr=AttrName:101:19 Extent=[105:54 - 105:62]
// CHECK: 105:54: DeclRefExpr=AttrName:101:19 Extent=[105:54 - 105:62]
@@ -2240,4 +2240,3 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
// CHECK: 183:25: DeclRefExpr=AT_thiscall:27:44 Extent=[183:25 - 183:36]
// CHECK: 184:11: StringLiteral= Extent=[184:11 - 184:21]
// CHECK: 184:23: DeclRefExpr=AT_pascal:26:38 Extent=[184:23 - 184:32]
-
diff --git a/test/Lexer/bcpl-escaped-newline.c b/test/Lexer/bcpl-escaped-newline.c
index 4d4a7b5..d87ee9b 100644
--- a/test/Lexer/bcpl-escaped-newline.c
+++ b/test/Lexer/bcpl-escaped-newline.c
@@ -5,7 +5,8 @@
#error bar
//??/
-#error qux // expected-error {{qux}}
+#error qux
+// expected-error@-1 {{qux}}
// Trailing whitespace!
//\
diff --git a/test/Lexer/c90.c b/test/Lexer/c90.c
index d910572..7142c09 100644
--- a/test/Lexer/c90.c
+++ b/test/Lexer/c90.c
@@ -32,3 +32,10 @@ void test3() {
(void)L"\u1234"; // expected-error {{unicode escape sequences are only valid in C99 or C++}}
(void)L'\u1234'; // expected-error {{unicode escape sequences are only valid in C99 or C++}}
}
+
+#define PREFIX(x) foo ## x
+int test4() {
+ int PREFIX(0p) = 0;
+ int *p = &PREFIX(0p+1);
+ return p[-1];
+}
diff --git a/test/Lexer/char-literal.cpp b/test/Lexer/char-literal.cpp
index 5dc5360..8556d46 100644
--- a/test/Lexer/char-literal.cpp
+++ b/test/Lexer/char-literal.cpp
@@ -22,3 +22,6 @@ char m = '👿'; // expected-error {{character too large for enclosing character
char32_t n = U'ab'; // expected-error {{Unicode character literals may not contain multiple characters}}
char16_t o = '👽'; // expected-error {{character too large for enclosing character literal type}}
+
+char16_t p[2] = u"\U0000FFFF";
+char16_t q[2] = u"\U00010000"; // expected-error {{too long}}
diff --git a/test/Lexer/hexfloat.cpp b/test/Lexer/hexfloat.cpp
index 6566933..9bd8f83 100644
--- a/test/Lexer/hexfloat.cpp
+++ b/test/Lexer/hexfloat.cpp
@@ -5,3 +5,11 @@ double e = 0x.p0; //expected-error{{hexadecimal floating constants require a sig
double d = 0x.2p2; // expected-warning{{hexadecimal floating constants are a C99 feature}}
float g = 0x1.2p2; // expected-warning{{hexadecimal floating constants are a C99 feature}}
double h = 0x1.p2; // expected-warning{{hexadecimal floating constants are a C99 feature}}
+
+// PR12717: In order to minimally diverge from the C++ standard, we do not lex
+// 'p[+-]' as part of a pp-number unless the token starts 0x and doesn't contain
+// an underscore.
+double i = 0p+3; // expected-error{{invalid suffix 'p' on integer constant}}
+#define PREFIX(x) foo ## x
+double foo0p = 1, j = PREFIX(0p+3); // ok
+double k = 0x42_amp+3; // expected-error-re{{invalid suffix '_amp' on integer constant|no matching literal operator for call to 'operator "" _amp'}}
diff --git a/test/Lexer/newline-eof.c b/test/Lexer/newline-eof.c
index 825a266..a4a1883 100644
--- a/test/Lexer/newline-eof.c
+++ b/test/Lexer/newline-eof.c
@@ -1,5 +1,9 @@
// RUN: %clang_cc1 -fsyntax-only -Wnewline-eof -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wnewline-eof %s 2>&1 | FileCheck %s
// rdar://9133072
+// Make sure the diagnostic shows up properly at the end of the last line.
+// CHECK: newline-eof.c:9:63
+
// The following line isn't terminated, don't fix it.
void foo() {} // expected-warning{{no newline at end of file}} \ No newline at end of file
diff --git a/test/Lexer/pragma-operators.cpp b/test/Lexer/pragma-operators.cpp
index d1645ad..a76e0b2 100644
--- a/test/Lexer/pragma-operators.cpp
+++ b/test/Lexer/pragma-operators.cpp
@@ -9,7 +9,6 @@
// CHECK: #line
// CHECK: #pragma warning(push)
// CHECK: int foo() { return 0; } }
-// CHECK: #line
// CHECK: #pragma warning(pop)
#define A(X) extern "C" { __pragma(warning(push)) \
int X() { return 0; } \
diff --git a/test/Lexer/wchar-signedness.c b/test/Lexer/wchar-signedness.c
new file mode 100644
index 0000000..fea0eca
--- /dev/null
+++ b/test/Lexer/wchar-signedness.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -fsyntax-only -dM -E %s -triple x86_64-none-linux-gnu | FileCheck %s --check-prefix=X86
+// RUN: %clang_cc1 -fsyntax-only -dM -E %s -triple armv7-none-eabi | FileCheck %s --check-prefix=ARM
+
+// CHECK-X86-NOT: #define __WCHAR_UNSIGNED__
+// CHECK-X86: #define __WINT_UNSIGNED__ 1
+
+// CHECK-ARM: #define __WCHAR_UNSIGNED__ 1
+// CHECK-ARM-NOT: #define __WINT_UNSIGNED__ 1
diff --git a/test/Misc/ast-dump-wchar.cpp b/test/Misc/ast-dump-wchar.cpp
new file mode 100644
index 0000000..87d962f
--- /dev/null
+++ b/test/Misc/ast-dump-wchar.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++11 -ast-dump %s -triple x86_64-linux-gnu | FileCheck %s
+
+char c8[] = u8"test\0\\\"\t\a\b\234";
+// CHECK: char c8[12] = (StringLiteral {{.*}} lvalue u8"test\000\\\"\t\a\b\234")
+
+char16_t c16[] = u"test\0\\\"\t\a\b\234\u1234";
+// CHECK: char16_t c16[13] = (StringLiteral {{.*}} lvalue u"test\000\\\"\t\a\b\234\u1234")
+
+char32_t c32[] = U"test\0\\\"\t\a\b\234\u1234\U0010ffff"; // \
+// CHECK: char32_t c32[14] = (StringLiteral {{.*}} lvalue U"test\000\\\"\t\a\b\234\u1234\U0010FFFF")
+
+wchar_t wc[] = L"test\0\\\"\t\a\b\234\u1234\xffffffff"; // \
+// CHECK: wchar_t wc[14] = (StringLiteral {{.*}} lvalue L"test\000\\\"\t\a\b\234\x1234\xFFFFFFFF")
diff --git a/test/Misc/diag-aka-types.cpp b/test/Misc/diag-aka-types.cpp
index 4c9a731..3a00b71 100644
--- a/test/Misc/diag-aka-types.cpp
+++ b/test/Misc/diag-aka-types.cpp
@@ -30,27 +30,6 @@ void test(Foo::foo* x) {
bar::f(x); // expected-error{{cannot initialize a parameter of type 'Foo::foo *' (aka 'bar::Foo::foo *') with an lvalue of type 'Foo::foo *'}}
}
-// PR9548 - "no known conversion from 'vector<string>' to 'vector<string>'"
-// vector<string> refers to two different types here. Make sure the message
-// gives a way to tell them apart.
-class versa_string;
-typedef versa_string string;
-
-namespace std {template <typename T> class vector;}
-using std::vector;
-
-void f(vector<string> v); // expected-note {{candidate function not viable: no known conversion from 'vector<string>' (aka 'std::vector<std::basic_string>') to 'vector<string>' (aka 'std::vector<versa_string>') for 1st argument}}
-
-namespace std {
- class basic_string;
- typedef basic_string string;
- template <typename T> class vector {};
- void g() {
- vector<string> v;
- f(v); // expected-error{{no matching function for call to 'f'}}
- }
-}
-
namespace ns {
struct str {
static void method(struct data *) {}
@@ -61,7 +40,7 @@ struct data { int i; };
typedef void (*callback)(struct data *);
-void helper(callback cb) {} // expected-note{{candidate function not viable: no known conversion from 'void (*)(struct data *)' (aka 'void (*)(ns::data *)') to 'callback' (aka 'void (*)(struct data *)') for 1st argument;}}
+void helper(callback cb) {} // expected-note{{candidate function not viable: no known conversion from 'void (*)(struct data *)' (aka 'void (*)(ns::data *)') to 'callback' (aka 'void (*)(struct data *)') for 1st argument}}
void test() {
helper(&ns::str::method); // expected-error{{no matching function for call to 'helper'}}
diff --git a/test/Misc/diag-template-diffing-color.cpp b/test/Misc/diag-template-diffing-color.cpp
new file mode 100644
index 0000000..6903e84
--- /dev/null
+++ b/test/Misc/diag-template-diffing-color.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -fsyntax-only -fcolor-diagnostics %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -fcolor-diagnostics -fdiagnostics-show-template-tree %s 2>&1 | FileCheck %s -check-prefix=TREE
+// REQUIRES: ansi-escape-sequences
+template<typename> struct foo {};
+void func(foo<int>);
+int main() {
+ func(foo<double>());
+}
+// CHECK: {{.*}}candidate function not viable: no known conversion from 'foo<{{.}}[0;1;36mdouble{{.}}[0m>' to 'foo<{{.}}[0;1;36mint{{.}}[0m>' for 1st argument{{.}}[0m
+// TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// TREE: foo<
+// TREE: [{{.}}[0;1;36mdouble{{.}}[0m != {{.}}[0;1;36mint{{.}}[0m]>{{.}}[0m
+
+foo<int> A;
+foo<double> &B = A;
+// CHECK: {{.*}}non-const lvalue reference to type 'foo<{{.}}[0;1;36mdouble{{.}}[0m{{.}}[1m>' cannot bind to a value of unrelated type 'foo<{{.}}[0;1;36mint{{.}}[0m{{.}}[1m>'{{.}}[0m
+// TREE: non-const lvalue reference cannot bind to a value of unrelated type
+// TREE: foo<
+// TREE: [{{.}}[0;1;36mdouble{{.}}[0m{{.}}[1m != {{.}}[0;1;36mint{{.}}[0m{{.}}[1m]>{{.}}[0m
diff --git a/test/Misc/diag-template-diffing.cpp b/test/Misc/diag-template-diffing.cpp
new file mode 100644
index 0000000..9addcc5
--- /dev/null
+++ b/test/Misc/diag-template-diffing.cpp
@@ -0,0 +1,433 @@
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-ELIDE-NOTREE
+// RUN: %clang_cc1 -fsyntax-only %s -fno-elide-type -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-NOELIDE-NOTREE
+// RUN: %clang_cc1 -fsyntax-only %s -fdiagnostics-show-template-tree -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-ELIDE-TREE
+// RUN: %clang_cc1 -fsyntax-only %s -fno-elide-type -fdiagnostics-show-template-tree -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-NOELIDE-TREE
+
+// PR9548 - "no known conversion from 'vector<string>' to 'vector<string>'"
+// vector<string> refers to two different types here. Make sure the message
+// gives a way to tell them apart.
+class versa_string;
+typedef versa_string string;
+
+namespace std {template <typename T> class vector;}
+using std::vector;
+
+void f(vector<string> v);
+
+namespace std {
+ class basic_string;
+ typedef basic_string string;
+ template <typename T> class vector {};
+ void g() {
+ vector<string> v;
+ f(v);
+ }
+} // end namespace std
+// CHECK-ELIDE-NOTREE: no matching function for call to 'f'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'vector<class std::basic_string>' to 'vector<class versa_string>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'f'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'vector<class std::basic_string>' to 'vector<class versa_string>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'f'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: vector<
+// CHECK-ELIDE-TREE: [class std::basic_string != class versa_string]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'f'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: vector<
+// CHECK-NOELIDE-TREE: [class std::basic_string != class versa_string]>
+
+template <int... A>
+class I1{};
+void set1(I1<1,2,3,4,2,3,4,3>) {};
+void test1() {
+ set1(I1<1,2,3,4,2,2,4,3,7>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set1'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'I1<[5 * ...], 2, [2 * ...], 7>' to 'I1<[5 * ...], 3, [2 * ...], (no argument)>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set1'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'I1<1, 2, 3, 4, 2, 2, 4, 3, 7>' to 'I1<1, 2, 3, 4, 2, 3, 4, 3, (no argument)>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set1'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: I1<
+// CHECK-ELIDE-TREE: [5 * ...],
+// CHECK-ELIDE-TREE: [2 != 3],
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: [7 != (no argument)]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set1'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: I1<
+// CHECK-NOELIDE-TREE: 1,
+// CHECK-NOELIDE-TREE: 2,
+// CHECK-NOELIDE-TREE: 3,
+// CHECK-NOELIDE-TREE: 4,
+// CHECK-NOELIDE-TREE: 2,
+// CHECK-NOELIDE-TREE: [2 != 3],
+// CHECK-NOELIDE-TREE: 4,
+// CHECK-NOELIDE-TREE: 3,
+// CHECK-NOELIDE-TREE: [7 != (no argument)]>
+
+template <class A, class B, class C = void>
+class I2{};
+void set2(I2<int, int>) {};
+void test2() {
+ set2(I2<double, int, int>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set2'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'I2<double, [...], int>' to 'I2<int, [...], (default) void>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set2'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'I2<double, int, int>' to 'I2<int, int, (default) void>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set2'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: I2<
+// CHECK-ELIDE-TREE: [double != int],
+// CHECK-ELIDE-TREE: [...],
+// CHECK-ELIDE-TREE: [int != (default) void]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set2'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: I2<
+// CHECK-NOELIDE-TREE: [double != int],
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: [int != (default) void]>
+
+int V1, V2, V3;
+template <int* A, int *B>
+class I3{};
+void set3(I3<&V1, &V2>) {};
+void test3() {
+ set3(I3<&V3, &V2>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set3'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'I3<&V3, [...]>' to 'I3<&V1, [...]>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set3'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'I3<&V3, &V2>' to 'I3<&V1, &V2>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set3'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: I3<
+// CHECK-ELIDE-TREE: [&V3 != &V1]
+// CHECK-ELIDE-TREE: [...]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set3'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: I3<
+// CHECK-NOELIDE-TREE: [&V3 != &V1]
+// CHECK-NOELIDE-TREE: &V2>
+
+template <class A, class B>
+class Alpha{};
+template <class A, class B>
+class Beta{};
+template <class A, class B>
+class Gamma{};
+template <class A, class B>
+class Delta{};
+
+void set4(Alpha<int, int>);
+void test4() {
+ set4(Beta<void, void>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set4'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'Beta<void, void>' to 'Alpha<int, int>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set4'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'Beta<void, void>' to 'Alpha<int, int>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set4'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from 'Beta<void, void>' to 'Alpha<int, int>' for 1st argument
+// CHECK-NOELIDE-TREE: no matching function for call to 'set4'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from 'Beta<void, void>' to 'Alpha<int, int>' for 1st argument
+
+void set5(Alpha<Beta<Gamma<Delta<int, int>, int>, int>, int>);
+void test5() {
+ set5(Alpha<Beta<Gamma<void, void>, double>, double>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set5'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha<Beta<Gamma<void, void>, double>, double>' to 'Alpha<Beta<Gamma<Delta<int, int>, int>, int>, int>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set5'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha<Beta<Gamma<void, void>, double>, double>' to 'Alpha<Beta<Gamma<Delta<int, int>, int>, int>, int>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set5'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: Alpha<
+// CHECK-ELIDE-TREE: Beta<
+// CHECK-ELIDE-TREE: Gamma<
+// CHECK-ELIDE-TREE: [void != Delta<int, int>],
+// CHECK-ELIDE-TREE: [void != int]>
+// CHECK-ELIDE-TREE: [double != int]>
+// CHECK-ELIDE-TREE: [double != int]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set5'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: Alpha<
+// CHECK-NOELIDE-TREE: Beta<
+// CHECK-NOELIDE-TREE: Gamma<
+// CHECK-NOELIDE-TREE: [void != Delta<int, int>],
+// CHECK-NOELIDE-TREE: [void != int]>
+// CHECK-NOELIDE-TREE: [double != int]>
+// CHECK-NOELIDE-TREE: [double != int]>
+
+void test6() {
+ set5(Alpha<Beta<Delta<int, int>, int>, int>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set5'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha<Beta<Delta<int, int>, [...]>, [...]>' to 'Alpha<Beta<Gamma<Delta<int, int>, int>, [...]>, [...]>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set5'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha<Beta<Delta<int, int>, int>, int>' to 'Alpha<Beta<Gamma<Delta<int, int>, int>, int>, int>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set5'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: Alpha<
+// CHECK-ELIDE-TREE: Beta<
+// CHECK-ELIDE-TREE: [Delta<int, int> != Gamma<Delta<int, int>, int>],
+// CHECK-ELIDE-TREE: [...]>
+// CHECK-ELIDE-TREE: [...]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set5'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: Alpha<
+// CHECK-NOELIDE-TREE: Beta<
+// CHECK-NOELIDE-TREE: [Delta<int, int> != Gamma<Delta<int, int>, int>],
+// CHECK-NOELIDE-TREE: int>
+// CHECK-NOELIDE-TREE: int>
+
+int a7, b7;
+int c7[] = {1,2,3};
+template<int *A>
+class class7 {};
+void set7(class7<&a7> A) {}
+void test7() {
+ set7(class7<&a7>());
+ set7(class7<&b7>());
+ set7(class7<c7>());
+ set7(class7<nullptr>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set7'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<&b7>' to 'class7<&a7>' for 1st argument
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set7'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<c7>' to 'class7<&a7>' for 1st argument
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set7'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<nullptr>' to 'class7<&a7>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set7'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<&b7>' to 'class7<&a7>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set7'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<c7>' to 'class7<&a7>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set7'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<nullptr>' to 'class7<&a7>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set7'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class7<
+// CHECK-ELIDE-TREE: [&b7 != &a7]>
+// CHECK-ELIDE-TREE: no matching function for call to 'set7'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class7<
+// CHECK-ELIDE-TREE: [c7 != &a7]>
+// CHECK-ELIDE-TREE: no matching function for call to 'set7'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class7<
+// CHECK-ELIDE-TREE: [nullptr != &a7]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set7'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class7<
+// CHECK-NOELIDE-TREE: [&b7 != &a7]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set7'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class7<
+// CHECK-NOELIDE-TREE: [c7 != &a7]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set7'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class7<
+// CHECK-NOELIDE-TREE: [nullptr != &a7]>
+
+template<typename ...T> struct S8 {};
+template<typename T> using U8 = S8<int, char, T>;
+int f8(S8<int, char, double>);
+int k8 = f8(U8<char>());
+// CHECK-ELIDE-NOTREE: no matching function for call to 'f8'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'S8<[2 * ...], char>' to 'S8<[2 * ...], double>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'f8'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'S8<int, char, char>' to 'S8<int, char, double>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'f8'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: S8<
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: [char != double]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'f8'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: S8<
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: char,
+// CHECK-NOELIDE-TREE: [char != double]>
+
+template<typename ...T> struct S9 {};
+template<typename T> using U9 = S9<int, char, T>;
+template<typename T> using V9 = U9<U9<T>>;
+int f9(S9<int, char, U9<const double>>);
+int k9 = f9(V9<double>());
+
+// CHECK-ELIDE-NOTREE: no matching function for call to 'f9'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'S9<[2 * ...], S9<[2 * ...], double>>' to 'S9<[2 * ...], S9<[2 * ...], const double>>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'f9'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'S9<int, char, S9<int, char, double>>' to 'S9<int, char, S9<int, char, const double>>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'f9'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: S9<
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: S9<
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: [double != const double]>>
+// CHECK-NOELIDE-TREE: no matching function for call to 'f9'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: S9<
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: char,
+// CHECK-NOELIDE-TREE: S9<
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: char,
+// CHECK-NOELIDE-TREE: [double != const double]>>
+
+template<typename ...A> class class_types {};
+void set10(class_types<int, int>) {}
+void test10() {
+ set10(class_types<int>());
+ set10(class_types<int, int, int>());
+}
+
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set10'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types<[...], (no argument)>' to 'class_types<[...], int>' for 1st argument
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set10'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types<[2 * ...], int>' to 'class_types<[2 * ...], (no argument)>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set10'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types<int, (no argument)>' to 'class_types<int, int>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set10'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types<int, int, int>' to 'class_types<int, int, (no argument)>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set10'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_types<
+// CHECK-ELIDE-TREE: [...],
+// CHECK-ELIDE-TREE: [(no argument) != int]>
+// CHECK-ELIDE-TREE: no matching function for call to 'set10'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_types<
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: [int != (no argument)]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set10'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_types<
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: [(no argument) != int]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set10'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_types<
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: int,
+// CHECK-NOELIDE-TREE: [int != (no argument)]>
+
+template<int ...A> class class_ints {};
+void set11(class_ints<2, 3>) {}
+void test11() {
+ set11(class_ints<1>());
+ set11(class_ints<0, 3, 6>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set11'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<1, (no argument)>' to 'class_ints<2, 3>' for 1st argument
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set11'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<0, [...], 6>' to 'class_ints<2, [...], (no argument)>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set11'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<1, (no argument)>' to 'class_ints<2, 3>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set11'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<0, 3, 6>' to 'class_ints<2, 3, (no argument)>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set11'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_ints<
+// CHECK-ELIDE-TREE: [1 != 2],
+// CHECK-ELIDE-TREE: [(no argument) != 3]>
+// CHECK-ELIDE-TREE: no matching function for call to 'set11'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_ints<
+// CHECK-ELIDE-TREE: [0 != 2],
+// CHECK-ELIDE-TREE: [...],
+// CHECK-ELIDE-TREE: [6 != (no argument)]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set11'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_ints<
+// CHECK-NOELIDE-TREE: [1 != 2],
+// CHECK-NOELIDE-TREE: [(no argument) != 3]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set11'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_ints<
+// CHECK-NOELIDE-TREE: [0 != 2],
+// CHECK-NOELIDE-TREE: 3,
+// CHECK-NOELIDE-TREE: [6 != (no argument)]>
+
+template<template<class> class ...A> class class_template_templates {};
+template<class> class tt1 {};
+template<class> class tt2 {};
+void set12(class_template_templates<tt1, tt1>) {}
+void test12() {
+ set12(class_template_templates<tt2>());
+ set12(class_template_templates<tt1, tt1, tt1>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set12'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_template_templates<template tt2, template (no argument)>' to 'class_template_templates<template tt1, template tt1>' for 1st argument
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set12'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_template_templates<[2 * ...], template tt1>' to 'class_template_templates<[2 * ...], template (no argument)>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set12'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_template_templates<template tt2, template (no argument)>' to 'class_template_templates<template tt1, template tt1>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set12'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_template_templates<template tt1, template tt1, template tt1>' to 'class_template_templates<template tt1, template tt1, template (no argument)>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set12'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_template_templates<
+// CHECK-ELIDE-TREE: [template tt2 != template tt1],
+// CHECK-ELIDE-TREE: [template (no argument) != template tt1]>
+// CHECK-ELIDE-TREE: no matching function for call to 'set12'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_template_templates<
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: [template tt1 != template (no argument)]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set12'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_template_templates<
+// CHECK-NOELIDE-TREE: [template tt2 != template tt1],
+// CHECK-NOELIDE-TREE: [template (no argument) != template tt1]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set12'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_template_templates<
+// CHECK-NOELIDE-TREE: template tt1,
+// CHECK-NOELIDE-TREE: template tt1,
+// CHECK-NOELIDE-TREE: [template tt1 != template (no argument)]>
+
+double a13, b13, c13, d13;
+template<double* ...A> class class_ptrs {};
+void set13(class_ptrs<&a13, &b13>) {}
+void test13() {
+ set13(class_ptrs<&c13>());
+ set13(class_ptrss<&a13, &b13, &d13>());
+}
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set13'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ptrs<&c13, (no argument)>' to 'class_ptrs<&a13, &b13>' for 1st argument
+// CHECK-ELIDE-NOTREE: no matching function for call to 'set13'
+// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ptrs<[2 * ...], &d13>' to 'class_ptrs<[2 * ...], (no argument)>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set13'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ptrs<&c13, (no argument)>' to 'class_ptrs<&a13, &b13>' for 1st argument
+// CHECK-NOELIDE-NOTREE: no matching function for call to 'set13'
+// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ptrs<&a13, &b13, &d13>' to 'class_ptrs<&a13, &b13, (no argument)>' for 1st argument
+// CHECK-ELIDE-TREE: no matching function for call to 'set13'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_ptrs<
+// CHECK-ELIDE-TREE: [&c13 != &a13],
+// CHECK-ELIDE-TREE: [(no argument) != &b13]>
+// CHECK-ELIDE-TREE: no matching function for call to 'set13'
+// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-ELIDE-TREE: class_ptrs<
+// CHECK-ELIDE-TREE: [2 * ...],
+// CHECK-ELIDE-TREE: [&d13 != (no argument)]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set13'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_ptrs<
+// CHECK-NOELIDE-TREE: [&c13 != &a13],
+// CHECK-NOELIDE-TREE: [(no argument) != &b13]>
+// CHECK-NOELIDE-TREE: no matching function for call to 'set13'
+// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from argument type to parameter type for 1st argument
+// CHECK-NOELIDE-TREE: class_ptrs<
+// CHECK-NOELIDE-TREE: &a13,
+// CHECK-NOELIDE-TREE: &b13,
+// CHECK-NOELIDE-TREE: [&d13 != (no argument)]>
+
+
+// CHECK-ELIDE-NOTREE: {{[0-9]*}} errors generated.
+// CHECK-NOELIDE-NOTREE: {{[0-9]*}} errors generated.
+// CHECK-ELIDE-TREE: {{[0-9]*}} errors generated.
+// CHECK-NOELIDE-TREE: {{[0-9]*}} errors generated.
diff --git a/test/Misc/diag-trailing-null-bytes.cpp b/test/Misc/diag-trailing-null-bytes.cpp
new file mode 100644
index 0000000..91b159c
--- /dev/null
+++ b/test/Misc/diag-trailing-null-bytes.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only %s 2>&1 | FileCheck -strict-whitespace %s
+// CHECK: {{ERR_DNS_SERVER_REQUIRES_TCP$}}
+
+// http://llvm.org/PR12674
+#define NET_ERROR(label, value) ERR_ ## label = value,
+
+NET_ERROR(DNS_SERVER_REQUIRES_TCP, -801)
+
+#undef NET_ERROR
+
diff --git a/test/Misc/emit-html.c b/test/Misc/emit-html.c
index 48c8b61..ec07a60 100644
--- a/test/Misc/emit-html.c
+++ b/test/Misc/emit-html.c
@@ -16,3 +16,11 @@ int main(int argc, char **argv) {
FOR_ALL_FILES(f) { }
#endif
+// <rdar://problem/11625964>
+// -emit-html filters out # directives, but not _Pragma (or MS __pragma)
+// Diagnostic push/pop is stateful, so re-lexing a file can cause problems
+// if these pragmas are interpreted normally.
+_Pragma("clang diagnostic push")
+_Pragma("clang diagnostic ignored \"-Wformat-extra-args\"")
+_Pragma("clang diagnostic pop")
+
diff --git a/test/Misc/integer-literal-printing.cpp b/test/Misc/integer-literal-printing.cpp
index d751730..4085d60 100644
--- a/test/Misc/integer-literal-printing.cpp
+++ b/test/Misc/integer-literal-printing.cpp
@@ -2,10 +2,10 @@
// PR11179
template <short T> class Type1 {};
-template <short T> void Function1(Type1<T>& x) {} // expected-note{{candidate function [with T = -42] not viable: no known conversion from 'Type1<-42>' to 'Type1<-42> &' for 1st argument;}}
+template <short T> void Function1(Type1<T>& x) {} // expected-note{{candidate function [with T = -42] not viable: expects an l-value for 1st argument}}
template <unsigned short T> class Type2 {};
-template <unsigned short T> void Function2(Type2<T>& x) {} // expected-note{{candidate function [with T = 42] not viable: no known conversion from 'Type2<42>' to 'Type2<42> &' for 1st argument;}}
+template <unsigned short T> void Function2(Type2<T>& x) {} // expected-note{{candidate function [with T = 42] not viable: expects an l-value for 1st argument}}
void Function() {
Function1(Type1<-42>()); // expected-error{{no matching function for call to 'Function1'}}
diff --git a/test/Misc/serialized-diags-frontend.c b/test/Misc/serialized-diags-frontend.c
new file mode 100644
index 0000000..453ed14
--- /dev/null
+++ b/test/Misc/serialized-diags-frontend.c
@@ -0,0 +1,8 @@
+// RUN: rm -f %t
+// RUN: %clang -fsyntax-only %s -Wblahblah --serialize-diagnostics %t > /dev/null 2>&1 || true
+// RUN: c-index-test -read-diagnostics %t 2>&1 | FileCheck %s
+
+// This test case tests that we can handle frontend diagnostics.
+
+// CHECK: warning: unknown warning option '-Wblahblah'
+// CHECK: Number of diagnostics: 1
diff --git a/test/Misc/show-diag-options.c b/test/Misc/show-diag-options.c
index f0404a8..ef0a5a6 100644
--- a/test/Misc/show-diag-options.c
+++ b/test/Misc/show-diag-options.c
@@ -23,5 +23,5 @@ void test(int x, int y) {
// OPTION_ERROR_CATEGORY: {{.*}}: error: {{[a-z ]+}} [-Werror,-Wparentheses,Semantic Issue]
// Leverage the fact that all these '//'s get warned about in C89 pedantic.
- // OPTION_PEDANTIC: {{.*}}: warning: {{[/a-z ]+}} [-pedantic,-Wcomment]
+ // OPTION_PEDANTIC: {{.*}}: warning: {{[/a-z ]+}} [-Wcomment]
}
diff --git a/test/Misc/tabstop.c b/test/Misc/tabstop.c
index 49c4d7b..7f59b6a 100644
--- a/test/Misc/tabstop.c
+++ b/test/Misc/tabstop.c
@@ -35,13 +35,22 @@ void f(void)
{
if (0 & 1 == 1)
{}
+
+ if (1 == 0 & 1)
+ {}
}
// CHECK-3: {{^ }}if (0 & 1 == 1)
// CHECK-3: {{^ }} ( )
+// CHECK-3: {{^ }}if (1 == 0 & 1)
+// CHECK-3: {{^ }} ( )
// CHECK-4: {{^ }}if (0 & 1 == 1)
// CHECK-4: {{^ }} ( )
+// CHECK-4: {{^ }}if (1 == 0 & 1)
+// CHECK-4: {{^ }} ( )
// CHECK-5: {{^ }}if (0 & 1 == 1)
// CHECK-5: {{^ }} ( )
+// CHECK-5: {{^ }}if (1 == 0 & 1)
+// CHECK-5: {{^ }} ( )
diff --git a/test/Misc/warning-flags-enabled.c b/test/Misc/warning-flags-enabled.c
new file mode 100644
index 0000000..7ef5c94
--- /dev/null
+++ b/test/Misc/warning-flags-enabled.c
@@ -0,0 +1,27 @@
+// RUN: diagtool show-enabled %s | FileCheck %s
+//
+// This shows warnings which are on by default.
+// We just check a few to make sure it's doing something sensible.
+//
+// CHECK: ext_unterminated_string
+// CHECK: warn_condition_is_assignment
+// CHECK: warn_null_arg
+
+
+// RUN: diagtool show-enabled -Wno-everything %s | count 0
+
+
+// RUN: diagtool show-enabled -Wno-everything -Wobjc-root-class %s | FileCheck -check-prefix CHECK-WARN %s
+// RUN: diagtool show-enabled -Wno-everything -Werror=objc-root-class %s | FileCheck -check-prefix CHECK-ERROR %s
+// RUN: diagtool show-enabled -Wno-everything -Wfatal-errors=objc-root-class %s | FileCheck -check-prefix CHECK-FATAL %s
+//
+// CHECK-WARN: W warn_objc_root_class_missing [-Wobjc-root-class]
+// CHECK-ERROR: E warn_objc_root_class_missing [-Wobjc-root-class]
+// CHECK-FATAL: F warn_objc_root_class_missing [-Wobjc-root-class]
+
+// RUN: diagtool show-enabled --no-levels -Wno-everything -Wobjc-root-class %s | FileCheck -check-prefix CHECK-NO-LEVELS %s
+//
+// CHECK-NO-LEVELS-NOT: W
+// CHECK-NO-LEVELS-NOT: E
+// CHECK-NO-LEVELS-NOT: F
+// CHECK-NO-LEVELS: warn_objc_root_class_missing [-Wobjc-root-class]
diff --git a/test/Misc/warning-flags-tree.c b/test/Misc/warning-flags-tree.c
new file mode 100644
index 0000000..a64e942
--- /dev/null
+++ b/test/Misc/warning-flags-tree.c
@@ -0,0 +1,56 @@
+// RUN: diagtool tree | FileCheck -strict-whitespace %s
+// RUN: diagtool tree -Weverything | FileCheck -strict-whitespace %s
+// RUN: diagtool tree everything | FileCheck -strict-whitespace %s
+//
+// These three ways of running diagtool tree are the same:
+// they produce a tree for every top-level diagnostic flag.
+// Just check a few to make sure we're actually showing more than one group.
+//
+// CHECK: -W
+// CHECK: -Wextra
+// CHECK: -Wmissing-field-initializers
+// CHECK: warn_missing_field_initializers
+// CHECK: -Wall
+// CHECK: -Wmost
+
+// These flags are currently unimplemented; test that we output them anyway.
+// CHECK: -Wstrict-aliasing
+// CHECK-NEXT: -Wstrict-aliasing=0
+// CHECK-NEXT: -Wstrict-aliasing=1
+// CHECK-NEXT: -Wstrict-aliasing=2
+// CHECK: -Wstrict-overflow
+// CHECK-NEXT: -Wstrict-overflow=0
+// CHECK-NEXT: -Wstrict-overflow=1
+// CHECK-NEXT: -Wstrict-overflow=2
+// CHECK-NEXT: -Wstrict-overflow=3
+// CHECK-NEXT: -Wstrict-overflow=4
+// CHECK-NEXT: -Wstrict-overflow=5
+
+
+// RUN: not diagtool tree -Wthis-is-not-a-valid-flag
+
+
+// RUN: diagtool tree -Wgnu | FileCheck -strict-whitespace -check-prefix CHECK-GNU %s
+// CHECK-GNU: -Wgnu
+// CHECK-GNU: -Wgnu-designator
+// CHECK-GNU: ext_gnu_array_range
+// CHECK-GNU: ext_gnu_missing_equal_designator
+// CHECK-GNU: ext_gnu_old_style_field_designator
+// CHECK-GNU: -Wvla
+// CHECK-GNU: ext_vla
+// CHECK-GNU: ext_array_init_copy
+// CHECK-GNU: ext_empty_struct_union
+// CHECK-GNU: ext_expr_not_ice
+// There are more GNU extensions but we don't need to check them all.
+
+// RUN: diagtool tree --flags-only -Wgnu | FileCheck -check-prefix CHECK-FLAGS-ONLY %s
+// CHECK-FLAGS-ONLY: -Wgnu
+// CHECK-FLAGS-ONLY: -Wgnu-designator
+// CHECK-FLAGS-ONLY-NOT: ext_gnu_array_range
+// CHECK-FLAGS-ONLY-NOT: ext_gnu_missing_equal_designator
+// CHECK-FLAGS-ONLY-NOT: ext_gnu_old_style_field_designator
+// CHECK-FLAGS-ONLY: -Wvla
+// CHECK-FLAGS-ONLY-NOT: ext_vla
+// CHECK-FLAGS-ONLY-NOT: ext_array_init_copy
+// CHECK-FLAGS-ONLY-NOT: ext_empty_struct_union
+// CHECK-FLAGS-ONLY-NOT: ext_expr_not_ice
diff --git a/test/Misc/warning-flags.c b/test/Misc/warning-flags.c
index bc0c941..06c70eb 100644
--- a/test/Misc/warning-flags.c
+++ b/test/Misc/warning-flags.c
@@ -1,4 +1,5 @@
-RUN: diagtool list-warnings 2>&1 | FileCheck %s
+RUN: diagtool list-warnings > %t 2>&1
+RUN: FileCheck --input-file=%t %s
This test serves two purposes:
@@ -17,73 +18,27 @@ This test serves two purposes:
The list of warnings below should NEVER grow. It should gradually shrink to 0.
-CHECK: Warnings without flags (253):
-CHECK-NEXT: ext_anonymous_struct_union_qualified
-CHECK-NEXT: ext_binary_literal
-CHECK-NEXT: ext_cast_fn_obj
+CHECK: Warnings without flags (159):
CHECK-NEXT: ext_delete_void_ptr_operand
-CHECK-NEXT: ext_designated_init
-CHECK-NEXT: ext_duplicate_declspec
-CHECK-NEXT: ext_ellipsis_exception_spec
-CHECK-NEXT: ext_empty_fnmacro_arg
-CHECK-NEXT: ext_empty_source_file
CHECK-NEXT: ext_enum_friend
-CHECK-NEXT: ext_enum_value_not_int
-CHECK-NEXT: ext_enumerator_list_comma
CHECK-NEXT: ext_expected_semi_decl_list
-CHECK-NEXT: ext_explicit_instantiation_without_qualified_id
CHECK-NEXT: ext_explicit_specialization_storage_class
-CHECK-NEXT: ext_extra_ivar_semi
-CHECK-NEXT: ext_extra_struct_semi
-CHECK-NEXT: ext_forward_ref_enum
-CHECK-NEXT: ext_freestanding_complex
-CHECK-NEXT: ext_hexconstant_invalid
-CHECK-NEXT: ext_ident_list_in_param
-CHECK-NEXT: ext_imaginary_constant
CHECK-NEXT: ext_implicit_lib_function_decl
-CHECK-NEXT: ext_in_class_initializer_non_constant
-CHECK-NEXT: ext_integer_complement_complex
-CHECK-NEXT: ext_integer_complex
-CHECK-NEXT: ext_integer_increment_complex
-CHECK-NEXT: ext_invalid_sign_spec
CHECK-NEXT: ext_missing_declspec
-CHECK-NEXT: ext_missing_varargs_arg
CHECK-NEXT: ext_missing_whitespace_after_macro_name
CHECK-NEXT: ext_new_paren_array_nonconst
-CHECK-NEXT: ext_nonstandard_escape
-CHECK-NEXT: ext_param_not_declared
-CHECK-NEXT: ext_paste_comma
CHECK-NEXT: ext_plain_complex
-CHECK-NEXT: ext_pp_bad_vaargs_use
-CHECK-NEXT: ext_pp_comma_expr
-CHECK-NEXT: ext_pp_ident_directive
-CHECK-NEXT: ext_pp_include_next_directive
-CHECK-NEXT: ext_pp_line_too_big
CHECK-NEXT: ext_pp_macro_redef
-CHECK-NEXT: ext_pp_warning_directive
-CHECK-NEXT: ext_return_has_void_expr
-CHECK-NEXT: ext_subscript_non_lvalue
CHECK-NEXT: ext_template_arg_extra_parens
-CHECK-NEXT: ext_thread_before
-CHECK-NEXT: ext_top_level_semi
-CHECK-NEXT: ext_typecheck_addrof_void
-CHECK-NEXT: ext_typecheck_cast_nonscalar
-CHECK-NEXT: ext_typecheck_cast_to_union
CHECK-NEXT: ext_typecheck_comparison_of_distinct_pointers
CHECK-NEXT: ext_typecheck_comparison_of_distinct_pointers_nonstandard
-CHECK-NEXT: ext_typecheck_comparison_of_fptr_to_void
CHECK-NEXT: ext_typecheck_comparison_of_pointer_integer
CHECK-NEXT: ext_typecheck_cond_incompatible_operands
CHECK-NEXT: ext_typecheck_cond_incompatible_operands_nonstandard
-CHECK-NEXT: ext_typecheck_cond_one_void
-CHECK-NEXT: ext_typecheck_convert_pointer_void_func
CHECK-NEXT: ext_typecheck_ordered_comparison_of_function_pointers
-CHECK-NEXT: ext_typecheck_ordered_comparison_of_pointer_and_zero
CHECK-NEXT: ext_typecheck_ordered_comparison_of_pointer_integer
-CHECK-NEXT: ext_typecheck_zero_array_size
CHECK-NEXT: ext_unknown_escape
CHECK-NEXT: ext_using_undefined_std
-CHECK-NEXT: ext_vla_folded_to_constant
CHECK-NEXT: pp_include_next_absolute_path
CHECK-NEXT: pp_include_next_in_primary
CHECK-NEXT: pp_invalid_string_literal
@@ -96,26 +51,6 @@ CHECK-NEXT: w_asm_qualifier_ignored
CHECK-NEXT: warn_accessor_property_type_mismatch
CHECK-NEXT: warn_anon_bitfield_width_exceeds_type_size
CHECK-NEXT: warn_asm_label_on_auto_decl
-CHECK-NEXT: warn_attribute_ibaction
-CHECK-NEXT: warn_attribute_iboutlet
-CHECK-NEXT: warn_attribute_ignored
-CHECK-NEXT: warn_attribute_ignored_for_field_of_type
-CHECK-NEXT: warn_attribute_malloc_pointer_only
-CHECK-NEXT: warn_attribute_nonnull_no_pointers
-CHECK-NEXT: warn_attribute_precede_definition
-CHECK-NEXT: warn_attribute_sentinel_named_arguments
-CHECK-NEXT: warn_attribute_sentinel_not_variadic
-CHECK-NEXT: warn_attribute_type_not_supported
-CHECK-NEXT: warn_attribute_unknown_visibility
-CHECK-NEXT: warn_attribute_void_function_method
-CHECK-NEXT: warn_attribute_weak_import_invalid_on_definition
-CHECK-NEXT: warn_attribute_weak_on_field
-CHECK-NEXT: warn_attribute_weak_on_local
-CHECK-NEXT: warn_attribute_wrong_decl_type
-CHECK-NEXT: warn_availability_and_unavailable
-CHECK-NEXT: warn_availability_unknown_platform
-CHECK-NEXT: warn_availability_version_ordering
-CHECK-NEXT: warn_bad_receiver_type
CHECK-NEXT: warn_bitfield_width_exceeds_type_size
CHECK-NEXT: warn_bool_switch_condition
CHECK-NEXT: warn_braces_around_scalar_init
@@ -124,7 +59,6 @@ CHECK-NEXT: warn_call_to_pure_virtual_member_function_from_ctor_dtor
CHECK-NEXT: warn_call_wrong_number_of_arguments
CHECK-NEXT: warn_case_empty_range
CHECK-NEXT: warn_char_constant_too_large
-CHECK-NEXT: warn_class_method_not_found
CHECK-NEXT: warn_cmdline_missing_macro_defs
CHECK-NEXT: warn_collection_expr_type
CHECK-NEXT: warn_conflicting_param_types
@@ -133,19 +67,15 @@ CHECK-NEXT: warn_conflicting_variadic
CHECK-NEXT: warn_conv_to_base_not_used
CHECK-NEXT: warn_conv_to_self_not_used
CHECK-NEXT: warn_conv_to_void_not_used
-CHECK-NEXT: warn_cxx0x_right_shift_in_template_arg
CHECK-NEXT: warn_delete_array_type
-CHECK-NEXT: warn_division_by_zero
CHECK-NEXT: warn_double_const_requires_fp64
CHECK-NEXT: warn_drv_assuming_mfloat_abi_is
CHECK-NEXT: warn_drv_clang_unsupported
-CHECK-NEXT: warn_drv_input_file_unused
CHECK-NEXT: warn_drv_not_using_clang_arch
CHECK-NEXT: warn_drv_not_using_clang_cpp
CHECK-NEXT: warn_drv_not_using_clang_cxx
CHECK-NEXT: warn_drv_objc_gc_unsupported
CHECK-NEXT: warn_drv_pch_not_first_include
-CHECK-NEXT: warn_drv_preprocessed_input_file_unused
CHECK-NEXT: warn_dup_category_def
CHECK-NEXT: warn_duplicate_protocol_def
CHECK-NEXT: warn_enum_too_large
@@ -155,24 +85,17 @@ CHECK-NEXT: warn_exception_caught_by_earlier_handler
CHECK-NEXT: warn_excess_initializers
CHECK-NEXT: warn_excess_initializers_in_char_array_initializer
CHECK-NEXT: warn_expected_qualified_after_typename
-CHECK-NEXT: warn_extern_init
CHECK-NEXT: warn_extraneous_char_constant
CHECK-NEXT: warn_fe_cc_log_diagnostics_failure
CHECK-NEXT: warn_fe_cc_print_header_failure
CHECK-NEXT: warn_fe_macro_contains_embedded_newline
CHECK-NEXT: warn_file_asm_volatile
-CHECK-NEXT: warn_function_attribute_wrong_type
-CHECK-NEXT: warn_gc_attribute_weak_on_local
-CHECK-NEXT: warn_gnu_inline_attribute_requires_inline
CHECK-NEXT: warn_hex_escape_too_large
CHECK-NEXT: warn_ignoring_ftabstop_value
-CHECK-NEXT: warn_illegal_constant_array_size
CHECK-NEXT: warn_implements_nscopying
CHECK-NEXT: warn_incompatible_qualified_id
CHECK-NEXT: warn_initializer_string_for_char_array_too_long
CHECK-NEXT: warn_inline_namespace_reopened_noninline
-CHECK-NEXT: warn_inst_method_not_found
-CHECK-NEXT: warn_instance_method_on_class_found
CHECK-NEXT: warn_integer_too_large
CHECK-NEXT: warn_integer_too_large_for_signed
CHECK-NEXT: warn_invalid_asm_cast_lvalue
@@ -189,19 +112,13 @@ CHECK-NEXT: warn_multiple_method_decl
CHECK-NEXT: warn_no_constructor_for_refconst
CHECK-NEXT: warn_nonnull_pointers_only
CHECK-NEXT: warn_not_compound_assign
-CHECK-NEXT: warn_ns_attribute_wrong_parameter_type
-CHECK-NEXT: warn_ns_attribute_wrong_return_type
-CHECK-NEXT: warn_objc_object_attribute_wrong_type
CHECK-NEXT: warn_objc_property_copy_missing_on_block
-CHECK-NEXT: warn_objc_property_default_assign_on_object
-CHECK-NEXT: warn_objc_property_no_assignment_attribute
CHECK-NEXT: warn_objc_protocol_qualifier_missing_id
CHECK-NEXT: warn_octal_escape_too_large
CHECK-NEXT: warn_odr_tag_type_inconsistent
CHECK-NEXT: warn_on_superclass_use
CHECK-NEXT: warn_param_default_argument_redefinition
CHECK-NEXT: warn_partial_specs_not_deducible
-CHECK-NEXT: warn_pointer_attribute_wrong_type
CHECK-NEXT: warn_pp_convert_lhs_to_positive
CHECK-NEXT: warn_pp_convert_rhs_to_positive
CHECK-NEXT: warn_pp_expr_overflow
@@ -238,14 +155,11 @@ CHECK-NEXT: warn_property_attribute
CHECK-NEXT: warn_property_getter_owning_mismatch
CHECK-NEXT: warn_property_types_are_incompatible
CHECK-NEXT: warn_readonly_property
-CHECK-NEXT: warn_receiver_forward_class
CHECK-NEXT: warn_redecl_library_builtin
CHECK-NEXT: warn_redeclaration_without_attribute_prev_attribute_ignored
CHECK-NEXT: warn_register_objc_catch_parm
CHECK-NEXT: warn_related_result_type_compatibility_class
CHECK-NEXT: warn_related_result_type_compatibility_protocol
-CHECK-NEXT: warn_remainder_by_zero
-CHECK-NEXT: warn_root_inst_method_not_found
CHECK-NEXT: warn_second_parameter_of_va_start_not_last_named_argument
CHECK-NEXT: warn_second_parameter_to_va_arg_never_compatible
CHECK-NEXT: warn_standalone_specifier
@@ -254,20 +168,17 @@ CHECK-NEXT: warn_static_non_static
CHECK-NEXT: warn_template_export_unsupported
CHECK-NEXT: warn_template_spec_extra_headers
CHECK-NEXT: warn_tentative_incomplete_array
-CHECK-NEXT: warn_transparent_union_attribute_field_size_align
-CHECK-NEXT: warn_transparent_union_attribute_floating
-CHECK-NEXT: warn_transparent_union_attribute_not_definition
-CHECK-NEXT: warn_transparent_union_attribute_zero_fields
CHECK-NEXT: warn_typecheck_function_qualifiers
CHECK-NEXT: warn_unavailable_fwdclass_message
CHECK-NEXT: warn_undef_interface
CHECK-NEXT: warn_undef_interface_suggest
CHECK-NEXT: warn_undef_protocolref
CHECK-NEXT: warn_undefined_internal
-CHECK-NEXT: warn_unknown_analyzer_checker
CHECK-NEXT: warn_unknown_method_family
-CHECK-NEXT: warn_unterminated_char
-CHECK-NEXT: warn_unterminated_string
CHECK-NEXT: warn_use_out_of_scope_declaration
CHECK-NEXT: warn_weak_identifier_undeclared
CHECK-NEXT: warn_weak_import
+
+The list of warnings in -Wpedantic should NEVER grow.
+
+CHECK: Number in -Wpedantic (not covered by other -W flags): 39
diff --git a/test/Misc/wnull-character.cpp b/test/Misc/wnull-character.cpp
deleted file mode 100644
index a618da2..0000000
--- a/test/Misc/wnull-character.cpp
+++ /dev/null
Binary files differ
diff --git a/test/Modules/Inputs/Module.framework/Headers/Module.h b/test/Modules/Inputs/Module.framework/Headers/Module.h
index 738b222..f894984 100644
--- a/test/Modules/Inputs/Module.framework/Headers/Module.h
+++ b/test/Modules/Inputs/Module.framework/Headers/Module.h
@@ -1,4 +1,8 @@
-// expected-warning{{umbrella header}}
+// expected-warning 0-1 {{umbrella header}}
+
+// FIXME: The "umbrella header" warning should be moved to a separate test.
+// This "0-1" is only here because the warning is only emitted when the
+// module is (otherwise) successfully included.
#ifndef MODULE_H
#define MODULE_H
diff --git a/test/Modules/Inputs/category_right.h b/test/Modules/Inputs/category_right.h
index d993b50..812a840 100644
--- a/test/Modules/Inputs/category_right.h
+++ b/test/Modules/Inputs/category_right.h
@@ -8,5 +8,5 @@
-(void)right2;
@end
-@interface Foo(Duplicate) // expected-warning {{duplicate definition of category}}
+@interface Foo(Duplicate)
@end
diff --git a/test/Modules/Inputs/import-decl.h b/test/Modules/Inputs/import-decl.h
new file mode 100644
index 0000000..9741d6c
--- /dev/null
+++ b/test/Modules/Inputs/import-decl.h
@@ -0,0 +1 @@
+struct T {};
diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map
index e8d1f2c..79056cb 100644
--- a/test/Modules/Inputs/module.map
+++ b/test/Modules/Inputs/module.map
@@ -84,3 +84,6 @@ module MethodPoolA {
module MethodPoolB {
header "MethodPoolB.h"
}
+module import_decl {
+ header "import-decl.h"
+}
diff --git a/test/Modules/cstd.m b/test/Modules/cstd.m
index 1752cd3..e262c7e 100644
--- a/test/Modules/cstd.m
+++ b/test/Modules/cstd.m
@@ -13,7 +13,7 @@ void test_fprintf(FILE *file) {
fprintf(file, "Hello, modules\n");
}
-// Supplied by compiler, which forwards to the the "/usr/include" version.
+// Supplied by compiler, which forwards to the "/usr/include" version.
@__experimental_modules_import cstd.stdint;
my_awesome_nonstandard_integer_type value;
diff --git a/test/Modules/import-decl.cpp b/test/Modules/import-decl.cpp
new file mode 100644
index 0000000..7696693
--- /dev/null
+++ b/test/Modules/import-decl.cpp
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t
+// RUN: %clang -fmodule-cache-path %t -fmodules -x objective-c -I %S/Inputs -emit-ast -o %t.ast %s
+// RUN: %clang -cc1 -ast-print -x ast - < %t.ast | FileCheck %s
+
+@__experimental_modules_import import_decl;
+// CHECK: struct T
+
+int main() {
+ return 0;
+}
diff --git a/test/Modules/lookup.cpp b/test/Modules/lookup.cpp
index 9839035..70d2107 100644
--- a/test/Modules/lookup.cpp
+++ b/test/Modules/lookup.cpp
@@ -5,6 +5,8 @@ import lookup_left_cxx;
#define IMPORT(X) @__experimental_modules_import X
IMPORT(lookup_right_cxx);
+// in lookup_left.hpp: expected-warning@3 {{weak identifier 'weak_identifier' never declared}}
+
void test(int i, float f) {
// unqualified lookup
f0(&i);
diff --git a/test/Modules/objc-categories.m b/test/Modules/objc-categories.m
index 340f279..b267592 100644
--- a/test/Modules/objc-categories.m
+++ b/test/Modules/objc-categories.m
@@ -12,6 +12,7 @@
// in category_left.h: expected-note {{previous definition}}
+// in category_right.h: expected-warning@11 {{duplicate definition of category}}
@interface Foo(Source)
-(void)source;
diff --git a/test/Modules/on-demand-build.m b/test/Modules/on-demand-build.m
index cf1ae99..4ee6b58 100644
--- a/test/Modules/on-demand-build.m
+++ b/test/Modules/on-demand-build.m
@@ -7,10 +7,7 @@
@interface OtherClass
@end
-
-
-
-// in module: expected-note{{class method 'alloc' is assumed to return an instance of its receiver type ('Module *')}}
+// in module: expected-note@17{{class method 'alloc' is assumed to return an instance of its receiver type ('Module *')}}
void test_getModuleVersion() {
const char *version = getModuleVersion();
const char *version2 = [Module version];
diff --git a/test/PCH/Inputs/__va_list_tag.h b/test/PCH/Inputs/__va_list_tag.h
new file mode 100644
index 0000000..e326603
--- /dev/null
+++ b/test/PCH/Inputs/__va_list_tag.h
@@ -0,0 +1,5 @@
+// Header for PCH test __va_list_tag.h
+
+typedef __builtin_va_list va_list;
+
+extern int myvfprintf(const char * , va_list);
diff --git a/test/PCH/__va_list_tag.c b/test/PCH/__va_list_tag.c
new file mode 100644
index 0000000..23c54ea
--- /dev/null
+++ b/test/PCH/__va_list_tag.c
@@ -0,0 +1,12 @@
+// PR13189
+// rdar://problem/11741429
+// Test this without pch.
+// RUN: %clang_cc1 -triple=x86_64-unknown-freebsd7.0 -include %S/Inputs/__va_list_tag.h %s -emit-llvm -o -
+
+// Test with pch.
+// RUN: %clang_cc1 -triple=x86_64-unknown-freebsd7.0 -emit-pch -x c-header -o %t %S/Inputs/__va_list_tag.h
+// RUN: %clang_cc1 -triple=x86_64-unknown-freebsd7.0 -include-pch %t %s -verify
+
+int myvprintf(const char *fmt, va_list args) {
+ return myvfprintf(fmt, args);
+}
diff --git a/test/PCH/attrs.c b/test/PCH/attrs.c
index 2f868ac..6a4b8f6 100644
--- a/test/PCH/attrs.c
+++ b/test/PCH/attrs.c
@@ -8,10 +8,11 @@
#ifndef HEADER
#define HEADER
-int f(int) __attribute__((visibility("default"), overloadable)); // expected-note{{previous overload}}
+int f(int) __attribute__((visibility("default"), overloadable));
#else
double f(double); // expected-error{{overloadable}}
+ // expected-note@11{{previous overload}}
#endif
diff --git a/test/PCH/badpch-dir.h.gch/.keep b/test/PCH/badpch-dir.h.gch/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/PCH/badpch-dir.h.gch/.keep
diff --git a/test/PCH/badpch-empty.h.gch b/test/PCH/badpch-empty.h.gch
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/PCH/badpch-empty.h.gch
diff --git a/test/PCH/badpch.c b/test/PCH/badpch.c
new file mode 100644
index 0000000..e687ef3
--- /dev/null
+++ b/test/PCH/badpch.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -include-pch %S/badpch-empty.h.gch %s 2>&1 | FileCheck -check-prefix=CHECK-EMPTY %s
+// RUN: %clang_cc1 -fsyntax-only -include-pch %S/badpch-dir.h.gch %s 2>&1 | FileCheck -check-prefix=CHECK-DIR %s
+
+// The purpose of this test is to verify that various invalid PCH files are
+// reported as such.
+
+// PR4568: The messages were much improved since the bug was filed on
+// 2009-07-16, though in the case of the PCH being a directory, the error
+// message still did not contain the name of the PCH. Also, r149918 which was
+// submitted on 2012-02-06 introduced a segfault in the case where the PCH is
+// an empty file and clang was built with assertions.
+// CHECK-EMPTY: error: input is not a PCH file: '{{.*[/\\]}}badpch-empty.h.gch'
+// CHECK-DIR: error: unable to read PCH file {{.*[/\\]}}badpch-dir.h.gch:
diff --git a/test/PCH/chain-staticvar-instantiation.cpp b/test/PCH/chain-staticvar-instantiation.cpp
new file mode 100644
index 0000000..850b8d1
--- /dev/null
+++ b/test/PCH/chain-staticvar-instantiation.cpp
@@ -0,0 +1,45 @@
+// Without PCH
+// RUN: %clang_cc1 -fsyntax-only -verify -include %s -include %s %s
+
+// With PCH
+// RUN: %clang_cc1 -fsyntax-only -verify %s -chain-include %s -chain-include %s
+
+#ifndef HEADER1
+#define HEADER1
+//===----------------------------------------------------------------------===//
+
+namespace NS {
+
+template <class _Tp, _Tp __v>
+struct TS
+{
+ static const _Tp value = __v;
+};
+
+template <class _Tp, _Tp __v>
+const _Tp TS<_Tp, __v>::value;
+
+TS<int, 2> g1;
+
+}
+
+//===----------------------------------------------------------------------===//
+#elif not defined(HEADER2)
+#define HEADER2
+#if !defined(HEADER1)
+#error Header inclusion order messed up
+#endif
+
+int g2 = NS::TS<int, 2>::value;
+
+//===----------------------------------------------------------------------===//
+#else
+//===----------------------------------------------------------------------===//
+
+// expected-warning@+1 {{reached main file}}
+#warning reached main file
+
+int g3 = NS::TS<int, 2>::value;
+
+//===----------------------------------------------------------------------===//
+#endif
diff --git a/test/PCH/chain-trivial.c b/test/PCH/chain-trivial.c
index 85b1eab..a0f5827 100644
--- a/test/PCH/chain-trivial.c
+++ b/test/PCH/chain-trivial.c
@@ -1,4 +1,2 @@
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-pch -o %t1 %S/Inputs/chain-trivial1.h
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-pch -o %t2 -include-pch %t1 %S/Inputs/chain-trivial2.h
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ast-print -include-pch %t2 %s | FileCheck %s
-// CHECK: struct __va_list_tag {
diff --git a/test/PCH/cxx-friends.cpp b/test/PCH/cxx-friends.cpp
index a8d7558..bdba42b 100644
--- a/test/PCH/cxx-friends.cpp
+++ b/test/PCH/cxx-friends.cpp
@@ -11,3 +11,11 @@ class F {
a->x = 0;
}
};
+
+template<typename T> class PR12585::future_base::setter {
+public:
+ int f() {
+ return promise<T*>().k;
+ }
+};
+int k = PR12585::future_base::setter<int>().f();
diff --git a/test/PCH/cxx-friends.h b/test/PCH/cxx-friends.h
index 2a33f15..05dcc96 100644
--- a/test/PCH/cxx-friends.h
+++ b/test/PCH/cxx-friends.h
@@ -4,3 +4,15 @@ class A {
int x;
friend class F;
};
+
+namespace PR12585 {
+ struct future_base {
+ template<typename> class setter;
+ };
+ template<typename> class promise {
+ // We used to inject this into future_base with no access specifier,
+ // then crash during AST writing.
+ template<typename> friend class future_base::setter;
+ int k;
+ };
+}
diff --git a/test/PCH/cxx-member-init.cpp b/test/PCH/cxx-member-init.cpp
index 2820665..20594d5 100644
--- a/test/PCH/cxx-member-init.cpp
+++ b/test/PCH/cxx-member-init.cpp
@@ -12,10 +12,14 @@ struct S {
int &m = n;
S *that = this;
};
+template<typename T> struct X { T t {0}; };
#endif
#ifdef SOURCE
S s;
+
+struct E { explicit E(int); };
+X<E> x;
#elif HEADER
#undef HEADER
#define SOURCE
diff --git a/test/PCH/cxx-static_assert.cpp b/test/PCH/cxx-static_assert.cpp
index ace12e0..8049525 100644
--- a/test/PCH/cxx-static_assert.cpp
+++ b/test/PCH/cxx-static_assert.cpp
@@ -9,11 +9,12 @@
#define HEADER
template<int N> struct T {
- static_assert(N == 2, "N is not 2!"); // expected-error {{static_assert failed "N is not 2!"}}
+ static_assert(N == 2, "N is not 2!");
};
#else
+// expected-error@12 {{static_assert failed "N is not 2!"}}
T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}}
T<2> t2;
diff --git a/test/PCH/cxx-trailing-return.cpp b/test/PCH/cxx-trailing-return.cpp
index ff85f6d..aa38790 100644
--- a/test/PCH/cxx-trailing-return.cpp
+++ b/test/PCH/cxx-trailing-return.cpp
@@ -4,12 +4,14 @@
#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED
-typedef auto f() -> int; // expected-note {{here}}
-typedef int g(); // expected-note {{here}}
+typedef auto f() -> int;
+typedef int g();
#else
typedef void f; // expected-error {{typedef redefinition with different types ('void' vs 'auto () -> int')}}
+ // expected-note@7 {{here}}
typedef void g; // expected-error {{typedef redefinition with different types ('void' vs 'int ()')}}
+ // expected-note@8 {{here}}
#endif
diff --git a/test/PCH/cxx0x-default-delete.cpp b/test/PCH/cxx0x-default-delete.cpp
index 6eb65d6..39a90b8 100644
--- a/test/PCH/cxx0x-default-delete.cpp
+++ b/test/PCH/cxx0x-default-delete.cpp
@@ -9,15 +9,15 @@
struct foo {
foo() = default;
- void bar() = delete; // expected-note{{deleted here}}
+ void bar() = delete;
};
struct baz {
- ~baz() = delete; // expected-note{{deleted here}}
+ ~baz() = delete;
};
class quux {
- ~quux() = default; // expected-note{{private here}}
+ ~quux() = default;
};
#else
@@ -25,10 +25,10 @@ class quux {
foo::foo() { } // expected-error{{definition of explicitly defaulted default constructor}}
foo f;
void fn() {
- f.bar(); // expected-error{{deleted function}}
+ f.bar(); // expected-error{{deleted function}} expected-note@12{{deleted here}}
}
-baz bz; // expected-error{{deleted function}}
-quux qx; // expected-error{{private destructor}}
+baz bz; // expected-error{{deleted function}} expected-note@16{{deleted here}}
+quux qx; // expected-error{{private destructor}} expected-note@20{{private here}}
#endif
diff --git a/test/PCH/cxx0x-delegating-ctors.cpp b/test/PCH/cxx0x-delegating-ctors.cpp
index f2b7e90..bf5daef 100644
--- a/test/PCH/cxx0x-delegating-ctors.cpp
+++ b/test/PCH/cxx0x-delegating-ctors.cpp
@@ -8,13 +8,17 @@
#ifndef PASS1
#define PASS1
struct foo {
- foo(int) : foo() { } // expected-note{{it delegates to}}
+ foo(int) : foo() { }
foo();
- foo(bool) : foo('c') { } // expected-note{{it delegates to}}
- foo(char) : foo(true) { } // expected-error{{creates a delegation cycle}} \
- // expected-note{{which delegates to}}
+ foo(bool) : foo('c') { }
+ foo(char) : foo(true) { }
};
#else
foo::foo() : foo(1) { } // expected-error{{creates a delegation cycle}} \
// expected-note{{which delegates to}}
+
+// expected-note@11{{it delegates to}}
+// expected-note@13{{it delegates to}}
+// expected-error@14{{creates a delegation cycle}}
+// expected-note@14{{which delegates to}}
#endif
diff --git a/test/PCH/cxx11-constexpr.cpp b/test/PCH/cxx11-constexpr.cpp
index 338543e..ce43206 100644
--- a/test/PCH/cxx11-constexpr.cpp
+++ b/test/PCH/cxx11-constexpr.cpp
@@ -6,11 +6,11 @@
#define HEADER_INCLUDED
struct B {
- B(); // expected-note {{here}}
+ B();
constexpr B(char) {}
};
-struct C { // expected-note {{not an aggregate and has no constexpr constructors}}
+struct C {
B b;
double d = 0.0;
};
@@ -24,6 +24,8 @@ struct D : B {
static_assert(D(4).k == 9, "");
constexpr int f(C c) { return 0; } // expected-error {{not a literal type}}
+// expected-note@13 {{not an aggregate and has no constexpr constructors}}
constexpr B b; // expected-error {{constant expression}} expected-note {{non-constexpr}}
+ // expected-note@9 {{here}}
#endif
diff --git a/test/PCH/cxx11-enum-template.cpp b/test/PCH/cxx11-enum-template.cpp
index 70b0ff9..ca70601 100644
--- a/test/PCH/cxx11-enum-template.cpp
+++ b/test/PCH/cxx11-enum-template.cpp
@@ -7,7 +7,7 @@
template<typename T> struct S {
enum class E {
- e = T() // expected-error {{conversion from 'double' to 'int'}}
+ e = T()
};
};
@@ -20,7 +20,7 @@ template struct S<char>;
int k1 = (int)S<int>::E::e;
int k2 = (int)decltype(b)::e;
-int k3 = (int)decltype(c)::e; // expected-note {{here}}
+int k3 = (int)decltype(c)::e; // expected-error@10 {{conversion from 'double' to 'int'}} expected-note {{here}}
int k4 = (int)S<char>::E::e;
#endif
diff --git a/test/PCH/cxx11-user-defined-literals.cpp b/test/PCH/cxx11-user-defined-literals.cpp
index 4a7c24b..7ad17f5 100644
--- a/test/PCH/cxx11-user-defined-literals.cpp
+++ b/test/PCH/cxx11-user-defined-literals.cpp
@@ -8,7 +8,7 @@
using size_t = decltype(sizeof(int));
int operator"" _foo(const char *p, size_t);
-template<typename T> auto f(T t) -> decltype(t + ""_foo) { return 0; } // expected-note {{substitution failure}}
+template<typename T> auto f(T t) -> decltype(t + ""_foo) { return 0; }
#else
@@ -17,5 +17,6 @@ int k = f(0);
int *l = f(&k);
struct S {};
int m = f(S()); // expected-error {{no matching}}
+ // expected-note@11 {{substitution failure}}
#endif
diff --git a/test/PCH/empty-with-headers.c b/test/PCH/empty-with-headers.c
new file mode 100644
index 0000000..751be1c
--- /dev/null
+++ b/test/PCH/empty-with-headers.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors %s
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors -include-pch %t %s
+
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic-errors -DINCLUDED %s -verify
+// This last one should warn for -Wempty-translation-unit (C99 6.9p1).
+
+#if defined(INCLUDED)
+
+// empty except for the prefix header
+
+#elif defined(HEADER)
+
+typedef int my_int;
+#define INCLUDED
+
+#else
+
+#define HEADER
+#include "empty-with-headers.c"
+// empty except for the header
+
+#endif
+
+// This should only fire if the header is not included,
+// either explicitly or as a prefix header.
+// expected-error{{ISO C requires a translation unit to contain at least one declaration.}}
diff --git a/test/PCH/exprs.h b/test/PCH/exprs.h
index 09a5013..d08b1f6 100644
--- a/test/PCH/exprs.h
+++ b/test/PCH/exprs.h
@@ -87,7 +87,7 @@ struct {
int x;
float y;
} designated_inits[3] = { [0].y = 17,
- [2].x = 12.3, // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ [2].x = 12.3, // expected-warning {{implicit conversion from 'double' to 'int' changes value from 12.3 to 12}}
3.5 };
// TypesCompatibleExpr
diff --git a/test/PCH/format-strings.c b/test/PCH/format-strings.c
new file mode 100644
index 0000000..7198c4d
--- /dev/null
+++ b/test/PCH/format-strings.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -D FOOBAR="\"\"" %s -emit-pch -o %t.pch
+// RUN: %clang_cc1 -D FOOBAR="\"\"" %s -include-pch %t.pch
+
+// rdar://11418366
+
+#ifndef HEADER
+#define HEADER
+
+extern int printf(const char *restrict, ...);
+#define LOG printf(FOOBAR "%f", __LINE__)
+
+#else
+
+void foo() {
+ LOG;
+}
+
+#endif
diff --git a/test/PCH/ms-if-exists.cpp b/test/PCH/ms-if-exists.cpp
index 4bea198..be29ac6 100644
--- a/test/PCH/ms-if-exists.cpp
+++ b/test/PCH/ms-if-exists.cpp
@@ -11,7 +11,7 @@ void f(T t) {
}
__if_not_exists(T::bar) {
- int *i = t; // expected-error{{no viable conversion from 'HasFoo' to 'int *'}}
+ int *i = t;
{ }
}
}
@@ -25,5 +25,6 @@ struct HasBar {
};
template void f(HasFoo); // expected-note{{in instantiation of function template specialization 'f<HasFoo>' requested here}}
+ // expected-error@14{{no viable conversion from 'HasFoo' to 'int *'}}
template void f(HasBar);
#endif
diff --git a/test/PCH/objc_methods.h b/test/PCH/objc_methods.h
index c9b1ad4..6dfa248 100644
--- a/test/PCH/objc_methods.h
+++ b/test/PCH/objc_methods.h
@@ -9,3 +9,33 @@
// FIXME: @compatibility_alias AliasForTestPCH TestPCH;
+// http://llvm.org/PR12689
+@interface PR12689
+@end
+
+@implementation PR12689
+-(void)mugi:(int)x {
+ switch(x) {
+ case 23: {}
+ }
+}
+-(void)bonk:(int)x {
+ switch(x) {
+ case 42: {}
+ }
+}
+@end
+
+@interface PR12689_2
+@end
+
+@implementation PR12689_2
+-(void)mugi:(int)x {
+ switch(x) {
+ case 23: [self bonk:x]; break;
+ case 82: break;
+ }
+}
+-(void)bonk:(int)x {
+}
+@end
diff --git a/test/PCH/objc_methods.m b/test/PCH/objc_methods.m
index e90a463..e8aab84 100644
--- a/test/PCH/objc_methods.m
+++ b/test/PCH/objc_methods.m
@@ -1,5 +1,5 @@
// Test this without pch.
-// RUN: %clang_cc1 -include %S/objc_methods.h -fsyntax-only -verify %s
+// RUN: %clang_cc1 -include %S/objc_methods.h -fsyntax-only -Wno-objc-root-class -verify %s
// Test with pch.
// RUN: %clang_cc1 -x objective-c -emit-pch -o %t %S/objc_methods.h
diff --git a/test/PCH/objc_stmts.m b/test/PCH/objc_stmts.m
index 3bc728c..b9b10c5 100644
--- a/test/PCH/objc_stmts.m
+++ b/test/PCH/objc_stmts.m
@@ -1,11 +1,11 @@
// Test this without pch.
// RUN: %clang_cc1 -include %S/objc_stmts.h -emit-llvm -fobjc-exceptions -o - %s
-// RUN: %clang_cc1 -include %S/objc_stmts.h -ast-dump -fobjc-exceptions -o - %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -include %S/objc_stmts.h -ast-dump -fobjc-exceptions -o - %s | FileCheck %s
// Test with pch.
// RUN: %clang_cc1 -x objective-c -emit-pch -fobjc-exceptions -o %t %S/objc_stmts.h
// RUN: %clang_cc1 -include-pch %t -emit-llvm -fobjc-exceptions -o - %s
-// RUN: %clang_cc1 -include-pch %t -ast-dump -fobjc-exceptions -o - %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -include-pch %t -ast-dump -fobjc-exceptions -o - %s | FileCheck %s
// CHECK: catch parm = "A *a"
// CHECK: catch parm = "B *b"
diff --git a/test/PCH/pch__VA_ARGS__.c b/test/PCH/pch__VA_ARGS__.c
new file mode 100644
index 0000000..9bc1d3c
--- /dev/null
+++ b/test/PCH/pch__VA_ARGS__.c
@@ -0,0 +1,6 @@
+// Test with pch.
+// RUN: %clang_cc1 -emit-pch -o %t %S/pch__VA_ARGS__.h
+// RUN: %clang_cc1 -include-pch %t -fsyntax-only -Weverything %s 2>&1 | FileCheck %s
+
+#define mylog(...) printf(__VA_ARGS__)
+// CHECK-NOT: warning: __VA_ARGS__ can only appear in the expansion of a C99 variadic macro
diff --git a/test/PCH/pch__VA_ARGS__.h b/test/PCH/pch__VA_ARGS__.h
new file mode 100644
index 0000000..9eb1005
--- /dev/null
+++ b/test/PCH/pch__VA_ARGS__.h
@@ -0,0 +1,2 @@
+// Header for PCH test fuzzy-pch.c
+void f(int X);
diff --git a/test/PCH/remap-file-from-pch.cpp b/test/PCH/remap-file-from-pch.cpp
new file mode 100644
index 0000000..8b965cf
--- /dev/null
+++ b/test/PCH/remap-file-from-pch.cpp
@@ -0,0 +1,10 @@
+// %clang_cc1 -remap-file "%s;%S/Inputs/remapped-file" -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-EXIST %s
+
+// RUN: %clang_cc1 -x c++-header %s.h -emit-pch -o %t.pch
+// RUN: %clang_cc1 %s -include-pch %t.pch -remap-file "%s.h;%s.remap.h" -fsyntax-only 2>&1 | FileCheck %s
+
+const char *str = STR;
+int ge = zool;
+
+// CHECK: file '{{.*[/\\]}}remap-file-from-pch.cpp.h' from the precompiled header has been overridden
+// CHECK: use of undeclared identifier 'zool'
diff --git a/test/PCH/remap-file-from-pch.cpp.h b/test/PCH/remap-file-from-pch.cpp.h
new file mode 100644
index 0000000..0edf6aa
--- /dev/null
+++ b/test/PCH/remap-file-from-pch.cpp.h
@@ -0,0 +1,2 @@
+
+#define STR "nexus"
diff --git a/test/PCH/remap-file-from-pch.cpp.remap.h b/test/PCH/remap-file-from-pch.cpp.remap.h
new file mode 100644
index 0000000..e8e8ab7
--- /dev/null
+++ b/test/PCH/remap-file-from-pch.cpp.remap.h
@@ -0,0 +1,4 @@
+
+#define STR "nexus"
+
+int zool;
diff --git a/test/PCH/replaced-decl.m b/test/PCH/replaced-decl.m
index b9fee95..5636a57 100644
--- a/test/PCH/replaced-decl.m
+++ b/test/PCH/replaced-decl.m
@@ -12,11 +12,12 @@
#elif !defined(HEADER2)
#define HEADER2
-@interface I // expected-note {{previous}}
+@interface I
@end
#else
typedef int I; // expected-error {{redefinition}}
+ // expected-note@15 {{previous}}
#endif
diff --git a/test/PCH/typo2.cpp b/test/PCH/typo2.cpp
index f9b4c83..9aa97bd 100644
--- a/test/PCH/typo2.cpp
+++ b/test/PCH/typo2.cpp
@@ -4,10 +4,11 @@
#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED
-void func(struct Test); // expected-note{{'Test' declared here}}
+void func(struct Test);
#else
::Yest *T; // expected-error{{did you mean 'Test'}}
+ // expected-note@7{{'Test' declared here}}
#endif
diff --git a/test/PCH/variables.c b/test/PCH/variables.c
index 9f90b37..2c8c136 100644
--- a/test/PCH/variables.c
+++ b/test/PCH/variables.c
@@ -11,12 +11,12 @@
extern float y;
extern int *ip, x;
-float z; // expected-note{{previous}}
+float z;
-int z2 = 17; // expected-note{{previous}}
+int z2 = 17;
#define MAKE_HAPPY(X) X##Happy
-int MAKE_HAPPY(Very); // expected-note{{previous definition is here}}
+int MAKE_HAPPY(Very);
#define A_MACRO_IN_THE_PCH 492
#define FUNCLIKE_MACRO(X, Y) X ## Y
@@ -32,9 +32,9 @@ int UNIQUE(a); // a1
int *ip2 = &x;
float *fp = &ip; // expected-warning{{incompatible pointer types}}
-double z; // expected-error{{redefinition}}
-int z2 = 18; // expected-error{{redefinition}}
-double VeryHappy; // expected-error{{redefinition}}
+double z; // expected-error{{redefinition}} expected-note@14{{previous}}
+int z2 = 18; // expected-error{{redefinition}} expected-note@16{{previous}}
+double VeryHappy; // expected-error{{redefinition}} expected-note@19{{previous definition is here}}
int Q = A_MACRO_IN_THE_PCH;
diff --git a/test/Parser/DelayedTemplateParsing.cpp b/test/Parser/DelayedTemplateParsing.cpp
index 9737c73..77b4723 100644
--- a/test/Parser/DelayedTemplateParsing.cpp
+++ b/test/Parser/DelayedTemplateParsing.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -verify -std=c++11 %s
template <class T>
class A {
@@ -90,3 +90,14 @@ Callback Bind() {
}
}
+
+namespace rdar11700604 {
+ template<typename T> void foo() = delete;
+
+ struct X {
+ X() = default;
+
+ template<typename T> void foo() = delete;
+ };
+}
+
diff --git a/test/Parser/MicrosoftExtensions.c b/test/Parser/MicrosoftExtensions.c
index 1ef326a..7703999 100644
--- a/test/Parser/MicrosoftExtensions.c
+++ b/test/Parser/MicrosoftExtensions.c
@@ -3,24 +3,24 @@ __stdcall int func0();
int __stdcall func();
typedef int (__cdecl *tptr)();
void (*__fastcall fastpfunc)();
-struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) __declspec(novtable) IUnknown {};
+struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) __declspec(novtable) IUnknown {}; /* expected-warning{{__declspec attribute 'novtable' is not supported}} */
extern __declspec(dllimport) void __stdcall VarR4FromDec();
__declspec(deprecated) __declspec(deprecated) char * __cdecl ltoa( long _Val, char * _DstBuf, int _Radix);
-__declspec(noalias) __declspec(restrict) void * __cdecl xxx( void * _Memory );
+__declspec(noalias) __declspec(restrict) void * __cdecl xxx( void * _Memory ); /* expected-warning{{__declspec attribute 'noalias' is not supported}} expected-warning{{__declspec attribute 'restrict' is not supported}} */
typedef __w64 unsigned long ULONG_PTR, *PULONG_PTR;
void * __ptr64 PtrToPtr64(const void *p)
{
- return((void * __ptr64) (unsigned __int64) (ULONG_PTR)p ); // expected-warning {{unknown attribute '__ptr64' ignored}}
+ return((void * __ptr64) (unsigned __int64) (ULONG_PTR)p );
}
void * __ptr32 PtrToPtr32(const void *p)
{
- return((void * __ptr32) (unsigned __int32) (ULONG_PTR)p ); // expected-warning {{unknown attribute '__ptr32' ignored}}
+ return((void * __ptr32) (unsigned __int32) (ULONG_PTR)p );
}
void __forceinline InterlockedBitTestAndSet (long *Base, long Bit)
{
- __asm {
+ __asm { // expected-warning {{MS-style inline assembly is not supported}}
mov eax, Bit
mov ecx, Base
lock bts [ecx], eax
@@ -29,6 +29,11 @@ void __forceinline InterlockedBitTestAndSet (long *Base, long Bit)
}
_inline int foo99() { return 99; }
+void test_ms_alignof_alias() {
+ unsigned int s = _alignof(int);
+ s = __builtin_alignof(int);
+}
+
void *_alloca(int);
void foo() {
@@ -49,8 +54,8 @@ char x = FOO(a);
typedef enum E { e1 };
-enum __declspec(deprecated) E2 { i, j, k };
-__declspec(deprecated) enum E3 { a, b, c } e;
+enum __declspec(deprecated) E2 { i, j, k }; // expected-note {{declared here}}
+__declspec(deprecated) enum E3 { a, b, c } e; // expected-note {{declared here}}
void deprecated_enum_test(void)
{
@@ -64,7 +69,7 @@ void deprecated_enum_test(void)
[repeatable][source_annotation_attribute( Parameter|ReturnValue )]
struct SA_Post{ SA_Post(); int attr; };
-[returnvalue:SA_Post( attr=1)]
+[returnvalue:SA_Post( attr=1)]
int foo1([SA_Post(attr=1)] void *param);
@@ -75,3 +80,25 @@ void ms_intrinsics(int a)
__assume(a);
__debugbreak();
}
+
+struct __declspec(frobble) S1 {}; /* expected-warning {{unknown __declspec attribute 'frobble' ignored}} */
+struct __declspec(12) S2 {}; /* expected-error {{__declspec attributes must be an identifier or string literal}} */
+struct __declspec("testing") S3 {}; /* expected-warning {{__declspec attribute '"testing"' is not supported}} */
+
+/* Ensure multiple declspec attributes are supported */
+struct __declspec(align(8) deprecated) S4 {};
+
+/* But multiple declspecs must still be legal */
+struct __declspec(deprecated frobble "testing") S5 {}; /* expected-warning {{unknown __declspec attribute 'frobble' ignored}} expected-warning {{__declspec attribute '"testing"' is not supported}} */
+struct __declspec(unknown(12) deprecated) S6 {}; /* expected-warning {{unknown __declspec attribute 'unknown' ignored}}*/
+
+struct S7 {
+ int foo() { return 12; }
+ __declspec(property(get=foo) deprecated) int t; // expected-note {{declared here}}
+};
+
+/* Technically, this is legal (though it does nothing) */
+__declspec() void quux( void ) {
+ struct S7 s;
+ int i = s.t; /* expected-warning {{'t' is deprecated}} */
+}
diff --git a/test/Parser/MicrosoftExtensions.cpp b/test/Parser/MicrosoftExtensions.cpp
index 3a1ffea..6219e29 100644
--- a/test/Parser/MicrosoftExtensions.cpp
+++ b/test/Parser/MicrosoftExtensions.cpp
@@ -151,11 +151,24 @@ void missing_template_keyword(){
class AAAA { };
+template <typename T>
+class SimpleTemplate {};
+
template <class T>
void redundant_typename() {
typename T t;// expected-warning {{expected a qualified name after 'typename'}}
typename AAAA a;// expected-warning {{expected a qualified name after 'typename'}}
+
t = 3;
+
+ typedef typename T* pointerT;// expected-warning {{expected a qualified name after 'typename'}}
+ typedef typename SimpleTemplate<int> templateT;// expected-warning {{expected a qualified name after 'typename'}}
+
+ pointerT pT = &t;
+ *pT = 4;
+
+ int var;
+ int k = typename var;// expected-error {{expected a qualified name after 'typename'}}
}
@@ -284,28 +297,29 @@ int main () {
missing_template_keyword<int>();
}
+namespace access_protected_PTM {
+ class A {
+ protected:
+ void f(); // expected-note {{must name member using the type of the current context 'access_protected_PTM::B'}}
+ };
+ class B : public A{
+ public:
+ void test_access();
+ static void test_access_static();
+ };
-
-namespace access_protected_PTM {
-
-class A {
-protected:
- void f(); // expected-note {{must name member using the type of the current context 'access_protected_PTM::B'}}
-};
-
-class B : public A{
-public:
- void test_access();
- static void test_access_static();
-};
-
-void B::test_access() {
- &A::f; // expected-error {{'f' is a protected member of 'access_protected_PTM::A'}}
-}
-
-void B::test_access_static() {
- &A::f;
-}
-
-} \ No newline at end of file
+ void B::test_access() {
+ &A::f; // expected-error {{'f' is a protected member of 'access_protected_PTM::A'}}
+ }
+
+ void B::test_access_static() {
+ &A::f;
+ }
+}
+
+namespace Inheritance {
+ class __single_inheritance A;
+ class __multiple_inheritance B;
+ class __virtual_inheritance C;
+}
diff --git a/test/Parser/altivec.c b/test/Parser/altivec.c
index d1e6552..0bdc3dc 100644
--- a/test/Parser/altivec.c
+++ b/test/Parser/altivec.c
@@ -76,6 +76,16 @@ vector bool unsigned int v_bsc2; // expected-error {{cannot use 'unsigned' w
vector bool long v_bl; // expected-error {{cannot use 'long' with '__vector bool'}}
vector bool long long v_bll; // expected-error {{cannot use 'long long' with '__vector bool'}}
+typedef char i8;
+typedef short i16;
+typedef int i32;
+struct S {
+ // i8, i16, i32 here are field names, not type names.
+ vector bool i8; // expected-error {{requires a specifier or qualifier}}
+ vector pixel i16;
+ vector long i32; // expected-warning {{deprecated}}
+};
+
void f() {
__vector unsigned int v = {0,0,0,0};
__vector int v__cast = (__vector int)v;
diff --git a/test/Parser/c1x-alignas.c b/test/Parser/c1x-alignas.c
index 5dccc99..81cd681 100644
--- a/test/Parser/c1x-alignas.c
+++ b/test/Parser/c1x-alignas.c
@@ -1,7 +1,13 @@
-// RUN: %clang_cc1 -std=c1x -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify %s
+// RUN: not %clang_cc1 -pedantic -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-EXT %s
_Alignas(4) char c1;
unsigned _Alignas(long) char c2;
char _Alignas(16) c3;
char c4 _Alignas(32); // expected-error {{expected ';' after top level declarator}}
+
+char _Alignas(_Alignof(int)) c5;
+
+// CHECK-EXT: _Alignas is a C11-specific feature
+// CHECK-EXT: _Alignof is a C11-specific feature
diff --git a/test/Parser/completely-empty-header-file.h b/test/Parser/completely-empty-header-file.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Parser/completely-empty-header-file.h
diff --git a/test/Parser/cuda-kernel-call.cu b/test/Parser/cuda-kernel-call.cu
index f95ae9e..92e46e3 100644
--- a/test/Parser/cuda-kernel-call.cu
+++ b/test/Parser/cuda-kernel-call.cu
@@ -1,9 +1,16 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
+template<typename> struct S {};
+template<typename> void f();
+
void foo(void) {
foo<<<1; // expected-error {{expected '>>>'}} expected-note {{to match this '<<<'}}
foo<<<1,1>>>; // expected-error {{expected '('}}
foo<<<>>>(); // expected-error {{expected expression}}
+
+ S<S<S<int>>> s; // expected-error 2{{use '> >'}}
+
+ (void)(&f<S<S<int>>>==0); // expected-error 2{{use '> >'}}
}
diff --git a/test/Parser/cxx-class.cpp b/test/Parser/cxx-class.cpp
index 1b3dd41..feccba8 100644
--- a/test/Parser/cxx-class.cpp
+++ b/test/Parser/cxx-class.cpp
@@ -12,11 +12,15 @@ protected:
int : 1, : 2;
public:
+ void m0() {}; // ok, one extra ';' is permitted
+ void m1() {}
+ ; // ok, one extra ';' is permitted
void m() {
int l = 2;
- };
+ };; // expected-warning{{extra ';' after member function definition}}
- template<typename T> void mt(T) { };
+ template<typename T> void mt(T) { }
+ ;
; // expected-warning{{extra ';' inside a class}}
virtual int vf() const volatile = 0;
diff --git a/test/Parser/cxx-decl.cpp b/test/Parser/cxx-decl.cpp
index 57f33d8..951cd3d 100644
--- a/test/Parser/cxx-decl.cpp
+++ b/test/Parser/cxx-decl.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux %s
+// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -pedantic %s
int x(*g); // expected-error {{use of undeclared identifier 'g'}}
@@ -44,7 +44,7 @@ class asm_class_test {
void foo() __asm__("baz");
};
-enum { fooenum = 1 };
+enum { fooenum = 1, }; // expected-warning {{commas at the end of enumerator lists are a C++11 extension}}
struct a {
int Type : fooenum;
@@ -125,5 +125,3 @@ test6a { ;// expected-error {{C++ requires a type specifier for all declarations
// expected-error {{expected ';' after top level declarator}}
int test6b;
-
-
diff --git a/test/Parser/cxx-extra-semi.cpp b/test/Parser/cxx-extra-semi.cpp
new file mode 100644
index 0000000..2aa18df
--- /dev/null
+++ b/test/Parser/cxx-extra-semi.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -fsyntax-only -pedantic -verify -DPEDANTIC %s
+// RUN: %clang_cc1 -fsyntax-only -Wextra-semi -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wextra-semi -verify -std=c++11 %s
+// RUN: cp %s %t
+// RUN: %clang_cc1 -x c++ -Wextra-semi -fixit %t
+// RUN: %clang_cc1 -x c++ -Wextra-semi -Werror %t
+
+class A {
+ void A1();
+ void A2() { };
+#ifndef PEDANTIC
+ // This warning is only produced if we specify -Wextra-semi, and not if only
+ // -pedantic is specified, since one semicolon is technically permitted.
+ // expected-warning@-4{{extra ';' after member function definition}}
+#endif
+ void A2b() { };; // expected-warning{{extra ';' after member function definition}}
+ ; // expected-warning{{extra ';' inside a class}}
+ void A2c() { }
+ ;
+#ifndef PEDANTIC
+ // expected-warning@-2{{extra ';' after member function definition}}
+#endif
+ void A3() { }; ;; // expected-warning{{extra ';' after member function definition}}
+ ;;;;;;; // expected-warning{{extra ';' inside a class}}
+ ; // expected-warning{{extra ';' inside a class}}
+ ; ;; ; ;;; // expected-warning{{extra ';' inside a class}}
+ ; ; ; ; ;; // expected-warning{{extra ';' inside a class}}
+ void A4();
+};
+
+union B {
+ int a1;
+ int a2;; // expected-warning{{extra ';' inside a union}}
+};
+
+;
+; ;;
+#if __cplusplus < 201103L
+// expected-warning@-3{{extra ';' outside of a function is a C++11 extension}}
+// expected-warning@-3{{extra ';' outside of a function is a C++11 extension}}
+#endif
diff --git a/test/Parser/cxx-template-argument.cpp b/test/Parser/cxx-template-argument.cpp
index c85b1c9..5479961 100644
--- a/test/Parser/cxx-template-argument.cpp
+++ b/test/Parser/cxx-template-argument.cpp
@@ -10,3 +10,18 @@ A<int x; // expected-error {{expected '>'}}
// PR8912
template <bool> struct S {};
S<bool(2 > 1)> s;
+
+// Test behavior when a template-id is ended by a token which starts with '>'.
+namespace greatergreater {
+ template<typename T> struct S { S(); S(T); };
+ void f(S<int>=0); // expected-error {{a space is required between a right angle bracket and an equals sign (use '> =')}}
+ void f(S<S<int>>=S<int>()); // expected-error {{use '> >'}} expected-error {{use '> ='}}
+ template<typename T> void t();
+ void g() {
+ void (*p)() = &t<int>;
+ (void)(&t<int>==p); // expected-error {{use '> ='}}
+ (void)(&t<int>>=p); // expected-error {{use '> >'}}
+ (void)(&t<S<int>>>=p); // expected-error {{use '> >'}}
+ (void)(&t<S<int>>==p); // expected-error {{use '> >'}} expected-error {{use '> ='}}
+ }
+}
diff --git a/test/Parser/cxx-throw.cpp b/test/Parser/cxx-throw.cpp
index d63b6d4..a1be710 100644
--- a/test/Parser/cxx-throw.cpp
+++ b/test/Parser/cxx-throw.cpp
@@ -13,3 +13,5 @@ void foo() {
__extension__ throw 1; // expected-error {{expected expression}}
(void)throw; // expected-error {{expected expression}}
}
+
+void f() throw(static); // expected-error {{expected a type}} expected-error {{does not allow storage class}}
diff --git a/test/Parser/cxx-undeclared-identifier.cpp b/test/Parser/cxx-undeclared-identifier.cpp
index f15deab..6ea2965 100644
--- a/test/Parser/cxx-undeclared-identifier.cpp
+++ b/test/Parser/cxx-undeclared-identifier.cpp
@@ -1,5 +1,17 @@
// RUN: %clang_cc1 -fsyntax-only -pedantic -verify %s
+namespace ImplicitInt {
+ static a(4); // expected-error {{requires a type specifier}}
+ b(int n); // expected-error {{requires a type specifier}}
+ c (*p)[]; // expected-error {{unknown type name 'c'}}
+ itn f(char *p, *q); // expected-error {{unknown type name 'itn'}} expected-error {{requires a type specifier}}
+
+ struct S {
+ void f();
+ };
+ S::f() {} // expected-error {{requires a type specifier}}
+}
+
// PR7180
int f(a::b::c); // expected-error {{use of undeclared identifier 'a'}}
diff --git a/test/Parser/cxx-using-declaration.cpp b/test/Parser/cxx-using-declaration.cpp
index 2b2a69d..320fd09 100644
--- a/test/Parser/cxx-using-declaration.cpp
+++ b/test/Parser/cxx-using-declaration.cpp
@@ -43,3 +43,19 @@ using F::X;
// Should have some errors here. Waiting for implementation.
void X(int);
struct X *x;
+
+
+namespace ShadowedTagNotes {
+
+namespace foo {
+ class Bar {};
+}
+
+void Bar(int); // expected-note{{class 'Bar' is hidden by a non-type declaration of 'Bar' here}}
+using foo::Bar;
+
+void ambiguity() {
+ const Bar *x; // expected-error{{must use 'class' tag to refer to type 'Bar' in this scope}}
+}
+
+} // namespace ShadowedTagNotes
diff --git a/test/Parser/cxx-using-directive.cpp b/test/Parser/cxx-using-directive.cpp
index 1d781fb..9a1a6de 100644
--- a/test/Parser/cxx-using-directive.cpp
+++ b/test/Parser/cxx-using-directive.cpp
@@ -3,7 +3,8 @@
class A {};
namespace B {
- namespace A {} // expected-note{{namespace '::B::A' defined here}}
+ namespace A {} // expected-note{{namespace '::B::A' defined here}} \
+ // expected-note{{namespace 'B::A' defined here}}
using namespace A ;
}
@@ -25,7 +26,7 @@ namespace D {
}
using namespace ! ; // expected-error{{expected namespace name}}
-using namespace A ; // expected-error{{expected namespace name}}
+using namespace A ; // expected-error{{no namespace named 'A'; did you mean 'B::A'?}}
using namespace ::A // expected-error{{expected namespace name}} \
// expected-error{{expected ';' after namespace name}}
B ;
diff --git a/test/Parser/cxx0x-ambig.cpp b/test/Parser/cxx0x-ambig.cpp
index 98757b4..96e2006 100644
--- a/test/Parser/cxx0x-ambig.cpp
+++ b/test/Parser/cxx0x-ambig.cpp
@@ -57,7 +57,7 @@ namespace bitfield {
// This could be a bit-field, but would be ill-formed due to the anonymous
// member being initialized.
struct S5 {
- enum E : int { a = 1 } { b = 2 }; // expected-error {{expected member name}}
+ enum E : int { a = 1 } { b = 2 }; // expected-error {{expected ';' after enum}} expected-error {{expected member name}}
};
// This could be a bit-field.
struct S6 {
@@ -85,7 +85,7 @@ namespace trailing_return {
struct S {
S(int);
- S *operator()() const;
+ S *operator()(...) const;
int n;
};
@@ -94,7 +94,9 @@ namespace trailing_return {
// This parses as a function declaration, but DR1223 makes the presence of
// 'auto' be used for disambiguation.
S(a)()->n; // ok, expression; expected-warning{{expression result unused}}
+ S(a)(int())->n; // ok, expression; expected-warning{{expression result unused}}
auto(a)()->n; // ok, function declaration
+ auto(b)(int())->n; // ok, function declaration
using T = decltype(a);
using T = auto() -> n;
}
diff --git a/test/Parser/cxx0x-attributes.cpp b/test/Parser/cxx0x-attributes.cpp
index f97995e..a0b8467 100644
--- a/test/Parser/cxx0x-attributes.cpp
+++ b/test/Parser/cxx0x-attributes.cpp
@@ -1,15 +1,50 @@
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 %s
+// Need std::initializer_list
+namespace std {
+ typedef decltype(sizeof(int)) size_t;
+
+ // libc++'s implementation
+ template <class _E>
+ class initializer_list
+ {
+ const _E* __begin_;
+ size_t __size_;
+
+ initializer_list(const _E* __b, size_t __s)
+ : __begin_(__b),
+ __size_(__s)
+ {}
+
+ public:
+ typedef _E value_type;
+ typedef const _E& reference;
+ typedef const _E& const_reference;
+ typedef size_t size_type;
+
+ typedef const _E* iterator;
+ typedef const _E* const_iterator;
+
+ initializer_list() : __begin_(nullptr), __size_(0) {}
+
+ size_t size() const {return __size_;}
+ const _E* begin() const {return __begin_;}
+ const _E* end() const {return __begin_ + __size_;}
+ };
+}
+
+
// Declaration syntax checks
[[]] int before_attr;
int [[]] between_attr;
+const [[]] int between_attr_2 = 0; // expected-error {{an attribute list cannot appear here}}
int after_attr [[]];
int * [[]] ptr_attr;
int & [[]] ref_attr = after_attr;
int && [[]] rref_attr = 0;
int array_attr [1] [[]];
alignas(8) int aligned_attr;
-[[test::valid(for 42 [very] **** '+' symbols went on a trip; the end.)]]
+[[test::valid(for 42 [very] **** '+' symbols went on a trip and had a "good"_time; the end.)]]
int garbage_attr;
[[,,,static, class, namespace,, inline, constexpr, mutable,, bi\
tand, bitor::compl(!.*_ Cx.!U^*R),,,]] int more_garbage_attr;
@@ -19,7 +54,18 @@ void noexcept_fn_attr () noexcept [[]];
struct MemberFnOrder {
virtual void f() const volatile && noexcept [[]] final = 0;
};
+struct [[]] struct_attr;
class [[]] class_attr {};
+union [[]] union_attr;
+[[]] struct with_init_declarators {} init_declarator;
+[[]] struct no_init_declarators; // expected-error {{an attribute list cannot appear here}}
+[[]];
+struct ctordtor {
+ [[]] ctordtor();
+ [[]] ~ctordtor();
+};
+[[]] ctordtor::ctordtor() {}
+[[]] ctordtor::~ctordtor() {}
extern "C++" [[]] int extern_attr;
template <typename T> [[]] void template_attr ();
[[]] [[]] int [[]] [[]] multi_attr [[]] [[]];
@@ -27,7 +73,8 @@ template <typename T> [[]] void template_attr ();
int comma_attr [[,]];
int scope_attr [[foo::]]; // expected-error {{expected identifier}}
int (paren_attr) [[]]; // expected-error {{an attribute list cannot appear here}}
-unsigned [[]] int attr_in_decl_spec; // expected-error {{expected unqualified-id}}
+unsigned [[]] int attr_in_decl_spec; // expected-error {{an attribute list cannot appear here}}
+unsigned [[]] int [[]] const double_decl_spec = 0; // expected-error 2{{an attribute list cannot appear here}}
class foo {
void const_after_attr () [[]] const; // expected-error {{expected ';'}}
};
@@ -40,6 +87,52 @@ extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}}
[[]] using ns::i; // expected-error {{an attribute list cannot appear here}}
[[]] using namespace ns;
+[[]] using T = int; // expected-error {{an attribute list cannot appear here}}
+using T [[]] = int; // ok
+template<typename T> using U [[]] = T;
+using ns::i [[]]; // expected-error {{an attribute list cannot appear here}}
+using [[]] ns::i; // expected-error {{an attribute list cannot appear here}}
+
+auto trailing() -> [[]] const int; // expected-error {{an attribute list cannot appear here}}
+auto trailing() -> const [[]] int; // expected-error {{an attribute list cannot appear here}}
+auto trailing() -> const int [[]];
+auto trailing_2() -> struct struct_attr [[]];
+
+namespace N {
+ struct S {};
+};
+template<typename> struct Template {};
+
+// FIXME: Improve this diagnostic
+struct [[]] N::S s; // expected-error {{an attribute list cannot appear here}}
+struct [[]] Template<int> t; // expected-error {{an attribute list cannot appear here}}
+struct [[]] ::template Template<int> u; // expected-error {{an attribute list cannot appear here}}
+template struct [[]] Template<char>; // expected-error {{an attribute list cannot appear here}}
+template <> struct [[]] Template<void>;
+
+enum [[]] E1 {};
+enum [[]] E2; // expected-error {{forbids forward references}}
+enum [[]] E1;
+enum [[]] E3 : int;
+enum [[]] {
+ k_123 [[]] = 123 // expected-error {{an attribute list cannot appear here}}
+};
+enum [[]] E1 e; // expected-error {{an attribute list cannot appear here}}
+enum [[]] class E4 { }; // expected-error {{an attribute list cannot appear here}}
+enum struct [[]] E5;
+
+struct S {
+ friend int f [[]] (); // expected-FIXME{{an attribute list cannot appear here}}
+ [[]] friend int g(); // expected-FIXME{{an attribute list cannot appear here}}
+ [[]] friend int h() {
+ }
+ friend class [[]] C; // expected-error{{an attribute list cannot appear here}}
+};
+template<typename T> void tmpl(T) {}
+template void tmpl [[]] (int); // expected-FIXME {{an attribute list cannot appear here}}
+template [[]] void tmpl(char); // expected-error {{an attribute list cannot appear here}}
+template void [[]] tmpl(short);
+
// Argument tests
alignas int aligned_no_params; // expected-error {{expected '('}}
alignas(i) int aligned_nonconst; // expected-error {{'aligned' attribute requires integer constant}} expected-note {{read of non-const variable 'i'}}
@@ -81,3 +174,41 @@ void foo () {
template<typename...Ts> void variadic() {
void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}}
}
+
+// Expression tests
+void bar () {
+ [] () [[noreturn]] { return; } (); // expected-error {{should not return}}
+ [] () [[noreturn]] { throw; } ();
+ new int[42][[]][5][[]]{};
+}
+
+// Condition tests
+void baz () {
+ if ([[]] bool b = true) {
+ switch ([[]] int n { 42 }) {
+ default:
+ for ([[]] int n = 0; [[]] char b = n < 5; ++b) {
+ }
+ }
+ }
+ int x;
+ // An attribute can be applied to an expression-statement, such as the first
+ // statement in a for. But it can't be applied to a condition which is an
+ // expression.
+ for ([[]] x = 0; ; ) {} // expected-error {{an attribute list cannot appear here}}
+ for (; [[]] x < 5; ) {} // expected-error {{an attribute list cannot appear here}}
+ while ([[]] bool k { false }) {
+ }
+ while ([[]] true) { // expected-error {{an attribute list cannot appear here}}
+ }
+ do {
+ } while ([[]] false); // expected-error {{an attribute list cannot appear here}}
+
+ for ([[]] int n : { 1, 2, 3 }) {
+ }
+}
+
+enum class __attribute__((visibility("hidden"))) SecretKeepers {
+ one, /* rest are deprecated */ two, three
+};
+enum class [[]] EvenMoreSecrets {};
diff --git a/test/Parser/cxx0x-decl.cpp b/test/Parser/cxx0x-decl.cpp
index b9f5141..a6fc49c 100644
--- a/test/Parser/cxx0x-decl.cpp
+++ b/test/Parser/cxx0x-decl.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify -fsyntax-only -std=c++0x %s
+// RUN: %clang_cc1 -verify -fsyntax-only -std=c++11 -pedantic %s
// Make sure we know these are legitimate commas and not typos for ';'.
namespace Commas {
@@ -8,7 +8,7 @@ namespace Commas {
}
struct S {};
-enum E { e };
+enum E { e, };
auto f() -> struct S {
return S();
@@ -16,3 +16,15 @@ auto f() -> struct S {
auto g() -> enum E {
return E();
}
+
+class ExtraSemiAfterMemFn {
+ // Due to a peculiarity in the C++11 grammar, a deleted or defaulted function
+ // is permitted to be followed by either one or two semicolons.
+ void f() = delete // expected-error {{expected ';' after delete}}
+ void g() = delete; // ok
+ void h() = delete;; // ok
+ void i() = delete;;; // expected-warning {{extra ';' after member function definition}}
+};
+
+int *const const p = 0; // ok
+const const int *q = 0; // expected-warning {{duplicate 'const' declaration specifier}}
diff --git a/test/Parser/cxx0x-lambda-expressions.cpp b/test/Parser/cxx0x-lambda-expressions.cpp
index 9c71941..7e9d475 100644
--- a/test/Parser/cxx0x-lambda-expressions.cpp
+++ b/test/Parser/cxx0x-lambda-expressions.cpp
@@ -40,4 +40,11 @@ class C {
int a5[3] = { []{return 0;}() };
int a6[1] = {[this] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'C *'}}
}
+
+ void delete_lambda(int *p) {
+ delete [] p;
+ delete [] (int*) { new int }; // ok, compound-literal, not lambda
+ delete [] { return new int; } (); // expected-error{{expected expression}}
+ delete [&] { return new int; } (); // ok, lambda
+ }
};
diff --git a/test/Parser/cxx11-type-specifier.cpp b/test/Parser/cxx11-type-specifier.cpp
index 2e629f3..c66462a 100644
--- a/test/Parser/cxx11-type-specifier.cpp
+++ b/test/Parser/cxx11-type-specifier.cpp
@@ -18,3 +18,7 @@ void f() {
(void) new struct S {}; // expected-error{{'S' can not be defined in a type specifier}}
(void) new enum E { e }; // expected-error{{'E' can not be defined in a type specifier}}
}
+
+// And for trailing-type-specifier-seq
+
+auto f() -> unknown; // expected-error{{unknown type name 'unknown'}}
diff --git a/test/Parser/declarators.c b/test/Parser/declarators.c
index a7a01d8..f63b59f 100644
--- a/test/Parser/declarators.c
+++ b/test/Parser/declarators.c
@@ -100,3 +100,15 @@ long struct X { int x; } test15(); // expected-error {{'long struct' is invalid}
void test16(i) int i j; { } // expected-error {{expected ';' at end of declaration}}
void test17(i, j) int i, j k; { } // expected-error {{expected ';' at end of declaration}}
+
+
+// PR12595
+void test18() {
+ int x = 4+(5-12)); // expected-error {{extraneous ')' before ';'}}
+}
+
+enum E1 { e1 }: // expected-error {{expected ';'}}
+struct EnumBitfield {
+ enum E2 { e2 } : 4; // ok
+ struct S { int n; }: // expected-error {{expected ';'}}
+};
diff --git a/test/Parser/empty-translation-unit.c b/test/Parser/empty-translation-unit.c
new file mode 100644
index 0000000..0dbf37e
--- /dev/null
+++ b/test/Parser/empty-translation-unit.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -pedantic -W -verify %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++03 -pedantic-errors -W %s
+
+#include "completely-empty-header-file.h"
+// no-warning -- an empty file is OK
+
+#define A_MACRO_IS_NOT_GOOD_ENOUGH 1
+
+// In C we should get this warning, but in C++ we shouldn't.
+// expected-warning{{ISO C requires a translation unit to contain at least one declaration.}}
diff --git a/test/Parser/missing-selector-name.mm b/test/Parser/missing-selector-name.mm
new file mode 100644
index 0000000..d5554c5
--- /dev/null
+++ b/test/Parser/missing-selector-name.mm
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+// rdar://11939584
+
+@interface PodiumWalkerController
+@property (assign) id PROP;
+- (void) // expected-error {{expected ';' after method prototype}}
+@end // expected-error {{expected selector for Objective-C method}}
+
+
+id GVAR;
+
+id StopProgressAnimation()
+{
+
+ PodiumWalkerController *controller;
+ return controller.PROP;
+}
+
+@interface P1
+@property (assign) id PROP;
+- (void); // expected-error {{expected selector for Objective-C method}}
+@end
+
+id GG=0;
+
+id Stop1()
+{
+
+ PodiumWalkerController *controller;
+ return controller.PROP;
+}
+
+@interface P2
+@property (assign) id PROP;
+- (void)Meth {} // expected-error {{expected ';' after method prototype}}
+@end
+
+@interface P3
+@property (assign) id PROP;
+- (void)
+- (void)Meth {} // expected-error {{expected selector for Objective-C method}} \
+ // expected-error {{expected ';' after method prototype}}
+@end
+
+id HH=0;
+
+id Stop2()
+{
+
+ PodiumWalkerController *controller;
+ return controller.PROP;
+}
diff --git a/test/Parser/ms-inline-asm.c b/test/Parser/ms-inline-asm.c
index b1af23e..2d18195 100644
--- a/test/Parser/ms-inline-asm.c
+++ b/test/Parser/ms-inline-asm.c
@@ -3,23 +3,36 @@
#define M __asm int 0x2c
#define M2 int
-void t1(void) { M }
-void t2(void) { __asm int 0x2c }
-void t3(void) { __asm M2 0x2c }
-void* t4(void) { __asm mov eax, fs:[0x10] }
+void t1(void) { M } // expected-warning {{MS-style inline assembly is not supported}}
+void t2(void) { __asm int 0x2c } // expected-warning {{MS-style inline assembly is not supported}}
+void t3(void) { __asm M2 0x2c } // expected-warning {{MS-style inline assembly is not supported}}
+void* t4(void) { __asm mov eax, fs:[0x10] } // expected-warning {{MS-style inline assembly is not supported}}
void t5() {
- __asm {
+ __asm { // expected-warning {{MS-style inline assembly is not supported}}
int 0x2c ; } asm comments are fun! }{
}
- __asm {}
+ __asm {} // no warning as this gets merged with the previous inline asm
}
int t6() {
- __asm int 3 ; } comments for single-line asm
- __asm {}
+ __asm int 3 ; } comments for single-line asm // expected-warning {{MS-style inline assembly is not supported}}
+ __asm {} // no warning as this gets merged with the previous inline asm
- __asm int 4
+ __asm int 4 // no warning as this gets merged with the previous inline asm
return 10;
}
-int t7() { // expected-note {{to match this}}
+int t7() {
+ __asm { // expected-warning {{MS-style inline assembly is not supported}}
+ push ebx
+ mov ebx, 0x07
+ pop ebx
+ }
+}
+void t8() {
+ __asm nop __asm nop __asm nop // expected-warning {{MS-style inline assembly is not supported}}
+}
+void t9() {
+ __asm nop __asm nop ; __asm nop // expected-warning {{MS-style inline assembly is not supported}}
+}
+int t_fail() { // expected-note {{to match this}}
__asm
__asm { // expected-error 3 {{expected}} expected-note {{to match this}}
diff --git a/test/Parser/objc-boxing.m b/test/Parser/objc-boxing.m
new file mode 100644
index 0000000..a16a137
--- /dev/null
+++ b/test/Parser/objc-boxing.m
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+@interface NSString @end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
+@end
+
+extern char *strdup(const char *str);
+
+id constant_string() {
+ return @("boxed constant string.");
+}
+
+id dynamic_string() {
+ return @(strdup("boxed dynamic string"));
+}
+
+id const_char_pointer() {
+ return @((const char *)"constant character pointer");
+}
+
+id missing_parentheses() {
+ return @(5; // expected-error {{expected ')'}} \
+ // expected-note {{to match this '('}}
+}
diff --git a/test/Parser/objc-diag-width.mm b/test/Parser/objc-diag-width.mm
new file mode 100644
index 0000000..3929ba2
--- /dev/null
+++ b/test/Parser/objc-diag-width.mm
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 %s 2>&1 | FileCheck %s
+
+// Just shouldn't crash. -verify suppresses the crash, so don't use it.
+// PR13417
+// CHECK-NOT: Assertion failed
+@interface ExtensionActionContextMenu @end
+@implementation ExtensionActionContextMenu
+namespace {
diff --git a/test/Parser/objc-forcollection-neg.m b/test/Parser/objc-forcollection-neg.m
index 1a989a1..9e5400c 100644
--- a/test/Parser/objc-forcollection-neg.m
+++ b/test/Parser/objc-forcollection-neg.m
@@ -26,12 +26,12 @@ typedef struct objc_object {
int i=0;
for (int * elem in elem) // expected-error {{selector element type 'int *' is not a valid object}} \
- expected-error {{collection expression type 'int *' is not a valid object}}
+ expected-error {{the type 'int *' is not a pointer to a fast-enumerable object}}
++i;
for (i in elem) // expected-error {{use of undeclared identifier 'elem'}} \
expected-error {{selector element type 'int' is not a valid object}}
++i;
- for (id se in i) // expected-error {{collection expression type 'int' is not a valid object}}
+ for (id se in i) // expected-error {{the type 'int' is not a pointer to a fast-enumerable object}}
++i;
}
@end
diff --git a/test/Parser/objc-init.m b/test/Parser/objc-init.m
index efa1266..c9d7d5d 100644
--- a/test/Parser/objc-init.m
+++ b/test/Parser/objc-init.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -fobjc-fragile-abi -verify -pedantic -Wno-objc-root-class %s
-// RUN: %clang_cc1 -fsyntax-only -fobjc-fragile-abi -verify -x objective-c++ -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-fragile -verify -pedantic -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-fragile -verify -x objective-c++ -Wno-objc-root-class %s
// rdar://5707001
@interface NSNumber;
diff --git a/test/Parser/objc-recover.mm b/test/Parser/objc-recover.mm
new file mode 100644
index 0000000..61444c7
--- /dev/null
+++ b/test/Parser/objc-recover.mm
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+
+@interface StopAtAtEnd
+// This used to eat the @end
+int 123 // expected-error{{expected unqualified-id}}
+@end
+
+@implementation StopAtAtEnd // no-warning
+int 123 // expected-error{{expected unqualified-id}}
+@end
+
+
+@interface StopAtMethodDecls
+// This used to eat the method declarations
+int 123 // expected-error{{expected unqualified-id}}
+- (void)foo; // expected-note{{here}}
+int 456 // expected-error{{expected unqualified-id}}
++ (void)bar; // expected-note{{here}}
+@end
+
+@implementation StopAtMethodDecls
+int 123 // expected-error{{expected unqualified-id}}
+- (id)foo {} // expected-warning{{conflicting return type}}
+int 456 // expected-error{{expected unqualified-id}}
++ (id)bar {} // expected-warning{{conflicting return type}}
+@end
+
+
+@interface EmbeddedNamespace
+// This used to cause an infinite loop.
+namespace NS { // expected-error{{expected unqualified-id}}
+}
+- (id)test; // expected-note{{here}}
+@end
+
+@implementation EmbeddedNamespace
+int 123 // expected-error{{expected unqualified-id}}
+// We should still stop here and parse this namespace.
+namespace NS {
+ void foo();
+}
+
+// Make sure the declaration of -test was recognized.
+- (void)test { // expected-warning{{conflicting return type}}
+ // Make sure the declaration of NS::foo was recognized.
+ NS::foo();
+}
+
+@end
+
+
+@protocol ProtocolWithEmbeddedNamespace
+namespace NS { // expected-error{{expected unqualified-id}}
+
+}
+- (void)PWEN_foo; // expected-note{{here}}
+@end
+
+@interface ImplementPWEN <ProtocolWithEmbeddedNamespace>
+@end
+
+@implementation ImplementPWEN
+- (id)PWEN_foo {} // expected-warning{{conflicting return type}}
+@end
diff --git a/test/Parser/objcxx11-attributes.mm b/test/Parser/objcxx11-attributes.mm
index 0c91392..1b9bf66 100644
--- a/test/Parser/objcxx11-attributes.mm
+++ b/test/Parser/objcxx11-attributes.mm
@@ -35,7 +35,8 @@ void f(X *noreturn) {
[[class, test(foo 'x' bar),,,]];
[[bitand, noreturn]]; // expected-warning {{attribute noreturn cannot be specified on a statement}}
- [[noreturn]]int(e)();
+ // FIXME: Suppress vexing parse warning
+ [[noreturn]]int(e)(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
int e2(); // expected-warning {{interpreted as a function declaration}} expected-note{{}}
// A function taking a noreturn function.
diff --git a/test/Parser/opencl-pragma.cl b/test/Parser/opencl-pragma.cl
index 1946077..4c48b2a 100644
--- a/test/Parser/opencl-pragma.cl
+++ b/test/Parser/opencl-pragma.cl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only
+// RUN: %clang_cc1 %s -verify -pedantic -Wno-empty-translation-unit -fsyntax-only
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
diff --git a/test/Parser/recovery.c b/test/Parser/recovery.c
index 3916acf..178427e 100644
--- a/test/Parser/recovery.c
+++ b/test/Parser/recovery.c
@@ -37,8 +37,9 @@ void test(int a) {
test(0);
else
;
-
- if (x.i == 0)) // expected-error {{expected expression}}
+
+ // PR12595
+ if (x.i == 0)) // expected-error {{extraneous ')' after condition, expected a statement}}
test(0);
else
;
diff --git a/test/Parser/recovery.cpp b/test/Parser/recovery.cpp
index ffa1bab..ff68758 100644
--- a/test/Parser/recovery.cpp
+++ b/test/Parser/recovery.cpp
@@ -12,13 +12,12 @@ inline namespace Std { // expected-error {{cannot be reopened as inline}}
int x;
Std::Important y;
-// FIXME: Recover as if the typo correction were applied.
-extenr "C" { // expected-error {{did you mean 'extern'}} expected-error {{unqualified-id}}
+extenr "C" { // expected-error {{did you mean the keyword 'extern'}}
void f();
}
void g() {
z = 1; // expected-error {{undeclared}}
- f(); // expected-error {{undeclared}}
+ f();
}
struct S {
@@ -37,6 +36,7 @@ namespace N {
int
} // expected-error {{unqualified-id}}
-// FIXME: Recover as if the typo correction were applied.
-strcut U { // expected-error {{did you mean 'struct'}}
-} *u[3]; // expected-error {{expected ';'}}
+strcut Uuuu { // expected-error {{did you mean the keyword 'struct'}} \
+ // expected-note {{'Uuuu' declared here}}
+} *u[3];
+uuuu v; // expected-error {{did you mean 'Uuuu'}}
diff --git a/test/Preprocessor/_Pragma-location.c b/test/Preprocessor/_Pragma-location.c
index 8b68d6c..5031ee4 100644
--- a/test/Preprocessor/_Pragma-location.c
+++ b/test/Preprocessor/_Pragma-location.c
@@ -1,4 +1,47 @@
-// RUN: %clang_cc1 %s -E | not grep 'scratch space'
+// RUN: %clang_cc1 %s -fms-extensions -E | FileCheck %s
+// We use -fms-extensions to test both _Pragma and __pragma.
-#define push _Pragma ("pack(push)")
-push
+// A long time ago the pragma lexer's buffer showed through in -E output.
+// CHECK-NOT: scratch space
+
+#define push_p _Pragma ("pack(push)")
+push_p
+// CHECK: #pragma pack(push)
+
+push_p _Pragma("pack(push)") __pragma(pack(push))
+// CHECK: #pragma pack(push)
+// CHECK-NEXT: #line 11 "{{.*}}_Pragma-location.c"
+// CHECK-NEXT: #pragma pack(push)
+// CHECK-NEXT: #line 11 "{{.*}}_Pragma-location.c"
+// CHECK-NEXT: #pragma pack(push)
+
+
+#define __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS _Pragma("clang diagnostic push") \
+_Pragma("clang diagnostic ignored \"-Wformat-extra-args\"")
+#define __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS _Pragma("clang diagnostic pop")
+
+void test () {
+ 1;_Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wformat-extra-args\"")
+ _Pragma("clang diagnostic pop")
+
+ 2;__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS
+ 3;__PRAGMA_POP_NO_EXTRA_ARG_WARNINGS
+}
+
+// CHECK: void test () {
+// CHECK-NEXT: 1;
+// CHECK-NEXT: #line 24 "{{.*}}_Pragma-location.c"
+// CHECK-NEXT: #pragma clang diagnostic push
+// CHECK-NEXT: #pragma clang diagnostic ignored "-Wformat-extra-args"
+// CHECK-NEXT: #pragma clang diagnostic pop
+
+// CHECK: 2;
+// CHECK-NEXT: #line 28 "{{.*}}_Pragma-location.c"
+// CHECK-NEXT: #pragma clang diagnostic push
+// CHECK-NEXT: #line 28 "{{.*}}_Pragma-location.c"
+// CHECK-NEXT: #pragma clang diagnostic ignored "-Wformat-extra-args"
+// CHECK-NEXT: 3;
+// CHECK-NEXT: #line 29 "{{.*}}_Pragma-location.c"
+// CHECK-NEXT: #pragma clang diagnostic pop
+// CHECK-NEXT: }
diff --git a/test/Preprocessor/comment_save.c b/test/Preprocessor/comment_save.c
index b860042..1100ea2 100644
--- a/test/Preprocessor/comment_save.c
+++ b/test/Preprocessor/comment_save.c
@@ -6,3 +6,17 @@
/* bar */
// CHECK: /* bar */
+#if FOO
+#endif
+/* baz */
+// CHECK: /* baz */
+
+_Pragma("unknown") // after unknown pragma
+// CHECK: #pragma unknown
+// CHECK-NEXT: #
+// CHECK-NEXT: // after unknown pragma
+
+_Pragma("comment(\"abc\")") // after known pragma
+// CHECK: #pragma comment("abc")
+// CHECK-NEXT: #
+// CHECK-NEXT: // after known pragma
diff --git a/test/Preprocessor/disabled-cond-diags2.c b/test/Preprocessor/disabled-cond-diags2.c
new file mode 100644
index 0000000..d0629ae
--- /dev/null
+++ b/test/Preprocessor/disabled-cond-diags2.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -Eonly -verify %s
+
+#if 0
+#if 1
+#endif junk // shouldn't produce diagnostics
+#endif
+
+#if 0
+#endif junk // expected-warning{{extra tokens at end of #endif directive}}
+
+#if 1 junk // expected-error{{token is not a valid binary operator in a preprocessor subexpression}}
+#X // shouldn't produce diagnostics (block #if condition not valid, so skipped)
+#else
+#X // expected-error{{invalid preprocessing directive}}
+#endif
+
+#if 0
+// diagnostics should not be produced until final #endif
+#X
+#include
+#if 1 junk
+#else junk
+#endif junk
+#line -2
+#error
+#warning
+#endif junk // expected-warning{{extra tokens at end of #endif directive}}
diff --git a/test/Preprocessor/has_attribute.c b/test/Preprocessor/has_attribute.c
index 80f53a5..711cf67 100644
--- a/test/Preprocessor/has_attribute.c
+++ b/test/Preprocessor/has_attribute.c
@@ -24,3 +24,13 @@ int has_has_attribute();
#if !__has_attribute(something_we_dont_have)
int has_something_we_dont_have();
#endif
+
+// rdar://10253857
+#if __has_attribute(__const)
+ int fn3() __attribute__ ((__const));
+#endif
+
+#if __has_attribute(const)
+ static int constFunction() __attribute__((const));
+#endif
+
diff --git a/test/Preprocessor/has_include.c b/test/Preprocessor/has_include.c
index fdcae78..13c8d56 100644
--- a/test/Preprocessor/has_include.c
+++ b/test/Preprocessor/has_include.c
@@ -65,19 +65,45 @@
#endif
// Try badly formed expressions.
-// FIXME: I don't quite know how to avoid preprocessor side effects.
-// Use FileCheck?
-// It also assert due to unterminated #if's.
+// FIXME: We can recover better in almost all of these cases. (PR13335)
+
+// expected-error@+1 {{missing '(' after '__has_include'}} expected-error@+1 {{expected end of line}}
+#if __has_include "stdint.h")
+#endif
+
+// expected-error@+1 {{expected "FILENAME" or <FILENAME>}} expected-error@+1 {{token is not a valid binary operator in a preprocessor subexpression}}
+#if __has_include(stdint.h)
+#endif
+
+// expected-error@+1 {{expected "FILENAME" or <FILENAME>}}
+#if __has_include()
+#endif
+
+// expected-error@+1 {{missing '(' after '__has_include'}}
+#if __has_include)
+#endif
+
+// expected-error@+1 {{missing '(' after '__has_include'}} expected-error@+1 {{token is not a valid binary operator in a preprocessor subexpression}}
+#if __has_include<stdint.h>)
+#endif
+
+// expected-error@+1 {{expected "FILENAME" or <FILENAME>}} expected-warning@+1 {{missing terminating '"' character}}
+#if __has_include("stdint.h)
+#endif
+
+// expected-error@+1 {{expected "FILENAME" or <FILENAME>}} expected-warning@+1 {{missing terminating '"' character}} expected-error@+1 {{token is not a valid binary operator in a preprocessor subexpression}}
+#if __has_include(stdint.h")
+#endif
+
+// expected-error@+1 {{expected "FILENAME" or <FILENAME>}} expected-error@+1 {{token is not a valid binary operator in a preprocessor subexpression}}
+#if __has_include(stdint.h>)
+#endif
+
+
+// FIXME: These test cases cause the compiler to crash. (PR13334)
//#if __has_include("stdint.h"
-//#if __has_include "stdint.h")
-//#if __has_include(stdint.h)
-//#if __has_include()
//#if __has_include(
-//#if __has_include)
//#if __has_include
//#if __has_include(<stdint.h>
-//#if __has_include<stdint.h>)
-//#if __has_include("stdint.h)
-//#if __has_include(stdint.h")
//#if __has_include(<stdint.h)
-//#if __has_include(stdint.h>)
+
diff --git a/test/Preprocessor/init.c b/test/Preprocessor/init.c
index e0f45f1..e7321e0 100644
--- a/test/Preprocessor/init.c
+++ b/test/Preprocessor/init.c
@@ -48,6 +48,9 @@
// COMMON:#define __GNUC_STDC_INLINE__ 1
// COMMON:#define __GNUC__
// COMMON:#define __GXX_ABI_VERSION
+// COMMON:#define __ORDER_BIG_ENDIAN__ 4321
+// COMMON:#define __ORDER_LITTLE_ENDIAN__ 1234
+// COMMON:#define __ORDER_PDP_ENDIAN__ 3412
// COMMON:#define __STDC_HOSTED__ 1
// COMMON:#define __STDC_VERSION__
// COMMON:#define __STDC__ 1
@@ -62,12 +65,21 @@
//
// RUN: %clang_cc1 -ffreestanding -E -dM < /dev/null | FileCheck -check-prefix FREESTANDING %s
// FREESTANDING:#define __STDC_HOSTED__ 0
-//
+//
+//
+// RUN: %clang_cc1 -x c++ -std=gnu++11 -E -dM < /dev/null | FileCheck -check-prefix GXX11 %s
+//
+// GXX11:#define __GNUG__
+// GXX11:#define __GXX_WEAK__ 1
+// GXX11:#define __cplusplus 201103L
+// GXX11:#define __private_extern__ extern
+//
+//
// RUN: %clang_cc1 -x c++ -std=gnu++98 -E -dM < /dev/null | FileCheck -check-prefix GXX98 %s
//
// GXX98:#define __GNUG__
// GXX98:#define __GXX_WEAK__ 1
-// GXX98:#define __cplusplus 1
+// GXX98:#define __cplusplus 199711L
// GXX98:#define __private_extern__ extern
//
//
@@ -76,7 +88,7 @@
// C94:#define __STDC_VERSION__ 199409L
//
//
-// RUN: %clang_cc1 -fms-extensions -triple i686-pc-win32 -fobjc-fragile-abi -E -dM < /dev/null | FileCheck -check-prefix MSEXT %s
+// RUN: %clang_cc1 -fms-extensions -triple i686-pc-win32 -fobjc-runtime=gcc -E -dM < /dev/null | FileCheck -check-prefix MSEXT %s
//
// MSEXT-NOT:#define __STDC__
// MSEXT:#define _INTEGRAL_MAX_BITS 64
@@ -99,12 +111,39 @@
// NONFRAGILE:#define OBJC_ZEROCOST_EXCEPTIONS 1
// NONFRAGILE:#define __OBJC2__ 1
//
-//
+//
+// RUN: %clang_cc1 -O0 -E -dM < /dev/null | FileCheck -check-prefix O0 %s
+//
+// O0:#define __NO_INLINE__ 1
+// O0-NOT:#define __OPTIMIZE_SIZE__
+// O0-NOT:#define __OPTIMIZE__
+//
+//
+// RUN: %clang_cc1 -fno-inline -O3 -E -dM < /dev/null | FileCheck -check-prefix NO_INLINE %s
+//
+// NO_INLINE:#define __NO_INLINE__ 1
+// NO_INLINE-NOT:#define __OPTIMIZE_SIZE__
+// NO_INLINE:#define __OPTIMIZE__
+//
+//
// RUN: %clang_cc1 -O1 -E -dM < /dev/null | FileCheck -check-prefix O1 %s
//
+// O1-NOT:#define __OPTIMIZE_SIZE__
// O1:#define __OPTIMIZE__ 1
//
-//
+//
+// RUN: %clang_cc1 -Os -E -dM < /dev/null | FileCheck -check-prefix Os %s
+//
+// Os:#define __OPTIMIZE_SIZE__ 1
+// Os:#define __OPTIMIZE__ 1
+//
+//
+// RUN: %clang_cc1 -Oz -E -dM < /dev/null | FileCheck -check-prefix Oz %s
+//
+// Oz:#define __OPTIMIZE_SIZE__ 1
+// Oz:#define __OPTIMIZE__ 1
+//
+//
// RUN: %clang_cc1 -fpascal-strings -E -dM < /dev/null | FileCheck -check-prefix PASCAL %s
//
// PASCAL:#define __PASCAL_STRINGS__ 1
@@ -125,9 +164,11 @@
//
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=arm-none-none < /dev/null | FileCheck -check-prefix ARM %s
//
+// ARM-NOT:#define _LP64
// ARM:#define __APCS_32__ 1
// ARM:#define __ARMEL__ 1
// ARM:#define __ARM_ARCH_6J__ 1
+// ARM:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// ARM:#define __CHAR16_TYPE__ unsigned short
// ARM:#define __CHAR32_TYPE__ unsigned int
// ARM:#define __CHAR_BIT__ 8
@@ -187,7 +228,7 @@
// ARM:#define __LITTLE_ENDIAN__ 1
// ARM:#define __LONG_LONG_MAX__ 9223372036854775807LL
// ARM:#define __LONG_MAX__ 2147483647L
-// ARM:#define __NO_INLINE__ 1
+// ARM-NOT:#define __LP64__
// ARM:#define __POINTER_WIDTH__ 32
// ARM:#define __PTRDIFF_TYPE__ int
// ARM:#define __PTRDIFF_WIDTH__ 32
@@ -222,6 +263,8 @@
//
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=i386-none-none < /dev/null | FileCheck -check-prefix I386 %s
//
+// I386-NOT:#define _LP64
+// I386:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// I386:#define __CHAR16_TYPE__ unsigned short
// I386:#define __CHAR32_TYPE__ unsigned int
// I386:#define __CHAR_BIT__ 8
@@ -281,7 +324,7 @@
// I386:#define __LITTLE_ENDIAN__ 1
// I386:#define __LONG_LONG_MAX__ 9223372036854775807LL
// I386:#define __LONG_MAX__ 2147483647L
-// I386:#define __NO_INLINE__ 1
+// I386-NOT:#define __LP64__
// I386:#define __NO_MATH_INLINES 1
// I386:#define __POINTER_WIDTH__ 32
// I386:#define __PTRDIFF_TYPE__ int
@@ -317,6 +360,8 @@
//
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=i386-pc-linux-gnu -target-cpu pentium4 < /dev/null | FileCheck -check-prefix I386-LINUX %s
//
+// I386-LINUX-NOT:#define _LP64
+// I386-LINUX:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// I386-LINUX:#define __CHAR16_TYPE__ unsigned short
// I386-LINUX:#define __CHAR32_TYPE__ unsigned int
// I386-LINUX:#define __CHAR_BIT__ 8
@@ -376,7 +421,7 @@
// I386-LINUX:#define __LITTLE_ENDIAN__ 1
// I386-LINUX:#define __LONG_LONG_MAX__ 9223372036854775807LL
// I386-LINUX:#define __LONG_MAX__ 2147483647L
-// I386-LINUX:#define __NO_INLINE__ 1
+// I386-LINUX-NOT:#define __LP64__
// I386-LINUX:#define __NO_MATH_INLINES 1
// I386-LINUX:#define __POINTER_WIDTH__ 32
// I386-LINUX:#define __PTRDIFF_TYPE__ int
@@ -414,11 +459,13 @@
//
// MIPS32BE:#define MIPSEB 1
// MIPS32BE:#define _ABIO32 1
+// MIPS32BE-NOT:#define _LP64
// MIPS32BE:#define _MIPSEB 1
// MIPS32BE:#define _MIPS_SIM _ABIO32
// MIPS32BE:#define _MIPS_SZINT 32
// MIPS32BE:#define _MIPS_SZLONG 32
// MIPS32BE:#define _MIPS_SZPTR 32
+// MIPS32BE:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// MIPS32BE:#define __CHAR16_TYPE__ unsigned short
// MIPS32BE:#define __CHAR32_TYPE__ unsigned int
// MIPS32BE:#define __CHAR_BIT__ 8
@@ -478,9 +525,9 @@
// MIPS32BE:#define __LDBL_MIN__ 2.2250738585072014e-308
// MIPS32BE:#define __LONG_LONG_MAX__ 9223372036854775807LL
// MIPS32BE:#define __LONG_MAX__ 2147483647L
+// MIPS32BE-NOT:#define __LP64__
// MIPS32BE:#define __MIPSEB 1
// MIPS32BE:#define __MIPSEB__ 1
-// MIPS32BE:#define __NO_INLINE__ 1
// MIPS32BE:#define __POINTER_WIDTH__ 32
// MIPS32BE:#define __PRAGMA_REDEFINE_EXTNAME 1
// MIPS32BE:#define __PTRDIFF_TYPE__ int
@@ -526,11 +573,13 @@
//
// MIPS32EL:#define MIPSEL 1
// MIPS32EL:#define _ABIO32 1
+// MIPS32EL-NOT:#define _LP64
// MIPS32EL:#define _MIPSEL 1
// MIPS32EL:#define _MIPS_SIM _ABIO32
// MIPS32EL:#define _MIPS_SZINT 32
// MIPS32EL:#define _MIPS_SZLONG 32
// MIPS32EL:#define _MIPS_SZPTR 32
+// MIPS32EL:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// MIPS32EL:#define __CHAR16_TYPE__ unsigned short
// MIPS32EL:#define __CHAR32_TYPE__ unsigned int
// MIPS32EL:#define __CHAR_BIT__ 8
@@ -590,9 +639,9 @@
// MIPS32EL:#define __LDBL_MIN__ 2.2250738585072014e-308
// MIPS32EL:#define __LONG_LONG_MAX__ 9223372036854775807LL
// MIPS32EL:#define __LONG_MAX__ 2147483647L
+// MIPS32EL-NOT:#define __LP64__
// MIPS32EL:#define __MIPSEL 1
// MIPS32EL:#define __MIPSEL__ 1
-// MIPS32EL:#define __NO_INLINE__ 1
// MIPS32EL:#define __POINTER_WIDTH__ 32
// MIPS32EL:#define __PRAGMA_REDEFINE_EXTNAME 1
// MIPS32EL:#define __PTRDIFF_TYPE__ int
@@ -635,11 +684,13 @@
//
// MIPS64BE:#define MIPSEB 1
// MIPS64BE:#define _ABI64 3
+// MIPS64BE:#define _LP64 1
// MIPS64BE:#define _MIPSEB 1
// MIPS64BE:#define _MIPS_SIM _ABI64
// MIPS64BE:#define _MIPS_SZINT 32
// MIPS64BE:#define _MIPS_SZLONG 64
// MIPS64BE:#define _MIPS_SZPTR 64
+// MIPS64BE:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// MIPS64BE:#define __CHAR16_TYPE__ unsigned short
// MIPS64BE:#define __CHAR32_TYPE__ unsigned int
// MIPS64BE:#define __CHAR_BIT__ 8
@@ -699,9 +750,9 @@
// MIPS64BE:#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
// MIPS64BE:#define __LONG_LONG_MAX__ 9223372036854775807LL
// MIPS64BE:#define __LONG_MAX__ 9223372036854775807L
+// MIPS64BE:#define __LP64__ 1
// MIPS64BE:#define __MIPSEB 1
// MIPS64BE:#define __MIPSEB__ 1
-// MIPS64BE:#define __NO_INLINE__ 1
// MIPS64BE:#define __POINTER_WIDTH__ 64
// MIPS64BE:#define __PRAGMA_REDEFINE_EXTNAME 1
// MIPS64BE:#define __PTRDIFF_TYPE__ long int
@@ -744,11 +795,13 @@
//
// MIPS64EL:#define MIPSEL 1
// MIPS64EL:#define _ABI64 3
+// MIPS64EL:#define _LP64 1
// MIPS64EL:#define _MIPSEL 1
// MIPS64EL:#define _MIPS_SIM _ABI64
// MIPS64EL:#define _MIPS_SZINT 32
// MIPS64EL:#define _MIPS_SZLONG 64
// MIPS64EL:#define _MIPS_SZPTR 64
+// MIPS64EL:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// MIPS64EL:#define __CHAR16_TYPE__ unsigned short
// MIPS64EL:#define __CHAR32_TYPE__ unsigned int
// MIPS64EL:#define __CHAR_BIT__ 8
@@ -808,9 +861,9 @@
// MIPS64EL:#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
// MIPS64EL:#define __LONG_LONG_MAX__ 9223372036854775807LL
// MIPS64EL:#define __LONG_MAX__ 9223372036854775807L
+// MIPS64EL:#define __LP64__ 1
// MIPS64EL:#define __MIPSEL 1
// MIPS64EL:#define __MIPSEL__ 1
-// MIPS64EL:#define __NO_INLINE__ 1
// MIPS64EL:#define __POINTER_WIDTH__ 64
// MIPS64EL:#define __PRAGMA_REDEFINE_EXTNAME 1
// MIPS64EL:#define __PTRDIFF_TYPE__ long int
@@ -864,11 +917,40 @@
// RUN: %clang_cc1 -target-feature +single-float -E -dM -ffreestanding \
// RUN: -triple=mips-none-none < /dev/null \
// RUN: | FileCheck -check-prefix MIPS-FABI-SINGLE %s
+// MIPS-FABI-SINGLE:#define __mips_hard_float 1
// MIPS-FABI-SINGLE:#define __mips_single_float 1
//
+// Check MIPS features macros
+//
+// RUN: %clang_cc1 -target-feature +mips16 \
+// RUN: -E -dM -triple=mips-none-none < /dev/null \
+// RUN: | FileCheck -check-prefix MIPS16 %s
+// MIPS16:#define __mips16 1
+//
+// RUN: %clang_cc1 -target-feature -mips16 \
+// RUN: -E -dM -triple=mips-none-none < /dev/null \
+// RUN: | FileCheck -check-prefix NOMIPS16 %s
+// NOMIPS16-NOT:#define __mips16 1
+//
+// RUN: %clang_cc1 -target-feature +dsp \
+// RUN: -E -dM -triple=mips-none-none < /dev/null \
+// RUN: | FileCheck -check-prefix MIPS-DSP %s
+// MIPS-DSP:#define __mips_dsp 1
+// MIPS-DSP:#define __mips_dsp_rev 1
+// MIPS-DSP-NOT:#define __mips_dspr2 1
+//
+// RUN: %clang_cc1 -target-feature +dspr2 \
+// RUN: -E -dM -triple=mips-none-none < /dev/null \
+// RUN: | FileCheck -check-prefix MIPS-DSPR2 %s
+// MIPS-DSPR2:#define __mips_dsp 1
+// MIPS-DSPR2:#define __mips_dsp_rev 2
+// MIPS-DSPR2:#define __mips_dspr2 1
+//
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=msp430-none-none < /dev/null | FileCheck -check-prefix MSP430 %s
//
// MSP430:#define MSP430 1
+// MSP430-NOT:#define _LP64
+// MSP430:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// MSP430:#define __CHAR16_TYPE__ unsigned short
// MSP430:#define __CHAR32_TYPE__ unsigned int
// MSP430:#define __CHAR_BIT__ 8
@@ -926,8 +1008,8 @@
// MSP430:#define __LDBL_MIN__ 2.2250738585072014e-308
// MSP430:#define __LONG_LONG_MAX__ 9223372036854775807LL
// MSP430:#define __LONG_MAX__ 2147483647L
+// MSP430-NOT:#define __LP64__
// MSP430:#define __MSP430__ 1
-// MSP430:#define __NO_INLINE__ 1
// MSP430:#define __POINTER_WIDTH__ 16
// MSP430:#define __PTRDIFF_TYPE__ int
// MSP430:#define __PTRDIFF_WIDTH__ 16
@@ -957,13 +1039,315 @@
// MSP430:#define __WINT_WIDTH__ 16
// MSP430:#define __clang__ 1
//
-// RUN: %clang_cc1 -E -dM -ffreestanding -triple=powerpc64-none-none -fno-signed-char < /dev/null | FileCheck -check-prefix PPC64 %s
+// RUN: %clang_cc1 -E -dM -ffreestanding -triple=nvptx-none-none < /dev/null | FileCheck -check-prefix NVPTX32 %s
+//
+// NVPTX32-NOT:#define _LP64
+// NVPTX32:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+// NVPTX32:#define __CHAR16_TYPE__ unsigned short
+// NVPTX32:#define __CHAR32_TYPE__ unsigned int
+// NVPTX32:#define __CHAR_BIT__ 8
+// NVPTX32:#define __CONSTANT_CFSTRINGS__ 1
+// NVPTX32:#define __DBL_DENORM_MIN__ 4.9406564584124654e-324
+// NVPTX32:#define __DBL_DIG__ 15
+// NVPTX32:#define __DBL_EPSILON__ 2.2204460492503131e-16
+// NVPTX32:#define __DBL_HAS_DENORM__ 1
+// NVPTX32:#define __DBL_HAS_INFINITY__ 1
+// NVPTX32:#define __DBL_HAS_QUIET_NAN__ 1
+// NVPTX32:#define __DBL_MANT_DIG__ 53
+// NVPTX32:#define __DBL_MAX_10_EXP__ 308
+// NVPTX32:#define __DBL_MAX_EXP__ 1024
+// NVPTX32:#define __DBL_MAX__ 1.7976931348623157e+308
+// NVPTX32:#define __DBL_MIN_10_EXP__ (-307)
+// NVPTX32:#define __DBL_MIN_EXP__ (-1021)
+// NVPTX32:#define __DBL_MIN__ 2.2250738585072014e-308
+// NVPTX32:#define __DECIMAL_DIG__ 17
+// NVPTX32:#define __FINITE_MATH_ONLY__ 0
+// NVPTX32:#define __FLT_DENORM_MIN__ 1.40129846e-45F
+// NVPTX32:#define __FLT_DIG__ 6
+// NVPTX32:#define __FLT_EPSILON__ 1.19209290e-7F
+// NVPTX32:#define __FLT_EVAL_METHOD__ 0
+// NVPTX32:#define __FLT_HAS_DENORM__ 1
+// NVPTX32:#define __FLT_HAS_INFINITY__ 1
+// NVPTX32:#define __FLT_HAS_QUIET_NAN__ 1
+// NVPTX32:#define __FLT_MANT_DIG__ 24
+// NVPTX32:#define __FLT_MAX_10_EXP__ 38
+// NVPTX32:#define __FLT_MAX_EXP__ 128
+// NVPTX32:#define __FLT_MAX__ 3.40282347e+38F
+// NVPTX32:#define __FLT_MIN_10_EXP__ (-37)
+// NVPTX32:#define __FLT_MIN_EXP__ (-125)
+// NVPTX32:#define __FLT_MIN__ 1.17549435e-38F
+// NVPTX32:#define __FLT_RADIX__ 2
+// NVPTX32:#define __INT16_TYPE__ short
+// NVPTX32:#define __INT32_TYPE__ int
+// NVPTX32:#define __INT64_C_SUFFIX__ LL
+// NVPTX32:#define __INT64_TYPE__ long long int
+// NVPTX32:#define __INT8_TYPE__ char
+// NVPTX32:#define __INTMAX_MAX__ 9223372036854775807LL
+// NVPTX32:#define __INTMAX_TYPE__ long long int
+// NVPTX32:#define __INTMAX_WIDTH__ 64
+// NVPTX32:#define __INTPTR_TYPE__ unsigned int
+// NVPTX32:#define __INTPTR_WIDTH__ 32
+// NVPTX32:#define __INT_MAX__ 2147483647
+// NVPTX32:#define __LDBL_DENORM_MIN__ 4.9406564584124654e-324
+// NVPTX32:#define __LDBL_DIG__ 15
+// NVPTX32:#define __LDBL_EPSILON__ 2.2204460492503131e-16
+// NVPTX32:#define __LDBL_HAS_DENORM__ 1
+// NVPTX32:#define __LDBL_HAS_INFINITY__ 1
+// NVPTX32:#define __LDBL_HAS_QUIET_NAN__ 1
+// NVPTX32:#define __LDBL_MANT_DIG__ 53
+// NVPTX32:#define __LDBL_MAX_10_EXP__ 308
+// NVPTX32:#define __LDBL_MAX_EXP__ 1024
+// NVPTX32:#define __LDBL_MAX__ 1.7976931348623157e+308
+// NVPTX32:#define __LDBL_MIN_10_EXP__ (-307)
+// NVPTX32:#define __LDBL_MIN_EXP__ (-1021)
+// NVPTX32:#define __LDBL_MIN__ 2.2250738585072014e-308
+// NVPTX32:#define __LONG_LONG_MAX__ 9223372036854775807LL
+// NVPTX32:#define __LONG_MAX__ 9223372036854775807L
+// NVPTX32-NOT:#define __LP64__
+// NVPTX32:#define __NVPTX__ 1
+// NVPTX32:#define __POINTER_WIDTH__ 32
+// NVPTX32:#define __PRAGMA_REDEFINE_EXTNAME 1
+// NVPTX32:#define __PTRDIFF_TYPE__ unsigned int
+// NVPTX32:#define __PTRDIFF_WIDTH__ 32
+// NVPTX32:#define __PTX__ 1
+// NVPTX32:#define __SCHAR_MAX__ 127
+// NVPTX32:#define __SHRT_MAX__ 32767
+// NVPTX32:#define __SIG_ATOMIC_WIDTH__ 32
+// NVPTX32:#define __SIZEOF_DOUBLE__ 8
+// NVPTX32:#define __SIZEOF_FLOAT__ 4
+// NVPTX32:#define __SIZEOF_INT__ 4
+// NVPTX32:#define __SIZEOF_LONG_DOUBLE__ 8
+// NVPTX32:#define __SIZEOF_LONG_LONG__ 8
+// NVPTX32:#define __SIZEOF_LONG__ 8
+// NVPTX32:#define __SIZEOF_POINTER__ 4
+// NVPTX32:#define __SIZEOF_PTRDIFF_T__ 4
+// NVPTX32:#define __SIZEOF_SHORT__ 2
+// NVPTX32:#define __SIZEOF_SIZE_T__ 4
+// NVPTX32:#define __SIZEOF_WCHAR_T__ 4
+// NVPTX32:#define __SIZEOF_WINT_T__ 4
+// NVPTX32:#define __SIZE_TYPE__ unsigned int
+// NVPTX32:#define __SIZE_WIDTH__ 32
+// NVPTX32:#define __UINTMAX_TYPE__ long long unsigned int
+// NVPTX32:#define __USER_LABEL_PREFIX__ _
+// NVPTX32:#define __WCHAR_MAX__ 2147483647
+// NVPTX32:#define __WCHAR_TYPE__ int
+// NVPTX32:#define __WCHAR_WIDTH__ 32
+// NVPTX32:#define __WINT_TYPE__ int
+// NVPTX32:#define __WINT_WIDTH__ 32
+//
+// RUN: %clang_cc1 -E -dM -ffreestanding -triple=nvptx64-none-none < /dev/null | FileCheck -check-prefix NVPTX64 %s
+//
+// NVPTX64:#define _LP64 1
+// NVPTX64:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+// NVPTX64:#define __CHAR16_TYPE__ unsigned short
+// NVPTX64:#define __CHAR32_TYPE__ unsigned int
+// NVPTX64:#define __CHAR_BIT__ 8
+// NVPTX64:#define __CONSTANT_CFSTRINGS__ 1
+// NVPTX64:#define __DBL_DENORM_MIN__ 4.9406564584124654e-324
+// NVPTX64:#define __DBL_DIG__ 15
+// NVPTX64:#define __DBL_EPSILON__ 2.2204460492503131e-16
+// NVPTX64:#define __DBL_HAS_DENORM__ 1
+// NVPTX64:#define __DBL_HAS_INFINITY__ 1
+// NVPTX64:#define __DBL_HAS_QUIET_NAN__ 1
+// NVPTX64:#define __DBL_MANT_DIG__ 53
+// NVPTX64:#define __DBL_MAX_10_EXP__ 308
+// NVPTX64:#define __DBL_MAX_EXP__ 1024
+// NVPTX64:#define __DBL_MAX__ 1.7976931348623157e+308
+// NVPTX64:#define __DBL_MIN_10_EXP__ (-307)
+// NVPTX64:#define __DBL_MIN_EXP__ (-1021)
+// NVPTX64:#define __DBL_MIN__ 2.2250738585072014e-308
+// NVPTX64:#define __DECIMAL_DIG__ 17
+// NVPTX64:#define __FINITE_MATH_ONLY__ 0
+// NVPTX64:#define __FLT_DENORM_MIN__ 1.40129846e-45F
+// NVPTX64:#define __FLT_DIG__ 6
+// NVPTX64:#define __FLT_EPSILON__ 1.19209290e-7F
+// NVPTX64:#define __FLT_EVAL_METHOD__ 0
+// NVPTX64:#define __FLT_HAS_DENORM__ 1
+// NVPTX64:#define __FLT_HAS_INFINITY__ 1
+// NVPTX64:#define __FLT_HAS_QUIET_NAN__ 1
+// NVPTX64:#define __FLT_MANT_DIG__ 24
+// NVPTX64:#define __FLT_MAX_10_EXP__ 38
+// NVPTX64:#define __FLT_MAX_EXP__ 128
+// NVPTX64:#define __FLT_MAX__ 3.40282347e+38F
+// NVPTX64:#define __FLT_MIN_10_EXP__ (-37)
+// NVPTX64:#define __FLT_MIN_EXP__ (-125)
+// NVPTX64:#define __FLT_MIN__ 1.17549435e-38F
+// NVPTX64:#define __FLT_RADIX__ 2
+// NVPTX64:#define __INT16_TYPE__ short
+// NVPTX64:#define __INT32_TYPE__ int
+// NVPTX64:#define __INT64_C_SUFFIX__ LL
+// NVPTX64:#define __INT64_TYPE__ long long int
+// NVPTX64:#define __INT8_TYPE__ char
+// NVPTX64:#define __INTMAX_MAX__ 9223372036854775807LL
+// NVPTX64:#define __INTMAX_TYPE__ long long int
+// NVPTX64:#define __INTMAX_WIDTH__ 64
+// NVPTX64:#define __INTPTR_TYPE__ long long unsigned int
+// NVPTX64:#define __INTPTR_WIDTH__ 64
+// NVPTX64:#define __INT_MAX__ 2147483647
+// NVPTX64:#define __LDBL_DENORM_MIN__ 4.9406564584124654e-324
+// NVPTX64:#define __LDBL_DIG__ 15
+// NVPTX64:#define __LDBL_EPSILON__ 2.2204460492503131e-16
+// NVPTX64:#define __LDBL_HAS_DENORM__ 1
+// NVPTX64:#define __LDBL_HAS_INFINITY__ 1
+// NVPTX64:#define __LDBL_HAS_QUIET_NAN__ 1
+// NVPTX64:#define __LDBL_MANT_DIG__ 53
+// NVPTX64:#define __LDBL_MAX_10_EXP__ 308
+// NVPTX64:#define __LDBL_MAX_EXP__ 1024
+// NVPTX64:#define __LDBL_MAX__ 1.7976931348623157e+308
+// NVPTX64:#define __LDBL_MIN_10_EXP__ (-307)
+// NVPTX64:#define __LDBL_MIN_EXP__ (-1021)
+// NVPTX64:#define __LDBL_MIN__ 2.2250738585072014e-308
+// NVPTX64:#define __LONG_LONG_MAX__ 9223372036854775807LL
+// NVPTX64:#define __LONG_MAX__ 9223372036854775807L
+// NVPTX64:#define __LP64__ 1
+// NVPTX64:#define __NVPTX__ 1
+// NVPTX64:#define __POINTER_WIDTH__ 64
+// NVPTX64:#define __PRAGMA_REDEFINE_EXTNAME 1
+// NVPTX64:#define __PTRDIFF_TYPE__ long long unsigned int
+// NVPTX64:#define __PTRDIFF_WIDTH__ 64
+// NVPTX64:#define __PTX__ 1
+// NVPTX64:#define __SCHAR_MAX__ 127
+// NVPTX64:#define __SHRT_MAX__ 32767
+// NVPTX64:#define __SIG_ATOMIC_WIDTH__ 32
+// NVPTX64:#define __SIZEOF_DOUBLE__ 8
+// NVPTX64:#define __SIZEOF_FLOAT__ 4
+// NVPTX64:#define __SIZEOF_INT__ 4
+// NVPTX64:#define __SIZEOF_LONG_DOUBLE__ 8
+// NVPTX64:#define __SIZEOF_LONG_LONG__ 8
+// NVPTX64:#define __SIZEOF_LONG__ 8
+// NVPTX64:#define __SIZEOF_POINTER__ 8
+// NVPTX64:#define __SIZEOF_PTRDIFF_T__ 8
+// NVPTX64:#define __SIZEOF_SHORT__ 2
+// NVPTX64:#define __SIZEOF_SIZE_T__ 8
+// NVPTX64:#define __SIZEOF_WCHAR_T__ 4
+// NVPTX64:#define __SIZEOF_WINT_T__ 4
+// NVPTX64:#define __SIZE_TYPE__ long long unsigned int
+// NVPTX64:#define __SIZE_WIDTH__ 64
+// NVPTX64:#define __UINTMAX_TYPE__ long long unsigned int
+// NVPTX64:#define __USER_LABEL_PREFIX__ _
+// NVPTX64:#define __WCHAR_MAX__ 2147483647
+// NVPTX64:#define __WCHAR_TYPE__ int
+// NVPTX64:#define __WCHAR_WIDTH__ 32
+// NVPTX64:#define __WINT_TYPE__ int
+// NVPTX64:#define __WINT_WIDTH__ 32
+//
+// RUN: %clang_cc1 -E -dM -ffreestanding -triple=powerpc-none-none -target-cpu 603e < /dev/null | FileCheck -check-prefix PPC603E %s
+//
+// PPC603E:#define _ARCH_603 1
+// PPC603E:#define _ARCH_603E 1
+// PPC603E:#define _ARCH_PPC 1
+// PPC603E:#define _ARCH_PPCGR 1
+// PPC603E:#define _BIG_ENDIAN 1
+// PPC603E-NOT:#define _LP64
+// PPC603E:#define __BIG_ENDIAN__ 1
+// PPC603E:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
+// PPC603E:#define __CHAR16_TYPE__ unsigned short
+// PPC603E:#define __CHAR32_TYPE__ unsigned int
+// PPC603E:#define __CHAR_BIT__ 8
+// PPC603E:#define __DBL_DENORM_MIN__ 4.9406564584124654e-324
+// PPC603E:#define __DBL_DIG__ 15
+// PPC603E:#define __DBL_EPSILON__ 2.2204460492503131e-16
+// PPC603E:#define __DBL_HAS_DENORM__ 1
+// PPC603E:#define __DBL_HAS_INFINITY__ 1
+// PPC603E:#define __DBL_HAS_QUIET_NAN__ 1
+// PPC603E:#define __DBL_MANT_DIG__ 53
+// PPC603E:#define __DBL_MAX_10_EXP__ 308
+// PPC603E:#define __DBL_MAX_EXP__ 1024
+// PPC603E:#define __DBL_MAX__ 1.7976931348623157e+308
+// PPC603E:#define __DBL_MIN_10_EXP__ (-307)
+// PPC603E:#define __DBL_MIN_EXP__ (-1021)
+// PPC603E:#define __DBL_MIN__ 2.2250738585072014e-308
+// PPC603E:#define __DECIMAL_DIG__ 33
+// PPC603E:#define __FLT_DENORM_MIN__ 1.40129846e-45F
+// PPC603E:#define __FLT_DIG__ 6
+// PPC603E:#define __FLT_EPSILON__ 1.19209290e-7F
+// PPC603E:#define __FLT_EVAL_METHOD__ 0
+// PPC603E:#define __FLT_HAS_DENORM__ 1
+// PPC603E:#define __FLT_HAS_INFINITY__ 1
+// PPC603E:#define __FLT_HAS_QUIET_NAN__ 1
+// PPC603E:#define __FLT_MANT_DIG__ 24
+// PPC603E:#define __FLT_MAX_10_EXP__ 38
+// PPC603E:#define __FLT_MAX_EXP__ 128
+// PPC603E:#define __FLT_MAX__ 3.40282347e+38F
+// PPC603E:#define __FLT_MIN_10_EXP__ (-37)
+// PPC603E:#define __FLT_MIN_EXP__ (-125)
+// PPC603E:#define __FLT_MIN__ 1.17549435e-38F
+// PPC603E:#define __FLT_RADIX__ 2
+// PPC603E:#define __INT16_TYPE__ short
+// PPC603E:#define __INT32_TYPE__ int
+// PPC603E:#define __INT64_C_SUFFIX__ LL
+// PPC603E:#define __INT64_TYPE__ long long int
+// PPC603E:#define __INT8_TYPE__ char
+// PPC603E:#define __INTMAX_MAX__ 9223372036854775807LL
+// PPC603E:#define __INTMAX_TYPE__ long long int
+// PPC603E:#define __INTMAX_WIDTH__ 64
+// PPC603E:#define __INTPTR_TYPE__ long int
+// PPC603E:#define __INTPTR_WIDTH__ 32
+// PPC603E:#define __INT_MAX__ 2147483647
+// PPC603E:#define __LDBL_DENORM_MIN__ 4.94065645841246544176568792868221e-324L
+// PPC603E:#define __LDBL_DIG__ 31
+// PPC603E:#define __LDBL_EPSILON__ 4.94065645841246544176568792868221e-324L
+// PPC603E:#define __LDBL_HAS_DENORM__ 1
+// PPC603E:#define __LDBL_HAS_INFINITY__ 1
+// PPC603E:#define __LDBL_HAS_QUIET_NAN__ 1
+// PPC603E:#define __LDBL_MANT_DIG__ 106
+// PPC603E:#define __LDBL_MAX_10_EXP__ 308
+// PPC603E:#define __LDBL_MAX_EXP__ 1024
+// PPC603E:#define __LDBL_MAX__ 1.79769313486231580793728971405301e+308L
+// PPC603E:#define __LDBL_MIN_10_EXP__ (-291)
+// PPC603E:#define __LDBL_MIN_EXP__ (-968)
+// PPC603E:#define __LDBL_MIN__ 2.00416836000897277799610805135016e-292L
+// PPC603E:#define __LONG_DOUBLE_128__ 1
+// PPC603E:#define __LONG_LONG_MAX__ 9223372036854775807LL
+// PPC603E:#define __LONG_MAX__ 2147483647L
+// PPC603E-NOT:#define __LP64__
+// PPC603E:#define __NATURAL_ALIGNMENT__ 1
+// PPC603E:#define __POINTER_WIDTH__ 32
+// PPC603E:#define __POWERPC__ 1
+// PPC603E:#define __PTRDIFF_TYPE__ long int
+// PPC603E:#define __PTRDIFF_WIDTH__ 32
+// PPC603E:#define __REGISTER_PREFIX__
+// PPC603E:#define __SCHAR_MAX__ 127
+// PPC603E:#define __SHRT_MAX__ 32767
+// PPC603E:#define __SIG_ATOMIC_WIDTH__ 32
+// PPC603E:#define __SIZEOF_DOUBLE__ 8
+// PPC603E:#define __SIZEOF_FLOAT__ 4
+// PPC603E:#define __SIZEOF_INT__ 4
+// PPC603E:#define __SIZEOF_LONG_DOUBLE__ 16
+// PPC603E:#define __SIZEOF_LONG_LONG__ 8
+// PPC603E:#define __SIZEOF_LONG__ 4
+// PPC603E:#define __SIZEOF_POINTER__ 4
+// PPC603E:#define __SIZEOF_PTRDIFF_T__ 4
+// PPC603E:#define __SIZEOF_SHORT__ 2
+// PPC603E:#define __SIZEOF_SIZE_T__ 4
+// PPC603E:#define __SIZEOF_WCHAR_T__ 4
+// PPC603E:#define __SIZEOF_WINT_T__ 4
+// PPC603E:#define __SIZE_TYPE__ long unsigned int
+// PPC603E:#define __SIZE_WIDTH__ 32
+// PPC603E:#define __UINTMAX_TYPE__ long long unsigned int
+// PPC603E:#define __USER_LABEL_PREFIX__ _
+// PPC603E:#define __WCHAR_MAX__ 2147483647
+// PPC603E:#define __WCHAR_TYPE__ int
+// PPC603E:#define __WCHAR_WIDTH__ 32
+// PPC603E:#define __WINT_TYPE__ int
+// PPC603E:#define __WINT_WIDTH__ 32
+// PPC603E:#define __powerpc__ 1
+// PPC603E:#define __ppc__ 1
+//
+// RUN: %clang_cc1 -E -dM -ffreestanding -triple=powerpc64-none-none -target-cpu pwr7 -fno-signed-char < /dev/null | FileCheck -check-prefix PPC64 %s
//
// PPC64:#define _ARCH_PPC 1
// PPC64:#define _ARCH_PPC64 1
+// PPC64:#define _ARCH_PPCGR 1
+// PPC64:#define _ARCH_PPCSQ 1
+// PPC64:#define _ARCH_PWR4 1
+// PPC64:#define _ARCH_PWR5 1
+// PPC64:#define _ARCH_PWR6 1
+// PPC64:#define _ARCH_PWR7 1
// PPC64:#define _BIG_ENDIAN 1
// PPC64:#define _LP64 1
// PPC64:#define __BIG_ENDIAN__ 1
+// PPC64:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// PPC64:#define __CHAR16_TYPE__ unsigned short
// PPC64:#define __CHAR32_TYPE__ unsigned int
// PPC64:#define __CHAR_BIT__ 8
@@ -1026,7 +1410,6 @@
// PPC64:#define __LONG_MAX__ 9223372036854775807L
// PPC64:#define __LP64__ 1
// PPC64:#define __NATURAL_ALIGNMENT__ 1
-// PPC64:#define __NO_INLINE__ 1
// PPC64:#define __POINTER_WIDTH__ 64
// PPC64:#define __POWERPC__ 1
// PPC64:#define __PTRDIFF_TYPE__ long int
@@ -1066,6 +1449,7 @@
// PPC64-LINUX:#define _BIG_ENDIAN 1
// PPC64-LINUX:#define _LP64 1
// PPC64-LINUX:#define __BIG_ENDIAN__ 1
+// PPC64-LINUX:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// PPC64-LINUX:#define __CHAR16_TYPE__ unsigned short
// PPC64-LINUX:#define __CHAR32_TYPE__ unsigned int
// PPC64-LINUX:#define __CHAR_BIT__ 8
@@ -1128,7 +1512,6 @@
// PPC64-LINUX:#define __LONG_MAX__ 9223372036854775807L
// PPC64-LINUX:#define __LP64__ 1
// PPC64-LINUX:#define __NATURAL_ALIGNMENT__ 1
-// PPC64-LINUX:#define __NO_INLINE__ 1
// PPC64-LINUX:#define __POINTER_WIDTH__ 64
// PPC64-LINUX:#define __POWERPC__ 1
// PPC64-LINUX:#define __PTRDIFF_TYPE__ long int
@@ -1168,7 +1551,9 @@
//
// PPC:#define _ARCH_PPC 1
// PPC:#define _BIG_ENDIAN 1
+// PPC-NOT:#define _LP64
// PPC:#define __BIG_ENDIAN__ 1
+// PPC:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// PPC:#define __CHAR16_TYPE__ unsigned short
// PPC:#define __CHAR32_TYPE__ unsigned int
// PPC:#define __CHAR_BIT__ 8
@@ -1229,8 +1614,8 @@
// PPC:#define __LONG_DOUBLE_128__ 1
// PPC:#define __LONG_LONG_MAX__ 9223372036854775807LL
// PPC:#define __LONG_MAX__ 2147483647L
+// PPC-NOT:#define __LP64__
// PPC:#define __NATURAL_ALIGNMENT__ 1
-// PPC:#define __NO_INLINE__ 1
// PPC:#define __POINTER_WIDTH__ 32
// PPC:#define __POWERPC__ 1
// PPC:#define __PTRDIFF_TYPE__ long int
@@ -1266,7 +1651,9 @@
//
// PPC-LINUX:#define _ARCH_PPC 1
// PPC-LINUX:#define _BIG_ENDIAN 1
+// PPC-LINUX-NOT:#define _LP64
// PPC-LINUX:#define __BIG_ENDIAN__ 1
+// PPC-LINUX:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// PPC-LINUX:#define __CHAR16_TYPE__ unsigned short
// PPC-LINUX:#define __CHAR32_TYPE__ unsigned int
// PPC-LINUX:#define __CHAR_BIT__ 8
@@ -1327,8 +1714,8 @@
// PPC-LINUX:#define __LONG_DOUBLE_128__ 1
// PPC-LINUX:#define __LONG_LONG_MAX__ 9223372036854775807LL
// PPC-LINUX:#define __LONG_MAX__ 2147483647L
+// PPC-LINUX-NOT:#define __LP64__
// PPC-LINUX:#define __NATURAL_ALIGNMENT__ 1
-// PPC-LINUX:#define __NO_INLINE__ 1
// PPC-LINUX:#define __POINTER_WIDTH__ 32
// PPC-LINUX:#define __POWERPC__ 1
// PPC-LINUX:#define __PTRDIFF_TYPE__ int
@@ -1364,6 +1751,8 @@
//
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=sparc-none-none < /dev/null | FileCheck -check-prefix SPARC %s
//
+// SPARC-NOT:#define _LP64
+// SPARC:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// SPARC:#define __CHAR16_TYPE__ unsigned short
// SPARC:#define __CHAR32_TYPE__ unsigned int
// SPARC:#define __CHAR_BIT__ 8
@@ -1422,7 +1811,7 @@
// SPARC:#define __LDBL_MIN__ 2.2250738585072014e-308
// SPARC:#define __LONG_LONG_MAX__ 9223372036854775807LL
// SPARC:#define __LONG_MAX__ 2147483647L
-// SPARC:#define __NO_INLINE__ 1
+// SPARC-NOT:#define __LP64__
// SPARC:#define __POINTER_WIDTH__ 32
// SPARC:#define __PTRDIFF_TYPE__ long int
// SPARC:#define __PTRDIFF_WIDTH__ 32
@@ -1459,6 +1848,8 @@
//
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=tce-none-none < /dev/null | FileCheck -check-prefix TCE %s
//
+// TCE-NOT:#define _LP64
+// TCE:#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
// TCE:#define __CHAR16_TYPE__ unsigned short
// TCE:#define __CHAR32_TYPE__ unsigned int
// TCE:#define __CHAR_BIT__ 8
@@ -1515,7 +1906,7 @@
// TCE:#define __LDBL_MIN__ 1.17549435e-38F
// TCE:#define __LONG_LONG_MAX__ 2147483647LL
// TCE:#define __LONG_MAX__ 2147483647L
-// TCE:#define __NO_INLINE__ 1
+// TCE-NOT:#define __LP64__
// TCE:#define __POINTER_WIDTH__ 32
// TCE:#define __PTRDIFF_TYPE__ int
// TCE:#define __PTRDIFF_WIDTH__ 32
@@ -1552,6 +1943,7 @@
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=x86_64-none-none < /dev/null | FileCheck -check-prefix X86_64 %s
//
// X86_64:#define _LP64 1
+// X86_64:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// X86_64:#define __CHAR16_TYPE__ unsigned short
// X86_64:#define __CHAR32_TYPE__ unsigned int
// X86_64:#define __CHAR_BIT__ 8
@@ -1613,7 +2005,6 @@
// X86_64:#define __LONG_MAX__ 9223372036854775807L
// X86_64:#define __LP64__ 1
// X86_64:#define __MMX__ 1
-// X86_64:#define __NO_INLINE__ 1
// X86_64:#define __NO_MATH_INLINES 1
// X86_64:#define __POINTER_WIDTH__ 64
// X86_64:#define __PTRDIFF_TYPE__ long int
@@ -1655,6 +2046,7 @@
// RUN: %clang_cc1 -E -dM -ffreestanding -triple=x86_64-pc-linux-gnu < /dev/null | FileCheck -check-prefix X86_64-LINUX %s
//
// X86_64-LINUX:#define _LP64 1
+// X86_64-LINUX:#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
// X86_64-LINUX:#define __CHAR16_TYPE__ unsigned short
// X86_64-LINUX:#define __CHAR32_TYPE__ unsigned int
// X86_64-LINUX:#define __CHAR_BIT__ 8
@@ -1716,7 +2108,6 @@
// X86_64-LINUX:#define __LONG_MAX__ 9223372036854775807L
// X86_64-LINUX:#define __LP64__ 1
// X86_64-LINUX:#define __MMX__ 1
-// X86_64-LINUX:#define __NO_INLINE__ 1
// X86_64-LINUX:#define __NO_MATH_INLINES 1
// X86_64-LINUX:#define __POINTER_WIDTH__ 64
// X86_64-LINUX:#define __PTRDIFF_TYPE__ long int
@@ -1755,10 +2146,13 @@
// X86_64-LINUX:#define __x86_64 1
// X86_64-LINUX:#define __x86_64__ 1
//
-// RUN: %clang_cc1 -x c++ -triple i686-pc-linux-gnu -fobjc-fragile-abi -E -dM < /dev/null | FileCheck -check-prefix GNUSOURCE %s
+// RUN: %clang_cc1 -x c++ -triple i686-pc-linux-gnu -fobjc-runtime=gcc -E -dM < /dev/null | FileCheck -check-prefix GNUSOURCE %s
// GNUSOURCE:#define _GNU_SOURCE 1
//
// RUN: %clang_cc1 -x c++ -std=c++98 -fno-rtti -E -dM < /dev/null | FileCheck -check-prefix NORTTI %s
// NORTTI: __GXX_ABI_VERSION
// NORTTI-NOT:#define __GXX_RTTI
// NORTTI: __STDC__
+//
+// RUN: %clang_cc1 -triple arm-linux-androideabi -E -dM < /dev/null | FileCheck -check-prefix ANDROID %s
+// ANDROID: __ANDROID__ 1
diff --git a/test/Preprocessor/line-directive.c b/test/Preprocessor/line-directive.c
index 28e9302..ffa7c5a 100644
--- a/test/Preprocessor/line-directive.c
+++ b/test/Preprocessor/line-directive.c
@@ -3,8 +3,8 @@
// RUN: %clang_cc1 -E %s 2>&1 | grep 'blonk.c:93:2: error: DEF'
#line 'a' // expected-error {{#line directive requires a positive integer argument}}
-#line 0 // expected-error {{#line directive requires a positive integer argument}}
-#line 00 // expected-error {{#line directive requires a positive integer argument}}
+#line 0 // expected-warning {{#line directive with zero argument is a GNU extension}}
+#line 00 // expected-warning {{#line directive with zero argument is a GNU extension}}
#line 2147483648 // expected-warning {{C requires #line number to be less than 2147483648, allowed as extension}}
#line 42 // ok
#line 42 'a' // expected-error {{invalid filename for #line directive}}
@@ -33,8 +33,11 @@
// These are checked by the RUN line.
#line 92 "blonk.c"
-#error ABC // expected-error {{#error ABC}}
-#error DEF // expected-error {{#error DEF}}
+#error ABC
+#error DEF
+// expected-error@-2 {{ABC}}
+#line 150
+// expected-error@-3 {{DEF}}
// Verify that linemarker diddling of the system header flag works.
@@ -88,5 +91,8 @@ extern char array2[\
_\
_LINE__ == 42 ? 1: -1]; /* line marker is location of first _ */
+// rdar://11550996
+#line 0 "line-directive.c" // expected-warning {{#line directive with zero argument is a GNU extension}}
+undefined t; // expected-error {{unknown type name 'undefined'}}
diff --git a/test/Preprocessor/macro_fn.c b/test/Preprocessor/macro_fn.c
index 85733b4..f93d52c 100644
--- a/test/Preprocessor/macro_fn.c
+++ b/test/Preprocessor/macro_fn.c
@@ -4,8 +4,8 @@
#define zero() 0
#define one(x) 0
#define two(x, y) 0
-#define zero_dot(...) 0 /* expected-warning {{variadic macros were introduced in C99}} */
-#define one_dot(x, ...) 0 /* expected-warning {{variadic macros were introduced in C99}} */
+#define zero_dot(...) 0 /* expected-warning {{variadic macros are a C99 feature}} */
+#define one_dot(x, ...) 0 /* expected-warning {{variadic macros are a C99 feature}} expected-note 2{{macro 'one_dot' defined here}} */
zero()
zero(1); /* expected-error {{too many arguments provided to function-like macro invocation}} */
@@ -19,25 +19,25 @@ one(a, b) /* expected-error {{too many arguments provided to function-li
two() /* expected-error {{too few arguments provided to function-like macro invocation}} */
two(a) /* expected-error {{too few arguments provided to function-like macro invocation}} */
two(a,b)
-two(a, ) /* expected-warning {{empty macro arguments were standardized in C99}} */
+two(a, ) /* expected-warning {{empty macro arguments are a C99 feature}} */
two(a,b,c) /* expected-error {{too many arguments provided to function-like macro invocation}} */
two(
- , /* expected-warning {{empty macro arguments were standardized in C99}} */
- , /* expected-warning {{empty macro arguments were standardized in C99}} \
+ , /* expected-warning {{empty macro arguments are a C99 feature}} */
+ , /* expected-warning {{empty macro arguments are a C99 feature}} \
expected-error {{too many arguments provided to function-like macro invocation}} */
)
-two(,) /* expected-warning 2 {{empty macro arguments were standardized in C99}} */
+two(,) /* expected-warning 2 {{empty macro arguments are a C99 feature}} */
/* PR4006 & rdar://6807000 */
-#define e(...) __VA_ARGS__ /* expected-warning {{variadic macros were introduced in C99}} */
+#define e(...) __VA_ARGS__ /* expected-warning {{variadic macros are a C99 feature}} */
e(x)
e()
zero_dot()
-one_dot(x) /* empty ... argument: expected-warning {{varargs argument missing, but tolerated as an extension}} */
-one_dot() /* empty first argument, elided ...: expected-warning {{varargs argument missing, but tolerated as an extension}} */
+one_dot(x) /* empty ... argument: expected-warning {{must specify at least one argument for '...' parameter of variadic macro}} */
+one_dot() /* empty first argument, elided ...: expected-warning {{must specify at least one argument for '...' parameter of variadic macro}} */
/* rdar://6816766 - Crash with function-like macro test at end of directive. */
diff --git a/test/Preprocessor/macro_paste_c_block_comment.c b/test/Preprocessor/macro_paste_c_block_comment.c
index c690a4c..c558be5 100644
--- a/test/Preprocessor/macro_paste_c_block_comment.c
+++ b/test/Preprocessor/macro_paste_c_block_comment.c
@@ -1,5 +1,9 @@
// RUN: %clang_cc1 %s -Eonly -verify
+// expected-error@9 {{EOF}}
#define COMM / ## *
COMM // expected-error {{pasting formed '/*', an invalid preprocessing token}}
+// Demonstrate that an invalid preprocessing token
+// doesn't swallow the rest of the file...
+#error EOF
diff --git a/test/Preprocessor/macro_paste_identifier_error.c b/test/Preprocessor/macro_paste_identifier_error.c
new file mode 100644
index 0000000..457e6f7
--- /dev/null
+++ b/test/Preprocessor/macro_paste_identifier_error.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fms-extensions -Wno-invalid-token-paste %s -verify
+// RUN: %clang_cc1 -E -fms-extensions -Wno-invalid-token-paste %s | FileCheck %s
+// RUN: %clang_cc1 -E -fms-extensions -Wno-invalid-token-paste -x assembler-with-cpp %s | FileCheck %s
+
+#define foo a ## b ## = 0
+int foo;
+// CHECK: int ab = 0;
diff --git a/test/Preprocessor/mmx.c b/test/Preprocessor/mmx.c
index 2613cb6..59e715e 100644
--- a/test/Preprocessor/mmx.c
+++ b/test/Preprocessor/mmx.c
@@ -1,8 +1,11 @@
// RUN: %clang -march=i386 -m32 -E -dM %s -msse -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=SSE_AND_MMX
// RUN: %clang -march=i386 -m32 -E -dM %s -msse -mno-mmx -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=SSE_NO_MMX
// RUN: %clang -march=i386 -m32 -E -dM %s -mno-mmx -msse -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=SSE_NO_MMX
// SSE_AND_MMX: #define __MMX__
diff --git a/test/Preprocessor/non_fragile_feature1.m b/test/Preprocessor/non_fragile_feature1.m
index 79cc488..6ea7fa8 100644
--- a/test/Preprocessor/non_fragile_feature1.m
+++ b/test/Preprocessor/non_fragile_feature1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-fragile-abi %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-runtime=gcc %s
#ifndef __has_feature
#error Should have __has_feature
#endif
diff --git a/test/Preprocessor/pp-record.c b/test/Preprocessor/pp-record.c
index f098683..dd958d0 100644
--- a/test/Preprocessor/pp-record.c
+++ b/test/Preprocessor/pp-record.c
@@ -10,3 +10,14 @@
#include STRINGIZE(INC)
CAKE;
+
+#define DIR 1
+#define FNM(x) x
+
+FNM(
+#if DIR
+ int a;
+#else
+ int b;
+#endif
+)
diff --git a/test/Preprocessor/predefined-arch-macros.c b/test/Preprocessor/predefined-arch-macros.c
index b063f7f..2361abe 100644
--- a/test/Preprocessor/predefined-arch-macros.c
+++ b/test/Preprocessor/predefined-arch-macros.c
@@ -4,16 +4,19 @@
// Begin X86/GCC/Linux tests ----------------
//
// RUN: %clang -march=i386 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I386_M32
// CHECK_I386_M32: #define __i386 1
// CHECK_I386_M32: #define __i386__ 1
// CHECK_I386_M32: #define __tune_i386__ 1
// CHECK_I386_M32: #define i386 1
// RUN: %clang -march=i386 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I386_M64
// CHECK_I386_M64: error:
//
// RUN: %clang -march=i486 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I486_M32
// CHECK_I486_M32: #define __i386 1
// CHECK_I486_M32: #define __i386__ 1
@@ -22,10 +25,12 @@
// CHECK_I486_M32: #define __tune_i486__ 1
// CHECK_I486_M32: #define i386 1
// RUN: %clang -march=i486 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I486_M64
// CHECK_I486_M64: error:
//
// RUN: %clang -march=i586 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I586_M32
// CHECK_I586_M32: #define __i386 1
// CHECK_I586_M32: #define __i386__ 1
@@ -37,10 +42,12 @@
// CHECK_I586_M32: #define __tune_pentium__ 1
// CHECK_I586_M32: #define i386 1
// RUN: %clang -march=i586 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I586_M64
// CHECK_I586_M64: error:
//
// RUN: %clang -march=pentium -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM_M32
// CHECK_PENTIUM_M32: #define __i386 1
// CHECK_PENTIUM_M32: #define __i386__ 1
@@ -52,10 +59,12 @@
// CHECK_PENTIUM_M32: #define __tune_pentium__ 1
// CHECK_PENTIUM_M32: #define i386 1
// RUN: %clang -march=pentium -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM_M64
// CHECK_PENTIUM_M64: error:
//
// RUN: %clang -march=pentium-mmx -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM_MMX_M32
// CHECK_PENTIUM_MMX_M32: #define __MMX__ 1
// CHECK_PENTIUM_MMX_M32: #define __i386 1
@@ -70,10 +79,12 @@
// CHECK_PENTIUM_MMX_M32: #define __tune_pentium_mmx__ 1
// CHECK_PENTIUM_MMX_M32: #define i386 1
// RUN: %clang -march=pentium-mmx -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM_MMX_M64
// CHECK_PENTIUM_MMX_M64: error:
//
// RUN: %clang -march=winchip-c6 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_WINCHIP_C6_M32
// CHECK_WINCHIP_C6_M32: #define __MMX__ 1
// CHECK_WINCHIP_C6_M32: #define __i386 1
@@ -83,10 +94,12 @@
// CHECK_WINCHIP_C6_M32: #define __tune_i486__ 1
// CHECK_WINCHIP_C6_M32: #define i386 1
// RUN: %clang -march=winchip-c6 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_WINCHIP_C6_M64
// CHECK_WINCHIP_C6_M64: error:
//
// RUN: %clang -march=winchip2 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_WINCHIP2_M32
// CHECK_WINCHIP2_M32: #define __3dNOW__ 1
// CHECK_WINCHIP2_M32: #define __MMX__ 1
@@ -97,10 +110,12 @@
// CHECK_WINCHIP2_M32: #define __tune_i486__ 1
// CHECK_WINCHIP2_M32: #define i386 1
// RUN: %clang -march=winchip2 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_WINCHIP2_M64
// CHECK_WINCHIP2_M64: error:
//
// RUN: %clang -march=c3 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_C3_M32
// CHECK_C3_M32: #define __3dNOW__ 1
// CHECK_C3_M32: #define __MMX__ 1
@@ -111,10 +126,12 @@
// CHECK_C3_M32: #define __tune_i486__ 1
// CHECK_C3_M32: #define i386 1
// RUN: %clang -march=c3 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_C3_M64
// CHECK_C3_M64: error:
//
// RUN: %clang -march=c3-2 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_C3_2_M32
// CHECK_C3_2_M32: #define __MMX__ 1
// CHECK_C3_2_M32: #define __SSE__ 1
@@ -129,10 +146,12 @@
// CHECK_C3_2_M32: #define __tune_pentiumpro__ 1
// CHECK_C3_2_M32: #define i386 1
// RUN: %clang -march=c3-2 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_C3_2_M64
// CHECK_C3_2_M64: error:
//
// RUN: %clang -march=i686 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I686_M32
// CHECK_I686_M32: #define __i386 1
// CHECK_I686_M32: #define __i386__ 1
@@ -142,10 +161,12 @@
// CHECK_I686_M32: #define __pentiumpro__ 1
// CHECK_I686_M32: #define i386 1
// RUN: %clang -march=i686 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_I686_M64
// CHECK_I686_M64: error:
//
// RUN: %clang -march=pentiumpro -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUMPRO_M32
// CHECK_PENTIUMPRO_M32: #define __i386 1
// CHECK_PENTIUMPRO_M32: #define __i386__ 1
@@ -157,10 +178,12 @@
// CHECK_PENTIUMPRO_M32: #define __tune_pentiumpro__ 1
// CHECK_PENTIUMPRO_M32: #define i386 1
// RUN: %clang -march=pentiumpro -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUMPRO_M64
// CHECK_PENTIUMPRO_M64: error:
//
// RUN: %clang -march=pentium2 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM2_M32
// CHECK_PENTIUM2_M32: #define __MMX__ 1
// CHECK_PENTIUM2_M32: #define __i386 1
@@ -174,10 +197,12 @@
// CHECK_PENTIUM2_M32: #define __tune_pentiumpro__ 1
// CHECK_PENTIUM2_M32: #define i386 1
// RUN: %clang -march=pentium2 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM2_M64
// CHECK_PENTIUM2_M64: error:
//
// RUN: %clang -march=pentium3 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM3_M32
// CHECK_PENTIUM3_M32: #define __MMX__ 1
// CHECK_PENTIUM3_M32: #define __SSE__ 1
@@ -193,10 +218,12 @@
// CHECK_PENTIUM3_M32: #define __tune_pentiumpro__ 1
// CHECK_PENTIUM3_M32: #define i386 1
// RUN: %clang -march=pentium3 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM3_M64
// CHECK_PENTIUM3_M64: error:
//
// RUN: %clang -march=pentium3m -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM3M_M32
// CHECK_PENTIUM3M_M32: #define __MMX__ 1
// CHECK_PENTIUM3M_M32: #define __SSE__ 1
@@ -210,10 +237,12 @@
// CHECK_PENTIUM3M_M32: #define __tune_pentiumpro__ 1
// CHECK_PENTIUM3M_M32: #define i386 1
// RUN: %clang -march=pentium3m -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM3M_M64
// CHECK_PENTIUM3M_M64: error:
//
// RUN: %clang -march=pentium-m -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM_M_M32
// CHECK_PENTIUM_M_M32: #define __MMX__ 1
// CHECK_PENTIUM_M_M32: #define __SSE2__ 1
@@ -228,10 +257,12 @@
// CHECK_PENTIUM_M_M32: #define __tune_pentiumpro__ 1
// CHECK_PENTIUM_M_M32: #define i386 1
// RUN: %clang -march=pentium-m -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM_M_M64
// CHECK_PENTIUM_M_M64: error:
//
// RUN: %clang -march=pentium4 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM4_M32
// CHECK_PENTIUM4_M32: #define __MMX__ 1
// CHECK_PENTIUM4_M32: #define __SSE2__ 1
@@ -243,10 +274,12 @@
// CHECK_PENTIUM4_M32: #define __tune_pentium4__ 1
// CHECK_PENTIUM4_M32: #define i386 1
// RUN: %clang -march=pentium4 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM4_M64
// CHECK_PENTIUM4_M64: error:
//
// RUN: %clang -march=pentium4m -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM4M_M32
// CHECK_PENTIUM4M_M32: #define __MMX__ 1
// CHECK_PENTIUM4M_M32: #define __SSE2__ 1
@@ -258,10 +291,12 @@
// CHECK_PENTIUM4M_M32: #define __tune_pentium4__ 1
// CHECK_PENTIUM4M_M32: #define i386 1
// RUN: %clang -march=pentium4m -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PENTIUM4M_M64
// CHECK_PENTIUM4M_M64: error:
//
// RUN: %clang -march=prescott -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PRESCOTT_M32
// CHECK_PRESCOTT_M32: #define __MMX__ 1
// CHECK_PRESCOTT_M32: #define __SSE2__ 1
@@ -274,10 +309,12 @@
// CHECK_PRESCOTT_M32: #define __tune_nocona__ 1
// CHECK_PRESCOTT_M32: #define i386 1
// RUN: %clang -march=prescott -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_PRESCOTT_M64
// CHECK_PRESCOTT_M64: error:
//
// RUN: %clang -march=nocona -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_NOCONA_M32
// CHECK_NOCONA_M32: #define __MMX__ 1
// CHECK_NOCONA_M32: #define __SSE2__ 1
@@ -290,6 +327,7 @@
// CHECK_NOCONA_M32: #define __tune_nocona__ 1
// CHECK_NOCONA_M32: #define i386 1
// RUN: %clang -march=nocona -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_NOCONA_M64
// CHECK_NOCONA_M64: #define __MMX__ 1
// CHECK_NOCONA_M64: #define __SSE2_MATH__ 1
@@ -306,6 +344,7 @@
// CHECK_NOCONA_M64: #define __x86_64__ 1
//
// RUN: %clang -march=core2 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_CORE2_M32
// CHECK_CORE2_M32: #define __MMX__ 1
// CHECK_CORE2_M32: #define __SSE2__ 1
@@ -319,6 +358,7 @@
// CHECK_CORE2_M32: #define __tune_core2__ 1
// CHECK_CORE2_M32: #define i386 1
// RUN: %clang -march=core2 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_CORE2_M64
// CHECK_CORE2_M64: #define __MMX__ 1
// CHECK_CORE2_M64: #define __SSE2_MATH__ 1
@@ -336,8 +376,10 @@
// CHECK_CORE2_M64: #define __x86_64__ 1
//
// RUN: %clang -march=corei7 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_COREI7_M32
// CHECK_COREI7_M32: #define __MMX__ 1
+// CHECK_COREI7_M32: #define __POPCNT__ 1
// CHECK_COREI7_M32: #define __SSE2__ 1
// CHECK_COREI7_M32: #define __SSE3__ 1
// CHECK_COREI7_M32: #define __SSE4_1__ 1
@@ -351,8 +393,10 @@
// CHECK_COREI7_M32: #define __tune_corei7__ 1
// CHECK_COREI7_M32: #define i386 1
// RUN: %clang -march=corei7 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_COREI7_M64
// CHECK_COREI7_M64: #define __MMX__ 1
+// CHECK_COREI7_M64: #define __POPCNT__ 1
// CHECK_COREI7_M64: #define __SSE2_MATH__ 1
// CHECK_COREI7_M64: #define __SSE2__ 1
// CHECK_COREI7_M64: #define __SSE3__ 1
@@ -370,11 +414,14 @@
// CHECK_COREI7_M64: #define __x86_64__ 1
//
// RUN: %clang -march=corei7-avx -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_COREI7_AVX_M32
// CHECK_COREI7_AVX_M32: #define __AES__ 1
-// FIXME: AVX is not yet enabled with Clang.
-// CHECK_COREI7_AVX_M32-NOT: #define __AVX__ 1
+// CHECK_COREI7_AVX_M32: #define __AVX__ 1
// CHECK_COREI7_AVX_M32: #define __MMX__ 1
+// CHECK_COREI7_AVX_M32: #define __PCLMUL__ 1
+// CHECK_COREI7_AVX_M32-NOT: __RDRND__
+// CHECK_COREI7_AVX_M32: #define __POPCNT__ 1
// CHECK_COREI7_AVX_M32: #define __SSE2__ 1
// CHECK_COREI7_AVX_M32: #define __SSE3__ 1
// CHECK_COREI7_AVX_M32: #define __SSE4_1__ 1
@@ -388,11 +435,14 @@
// CHECK_COREI7_AVX_M32: #define __tune_corei7__ 1
// CHECK_COREI7_AVX_M32: #define i386 1
// RUN: %clang -march=corei7-avx -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_COREI7_AVX_M64
// CHECK_COREI7_AVX_M64: #define __AES__ 1
-// FIXME: AVX is not yet enabled with Clang.
-// CHECK_COREI7_AVX_M64-NOT: #define __AVX__ 1
+// CHECK_COREI7_AVX_M64: #define __AVX__ 1
// CHECK_COREI7_AVX_M64: #define __MMX__ 1
+// CHECK_COREI7_AVX_M64: #define __PCLMUL__ 1
+// CHECK_COREI7_AVX_M64-NOT: __RDRND__
+// CHECK_COREI7_AVX_M64: #define __POPCNT__ 1
// CHECK_COREI7_AVX_M64: #define __SSE2_MATH__ 1
// CHECK_COREI7_AVX_M64: #define __SSE2__ 1
// CHECK_COREI7_AVX_M64: #define __SSE3__ 1
@@ -410,11 +460,13 @@
// CHECK_COREI7_AVX_M64: #define __x86_64__ 1
//
// RUN: %clang -march=core-avx-i -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_CORE_AVX_I_M32
// CHECK_CORE_AVX_I_M32: #define __AES__ 1
-// FIXME: AVX is not yet enabled with Clang.
-// CHECK_CORE_AVX_I_M32-NOT: #define __AVX__ 1
+// CHECK_CORE_AVX_I_M32: #define __AVX__ 1
// CHECK_CORE_AVX_I_M32: #define __MMX__ 1
+// CHECK_CORE_AVX_I_M32: #define __PCLMUL__ 1
+// CHECK_CORE_AVX_I_M32: #define __RDRND__ 1
// CHECK_CORE_AVX_I_M32: #define __SSE2__ 1
// CHECK_CORE_AVX_I_M32: #define __SSE3__ 1
// CHECK_CORE_AVX_I_M32: #define __SSE4_1__ 1
@@ -428,11 +480,13 @@
// CHECK_CORE_AVX_I_M32: #define __tune_corei7__ 1
// CHECK_CORE_AVX_I_M32: #define i386 1
// RUN: %clang -march=core-avx-i -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_CORE_AVX_I_M64
// CHECK_CORE_AVX_I_M64: #define __AES__ 1
-// FIXME: AVX is not yet enabled with Clang.
-// CHECK_CORE_AVX_I_M64-NOT: #define __AVX__ 1
+// CHECK_CORE_AVX_I_M64: #define __AVX__ 1
// CHECK_CORE_AVX_I_M64: #define __MMX__ 1
+// CHECK_CORE_AVX_I_M64: #define __PCLMUL__ 1
+// CHECK_CORE_AVX_I_M64: #define __RDRND__ 1
// CHECK_CORE_AVX_I_M64: #define __SSE2_MATH__ 1
// CHECK_CORE_AVX_I_M64: #define __SSE2__ 1
// CHECK_CORE_AVX_I_M64: #define __SSE3__ 1
@@ -449,7 +503,62 @@
// CHECK_CORE_AVX_I_M64: #define __x86_64 1
// CHECK_CORE_AVX_I_M64: #define __x86_64__ 1
//
+// RUN: %clang -march=core-avx2 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
+// RUN: | FileCheck %s -check-prefix=CHECK_CORE_AVX2_M32
+// CHECK_CORE_AVX2_M32: #define __AES__ 1
+// CHECK_CORE_AVX2_M32: #define __AVX__ 1
+// CHECK_CORE_AVX2_M32: #define __BMI2__ 1
+// CHECK_CORE_AVX2_M32: #define __BMI__ 1
+// CHECK_CORE_AVX2_M32: #define __FMA__ 1
+// CHECK_CORE_AVX2_M32: #define __LZCNT__ 1
+// CHECK_CORE_AVX2_M32: #define __MMX__ 1
+// CHECK_CORE_AVX2_M32: #define __PCLMUL__ 1
+// CHECK_CORE_AVX2_M32: #define __POPCNT__ 1
+// CHECK_CORE_AVX2_M32: #define __RDRND__ 1
+// CHECK_CORE_AVX2_M32: #define __SSE2__ 1
+// CHECK_CORE_AVX2_M32: #define __SSE3__ 1
+// CHECK_CORE_AVX2_M32: #define __SSE4_1__ 1
+// CHECK_CORE_AVX2_M32: #define __SSE4_2__ 1
+// CHECK_CORE_AVX2_M32: #define __SSE__ 1
+// CHECK_CORE_AVX2_M32: #define __SSSE3__ 1
+// CHECK_CORE_AVX2_M32: #define __corei7 1
+// CHECK_CORE_AVX2_M32: #define __corei7__ 1
+// CHECK_CORE_AVX2_M32: #define __i386 1
+// CHECK_CORE_AVX2_M32: #define __i386__ 1
+// CHECK_CORE_AVX2_M32: #define __tune_corei7__ 1
+// CHECK_CORE_AVX2_M32: #define i386 1
+// RUN: %clang -march=core-avx2 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
+// RUN: | FileCheck %s -check-prefix=CHECK_CORE_AVX2_M64
+// CHECK_CORE_AVX2_M64: #define __AES__ 1
+// CHECK_CORE_AVX2_M64: #define __AVX__ 1
+// CHECK_CORE_AVX2_M64: #define __BMI2__ 1
+// CHECK_CORE_AVX2_M64: #define __BMI__ 1
+// CHECK_CORE_AVX2_M64: #define __FMA__ 1
+// CHECK_CORE_AVX2_M64: #define __LZCNT__ 1
+// CHECK_CORE_AVX2_M64: #define __MMX__ 1
+// CHECK_CORE_AVX2_M64: #define __PCLMUL__ 1
+// CHECK_CORE_AVX2_M64: #define __POPCNT__ 1
+// CHECK_CORE_AVX2_M64: #define __RDRND__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE2_MATH__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE2__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE3__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE4_1__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE4_2__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE_MATH__ 1
+// CHECK_CORE_AVX2_M64: #define __SSE__ 1
+// CHECK_CORE_AVX2_M64: #define __SSSE3__ 1
+// CHECK_CORE_AVX2_M64: #define __amd64 1
+// CHECK_CORE_AVX2_M64: #define __amd64__ 1
+// CHECK_CORE_AVX2_M64: #define __corei7 1
+// CHECK_CORE_AVX2_M64: #define __corei7__ 1
+// CHECK_CORE_AVX2_M64: #define __tune_corei7__ 1
+// CHECK_CORE_AVX2_M64: #define __x86_64 1
+// CHECK_CORE_AVX2_M64: #define __x86_64__ 1
+//
// RUN: %clang -march=atom -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATOM_M32
// CHECK_ATOM_M32: #define __MMX__ 1
// CHECK_ATOM_M32: #define __SSE2__ 1
@@ -463,6 +572,7 @@
// CHECK_ATOM_M32: #define __tune_atom__ 1
// CHECK_ATOM_M32: #define i386 1
// RUN: %clang -march=atom -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATOM_M64
// CHECK_ATOM_M64: #define __MMX__ 1
// CHECK_ATOM_M64: #define __SSE2_MATH__ 1
@@ -480,6 +590,7 @@
// CHECK_ATOM_M64: #define __x86_64__ 1
//
// RUN: %clang -march=geode -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_GEODE_M32
// CHECK_GEODE_M32: #define __3dNOW_A__ 1
// CHECK_GEODE_M32: #define __3dNOW__ 1
@@ -491,10 +602,12 @@
// CHECK_GEODE_M32: #define __tune_geode__ 1
// CHECK_GEODE_M32: #define i386 1
// RUN: %clang -march=geode -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_GEODE_M64
// CHECK_GEODE_M64: error:
//
// RUN: %clang -march=k6 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K6_M32
// CHECK_K6_M32: #define __MMX__ 1
// CHECK_K6_M32: #define __i386 1
@@ -504,10 +617,12 @@
// CHECK_K6_M32: #define __tune_k6__ 1
// CHECK_K6_M32: #define i386 1
// RUN: %clang -march=k6 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K6_M64
// CHECK_K6_M64: error:
//
// RUN: %clang -march=k6-2 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K6_2_M32
// CHECK_K6_2_M32: #define __3dNOW__ 1
// CHECK_K6_2_M32: #define __MMX__ 1
@@ -520,10 +635,12 @@
// CHECK_K6_2_M32: #define __tune_k6__ 1
// CHECK_K6_2_M32: #define i386 1
// RUN: %clang -march=k6-2 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K6_2_M64
// CHECK_K6_2_M64: error:
//
// RUN: %clang -march=k6-3 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K6_3_M32
// CHECK_K6_3_M32: #define __3dNOW__ 1
// CHECK_K6_3_M32: #define __MMX__ 1
@@ -536,10 +653,12 @@
// CHECK_K6_3_M32: #define __tune_k6__ 1
// CHECK_K6_3_M32: #define i386 1
// RUN: %clang -march=k6-3 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K6_3_M64
// CHECK_K6_3_M64: error:
//
// RUN: %clang -march=athlon -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_M32
// CHECK_ATHLON_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON_M32: #define __3dNOW__ 1
@@ -551,10 +670,12 @@
// CHECK_ATHLON_M32: #define __tune_athlon__ 1
// CHECK_ATHLON_M32: #define i386 1
// RUN: %clang -march=athlon -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_M64
// CHECK_ATHLON_M64: error:
//
// RUN: %clang -march=athlon-tbird -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_TBIRD_M32
// CHECK_ATHLON_TBIRD_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON_TBIRD_M32: #define __3dNOW__ 1
@@ -566,10 +687,12 @@
// CHECK_ATHLON_TBIRD_M32: #define __tune_athlon__ 1
// CHECK_ATHLON_TBIRD_M32: #define i386 1
// RUN: %clang -march=athlon-tbird -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_TBIRD_M64
// CHECK_ATHLON_TBIRD_M64: error:
//
// RUN: %clang -march=athlon-4 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_4_M32
// CHECK_ATHLON_4_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON_4_M32: #define __3dNOW__ 1
@@ -584,10 +707,12 @@
// CHECK_ATHLON_4_M32: #define __tune_athlon_sse__ 1
// CHECK_ATHLON_4_M32: #define i386 1
// RUN: %clang -march=athlon-4 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_4_M64
// CHECK_ATHLON_4_M64: error:
//
// RUN: %clang -march=athlon-xp -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_XP_M32
// CHECK_ATHLON_XP_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON_XP_M32: #define __3dNOW__ 1
@@ -602,10 +727,12 @@
// CHECK_ATHLON_XP_M32: #define __tune_athlon_sse__ 1
// CHECK_ATHLON_XP_M32: #define i386 1
// RUN: %clang -march=athlon-xp -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_XP_M64
// CHECK_ATHLON_XP_M64: error:
//
// RUN: %clang -march=athlon-mp -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_MP_M32
// CHECK_ATHLON_MP_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON_MP_M32: #define __3dNOW__ 1
@@ -620,10 +747,12 @@
// CHECK_ATHLON_MP_M32: #define __tune_athlon_sse__ 1
// CHECK_ATHLON_MP_M32: #define i386 1
// RUN: %clang -march=athlon-mp -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_MP_M64
// CHECK_ATHLON_MP_M64: error:
//
// RUN: %clang -march=x86-64 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_X86_64_M32
// CHECK_X86_64_M32: #define __MMX__ 1
// CHECK_X86_64_M32: #define __SSE2__ 1
@@ -634,6 +763,7 @@
// CHECK_X86_64_M32: #define __k8__ 1
// CHECK_X86_64_M32: #define i386 1
// RUN: %clang -march=x86-64 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_X86_64_M64
// CHECK_X86_64_M64: #define __MMX__ 1
// CHECK_X86_64_M64: #define __SSE2_MATH__ 1
@@ -648,6 +778,7 @@
// CHECK_X86_64_M64: #define __x86_64__ 1
//
// RUN: %clang -march=k8 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K8_M32
// CHECK_K8_M32: #define __3dNOW_A__ 1
// CHECK_K8_M32: #define __3dNOW__ 1
@@ -661,6 +792,7 @@
// CHECK_K8_M32: #define __tune_k8__ 1
// CHECK_K8_M32: #define i386 1
// RUN: %clang -march=k8 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K8_M64
// CHECK_K8_M64: #define __3dNOW_A__ 1
// CHECK_K8_M64: #define __3dNOW__ 1
@@ -678,6 +810,7 @@
// CHECK_K8_M64: #define __x86_64__ 1
//
// RUN: %clang -march=k8-sse3 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K8_SSE3_M32
// CHECK_K8_SSE3_M32: #define __3dNOW_A__ 1
// CHECK_K8_SSE3_M32: #define __3dNOW__ 1
@@ -692,6 +825,7 @@
// CHECK_K8_SSE3_M32: #define __tune_k8__ 1
// CHECK_K8_SSE3_M32: #define i386 1
// RUN: %clang -march=k8-sse3 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_K8_SSE3_M64
// CHECK_K8_SSE3_M64: #define __3dNOW_A__ 1
// CHECK_K8_SSE3_M64: #define __3dNOW__ 1
@@ -710,6 +844,7 @@
// CHECK_K8_SSE3_M64: #define __x86_64__ 1
//
// RUN: %clang -march=opteron -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_OPTERON_M32
// CHECK_OPTERON_M32: #define __3dNOW_A__ 1
// CHECK_OPTERON_M32: #define __3dNOW__ 1
@@ -723,6 +858,7 @@
// CHECK_OPTERON_M32: #define __tune_k8__ 1
// CHECK_OPTERON_M32: #define i386 1
// RUN: %clang -march=opteron -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_OPTERON_M64
// CHECK_OPTERON_M64: #define __3dNOW_A__ 1
// CHECK_OPTERON_M64: #define __3dNOW__ 1
@@ -740,6 +876,7 @@
// CHECK_OPTERON_M64: #define __x86_64__ 1
//
// RUN: %clang -march=opteron-sse3 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_OPTERON_SSE3_M32
// CHECK_OPTERON_SSE3_M32: #define __3dNOW_A__ 1
// CHECK_OPTERON_SSE3_M32: #define __3dNOW__ 1
@@ -754,6 +891,7 @@
// CHECK_OPTERON_SSE3_M32: #define __tune_k8__ 1
// CHECK_OPTERON_SSE3_M32: #define i386 1
// RUN: %clang -march=opteron-sse3 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_OPTERON_SSE3_M64
// CHECK_OPTERON_SSE3_M64: #define __3dNOW_A__ 1
// CHECK_OPTERON_SSE3_M64: #define __3dNOW__ 1
@@ -772,6 +910,7 @@
// CHECK_OPTERON_SSE3_M64: #define __x86_64__ 1
//
// RUN: %clang -march=athlon64 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON64_M32
// CHECK_ATHLON64_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON64_M32: #define __3dNOW__ 1
@@ -785,6 +924,7 @@
// CHECK_ATHLON64_M32: #define __tune_k8__ 1
// CHECK_ATHLON64_M32: #define i386 1
// RUN: %clang -march=athlon64 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON64_M64
// CHECK_ATHLON64_M64: #define __3dNOW_A__ 1
// CHECK_ATHLON64_M64: #define __3dNOW__ 1
@@ -802,6 +942,7 @@
// CHECK_ATHLON64_M64: #define __x86_64__ 1
//
// RUN: %clang -march=athlon64-sse3 -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON64_SSE3_M32
// CHECK_ATHLON64_SSE3_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON64_SSE3_M32: #define __3dNOW__ 1
@@ -816,6 +957,7 @@
// CHECK_ATHLON64_SSE3_M32: #define __tune_k8__ 1
// CHECK_ATHLON64_SSE3_M32: #define i386 1
// RUN: %clang -march=athlon64-sse3 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON64_SSE3_M64
// CHECK_ATHLON64_SSE3_M64: #define __3dNOW_A__ 1
// CHECK_ATHLON64_SSE3_M64: #define __3dNOW__ 1
@@ -834,6 +976,7 @@
// CHECK_ATHLON64_SSE3_M64: #define __x86_64__ 1
//
// RUN: %clang -march=athlon-fx -m32 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_FX_M32
// CHECK_ATHLON_FX_M32: #define __3dNOW_A__ 1
// CHECK_ATHLON_FX_M32: #define __3dNOW__ 1
@@ -847,6 +990,7 @@
// CHECK_ATHLON_FX_M32: #define __tune_k8__ 1
// CHECK_ATHLON_FX_M32: #define i386 1
// RUN: %clang -march=athlon-fx -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
// RUN: | FileCheck %s -check-prefix=CHECK_ATHLON_FX_M64
// CHECK_ATHLON_FX_M64: #define __3dNOW_A__ 1
// CHECK_ATHLON_FX_M64: #define __3dNOW__ 1
@@ -862,5 +1006,50 @@
// CHECK_ATHLON_FX_M64: #define __tune_k8__ 1
// CHECK_ATHLON_FX_M64: #define __x86_64 1
// CHECK_ATHLON_FX_M64: #define __x86_64__ 1
+// RUN: %clang -march=amdfam10 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
+// RUN: | FileCheck %s -check-prefix=CHECK_AMDFAM10_M64
+// CHECK_AMDFAM10_M64: #define __3dNOW_A__ 1
+// CHECK_AMDFAM10_M64: #define __3dNOW__ 1
+// CHECK_AMDFAM10_M64: #define __MMX__ 1
+// CHECK_AMDFAM10_M64: #define __SSE2_MATH__ 1
+// CHECK_AMDFAM10_M64: #define __SSE2__ 1
+// CHECK_AMDFAM10_M64: #define __SSE3__ 1
+// CHECK_AMDFAM10_M64: #define __SSE4A__ 1
+// CHECK_AMDFAM10_M64: #define __SSE_MATH__ 1
+// CHECK_AMDFAM10_M64: #define __SSE__ 1
+// CHECK_AMDFAM10_M64: #define __amd64 1
+// CHECK_AMDFAM10_M64: #define __amd64__ 1
+// CHECK_AMDFAM10_M64: #define __amdfam10 1
+// CHECK_AMDFAM10_M64: #define __amdfam10__ 1
+// CHECK_AMDFAM10_M64: #define __tune_amdfam10__ 1
+// CHECK_AMDFAM10_M64: #define __x86_64 1
+// CHECK_AMDFAM10_M64: #define __x86_64__ 1
+// RUN: %clang -march=bdver1 -m64 -E -dM %s -o - 2>&1 \
+// RUN: -target i386-unknown-linux \
+// RUN: | FileCheck %s -check-prefix=CHECK_BDVER1_M64
+// CHECK_BDVER1_M64: #define __AVX__ 1
+// CHECK_BDVER1_M64-NOT: #define __3dNOW_A__ 1
+// CHECK_BDVER1_M64-NOT: #define __3dNOW__ 1
+// CHECK_BDVER1_M64: #define __FMA4__ 1
+// CHECK_BDVER1_M64: #define __MMX__ 1
+// CHECK_BDVER1_M64: #define __PCLMUL__ 1
+// CHECK_BDVER1_M64: #define __SSE2_MATH__ 1
+// CHECK_BDVER1_M64: #define __SSE2__ 1
+// CHECK_BDVER1_M64: #define __SSE3__ 1
+// CHECK_BDVER1_M64: #define __SSE4A__ 1
+// CHECK_BDVER1_M64: #define __SSE4_1__ 1
+// CHECK_BDVER1_M64: #define __SSE4_2__ 1
+// CHECK_BDVER1_M64: #define __SSE_MATH__ 1
+// CHECK_BDVER1_M64: #define __SSE__ 1
+// CHECK_BDVER1_M64: #define __SSSE3__ 1
+// CHECK_BDVER1_M64: #define __XOP__ 1
+// CHECK_BDVER1_M64: #define __amd64 1
+// CHECK_BDVER1_M64: #define __amd64__ 1
+// CHECK_BDVER1_M64: #define __bdver1 1
+// CHECK_BDVER1_M64: #define __bdver1__ 1
+// CHECK_BDVER1_M64: #define __tune_bdver1__ 1
+// CHECK_BDVER1_M64: #define __x86_64 1
+// CHECK_BDVER1_M64: #define __x86_64__ 1
//
// End X86/GCC/Linux tests ------------------
diff --git a/test/Preprocessor/predefined-macros.c b/test/Preprocessor/predefined-macros.c
index 5c11c3b..2c19301 100644
--- a/test/Preprocessor/predefined-macros.c
+++ b/test/Preprocessor/predefined-macros.c
@@ -13,3 +13,16 @@
// RUN: %clang_cc1 %s -E -dM -ffast-math -o - \
// RUN: | FileCheck %s --check-prefix=CHECK-FAST-MATH
// CHECK-FAST-MATH: #define __FAST_MATH__
+// CHECK-FAST-MATH: #define __FINITE_MATH_ONLY__ 1
+//
+// RUN: %clang_cc1 %s -E -dM -ffinite-math-only -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-FINITE-MATH-ONLY
+// CHECK-FINITE-MATH-ONLY: #define __FINITE_MATH_ONLY__ 1
+//
+// RUN: %clang %s -E -dM -fno-finite-math-only -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-NO-FINITE-MATH-ONLY
+// CHECK-NO-FINITE-MATH-ONLY: #define __FINITE_MATH_ONLY__ 0
+//
+// RUN: %clang_cc1 %s -E -dM -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-FINITE-MATH-FLAG-UNDEFINED
+// CHECK-FINITE-MATH-FLAG-UNDEFINED: #define __FINITE_MATH_ONLY__ 0
diff --git a/test/Preprocessor/undef-error.c b/test/Preprocessor/undef-error.c
index ad611de..959c163 100644
--- a/test/Preprocessor/undef-error.c
+++ b/test/Preprocessor/undef-error.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -pedantic-errors -verify
+// RUN: %clang_cc1 %s -pedantic-errors -Wno-empty-translation-unit -verify
// PR2045
#define b
diff --git a/test/Preprocessor/warning_tests.c b/test/Preprocessor/warning_tests.c
index 96b96ef..3f2865c 100644
--- a/test/Preprocessor/warning_tests.c
+++ b/test/Preprocessor/warning_tests.c
@@ -6,14 +6,16 @@
#if __has_warning("not valid") // expected-warning {{__has_warning expected option name}}
#endif
+// expected-warning@+2 {{Should have -Wparentheses}}
#if __has_warning("-Wparentheses")
-#warning Should have -Wparentheses // expected-warning {{Should have -Wparentheses}}
+#warning Should have -Wparentheses
#endif
#if __has_warning(-Wfoo) // expected-error {{builtin warning check macro requires a parenthesized string}}
#endif
+// expected-warning@+3 {{Not a valid warning flag}}
#if __has_warning("-Wnot-a-valid-warning-flag-at-all")
#else
-#warning Not a valid warning flag // expected-warning {{Not a valid warning flag}}
-#endif \ No newline at end of file
+#warning Not a valid warning flag
+#endif
diff --git a/test/Rewriter/blockcast3.mm b/test/Rewriter/blockcast3.mm
index ceafcff..697a465 100644
--- a/test/Rewriter/blockcast3.mm
+++ b/test/Rewriter/blockcast3.mm
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -E %s -o %t.mm
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %t.mm -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %t.mm -o %t-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o %t-modern-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-modern-rw.cpp %s
diff --git a/test/Rewriter/blockstruct.m b/test/Rewriter/blockstruct.m
index ef85c58..2c44361 100644
--- a/test/Rewriter/blockstruct.m
+++ b/test/Rewriter/blockstruct.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar://8918702
diff --git a/test/Rewriter/crash.m b/test/Rewriter/crash.m
index c61100e..7908d9f 100644
--- a/test/Rewriter/crash.m
+++ b/test/Rewriter/crash.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi -o - %s
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -o - %s
// rdar://5950938
@interface NSArray {}
+ (id)arrayWithObjects:(id)firstObj, ...;
diff --git a/test/Rewriter/finally.m b/test/Rewriter/finally.m
index 8fd475c..f46b4b0 100644
--- a/test/Rewriter/finally.m
+++ b/test/Rewriter/finally.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi -fobjc-exceptions -verify %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -fobjc-exceptions -verify %s -o -
int main() {
@try {
diff --git a/test/Rewriter/func-in-impl.m b/test/Rewriter/func-in-impl.m
index 350a726..9c4a159 100644
--- a/test/Rewriter/func-in-impl.m
+++ b/test/Rewriter/func-in-impl.m
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -E %s -o %t.m
-// RUN: %clang_cc1 -fobjc-fragile-abi -rewrite-objc %t.m -o - | FileCheck %s
+// RUN: %clang_cc1 -fobjc-runtime=macosx-fragile-10.5 -rewrite-objc %t.m -o - | FileCheck %s
@interface I {
id _delegate;
diff --git a/test/Rewriter/id-test-3.m b/test/Rewriter/id-test-3.m
index d7a7bf3..ec4cfe4 100644
--- a/test/Rewriter/id-test-3.m
+++ b/test/Rewriter/id-test-3.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol P
- (id<P>) Meth: (id<P>) Arg;
diff --git a/test/Rewriter/inner-block-helper-funcs.mm b/test/Rewriter/inner-block-helper-funcs.mm
index d30e1dc..fb565b6 100644
--- a/test/Rewriter/inner-block-helper-funcs.mm
+++ b/test/Rewriter/inner-block-helper-funcs.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
// rdar://9846759
diff --git a/test/Rewriter/instancetype-test.mm b/test/Rewriter/instancetype-test.mm
index 7885055..4a7f961 100644
--- a/test/Rewriter/instancetype-test.mm
+++ b/test/Rewriter/instancetype-test.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
diff --git a/test/Rewriter/ivar-encoding-1.m b/test/Rewriter/ivar-encoding-1.m
index d294943..3f5543e 100644
--- a/test/Rewriter/ivar-encoding-1.m
+++ b/test/Rewriter/ivar-encoding-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface Intf
{
diff --git a/test/Rewriter/ivar-encoding-2.m b/test/Rewriter/ivar-encoding-2.m
index da60c79..422568b 100644
--- a/test/Rewriter/ivar-encoding-2.m
+++ b/test/Rewriter/ivar-encoding-2.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@implementation Intf
{
diff --git a/test/Rewriter/metadata-test-1.m b/test/Rewriter/metadata-test-1.m
index b2d6e8d..caa7ca3 100644
--- a/test/Rewriter/metadata-test-1.m
+++ b/test/Rewriter/metadata-test-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface Intf
@end
diff --git a/test/Rewriter/metadata-test-2.m b/test/Rewriter/metadata-test-2.m
index 90399f7..3ffda58 100644
--- a/test/Rewriter/metadata-test-2.m
+++ b/test/Rewriter/metadata-test-2.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
typedef struct _NSPoint {
float x;
diff --git a/test/Rewriter/method-encoding-1.m b/test/Rewriter/method-encoding-1.m
index 27abea5..2cd309e 100644
--- a/test/Rewriter/method-encoding-1.m
+++ b/test/Rewriter/method-encoding-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol P1
- (void) MyProtoMeth : (int **) arg1 : (void*) arg2;
diff --git a/test/Rewriter/objc-bool-literal-modern-1.mm b/test/Rewriter/objc-bool-literal-modern-1.mm
index 7825172..7aaa79b 100644
--- a/test/Rewriter/objc-bool-literal-modern-1.mm
+++ b/test/Rewriter/objc-bool-literal-modern-1.mm
@@ -2,6 +2,9 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"__declspec(X)=" %t-rw.cpp
// rdar://11231426
+// rdar://11375908
+typedef unsigned long size_t;
+
typedef bool BOOL;
BOOL yes() {
diff --git a/test/Rewriter/objc-encoding-bug-1.m b/test/Rewriter/objc-encoding-bug-1.m
index 083b570..55e9804 100644
--- a/test/Rewriter/objc-encoding-bug-1.m
+++ b/test/Rewriter/objc-encoding-bug-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
typedef struct NSMethodFrameArgInfo {
struct NSMethodFrameArgInfo *subInfo;
diff --git a/test/Rewriter/objc-ivar-receiver-1.m b/test/Rewriter/objc-ivar-receiver-1.m
index 5195042..6cccf88 100644
--- a/test/Rewriter/objc-ivar-receiver-1.m
+++ b/test/Rewriter/objc-ivar-receiver-1.m
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o - | grep 'newInv->_container'
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o - | grep 'newInv->_container'
@interface NSMutableArray
- (void)addObject:(id)addObject;
diff --git a/test/Rewriter/objc-modern-StretAPI.mm b/test/Rewriter/objc-modern-StretAPI.mm
new file mode 100644
index 0000000..129b56c
--- /dev/null
+++ b/test/Rewriter/objc-modern-StretAPI.mm
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %s -o %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// rdar://11359268
+
+extern "C" void *sel_registerName(const char *);
+typedef unsigned long size_t;
+
+union U {
+ double d1;
+ char filler[32];
+};
+
+struct S {
+ char filler[128];
+};
+
+@interface I
+- (struct S) Meth : (int) arg1 : (id) arg2;
+- (struct S) Meth1;
+- (union U) Meth2 : (double)d;
+- (struct S) VAMeth : (int)anchor, ...;
+@end
+
+I* PI();
+
+extern "C" {
+
+struct S foo () {
+ struct S s = [PI() Meth : 1 : (id)0];
+
+ U u = [PI() Meth2 : 3.14];
+
+ S s1 = [PI() VAMeth : 12, 13.4, 1000, "hello"];
+
+ S s2 = [PI() VAMeth : 12];
+
+ S s3 = [PI() VAMeth : 0, "hello", "there"];
+
+ S s4 = [PI() VAMeth : 2, ^{}, &foo];
+
+ return [PI() Meth1];
+}
+
+}
+
diff --git a/test/Rewriter/objc-modern-boxing.mm b/test/Rewriter/objc-modern-boxing.mm
new file mode 100644
index 0000000..8f8ed75
--- /dev/null
+++ b/test/Rewriter/objc-modern-boxing.mm
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -E %s -o %t.mm
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o - | FileCheck %s
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp -Wno-attributes
+
+extern char *strdup(const char *str);
+extern "C" void *sel_registerName(const char *);
+
+typedef signed char BOOL;
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+
+#if __has_feature(objc_bool)
+#define YES __objc_yes
+#define NO __objc_no
+#else
+#define YES ((BOOL)1)
+#define NO ((BOOL)0)
+#endif
+
+@interface NSNumber
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value ;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value ;
+@end
+
+@interface NSString
++ (id)stringWithUTF8String:(const char *)str;
+@end
+
+int main(int argc, const char *argv[]) {
+ // character.
+ NSNumber *theLetterZ = @('Z'); // equivalent to [NSNumber numberWithChar:('Z')]
+
+ // integral.
+ NSNumber *fortyTwo = @(42); // equivalent to [NSNumber numberWithInt:(42)]
+ NSNumber *fortyTwoUnsigned = @(42U); // equivalent to [NSNumber numberWithUnsignedInt:(42U)]
+ NSNumber *fortyTwoLong = @(42L); // equivalent to [NSNumber numberWithLong:(42L)]
+ NSNumber *fortyTwoLongLong = @(42LL); // equivalent to [NSNumber numberWithLongLong:(42LL)]
+
+ // floating point.
+ NSNumber *piFloat = @(3.141592654F); // equivalent to [NSNumber numberWithFloat:(3.141592654F)]
+ NSNumber *piDouble = @(3.1415926535); // equivalent to [NSNumber numberWithDouble:(3.1415926535)]
+
+ BOOL b;
+ NSNumber *nsb = @(b);
+
+ // Strings.
+ NSString *duplicateString = @(strdup("Hello"));
+}
+
+// CHECK: NSNumber *theLetterZ = ((NSNumber *(*)(id, SEL, char))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithChar:"), ('Z'));
+// CHECK: NSNumber *fortyTwo = ((NSNumber *(*)(id, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (42));
+// CHECK: NSNumber *fortyTwoUnsigned = ((NSNumber *(*)(id, SEL, unsigned int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithUnsignedInt:"), (42U));
+// CHECK: NSNumber *fortyTwoLong = ((NSNumber *(*)(id, SEL, long))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithLong:"), (42L));
+// CHECK: NSNumber *fortyTwoLongLong = ((NSNumber *(*)(id, SEL, long long))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithLongLong:"), (42LL));
+// CHECK: NSNumber *piFloat = ((NSNumber *(*)(id, SEL, float))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithFloat:"), (3.1415927));
+// CHECK: NSNumber *piDouble = ((NSNumber *(*)(id, SEL, double))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithDouble:"), (3.1415926535));
+// CHECK: NSNumber *nsb = ((NSNumber *(*)(id, SEL, BOOL))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithBool:"), (BOOL)(b));
+// CHECK: NSString *duplicateString = ((NSString *(*)(id, SEL, const char *))(void *)objc_msgSend)(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), (const char *)(strdup("Hello")));
diff --git a/test/Rewriter/objc-modern-container-subscript.mm b/test/Rewriter/objc-modern-container-subscript.mm
index d6bb9c2..cdcff03 100644
--- a/test/Rewriter/objc-modern-container-subscript.mm
+++ b/test/Rewriter/objc-modern-container-subscript.mm
@@ -2,9 +2,10 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar://11203853
+typedef unsigned long size_t;
+
void *sel_registerName(const char *);
-typedef unsigned int size_t;
@protocol P @end
@interface NSMutableArray
diff --git a/test/Rewriter/objc-modern-getclass-proto.mm b/test/Rewriter/objc-modern-getclass-proto.mm
new file mode 100644
index 0000000..98e76e0
--- /dev/null
+++ b/test/Rewriter/objc-modern-getclass-proto.mm
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -E %s -o %t.mm
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %t.mm -o %t-rw.cpp
+// rdar://11375495
+
+@interface I @end
+@implementation I @end
+
+// CHECK: __OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *);
+// CHECK: __OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *);
+
diff --git a/test/Rewriter/objc-modern-property-attributes.mm b/test/Rewriter/objc-modern-property-attributes.mm
index 7d74a95..abebb2c 100644
--- a/test/Rewriter/objc-modern-property-attributes.mm
+++ b/test/Rewriter/objc-modern-property-attributes.mm
@@ -33,3 +33,13 @@ typedef void (^void_block_t)(void);
// CHECK: T@?,C,V__completion
// CHECK: T@\"PropertyClass\",&,VYVAR
+@interface Test @end
+@interface Test (Category)
+@property int q;
+@end
+
+@implementation Test (Category)
+@dynamic q;
+@end
+
+// CHECK: {{"q","Ti,D"}}
diff --git a/test/Rewriter/objc-string-concat-1.m b/test/Rewriter/objc-string-concat-1.m
index 80a9f04..9a23abc 100644
--- a/test/Rewriter/objc-string-concat-1.m
+++ b/test/Rewriter/objc-string-concat-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@class NSString;
diff --git a/test/Rewriter/objc-super-test.m b/test/Rewriter/objc-super-test.m
index fa95ad2..68412d8 100644
--- a/test/Rewriter/objc-super-test.m
+++ b/test/Rewriter/objc-super-test.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o - | grep objc_msgSendSuper | grep MainMethod
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o - | grep objc_msgSendSuper | grep MainMethod
typedef struct objc_selector *SEL;
typedef struct objc_object *id;
diff --git a/test/Rewriter/objc-synchronized-1.m b/test/Rewriter/objc-synchronized-1.m
index df55536..0e985ab 100644
--- a/test/Rewriter/objc-synchronized-1.m
+++ b/test/Rewriter/objc-synchronized-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
id SYNCH_EXPR();
void SYNCH_BODY();
diff --git a/test/Rewriter/properties.m b/test/Rewriter/properties.m
index 493fc3f..67e864ed 100644
--- a/test/Rewriter/properties.m
+++ b/test/Rewriter/properties.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fms-extensions -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
void *sel_registerName(const char *);
diff --git a/test/Rewriter/property-dot-syntax.mm b/test/Rewriter/property-dot-syntax.mm
index c8ee723..c3e4405 100644
--- a/test/Rewriter/property-dot-syntax.mm
+++ b/test/Rewriter/property-dot-syntax.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar:// 8520727
diff --git a/test/Rewriter/protocol-rewrite-1.m b/test/Rewriter/protocol-rewrite-1.m
index 687a259..541b7ee 100644
--- a/test/Rewriter/protocol-rewrite-1.m
+++ b/test/Rewriter/protocol-rewrite-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
typedef struct MyWidget {
int a;
diff --git a/test/Rewriter/protocol-rewrite-2.m b/test/Rewriter/protocol-rewrite-2.m
index e0ec4f9..e0d59e8 100644
--- a/test/Rewriter/protocol-rewrite-2.m
+++ b/test/Rewriter/protocol-rewrite-2.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o %t.cpp
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t.cpp
// RUN: %clang_cc1 -fsyntax-only %t.cpp
// rdar://10234024
diff --git a/test/Rewriter/rewrite-anonymous-union.m b/test/Rewriter/rewrite-anonymous-union.m
index 339524f..bc35fd0 100644
--- a/test/Rewriter/rewrite-anonymous-union.m
+++ b/test/Rewriter/rewrite-anonymous-union.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi -o - %s
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -o - %s
// rdar://6948022
typedef unsigned int uint32_t;
diff --git a/test/Rewriter/rewrite-api-bug.m b/test/Rewriter/rewrite-api-bug.m
index ebaa0be..f4a88dd 100644
--- a/test/Rewriter/rewrite-api-bug.m
+++ b/test/Rewriter/rewrite-api-bug.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface MyDerived
- (void) instanceMethod;
diff --git a/test/Rewriter/rewrite-block-argument.m b/test/Rewriter/rewrite-block-argument.m
index 898f983..4e1194d 100644
--- a/test/Rewriter/rewrite-block-argument.m
+++ b/test/Rewriter/rewrite-block-argument.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" -emit-llvm -o %t %t-rw.cpp
// radar 7987817
diff --git a/test/Rewriter/rewrite-block-consts.mm b/test/Rewriter/rewrite-block-consts.mm
index 1d6de8c..1b32b35 100644
--- a/test/Rewriter/rewrite-block-consts.mm
+++ b/test/Rewriter/rewrite-block-consts.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D__block="" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar:// 8243071
diff --git a/test/Rewriter/rewrite-block-ivar-call.mm b/test/Rewriter/rewrite-block-ivar-call.mm
index 2ec27b3..8e5a2ea 100644
--- a/test/Rewriter/rewrite-block-ivar-call.mm
+++ b/test/Rewriter/rewrite-block-ivar-call.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -rewrite-objc -fobjc-fragile-abi -o - %s
+// RUN: %clang_cc1 -x objective-c++ -fblocks -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -o - %s
@interface Foo {
void (^_block)(void);
diff --git a/test/Rewriter/rewrite-block-literal-1.mm b/test/Rewriter/rewrite-block-literal-1.mm
index f152117..88ad574 100644
--- a/test/Rewriter/rewrite-block-literal-1.mm
+++ b/test/Rewriter/rewrite-block-literal-1.mm
@@ -1,10 +1,13 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 9254348
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// rdar://11259664
+// rdar://11375908
+typedef unsigned long size_t;
+
void *sel_registerName(const char *);
typedef void (^BLOCK_TYPE)(void);
diff --git a/test/Rewriter/rewrite-block-literal.mm b/test/Rewriter/rewrite-block-literal.mm
index 083312e..43f5811 100644
--- a/test/Rewriter/rewrite-block-literal.mm
+++ b/test/Rewriter/rewrite-block-literal.mm
@@ -1,10 +1,13 @@
// RUN: %clang_cc1 -E %s -o %t.mm
// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o - | FileCheck %s
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
+// rdar://11375908
+typedef unsigned long size_t;
+
// rdar: // 11006566
void I( void (^)(void));
diff --git a/test/Rewriter/rewrite-block-pointer.mm b/test/Rewriter/rewrite-block-pointer.mm
index 58407a7..9f33680 100644
--- a/test/Rewriter/rewrite-block-pointer.mm
+++ b/test/Rewriter/rewrite-block-pointer.mm
@@ -1,9 +1,12 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// radar 7638400
+// rdar://11375908
+typedef unsigned long size_t;
+
typedef void * id;
void *sel_registerName(const char *);
diff --git a/test/Rewriter/rewrite-block-property.m b/test/Rewriter/rewrite-block-property.m
index 5c44b7d..1328ab7 100644
--- a/test/Rewriter/rewrite-block-property.m
+++ b/test/Rewriter/rewrite-block-property.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar://9055596
diff --git a/test/Rewriter/rewrite-byref-in-nested-blocks.mm b/test/Rewriter/rewrite-byref-in-nested-blocks.mm
index a659383..022bb5f 100644
--- a/test/Rewriter/rewrite-byref-in-nested-blocks.mm
+++ b/test/Rewriter/rewrite-byref-in-nested-blocks.mm
@@ -1,9 +1,12 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
-// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
-// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-modern-rw.cpp
// radar 7692350
+// rdar://11375908
+typedef unsigned long size_t;
+
void f(void (^block)(void));
@interface X {
diff --git a/test/Rewriter/rewrite-byref-vars.mm b/test/Rewriter/rewrite-byref-vars.mm
index 14a182a..cfc5134 100644
--- a/test/Rewriter/rewrite-byref-vars.mm
+++ b/test/Rewriter/rewrite-byref-vars.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7540194
@@ -53,5 +53,5 @@ __declspec(dllexport) extern "C" __declspec(dllexport) void XXXXBreakTheRewriter
- (void) Meth { __attribute__((__blocks__(byref))) void ** listp = (void **)list; }
@end
-// $CLANG -cc1 -fms-extensions -rewrite-objc -fobjc-fragile-abi -x objective-c++ -fblocks bug.mm
+// $CLANG -cc1 -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -x objective-c++ -fblocks bug.mm
// g++ -c -D"__declspec(X)=" bug.cpp
diff --git a/test/Rewriter/rewrite-cast-ivar-access.mm b/test/Rewriter/rewrite-cast-ivar-access.mm
index ccc1cde..4e50ff1 100644
--- a/test/Rewriter/rewrite-cast-ivar-access.mm
+++ b/test/Rewriter/rewrite-cast-ivar-access.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
// radar 7575882
diff --git a/test/Rewriter/rewrite-cast-to-bool.mm b/test/Rewriter/rewrite-cast-to-bool.mm
index b3272b6..58db706 100644
--- a/test/Rewriter/rewrite-cast-to-bool.mm
+++ b/test/Rewriter/rewrite-cast-to-bool.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 9899834
diff --git a/test/Rewriter/rewrite-category-property.mm b/test/Rewriter/rewrite-category-property.mm
index b54bb67..50ef432 100644
--- a/test/Rewriter/rewrite-category-property.mm
+++ b/test/Rewriter/rewrite-category-property.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
// radar 7630636
diff --git a/test/Rewriter/rewrite-constructor-init.mm b/test/Rewriter/rewrite-constructor-init.mm
index f12de1f..fa0e148 100644
--- a/test/Rewriter/rewrite-constructor-init.mm
+++ b/test/Rewriter/rewrite-constructor-init.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar : // 8213998
diff --git a/test/Rewriter/rewrite-eh.m b/test/Rewriter/rewrite-eh.m
index 46d1930..6e6fb1c 100644
--- a/test/Rewriter/rewrite-eh.m
+++ b/test/Rewriter/rewrite-eh.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi -fobjc-exceptions -o - %s
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -fobjc-exceptions -o - %s
// rdar://7522880
@interface NSException
diff --git a/test/Rewriter/rewrite-elaborated-type.mm b/test/Rewriter/rewrite-elaborated-type.mm
index a0c7e43..f2faa55 100644
--- a/test/Rewriter/rewrite-elaborated-type.mm
+++ b/test/Rewriter/rewrite-elaborated-type.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D_Bool=bool -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D_Bool=bool -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
diff --git a/test/Rewriter/rewrite-extern-c.mm b/test/Rewriter/rewrite-extern-c.mm
index c610493..cf37d75 100644
--- a/test/Rewriter/rewrite-extern-c.mm
+++ b/test/Rewriter/rewrite-extern-c.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -rewrite-objc -fobjc-fragile-abi -o - %s
+// RUN: %clang_cc1 -x objective-c++ -fblocks -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -o - %s
// radar 7546096
extern "C" {
diff --git a/test/Rewriter/rewrite-foreach-1.m b/test/Rewriter/rewrite-foreach-1.m
index f57e13c..5263fff 100644
--- a/test/Rewriter/rewrite-foreach-1.m
+++ b/test/Rewriter/rewrite-foreach-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol P @end
diff --git a/test/Rewriter/rewrite-foreach-2.m b/test/Rewriter/rewrite-foreach-2.m
index 228612a..120d7d7 100644
--- a/test/Rewriter/rewrite-foreach-2.m
+++ b/test/Rewriter/rewrite-foreach-2.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol P @end
diff --git a/test/Rewriter/rewrite-foreach-3.m b/test/Rewriter/rewrite-foreach-3.m
index ef3803f..6e67415 100644
--- a/test/Rewriter/rewrite-foreach-3.m
+++ b/test/Rewriter/rewrite-foreach-3.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol P @end
diff --git a/test/Rewriter/rewrite-foreach-4.m b/test/Rewriter/rewrite-foreach-4.m
index 42cb2fb..e852c75 100644
--- a/test/Rewriter/rewrite-foreach-4.m
+++ b/test/Rewriter/rewrite-foreach-4.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface MyList
- (id) allKeys;
diff --git a/test/Rewriter/rewrite-foreach-5.m b/test/Rewriter/rewrite-foreach-5.m
index 2940f45..1d3cc57 100644
--- a/test/Rewriter/rewrite-foreach-5.m
+++ b/test/Rewriter/rewrite-foreach-5.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
void *sel_registerName(const char *);
diff --git a/test/Rewriter/rewrite-foreach-6.m b/test/Rewriter/rewrite-foreach-6.m
index 968c6f4..5159d38 100644
--- a/test/Rewriter/rewrite-foreach-6.m
+++ b/test/Rewriter/rewrite-foreach-6.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar://5716356
// FIXME: Should be able to pipe into clang, but code is not
diff --git a/test/Rewriter/rewrite-foreach-7.m b/test/Rewriter/rewrite-foreach-7.m
index 8c9293f..8f3a7c8 100644
--- a/test/Rewriter/rewrite-foreach-7.m
+++ b/test/Rewriter/rewrite-foreach-7.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@class NSArray;
int main() {
diff --git a/test/Rewriter/rewrite-foreach-in-block.mm b/test/Rewriter/rewrite-foreach-in-block.mm
index fd34212..2c1023b 100644
--- a/test/Rewriter/rewrite-foreach-in-block.mm
+++ b/test/Rewriter/rewrite-foreach-in-block.mm
@@ -1,9 +1,11 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// rdar:// 9878420
+typedef unsigned long size_t;
+
void objc_enumerationMutation(id);
void *sel_registerName(const char *);
typedef void (^CoreDAVCompletionBlock)(void);
diff --git a/test/Rewriter/rewrite-foreach-protocol-id.m b/test/Rewriter/rewrite-foreach-protocol-id.m
index 034fb7a..5680110 100644
--- a/test/Rewriter/rewrite-foreach-protocol-id.m
+++ b/test/Rewriter/rewrite-foreach-protocol-id.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar:// 9039342
diff --git a/test/Rewriter/rewrite-forward-class.m b/test/Rewriter/rewrite-forward-class.m
index 0ac620a..28bab9d 100644
--- a/test/Rewriter/rewrite-forward-class.m
+++ b/test/Rewriter/rewrite-forward-class.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar://6969189
diff --git a/test/Rewriter/rewrite-forward-class.mm b/test/Rewriter/rewrite-forward-class.mm
index 8e4eda6..9f4e7e2 100644
--- a/test/Rewriter/rewrite-forward-class.mm
+++ b/test/Rewriter/rewrite-forward-class.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
extern "C" {
diff --git a/test/Rewriter/rewrite-function-decl.mm b/test/Rewriter/rewrite-function-decl.mm
index 883d393..af85f68 100644
--- a/test/Rewriter/rewrite-function-decl.mm
+++ b/test/Rewriter/rewrite-function-decl.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fms-extensions -rewrite-objc -fobjc-fragile-abi -x objective-c++ -fblocks -o - %s
+// RUN: %clang_cc1 -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -x objective-c++ -fblocks -o - %s
extern "C" __declspec(dllexport) void BreakTheRewriter(void) {
__block int aBlockVariable = 0;
diff --git a/test/Rewriter/rewrite-implementation.mm b/test/Rewriter/rewrite-implementation.mm
index 2cc3387..8ef4ebe 100644
--- a/test/Rewriter/rewrite-implementation.mm
+++ b/test/Rewriter/rewrite-implementation.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7649577
diff --git a/test/Rewriter/rewrite-ivar-use.m b/test/Rewriter/rewrite-ivar-use.m
index 53b07c4..17bbf01 100644
--- a/test/Rewriter/rewrite-ivar-use.m
+++ b/test/Rewriter/rewrite-ivar-use.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7490331
diff --git a/test/Rewriter/rewrite-local-externs-in-block.mm b/test/Rewriter/rewrite-local-externs-in-block.mm
index 35d282b..b31b422 100644
--- a/test/Rewriter/rewrite-local-externs-in-block.mm
+++ b/test/Rewriter/rewrite-local-externs-in-block.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7735987
diff --git a/test/Rewriter/rewrite-local-static-id.mm b/test/Rewriter/rewrite-local-static-id.mm
index b002b6e..b3d2c48 100644
--- a/test/Rewriter/rewrite-local-static-id.mm
+++ b/test/Rewriter/rewrite-local-static-id.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" -emit-llvm -o %t %t-rw.cpp
// radar 7946975
diff --git a/test/Rewriter/rewrite-message-expr.mm b/test/Rewriter/rewrite-message-expr.mm
index dafb31b..72569cb 100644
--- a/test/Rewriter/rewrite-message-expr.mm
+++ b/test/Rewriter/rewrite-message-expr.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
// radar 7617047
diff --git a/test/Rewriter/rewrite-modern-atautoreleasepool.mm b/test/Rewriter/rewrite-modern-atautoreleasepool.mm
new file mode 100644
index 0000000..57c08ab
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-atautoreleasepool.mm
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -E %s -o %t.mm
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o %t-rw.cpp
+// RUN: FileCheck --input-file=%t-rw.cpp %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// radar 11474836
+
+extern "C"
+void *sel_registerName(const char *);
+
+@interface I
+{
+ id ivar;
+}
+- (id) Meth;
++ (id) MyAlloc;;
+@end
+
+@implementation I
+- (id) Meth {
+ @autoreleasepool {
+ id p = [I MyAlloc];
+ if (!p)
+ return ivar;
+ }
+ return 0;
+}
++ (id) MyAlloc {
+ return 0;
+}
+@end
+
+// CHECK: /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
diff --git a/test/Rewriter/rewrite-modern-block-consts.mm b/test/Rewriter/rewrite-modern-block-consts.mm
new file mode 100644
index 0000000..48c8d40
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-block-consts.mm
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -U__declspec -D"__declspec(X)=" %t-modern-rw.cpp
+// rdar:// 8243071
+
+// rdar://11375908
+typedef unsigned long size_t;
+
+void x(int y) {}
+void f() {
+ const int bar = 3;
+ int baz = 4;
+ __block int bab = 4;
+ __block const int bas = 5;
+ void (^b)() = ^{
+ x(bar);
+ x(baz);
+ x(bab);
+ x(bas);
+ b();
+ };
+ b();
+}
diff --git a/test/Rewriter/rewrite-modern-block.mm b/test/Rewriter/rewrite-modern-block.mm
index 8da723d..2302965 100644
--- a/test/Rewriter/rewrite-modern-block.mm
+++ b/test/Rewriter/rewrite-modern-block.mm
@@ -1,7 +1,8 @@
// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %s -o %t-rw.cpp
-// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
// rdar://11230308
+typedef unsigned long size_t;
typedef struct {
char byte0;
char byte1;
@@ -20,4 +21,45 @@ void y() {
// rdar://11236342
int foo() {
__block int hello;
+ return hello;
}
+
+// rdar://7547630
+// rewriting multiple __block decls on wintin same decl stmt.
+void radar7547630() {
+ __block int BI1, BI2;
+
+ __block float FLOAT1, FT2, FFFFFFFF3,
+ FFFXXX4;
+
+ __block void (^B)(), (^BB)();
+}
+
+// rewriting multiple __block decls on wintin same decl stmt
+// with initializers.
+int rdar7547630(const char *keybuf, const char *valuebuf) {
+ __block int BI1 = 1, BI2 = 2;
+
+ double __block BYREFVAR = 1.34, BYREFVAR_NO_INIT, BYREFVAR2 = 1.37;
+
+ __block const char *keys = keybuf, *values = valuebuf, *novalues;
+
+ return BI2;
+}
+
+// rdar://11326988
+typedef struct _z {
+ int location;
+ int length;
+} z;
+
+z w(int loc, int len);
+
+@interface rdar11326988
+@end
+@implementation rdar11326988
+- (void)y:(int)options {
+ __attribute__((__blocks__(byref))) z firstRange = w(1, 0);
+ options &= ~(1 | 2);
+}
+@end
diff --git a/test/Rewriter/rewrite-modern-captured-nested-bvar.mm b/test/Rewriter/rewrite-modern-captured-nested-bvar.mm
new file mode 100644
index 0000000..a8fd180
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-captured-nested-bvar.mm
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -E %s -o %t.mm
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o - | FileCheck %s
+// rdar://9006279
+
+void q(void (^p)(void)) {
+ p();
+}
+
+void f() {
+ __block char BYREF_VAR_CHECK = 'a';
+ __block char d = 'd';
+ q(^{
+ q(^{
+ __block char e = 'e';
+ char l = 'l';
+ BYREF_VAR_CHECK = 'b';
+ d = 'd';
+ q(^{
+ e = '1';
+ BYREF_VAR_CHECK = '2';
+ d = '3';
+ }
+ );
+ });
+ });
+}
+
+int main() {
+ f();
+ return 0;
+}
+
+// CHECK 2: (__Block_byref_BYREF_VAR_CHECK_0 *)BYREF_VAR_CHECK
+// CHECK: {(void*)0,(__Block_byref_BYREF_VAR_CHECK_0 *)&BYREF_VAR_CHECK, 0, sizeof(__Block_byref_BYREF_VAR_CHECK_0), 'a'}
+// CHECK: __Block_byref_BYREF_VAR_CHECK_0 *)&BYREF_VAR_CHECK, (__Block_byref_d_1 *)&d, 570425344));
diff --git a/test/Rewriter/rewrite-modern-default-property-synthesis.mm b/test/Rewriter/rewrite-modern-default-property-synthesis.mm
new file mode 100644
index 0000000..fd1a578
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-default-property-synthesis.mm
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -E %s -o %t.mm
+// RUN: %clang_cc1 -x objective-c++ -fms-extensions -fobjc-default-synthesize-properties -rewrite-objc %t.mm -o %t-rw.cpp
+// RUN: FileCheck --input-file=%t-rw.cpp %s
+// RUN: %clang_cc1 -fsyntax-only -Werror -DSEL="void *" -Did="struct objc_object *" -Wno-attributes -Wno-address-of-temporary -U__declspec -D"__declspec(X)=" %t-rw.cpp
+// rdar://11374235
+
+extern "C" void *sel_registerName(const char *);
+
+@interface NSObject
+- (void) release;
+- (id) retain;
+@end
+@class NSString;
+
+@interface SynthItAll : NSObject
+@property int howMany;
+@property (retain) NSString* what;
+@end
+
+@implementation SynthItAll
+@end
+
+
+@interface SynthSetter : NSObject
+@property (nonatomic) int howMany;
+@property (nonatomic, retain) NSString* what;
+@end
+
+@implementation SynthSetter
+
+- (int) howMany {
+ return _howMany;
+}
+// - (void) setHowMany: (int) value
+
+- (NSString*) what {
+ return _what;
+}
+// - (void) setWhat: (NSString*) value
+@end
+
+
+@interface SynthGetter : NSObject
+@property (nonatomic) int howMany;
+@property (nonatomic, retain) NSString* what;
+@end
+
+@implementation SynthGetter
+// - (int) howMany
+- (void) setHowMany: (int) value {
+ _howMany = value;
+}
+
+// - (NSString*) what
+- (void) setWhat: (NSString*) value {
+ if (_what != value) {
+ [_what release];
+ _what = [value retain];
+ }
+}
+@end
+
+typedef struct {
+ int x:1;
+ int y:1;
+} TBAR;
+
+@interface NONAME
+{
+ TBAR _bar;
+}
+@property TBAR bad;
+@end
+
+@implementation NONAME
+@end
+
+// CHECK: (*(int *)((char *)self + OBJC_IVAR_$_SynthItAll$_howMany)) = howMany;
+// CHECK: return (*(int *)((char *)self + OBJC_IVAR_$_SynthGetter$_howMany));
+// CHECK: (*(TBAR *)((char *)self + OBJC_IVAR_$_NONAME$_bad)) = bad;
diff --git a/test/Rewriter/rewrite-modern-extern-c-func-decl.mm b/test/Rewriter/rewrite-modern-extern-c-func-decl.mm
index 82d5a4d..c3bfec9 100644
--- a/test/Rewriter/rewrite-modern-extern-c-func-decl.mm
+++ b/test/Rewriter/rewrite-modern-extern-c-func-decl.mm
@@ -2,6 +2,7 @@
// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -Wno-attributes -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
// rdar://11131490
+typedef unsigned long size_t;
extern "C" __declspec(dllexport) void BreakTheRewriter(void) {
__block int aBlockVariable = 0;
void (^aBlock)(void) = ^ {
@@ -43,3 +44,51 @@ main (int argc, char *argv[])
bBlockVariable = 42;
};
}
+
+// rdar://11275241
+static char stringtype;
+char CFStringGetTypeID();
+void x(void (^)());
+
+static void initStatics(int arg, ...) {
+ x(^{
+ stringtype = CFStringGetTypeID();
+ });
+}
+static void initStatics1(...) {
+ x(^{
+ stringtype = CFStringGetTypeID();
+ });
+}
+static void initStatics2() {
+ x(^{
+ stringtype = CFStringGetTypeID();
+ });
+}
+
+// rdar://11314329
+static inline const void *auto_zone_base_pointer(void *zone, const void *ptr) { return 0; }
+
+@interface I
+{
+ id list;
+}
+- (void) Meth;
+// radar 7589385 use before definition
+- (void) allObjects;
+@end
+
+@implementation I
+// radar 7589385 use before definition
+- (void) allObjects {
+ __attribute__((__blocks__(byref))) id *listp;
+
+ void (^B)(void) = ^(void) {
+ *listp++ = 0;
+ };
+
+ B();
+}
+- (void) Meth { __attribute__((__blocks__(byref))) void ** listp = (void **)list; }
+@end
+
diff --git a/test/Rewriter/rewrite-modern-ivar-access.mm b/test/Rewriter/rewrite-modern-ivar-access.mm
new file mode 100644
index 0000000..4bde1a4
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-ivar-access.mm
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -fblocks -rewrite-objc -fms-extensions %s -o %t-rw.cpp
+// RUN: %clang_cc1 -Werror -fsyntax-only -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
+
+struct OUTSIDE {
+ int i_OUTSIDE;
+ double d_OUTSIDE;
+};
+
+
+@interface I1 {
+@protected
+ struct OUTSIDE ivar_I1;
+
+ struct INNER_I1 {
+ int i_INNER_I1;
+ double d_INNER_I1;
+ };
+
+ struct INNER_I1 ivar_I2;
+
+ struct OUTSIDE ivar_I3;
+
+ struct {
+ int i_noname;
+ double d_noname;
+ } NONAME_I4;
+
+ struct {
+ int i_noname;
+ double d_noname;
+ } NONAME_I5;
+}
+@end
+
+@implementation I1
+- (void) I1_Meth {
+ ivar_I1.i_OUTSIDE = 0;
+
+ ivar_I2.i_INNER_I1 = 1;
+
+ ivar_I3.i_OUTSIDE = 2;
+
+ NONAME_I4.i_noname = 3;
+
+ NONAME_I5.i_noname = 4;
+}
+@end
+
+@interface INTF2 {
+@protected
+ struct OUTSIDE ivar_INTF2;
+
+ struct {
+ int i_noname;
+ double d_noname;
+ } NONAME_INTF4;
+
+
+ struct OUTSIDE ivar_INTF3;
+
+ struct INNER_I1 ivar_INTF4;
+
+ struct {
+ int i_noname;
+ double d_noname;
+ } NONAME_INTF5;
+
+ struct INNER_INTF2 {
+ int i_INNER_INTF2;
+ double d_INNER_INTF2;
+ };
+
+ struct INNER_INTF2 ivar_INTF6, ivar_INTF7;
+
+ struct INNER_INTF3 {
+ int i;
+ } X1,X2,X3;
+
+}
+@end
+
+@implementation INTF2
+- (void) I2_Meth {
+ ivar_INTF2.i_OUTSIDE = 0;
+
+ ivar_INTF4.i_INNER_I1 = 1;
+
+ ivar_INTF3.i_OUTSIDE = 2;
+
+ NONAME_INTF4.i_noname = 3;
+
+ NONAME_INTF5.i_noname = 4;
+ ivar_INTF6.i_INNER_INTF2 = 5;
+ ivar_INTF7.i_INNER_INTF2 = 5;
+ X1.i = X2.i = X3.i = 1;
+}
+@end
+
diff --git a/test/Rewriter/rewrite-modern-ivars-1.mm b/test/Rewriter/rewrite-modern-ivars-1.mm
index 376d300..dbd28d1 100644
--- a/test/Rewriter/rewrite-modern-ivars-1.mm
+++ b/test/Rewriter/rewrite-modern-ivars-1.mm
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-rw.cpp
-// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
@interface NSCheapMutableString {
@private
@@ -87,3 +87,38 @@
}
@end
+enum OUTSIDE {
+ yes
+};
+
+@interface MoreEnumTests {
+@private
+ enum INSIDE {
+ no
+ } others;
+
+ enum OUTSIDE meetoo;
+
+ enum {
+ one,
+ two
+ } eu;
+}
+@end
+
+@interface I {
+ enum INSIDE I1;
+ enum OUTSIDE I2;
+ enum ALSO_INSIDE {
+ maybe
+ } I3;
+
+ enum ALSO_INSIDE I4;
+
+ enum {
+ three,
+ four
+ } I5;
+}
+@end
+
diff --git a/test/Rewriter/rewrite-modern-private-ivars.mm b/test/Rewriter/rewrite-modern-private-ivars.mm
new file mode 100644
index 0000000..fc2e73b
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-private-ivars.mm
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -fblocks -rewrite-objc -fms-extensions %s -o %t-rw.cpp
+// RUN: %clang_cc1 -Werror -fsyntax-only -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
+// rdar://11351299
+
+struct Q {
+ int x;
+};
+
+@interface I
+@end
+
+@interface I() {
+
+ struct {
+ int x;
+ } unnamed;
+
+ struct S {
+ int x;
+ } foo;
+
+ double dd;
+
+ struct S foo1;
+}
+@end
+
+@implementation I
+{
+ struct P {
+ int x;
+ } bar;
+
+ double ee;
+
+ struct Q bar1;
+
+ struct {
+ int x;
+ } noname;
+}
+
+- (void) Meth {
+ foo.x = 1;
+ bar.x = 2;
+ dd = 1.23;
+ ee = 0.0;
+ foo1.x = 3;
+ bar1.x = 4;
+ noname.x = 3;
+ unnamed.x = 10;
+}
+@end
diff --git a/test/Rewriter/rewrite-modern-struct-ivar-1.mm b/test/Rewriter/rewrite-modern-struct-ivar-1.mm
new file mode 100644
index 0000000..a17fe3f
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-struct-ivar-1.mm
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -fblocks -rewrite-objc -fms-extensions %s -o %t-rw.cpp
+// RUN: %clang_cc1 -Werror -fsyntax-only -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
+// rdar://11323187
+
+typedef unsigned long NSUInteger;
+
+typedef struct _NSRange {
+ NSUInteger location;
+ NSUInteger length;
+} NSRange;
+
+typedef struct {
+ NSUInteger _capacity;
+ NSRange _ranges[0];
+} _NSRangeInfo;
+
+@interface Foo{
+ @protected
+ struct _bar {
+ int x:1;
+ int y:1;
+ } bar;
+ union {
+ struct {
+ NSRange _range;
+ } _singleRange;
+ struct {
+ void * _data;
+ void *_reserved;
+ } _multipleRanges;
+ } _internal;
+}
+@end
+@implementation Foo
+- (void)x:(Foo *)other {
+ bar.x = 0;
+ bar.y = 1;
+ self->_internal._singleRange._range = (( other ->bar.x) ? &( other ->_internal._singleRange._range) : ((NSRange *)(&(((_NSRangeInfo *)( other ->_internal._multipleRanges._data))->_ranges))))[0];
+}
+@end
+@interface FooS : Foo
+@end
+@implementation FooS
+- (void)y {
+
+ NSUInteger asdf = (( self ->bar.x) ? 1 : ((_NSRangeInfo *)( self ->_internal._multipleRanges._data))->_capacity );
+}
+@end
diff --git a/test/Rewriter/rewrite-modern-struct-ivar.mm b/test/Rewriter/rewrite-modern-struct-ivar.mm
index 09c02df..9f56cd8 100644
--- a/test/Rewriter/rewrite-modern-struct-ivar.mm
+++ b/test/Rewriter/rewrite-modern-struct-ivar.mm
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -E %s -o %t.mm
// RUN: %clang_cc1 -fblocks -rewrite-objc -fms-extensions %t.mm -o %t-rw.cpp
// RUN: FileCheck --input-file=%t-rw.cpp %s
-// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
struct S {
int i1;
@@ -20,5 +20,33 @@ struct S {
@implementation I
- (struct S) dMeth{ return struct_ivar; }
@end
-
+
// CHECK: return (*(struct S *)((char *)self + OBJC_IVAR_$_I$struct_ivar));
+
+// rdar://11323187
+@interface Foo{
+ @protected
+ struct {
+ int x:1;
+ int y:1;
+ } bar;
+
+ struct _S {
+ int x:1;
+ int y:1;
+ } s;
+
+}
+@end
+@implementation Foo
+- (void)x {
+ bar.x = 0;
+ bar.y = 1;
+
+ s.x = 0;
+ s.y = 1;
+}
+@end
+
+// CHECK: (*(decltype(((Foo_IMPL *)0U)->bar) *)((char *)self + OBJC_IVAR_$_Foo$bar)).x = 0;
+// CHECK: (*(struct _S *)((char *)self + OBJC_IVAR_$_Foo$s)).x = 0;
diff --git a/test/Rewriter/rewrite-modern-typeof.mm b/test/Rewriter/rewrite-modern-typeof.mm
index 4650ab7..a493c11 100644
--- a/test/Rewriter/rewrite-modern-typeof.mm
+++ b/test/Rewriter/rewrite-modern-typeof.mm
@@ -2,6 +2,7 @@
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -Wno-attributes -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+typedef unsigned long size_t;
extern "C" {
extern "C" void *_Block_copy(const void *aBlock);
extern "C" void _Block_release(const void *aBlock);
diff --git a/test/Rewriter/rewrite-nest.m b/test/Rewriter/rewrite-nest.m
index a08f947..41bb875 100644
--- a/test/Rewriter/rewrite-nest.m
+++ b/test/Rewriter/rewrite-nest.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface NSMapTable @end
@interface NSEnumerator @end
diff --git a/test/Rewriter/rewrite-nested-blocks-1.mm b/test/Rewriter/rewrite-nested-blocks-1.mm
index 25b17cf..8981777 100644
--- a/test/Rewriter/rewrite-nested-blocks-1.mm
+++ b/test/Rewriter/rewrite-nested-blocks-1.mm
@@ -1,9 +1,10 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// radar 7696893
+typedef unsigned long size_t;
void *sel_registerName(const char *);
void f(void (^block)(void));
diff --git a/test/Rewriter/rewrite-nested-blocks-2.mm b/test/Rewriter/rewrite-nested-blocks-2.mm
index d1513f3..46afdf1 100644
--- a/test/Rewriter/rewrite-nested-blocks-2.mm
+++ b/test/Rewriter/rewrite-nested-blocks-2.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// grep "static void __FUNC_block_copy_" %t-rw.cpp | count 2
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
@@ -6,6 +6,7 @@
// grep "static void __FUNC_block_copy_" %t-modern-rw.cpp | count 2
// rdar://8499592
+typedef unsigned long size_t;
void Outer(void (^bk)());
void Inner(void (^bk)());
void INNER_FUNC(id d);
diff --git a/test/Rewriter/rewrite-nested-blocks.mm b/test/Rewriter/rewrite-nested-blocks.mm
index d492afa..16a9b31 100644
--- a/test/Rewriter/rewrite-nested-blocks.mm
+++ b/test/Rewriter/rewrite-nested-blocks.mm
@@ -1,10 +1,11 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// radar 7682149
+typedef unsigned long size_t;
void f(void (^block)(void));
@interface X {
diff --git a/test/Rewriter/rewrite-nested-ivar.mm b/test/Rewriter/rewrite-nested-ivar.mm
index bab221d..07fc2f5 100644
--- a/test/Rewriter/rewrite-nested-ivar.mm
+++ b/test/Rewriter/rewrite-nested-ivar.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-rw-modern.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw-modern.cpp
diff --git a/test/Rewriter/rewrite-nested-property-in-blocks.mm b/test/Rewriter/rewrite-nested-property-in-blocks.mm
index 7f9361e..d029559 100755
--- a/test/Rewriter/rewrite-nested-property-in-blocks.mm
+++ b/test/Rewriter/rewrite-nested-property-in-blocks.mm
@@ -1,9 +1,10 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fms-extensions -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fms-extensions -Wno-address-of-temporary -Did="void *" -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// radar 8608293
+typedef unsigned long size_t;
void *sel_registerName(const char *);
extern "C" void nowarn(id);
diff --git a/test/Rewriter/rewrite-no-nextline.mm b/test/Rewriter/rewrite-no-nextline.mm
index 4ef3715..51f412f 100644
--- a/test/Rewriter/rewrite-no-nextline.mm
+++ b/test/Rewriter/rewrite-no-nextline.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7946975
diff --git a/test/Rewriter/rewrite-property-attributes.mm b/test/Rewriter/rewrite-property-attributes.mm
index c707603..4a8b197 100644
--- a/test/Rewriter/rewrite-property-attributes.mm
+++ b/test/Rewriter/rewrite-property-attributes.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7214439
diff --git a/test/Rewriter/rewrite-property-set-cfstring.mm b/test/Rewriter/rewrite-property-set-cfstring.mm
index 3ac9065..9de7642 100644
--- a/test/Rewriter/rewrite-property-set-cfstring.mm
+++ b/test/Rewriter/rewrite-property-set-cfstring.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar:// 8527018
diff --git a/test/Rewriter/rewrite-protocol-property.mm b/test/Rewriter/rewrite-protocol-property.mm
index 3a53f23..c3f154c 100644
--- a/test/Rewriter/rewrite-protocol-property.mm
+++ b/test/Rewriter/rewrite-protocol-property.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -Did="void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar:// 8558702
diff --git a/test/Rewriter/rewrite-protocol-qualified.mm b/test/Rewriter/rewrite-protocol-qualified.mm
index f828724..7a7eed7 100644
--- a/test/Rewriter/rewrite-protocol-qualified.mm
+++ b/test/Rewriter/rewrite-protocol-qualified.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"id=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7589414
diff --git a/test/Rewriter/rewrite-protocol-type-1.m b/test/Rewriter/rewrite-protocol-type-1.m
index 5246df4..2ecae8b 100644
--- a/test/Rewriter/rewrite-protocol-type-1.m
+++ b/test/Rewriter/rewrite-protocol-type-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol MyProto1
@end
diff --git a/test/Rewriter/rewrite-qualified-id.mm b/test/Rewriter/rewrite-qualified-id.mm
index f54bae9..428b655 100644
--- a/test/Rewriter/rewrite-qualified-id.mm
+++ b/test/Rewriter/rewrite-qualified-id.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// radar 7680953
diff --git a/test/Rewriter/rewrite-rewritten-initializer.mm b/test/Rewriter/rewrite-rewritten-initializer.mm
index 14734f9..25d1625 100644
--- a/test/Rewriter/rewrite-rewritten-initializer.mm
+++ b/test/Rewriter/rewrite-rewritten-initializer.mm
@@ -1,7 +1,10 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
-// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-rw-modern.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -D"SEL=void*" -U__declspec -D"__declspec(X)=" %t-rw-modern.cpp
// radar 7669784
+typedef unsigned long size_t;
typedef void * id;
void *sel_registerName(const char *);
diff --git a/test/Rewriter/rewrite-static-block.mm b/test/Rewriter/rewrite-static-block.mm
index 4ee227e..ed720e0 100644
--- a/test/Rewriter/rewrite-static-block.mm
+++ b/test/Rewriter/rewrite-static-block.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp -emit-llvm -o %t-rw.ll
// RUN: FileCheck --input-file=%t-rw.ll %s
diff --git a/test/Rewriter/rewrite-super-message.mm b/test/Rewriter/rewrite-super-message.mm
index d770b33..45d7e13 100644
--- a/test/Rewriter/rewrite-super-message.mm
+++ b/test/Rewriter/rewrite-super-message.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -DKEEP_ATTRIBUTES -D"id=struct objc_object *" -D"Class=struct objc_class *" -D"SEL=void*" -D"__declspec(X)=" -emit-llvm -o - %t-rw.cpp | FileCheck %t-rw.cpp
// radar 7738453
diff --git a/test/Rewriter/rewrite-trivial-constructor.mm b/test/Rewriter/rewrite-trivial-constructor.mm
index f14f4b1..e343a38 100644
--- a/test/Rewriter/rewrite-trivial-constructor.mm
+++ b/test/Rewriter/rewrite-trivial-constructor.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fms-extensions -rewrite-objc -fobjc-fragile-abi -x objective-c++ -fblocks -o - %s
+// RUN: %clang_cc1 -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -x objective-c++ -fblocks -o - %s
// radar 7537770
typedef struct {
diff --git a/test/Rewriter/rewrite-try-catch.m b/test/Rewriter/rewrite-try-catch.m
index b4d13b6..8720d0c 100644
--- a/test/Rewriter/rewrite-try-catch.m
+++ b/test/Rewriter/rewrite-try-catch.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface Foo @end
@interface GARF @end
diff --git a/test/Rewriter/rewrite-typeof.mm b/test/Rewriter/rewrite-typeof.mm
index 24e3bcc..02c36f8 100644
--- a/test/Rewriter/rewrite-typeof.mm
+++ b/test/Rewriter/rewrite-typeof.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: FileCheck -check-prefix LP --input-file=%t-rw.cpp %s
extern "C" {
diff --git a/test/Rewriter/rewrite-unique-block-api.mm b/test/Rewriter/rewrite-unique-block-api.mm
index 1dcc1b6..85ead11 100644
--- a/test/Rewriter/rewrite-unique-block-api.mm
+++ b/test/Rewriter/rewrite-unique-block-api.mm
@@ -1,9 +1,10 @@
-// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %s -o %t-modern-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -D"SEL=void*" -D"__declspec(X)=" %t-modern-rw.cpp
// radar 7630551
+typedef unsigned long size_t;
void f(void (^b)(char c));
@interface a
diff --git a/test/Rewriter/rewrite-user-defined-accessors.mm b/test/Rewriter/rewrite-user-defined-accessors.mm
index 3c38e17..24d7b05 100644
--- a/test/Rewriter/rewrite-user-defined-accessors.mm
+++ b/test/Rewriter/rewrite-user-defined-accessors.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -Did="void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar:// 8570020
diff --git a/test/Rewriter/rewrite-vararg.m b/test/Rewriter/rewrite-vararg.m
index bd58a6a..ff7b250 100644
--- a/test/Rewriter/rewrite-vararg.m
+++ b/test/Rewriter/rewrite-vararg.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-fragile-abi %s -o %t-rw.cpp
+// RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o %t-rw.cpp
// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
// rdar://9056351
diff --git a/test/Rewriter/rewrite-weak-attr.m b/test/Rewriter/rewrite-weak-attr.m
index 60c6fc8..f8eb3b7 100644
--- a/test/Rewriter/rewrite-weak-attr.m
+++ b/test/Rewriter/rewrite-weak-attr.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i686-pc-win32 -fms-extensions -fblocks -Dnil=0 -rewrite-objc -fobjc-fragile-abi -o - %s
+// RUN: %clang_cc1 -triple i686-pc-win32 -fms-extensions -fblocks -Dnil=0 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 -o - %s
int main() {
__weak __block id foo = nil;
__block id foo2 = nil;
diff --git a/test/Rewriter/static-type-protocol-1.m b/test/Rewriter/static-type-protocol-1.m
index bafe286..dbf9d38 100644
--- a/test/Rewriter/static-type-protocol-1.m
+++ b/test/Rewriter/static-type-protocol-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@protocol Proto
- (void) ProtoDidget;
diff --git a/test/Rewriter/undecl-objc-h.m b/test/Rewriter/undecl-objc-h.m
index 5c3e693..2581aa5 100644
--- a/test/Rewriter/undecl-objc-h.m
+++ b/test/Rewriter/undecl-objc-h.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
typedef struct S {
int * pint;
diff --git a/test/Rewriter/undeclared-method-1.m b/test/Rewriter/undeclared-method-1.m
index 9b4ca19..a52c677 100644
--- a/test/Rewriter/undeclared-method-1.m
+++ b/test/Rewriter/undeclared-method-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface Derived @end
diff --git a/test/Rewriter/undef-field-reference-1.m b/test/Rewriter/undef-field-reference-1.m
index 9c067a8..07bd21b 100644
--- a/test/Rewriter/undef-field-reference-1.m
+++ b/test/Rewriter/undef-field-reference-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
@interface MyDerived
{
diff --git a/test/Rewriter/va-method.m b/test/Rewriter/va-method.m
index c232d23..78d9367 100644
--- a/test/Rewriter/va-method.m
+++ b/test/Rewriter/va-method.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
#include <stdarg.h>
diff --git a/test/Rewriter/weak_byref_objects.m b/test/Rewriter/weak_byref_objects.m
index 1b9efbe..09d30ed 100644
--- a/test/Rewriter/weak_byref_objects.m
+++ b/test/Rewriter/weak_byref_objects.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fblocks -triple i386-apple-darwin9 -fobjc-gc -rewrite-objc -fobjc-fragile-abi %s -o -
+// RUN: %clang_cc1 -fblocks -triple i386-apple-darwin9 -fobjc-gc -rewrite-objc -fobjc-runtime=macosx-fragile-10.5 %s -o -
#define nil 0
int main() {
diff --git a/test/Sema/128bitint.c b/test/Sema/128bitint.c
index 89d3ee2..ddad835 100644
--- a/test/Sema/128bitint.c
+++ b/test/Sema/128bitint.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin9 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin9 -fms-extensions %s
typedef int i128 __attribute__((__mode__(TI)));
typedef unsigned u128 __attribute__((__mode__(TI)));
@@ -11,3 +11,10 @@ __uint128_t b = (__uint128_t)-1;
// PR11916: Support for libstdc++ 4.7
__int128 i = (__int128)0;
unsigned __int128 u = (unsigned __int128)-1;
+
+long long SignedTooBig = 123456789012345678901234567890; // expected-warning {{integer constant is too large for its type}}
+__int128_t Signed128 = 123456789012345678901234567890i128;
+long long Signed64 = 123456789012345678901234567890i128; // expected-warning {{implicit conversion from '__int128' to 'long long' changes value from 123456789012345678901234567890 to -4362896299872285998}}
+unsigned long long UnsignedTooBig = 123456789012345678901234567890; // expected-warning {{integer constant is too large for its type}}
+__uint128_t Unsigned128 = 123456789012345678901234567890Ui128;
+unsigned long long Unsigned64 = 123456789012345678901234567890Ui128; // expected-warning {{implicit conversion from 'unsigned __int128' to 'unsigned long long' changes value from 123456789012345678901234567890 to 14083847773837265618}}
diff --git a/test/Sema/Inputs/format-unused-system-args.h b/test/Sema/Inputs/format-unused-system-args.h
new file mode 100644
index 0000000..3a6b165
--- /dev/null
+++ b/test/Sema/Inputs/format-unused-system-args.h
@@ -0,0 +1,8 @@
+// "System header" for testing that -Wformat-extra-args does not apply to
+// arguments specified in system headers.
+
+#define PRINT2(fmt, a1, a2) \
+ printf((fmt), (a1), (a2))
+
+#define PRINT1(fmt, a1) \
+ PRINT2((fmt), (a1), 0)
diff --git a/test/Sema/MicrosoftCompatibility.c b/test/Sema/MicrosoftCompatibility.c
index f148e869..6b137a6 100644
--- a/test/Sema/MicrosoftCompatibility.c
+++ b/test/Sema/MicrosoftCompatibility.c
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 %s -fsyntax-only -Wno-unused-value -Wmicrosoft -verify -fms-compatibility
-enum ENUM1; // expected-warning {{forward references to 'enum' types are a Microsoft extension}}
+enum ENUM1; // expected-warning {{forward references to 'enum' types are a Microsoft extension}}
enum ENUM1 var1 = 3;
enum ENUM1* var2 = 0;
@@ -14,3 +14,8 @@ enum ENUM2 {
__declspec(noreturn) void f6( void ) {
return; // expected-warning {{function 'f6' declared 'noreturn' should not return}}
}
+
+__declspec(align(32768)) struct S1 { int a; } s; /* expected-error {{requested alignment must be 8192 bytes or smaller}} */
+struct __declspec(aligned) S2 {}; /* expected-warning {{unknown __declspec attribute 'aligned' ignored}} */
+
+struct __declspec(appdomain) S3 {}; /* expected-warning {{__declspec attribute 'appdomain' is not supported}} */ \ No newline at end of file
diff --git a/test/Sema/MicrosoftExtensions.c b/test/Sema/MicrosoftExtensions.c
index fb0c6bd..5d7330e 100644
--- a/test/Sema/MicrosoftExtensions.c
+++ b/test/Sema/MicrosoftExtensions.c
@@ -87,11 +87,13 @@ typedef struct {
AA; // expected-warning {{anonymous structs are a Microsoft extension}}
} BB;
-__declspec(deprecated("This is deprecated")) enum DE1 { one, two } e1;
-struct __declspec(deprecated) DS1 { int i; float f; };
+__declspec(deprecated("This is deprecated")) enum DE1 { one, two } e1; // expected-note {{'e1' declared here}}
+struct __declspec(deprecated) DS1 { int i; float f; }; // expected-note {{declared here}}
#define MY_TEXT "This is also deprecated"
-__declspec(deprecated(MY_TEXT)) void Dfunc1( void ) {}
+__declspec(deprecated(MY_TEXT)) void Dfunc1( void ) {} // expected-note {{'Dfunc1' declared here}}
+
+struct __declspec(deprecated(123)) DS2 {}; // expected-error {{argument to deprecated attribute was not a string literal}}
void test( void ) {
e1 = one; // expected-warning {{'e1' is deprecated: This is deprecated}}
diff --git a/test/Sema/alignas.c b/test/Sema/alignas.c
index 5832393..d9a0164 100644
--- a/test/Sema/alignas.c
+++ b/test/Sema/alignas.c
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c1x %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c11 -Dalignof=__alignof %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c11 -Dalignof=_Alignof %s
_Alignas(3) int align_illegal; //expected-error {{requested alignment is not a power of 2}}
_Alignas(int) char align_big;
@@ -11,9 +12,9 @@ struct align_member {
typedef _Alignas(8) char align_typedef; // FIXME: this should be rejected
-_Static_assert(__alignof(align_big) == __alignof(int), "k's alignment is wrong");
-_Static_assert(__alignof(align_small) == 1, "j's alignment is wrong");
-_Static_assert(__alignof(align_multiple) == 8, "l's alignment is wrong");
-_Static_assert(__alignof(struct align_member) == 8, "quuux's alignment is wrong");
+_Static_assert(alignof(align_big) == alignof(int), "k's alignment is wrong");
+_Static_assert(alignof(align_small) == 1, "j's alignment is wrong");
+_Static_assert(alignof(align_multiple) == 8, "l's alignment is wrong");
+_Static_assert(alignof(struct align_member) == 8, "quuux's alignment is wrong");
_Static_assert(sizeof(struct align_member) == 8, "quuux's size is wrong");
-_Static_assert(__alignof(align_typedef) == 8, "typedef's alignment is wrong");
+_Static_assert(alignof(align_typedef) == 8, "typedef's alignment is wrong");
diff --git a/test/Sema/alloc_size.c b/test/Sema/alloc_size.c
new file mode 100644
index 0000000..e2f5298
--- /dev/null
+++ b/test/Sema/alloc_size.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+void* my_malloc(unsigned char) __attribute__((alloc_size(1)));
+void* my_calloc(unsigned char, short) __attribute__((alloc_size(1,2)));
+void* my_realloc(void*, unsigned) __attribute__((alloc_size(2)));
+
+
+void* fn1(int) __attribute__((alloc_size("xpto"))); // expected-error{{attribute requires integer constant}}
+
+void* fn2(void*) __attribute__((alloc_size(1))); // expected-error{{attribute requires integer constant}}
+
+void* fn3(unsigned) __attribute__((alloc_size(0))); // expected-error{{attribute parameter 1 is out of bounds}}
+void* fn4(unsigned) __attribute__((alloc_size(2))); // expected-error{{attribute parameter 1 is out of bounds}}
+
+void fn5(unsigned) __attribute__((alloc_size(1))); // expected-warning{{only applies to functions that return a pointer}}
+char fn6(unsigned) __attribute__((alloc_size(1))); // expected-warning{{only applies to functions that return a pointer}}
+
+void* fn7(unsigned) __attribute__((alloc_size)); // expected-error {{attribute takes at least 1 argument}}
+
+void *fn8(int, int) __attribute__((alloc_size(1, 1))); // OK
+
+void* fn9(unsigned) __attribute__((alloc_size(12345678901234567890123))); // expected-warning {{integer constant is too large for its type}} // expected-error {{attribute parameter 1 is out of bounds}}
+
+void* fn10(size_t, size_t) __attribute__((alloc_size(1,2))); // expected-error{{redefinition of parameter}} \
+ // expected-error{{a parameter list without types is only allowed in a function definition}} \
+ // expected-warning{{alloc_size attribute only applies to functions and methods}}
diff --git a/test/Sema/annotate.c b/test/Sema/annotate.c
index 5b27277..ef878d4 100644
--- a/test/Sema/annotate.c
+++ b/test/Sema/annotate.c
@@ -4,7 +4,8 @@ void __attribute__((annotate("foo"))) foo(float *a) {
__attribute__((annotate("bar"))) int x;
__attribute__((annotate(1))) int y; // expected-error {{argument to annotate attribute was not a string literal}}
__attribute__((annotate("bar", 1))) int z; // expected-error {{attribute takes one argument}}
- int u = __builtin_annotation(z, (char*) 0); // expected-error {{__builtin_annotation requires a non wide string constant}}
- int v = __builtin_annotation(z, (char*) L"bar"); // expected-error {{__builtin_annotation requires a non wide string constant}}
+ int u = __builtin_annotation(z, (char*) 0); // expected-error {{second argument to __builtin_annotation must be a non-wide string constant}}
+ int v = __builtin_annotation(z, (char*) L"bar"); // expected-error {{second argument to __builtin_annotation must be a non-wide string constant}}
int w = __builtin_annotation(z, "foo");
+ float b = __builtin_annotation(*a, "foo"); // expected-error {{first argument to __builtin_annotation must be an integer}}
}
diff --git a/test/Sema/array-bounds-ptr-arith.c b/test/Sema/array-bounds-ptr-arith.c
index 022335b..e3de06a 100644
--- a/test/Sema/array-bounds-ptr-arith.c
+++ b/test/Sema/array-bounds-ptr-arith.c
@@ -19,3 +19,21 @@ void pr11594(struct S *s) {
int a[10];
int *p = a - s->n;
}
+
+// Test case reduced from <rdar://problem/11387038>. This resulted in
+// an assertion failure because of the typedef instead of an explicit
+// constant array type.
+struct RDar11387038 {};
+typedef struct RDar11387038 RDar11387038Array[1];
+struct RDar11387038_Table {
+ RDar11387038Array z;
+};
+typedef struct RDar11387038_Table * TPtr;
+typedef TPtr *TabHandle;
+struct RDar11387038_B { TabHandle x; };
+typedef struct RDar11387038_B RDar11387038_B;
+
+void radar11387038() {
+ RDar11387038_B *pRDar11387038_B;
+ struct RDar11387038* y = &(*pRDar11387038_B->x)->z[4];
+}
diff --git a/test/Sema/array-init.c b/test/Sema/array-init.c
index 26c0b24..cfdf8e2 100644
--- a/test/Sema/array-init.c
+++ b/test/Sema/array-init.c
@@ -50,7 +50,7 @@ void func() {
static long x2[3] = { 1.0,
"abc", // expected-warning{{incompatible pointer to integer conversion initializing 'long' with an expression of type 'char [4]'}}
- 5.8 }; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ 5.8 }; // expected-warning {{implicit conversion from 'double' to 'long' changes value from 5.8 to 5}}
}
void test() {
diff --git a/test/Sema/attr-aligned.c b/test/Sema/attr-aligned.c
index c094ff1..92f2742 100644
--- a/test/Sema/attr-aligned.c
+++ b/test/Sema/attr-aligned.c
@@ -32,7 +32,7 @@ struct D { int member __attribute__((aligned(2))) __attribute__((packed)); } d;
char d1[__alignof__(d) == 2 ?: -1] = {0};
char d2[__alignof__(d.member) == 2 ?: -1] = {0};
-struct E { int member __attribute__((aligned(2))); } __attribute__((packed));
+struct E { int member __attribute__((align(2))); } __attribute__((packed));
struct E e;
char e1[__alignof__(e) == 2 ?: -1] = {0};
char e2[__alignof__(e.member) == 2 ?: -1] = {0};
diff --git a/test/Sema/attr-availability-ios.c b/test/Sema/attr-availability-ios.c
index ea05e17..329068c 100644
--- a/test/Sema/attr-availability-ios.c
+++ b/test/Sema/attr-availability-ios.c
@@ -1,14 +1,14 @@
// RUN: %clang_cc1 "-triple" "x86_64-apple-ios3.0" -fsyntax-only -verify %s
-void f0(int) __attribute__((availability(ios,introduced=2.0,deprecated=2.1)));
+void f0(int) __attribute__((availability(ios,introduced=2.0,deprecated=2.1))); // expected-note {{'f0' declared here}}
void f1(int) __attribute__((availability(ios,introduced=2.1)));
-void f2(int) __attribute__((availability(ios,introduced=2.0,deprecated=3.0)));
+void f2(int) __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{'f2' declared here}}
void f3(int) __attribute__((availability(ios,introduced=3.0)));
void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=2.1,obsoleted=3.0))); // expected-note{{explicitly marked unavailable}}
-void f5(int) __attribute__((availability(ios,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0)));
+void f5(int) __attribute__((availability(ios,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{'f5' declared here}}
void f6(int) __attribute__((availability(ios,deprecated=3.0)));
-void f6(int) __attribute__((availability(ios,introduced=2.0)));
+void f6(int) __attribute__((availability(ios,introduced=2.0))); // expected-note {{'f6' declared here}}
void test() {
f0(0); // expected-warning{{'f0' is deprecated: first deprecated in iOS 2.1}}
diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c
index 1de26e9..781523a 100644
--- a/test/Sema/attr-availability-macosx.c
+++ b/test/Sema/attr-availability-macosx.c
@@ -2,7 +2,7 @@
void f0(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6)));
void f1(int) __attribute__((availability(macosx,introduced=10.5)));
-void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5)));
+void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5))); // expected-note {{'f2' declared here}}
void f3(int) __attribute__((availability(macosx,introduced=10.6)));
void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}}
void f5(int) __attribute__((availability(ios,introduced=3.2), availability(macosx,unavailable))); // expected-note{{function has been explicitly marked unavailable here}}
diff --git a/test/Sema/attr-availability.c b/test/Sema/attr-availability.c
index 0e6ea96..b4a6f96 100644
--- a/test/Sema/attr-availability.c
+++ b/test/Sema/attr-availability.c
@@ -8,7 +8,7 @@ void f3() __attribute__((availability(otheros,introduced=2.2))); // expected-war
// rdar://10095131
extern void
-ATSFontGetName(const char *oName) __attribute__((availability(macosx,introduced=8.0,deprecated=9.0, message="use CTFontCopyFullName")));
+ATSFontGetName(const char *oName) __attribute__((availability(macosx,introduced=8.0,deprecated=9.0, message="use CTFontCopyFullName"))); // expected-note {{'ATSFontGetName' declared here}}
extern void
ATSFontGetPostScriptName(int flags) __attribute__((availability(macosx,introduced=8.0,obsoleted=9.0, message="use ATSFontGetFullPostScriptName"))); // expected-note {{function has been explicitly marked unavailable here}}
@@ -24,3 +24,22 @@ enum {
NSDataWritingFileProtectionWriteOnly = 0x30000000,
NSDataWritingFileProtectionCompleteUntilUserAuthentication = 0x40000000,
};
+
+void f4(int) __attribute__((availability(ios,deprecated=3.0)));
+void f4(int) __attribute__((availability(ios,introduced=4.0))); // expected-warning {{feature cannot be deprecated in iOS version 3.0 before it was introduced in version 4.0; attribute ignored}}
+
+void f5(int) __attribute__((availability(ios,deprecated=3.0),
+ availability(ios,introduced=4.0))); // expected-warning {{feature cannot be deprecated in iOS version 3.0 before it was introduced in version 4.0; attribute ignored}}
+
+void f6(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{previous attribute is here}}
+void f6(int) __attribute__((availability(ios,deprecated=4.0))); // expected-warning {{availability does not match previous declaration}}
+
+void f7(int) __attribute__((availability(ios,introduced=2.0)));
+void f7(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{previous attribute is here}}
+void f7(int) __attribute__((availability(ios,deprecated=4.0))); // expected-warning {{availability does not match previous declaration}}
+
+
+// <rdar://problem/11886458>
+#if !__has_feature(attribute_availability_with_message)
+# error "Missing __has_feature"
+#endif
diff --git a/test/Sema/attr-coldhot.c b/test/Sema/attr-coldhot.c
new file mode 100644
index 0000000..253b189
--- /dev/null
+++ b/test/Sema/attr-coldhot.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+int foo() __attribute__((__hot__));
+int bar() __attribute__((__cold__));
+
+int var1 __attribute__((__cold__)); // expected-warning{{'__cold__' attribute only applies to functions}}
+int var2 __attribute__((__hot__)); // expected-warning{{'__hot__' attribute only applies to functions}}
+
+int qux() __attribute__((__hot__)) __attribute__((__cold__)); // expected-error{{'__hot__' and cold attributes are not compatible}}
+int baz() __attribute__((__cold__)) __attribute__((__hot__)); // expected-error{{'__cold__' and hot attributes are not compatible}}
diff --git a/test/Sema/attr-decl-after-definition.c b/test/Sema/attr-decl-after-definition.c
index 4d32e00..17b94cc 100644
--- a/test/Sema/attr-decl-after-definition.c
+++ b/test/Sema/attr-decl-after-definition.c
@@ -14,6 +14,26 @@ int bar __attribute__((weak));
int bar __attribute__((used));
extern int bar __attribute__((weak));
int bar = 0; // expected-note {{previous definition is here}}
-int bar __attribute__((weak)); // expected-warning {{must precede definition}}
+int bar __attribute__((weak)); // no warning as it matches the existing
+ // attribute.
+int bar __attribute__((used,
+ visibility("hidden"))); // expected-warning {{must precede definition}}
int bar;
+struct zed { // expected-note {{previous definition is here}}
+};
+struct __attribute__((visibility("hidden"))) zed; // expected-warning {{must precede definition}}
+
+struct __attribute__((visibility("hidden"))) zed2 {
+};
+struct __attribute__((visibility("hidden"))) zed2;
+
+struct __attribute__((visibility("hidden"))) zed3 { // expected-note {{previous definition is here}}
+};
+struct __attribute__((visibility("hidden"),
+ packed // expected-warning {{must precede definition}}
+ )) zed3;
+
+struct __attribute__((visibility("hidden"))) zed4 { // expected-note {{previous attribute is here}}
+};
+struct __attribute__((visibility("default"))) zed4; // expected-error {{visibility does not match previous declaration}}
diff --git a/test/Sema/attr-deprecated-message.c b/test/Sema/attr-deprecated-message.c
index 5de31d0..f48d13e 100644
--- a/test/Sema/attr-deprecated-message.c
+++ b/test/Sema/attr-deprecated-message.c
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
// rdar: // 6734520
-typedef int INT1 __attribute__((deprecated("Please avoid INT1")));
+typedef int INT1 __attribute__((deprecated("Please avoid INT1"))); // expected-note 3 {{'INT1' declared here}}
typedef INT1 INT2 __attribute__ ((__deprecated__("Please avoid INT2")));
@@ -12,16 +12,16 @@ typedef INT1 INT1b __attribute__ ((deprecated("Please avoid INT1b")));
INT1 should_be_unavailable; // expected-warning {{'INT1' is deprecated: Please avoid INT1}}
INT1a should_not_be_deprecated;
-INT1 f1(void) __attribute__ ((deprecated("Please avoid f1")));
+INT1 f1(void) __attribute__ ((deprecated("Please avoid f1"))); // expected-note {{'f1' declared here}}
INT1 f2(void); // expected-warning {{'INT1' is deprecated: Please avoid INT1}}
-typedef enum {red, green, blue} Color __attribute__((deprecated("Please avoid Color")));
+typedef enum {red, green, blue} Color __attribute__((deprecated("Please avoid Color"))); // expected-note {{'Color' declared here}}
Color c1; // expected-warning {{'Color' is deprecated: Please avoid Color}}
int g1;
-int g2 __attribute__ ((deprecated("Please avoid g2")));
+int g2 __attribute__ ((deprecated("Please avoid g2"))); // expected-note {{'g2' declared here}}
int func1()
{
diff --git a/test/Sema/attr-deprecated.c b/test/Sema/attr-deprecated.c
index 4760dab..565be7f 100644
--- a/test/Sema/attr-deprecated.c
+++ b/test/Sema/attr-deprecated.c
@@ -1,10 +1,10 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
-int f() __attribute__((deprecated));
+int f() __attribute__((deprecated)); // expected-note 2 {{declared here}}
void g() __attribute__((deprecated));
-void g();
+void g(); // expected-note {{declared here}}
-extern int var __attribute__((deprecated));
+extern int var __attribute__((deprecated)); // expected-note {{declared here}}
int a() {
int (*ptr)() = f; // expected-warning {{'f' is deprecated}}
@@ -17,13 +17,13 @@ int a() {
}
// test if attributes propagate to variables
-extern int var;
+extern int var; // expected-note {{declared here}}
int w() {
return var; // expected-warning {{'var' is deprecated}}
}
int old_fn() __attribute__ ((deprecated));
-int old_fn();
+int old_fn(); // expected-note {{declared here}}
int (*fn_ptr)() = old_fn; // expected-warning {{'old_fn' is deprecated}}
int old_fn() {
@@ -32,7 +32,7 @@ int old_fn() {
struct foo {
- int x __attribute__((deprecated));
+ int x __attribute__((deprecated)); // expected-note 3 {{declared here}}
};
void test1(struct foo *F) {
@@ -41,11 +41,11 @@ void test1(struct foo *F) {
struct foo f2 = { 17 }; // expected-warning {{'x' is deprecated}}
}
-typedef struct foo foo_dep __attribute__((deprecated));
+typedef struct foo foo_dep __attribute__((deprecated)); // expected-note 12 {{declared here}}
foo_dep *test2; // expected-warning {{'foo_dep' is deprecated}}
struct __attribute__((deprecated,
- invalid_attribute)) bar_dep ; // expected-warning {{unknown attribute 'invalid_attribute' ignored}}
+ invalid_attribute)) bar_dep ; // expected-warning {{unknown attribute 'invalid_attribute' ignored}} expected-note 2 {{declared here}}
struct bar_dep *test3; // expected-warning {{'bar_dep' is deprecated}}
@@ -102,9 +102,9 @@ foo_dep test17, // expected-warning {{'foo_dep' is deprecated}}
test19;
// rdar://problem/8518751
-enum __attribute__((deprecated)) Test20 {
- test20_a __attribute__((deprecated)),
- test20_b
+enum __attribute__((deprecated)) Test20 { // expected-note {{declared here}}
+ test20_a __attribute__((deprecated)), // expected-note {{declared here}}
+ test20_b // expected-note {{declared here}}
};
void test20() {
enum Test20 f; // expected-warning {{'Test20' is deprecated}}
@@ -113,3 +113,10 @@ void test20() {
}
char test21[__has_feature(attribute_deprecated_with_message) ? 1 : -1];
+
+struct test22 {
+ foo_dep a __attribute((deprecated));
+ foo_dep b; // expected-warning {{'foo_dep' is deprecated}}
+ foo_dep c, d __attribute((deprecated)); // expected-warning {{'foo_dep' is deprecated}}
+ __attribute((deprecated)) foo_dep e, f;
+};
diff --git a/test/Sema/attr-nodebug.c b/test/Sema/attr-nodebug.c
index a66e961..3cc4088 100644
--- a/test/Sema/attr-nodebug.c
+++ b/test/Sema/attr-nodebug.c
@@ -1,8 +1,11 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
-int a __attribute__((nodebug)); // expected-warning {{'nodebug' attribute only applies to functions}}
+int a __attribute__((nodebug));
+
+void b() {
+ int b __attribute__((nodebug)); // expected-warning {{'nodebug' only applies to variables with static storage duration and functions}}
+}
void t1() __attribute__((nodebug));
void t2() __attribute__((nodebug(2))); // expected-error {{attribute takes no arguments}}
-
diff --git a/test/Sema/attr-section.c b/test/Sema/attr-section.c
index a932525..69ca732 100644
--- a/test/Sema/attr-section.c
+++ b/test/Sema/attr-section.c
@@ -13,3 +13,7 @@ void test() {
__attribute__((section("NEAR,x"))) int n1; // expected-error {{'section' attribute is not valid on local variables}}
__attribute__((section("NEAR,x"))) static int n2; // ok.
}
+
+// pr9356
+void __attribute__((section("foo,zed"))) test2(void); // expected-note {{previous attribute is here}}
+void __attribute__((section("bar,zed"))) test2(void) {} // expected-warning {{section does not match previous declaration}}
diff --git a/test/Sema/attr-tls_model.c b/test/Sema/attr-tls_model.c
new file mode 100644
index 0000000..e184ebc
--- /dev/null
+++ b/test/Sema/attr-tls_model.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -verify -fsyntax-only %s
+
+#if !__has_attribute(tls_model)
+#error "Should support tls_model attribute"
+#endif
+
+int f() __attribute((tls_model("global-dynamic"))); // expected-error {{'tls_model' attribute only applies to thread-local variables}}
+
+int x __attribute((tls_model("global-dynamic"))); // expected-error {{'tls_model' attribute only applies to thread-local variables}}
+static __thread int y __attribute((tls_model("global-dynamic"))); // no-warning
+
+static __thread int y __attribute((tls_model("local", "dynamic"))); // expected-error {{attribute takes one argument}}
+static __thread int y __attribute((tls_model(123))); // expected-error {{argument to tls_model attribute was not a string literal}}
+static __thread int y __attribute((tls_model("foobar"))); // expected-error {{tls_model must be "global-dynamic", "local-dynamic", "initial-exec" or "local-exec"}}
diff --git a/test/Sema/attr-unavailable-message.c b/test/Sema/attr-unavailable-message.c
index b2956d8..9710496 100644
--- a/test/Sema/attr-unavailable-message.c
+++ b/test/Sema/attr-unavailable-message.c
@@ -29,8 +29,8 @@ void unavail(void) {
// rdar://10201690
enum foo {
- a = 1,
- b __attribute__((deprecated())) = 2,
+ a = 1, // expected-note {{declared here}}
+ b __attribute__((deprecated())) = 2, // expected-note {{declared here}}
c = 3
}__attribute__((deprecated()));
diff --git a/test/Sema/attr-visibility.c b/test/Sema/attr-visibility.c
index 5cf2695..77bc39c 100644
--- a/test/Sema/attr-visibility.c
+++ b/test/Sema/attr-visibility.c
@@ -7,3 +7,18 @@ void test2() __attribute__((visibility("internal")));
// rdar://problem/10753392
void test3() __attribute__((visibility("protected"))); // expected-warning {{target does not support 'protected' visibility; using 'default'}}
+struct __attribute__((visibility("hidden"))) test4; // expected-note {{previous attribute is here}}
+struct test4;
+struct __attribute__((visibility("default"))) test4; // expected-error {{visibility does not match previous declaration}}
+
+struct test5;
+struct __attribute__((visibility("hidden"))) test5; // expected-note {{previous attribute is here}}
+struct __attribute__((visibility("default"))) test5; // expected-error {{visibility does not match previous declaration}}
+
+void test6() __attribute__((visibility("hidden"), // expected-note {{previous attribute is here}}
+ visibility("default"))); // expected-error {{visibility does not match previous declaration}}
+
+extern int test7 __attribute__((visibility("default"))); // expected-note {{previous attribute is here}}
+extern int test7 __attribute__((visibility("hidden"))); // expected-error {{visibility does not match previous declaration}}
+
+typedef int __attribute__((visibility("default"))) bar; // expected-warning {{visibility attribute ignored}}
diff --git a/test/Sema/c89-2.c b/test/Sema/c89-2.c
index f6f6bd9..14b955a 100644
--- a/test/Sema/c89-2.c
+++ b/test/Sema/c89-2.c
@@ -1,4 +1,4 @@
-/* RUN: %clang_cc1 %s -std=c89 -pedantic-errors -verify
+/* RUN: %clang_cc1 %s -std=c89 -pedantic-errors -Wno-empty-translation-unit -verify
*/
#if 1LL /* expected-error {{long long}} */
diff --git a/test/Sema/compare.c b/test/Sema/compare.c
index 03aebb3..406ade8 100644
--- a/test/Sema/compare.c
+++ b/test/Sema/compare.c
@@ -333,3 +333,10 @@ struct test11S { unsigned x : 30; };
int test11(unsigned y, struct test11S *p) {
return y > (p->x >> 24); // no-warning
}
+
+typedef char one_char[1];
+typedef char two_chars[2];
+
+void test12(unsigned a) {
+ if (0 && -1 > a) { }
+}
diff --git a/test/Sema/conditional-expr.c b/test/Sema/conditional-expr.c
index 184ac4a..5ff58a2 100644
--- a/test/Sema/conditional-expr.c
+++ b/test/Sema/conditional-expr.c
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify -pedantic -Wsign-conversion %s
void foo() {
*(0 ? (double *)0 : (void *)0) = 0;
- // FIXME: GCC doesn't consider the the following two statements to be errors.
+ // FIXME: GCC doesn't consider the following two statements to be errors.
*(0 ? (double *)0 : (void *)(int *)0) = 0; // expected-error {{incomplete type 'void' is not assignable}}
*(0 ? (double *)0 : (void *)(double *)0) = 0; // expected-error {{incomplete type 'void' is not assignable}}
*(0 ? (double *)0 : (int *)(void *)0) = 0; // expected-error {{incomplete type 'void' is not assignable}} expected-warning {{pointer type mismatch ('double *' and 'int *')}}
diff --git a/test/Sema/const-eval.c b/test/Sema/const-eval.c
index bc8b227..16d028e 100644
--- a/test/Sema/const-eval.c
+++ b/test/Sema/const-eval.c
@@ -131,3 +131,6 @@ EVAL_EXPR(49, &x < &x - 100 ? 1 : -1) // expected-error {{must have a constant s
extern struct Test50S Test50;
EVAL_EXPR(50, &Test50 < (struct Test50S*)((unsigned)&Test50 + 10)) // expected-error {{must have a constant size}}
+
+// <rdar://problem/11874571>
+EVAL_EXPR(51, 0 != (float)1e99)
diff --git a/test/Sema/dllimport-dllexport.c b/test/Sema/dllimport-dllexport.c
index 610059e..00c9df5 100644
--- a/test/Sema/dllimport-dllexport.c
+++ b/test/Sema/dllimport-dllexport.c
@@ -35,3 +35,9 @@ typedef int __declspec(dllimport) type2; // expected-warning{{'dllimport' attrib
void __declspec(dllimport) foo12();
void foo12(){} // expected-warning {{'foo12' redeclared without dllimport attribute: previous dllimport ignored}}
+
+void __attribute__((dllimport)) foo13(); // expected-warning{{dllimport attribute ignored}}
+void __attribute__((dllexport)) foo13();
+
+extern int foo14 __attribute__((dllexport));
+extern int foo14 __attribute__((dllimport)); // expected-warning{{dllimport attribute ignored}}
diff --git a/test/Sema/exprs.c b/test/Sema/exprs.c
index 72cff65..a93e12e 100644
--- a/test/Sema/exprs.c
+++ b/test/Sema/exprs.c
@@ -162,11 +162,20 @@ void test17(int x) {
x = sizeof(x/0); // no warning.
}
-// PR6501
+// PR6501 & PR11857
void test18_a(int a); // expected-note 2 {{'test18_a' declared here}}
+void test18_b(int); // expected-note {{'test18_b' declared here}}
+void test18_c(int a, int b); // expected-note 2 {{'test18_c' declared here}}
+void test18_d(int a, ...); // expected-note {{'test18_d' declared here}}
+void test18_e(int a, int b, ...); // expected-note {{'test18_e' declared here}}
void test18(int b) {
- test18_a(b, b); // expected-error {{too many arguments to function call, expected 1, have 2}}
- test18_a(); // expected-error {{too few arguments to function call, expected 1, have 0}}
+ test18_a(b, b); // expected-error {{too many arguments to function call, expected single argument 'a', have 2}}
+ test18_a(); // expected-error {{too few arguments to function call, single argument 'a' was not specified}}
+ test18_b(); // expected-error {{too few arguments to function call, expected 1, have 0}}
+ test18_c(b); // expected-error {{too few arguments to function call, expected 2, have 1}}
+ test18_c(b, b, b); // expected-error {{too many arguments to function call, expected 2, have 3}}
+ test18_d(); // expected-error {{too few arguments to function call, at least argument 'a' must be specified}}
+ test18_e(); // expected-error {{too few arguments to function call, expected at least 2, have 0}}
}
// PR7569
diff --git a/test/Sema/format-strings-enum-fixed-type.cpp b/test/Sema/format-strings-enum-fixed-type.cpp
new file mode 100644
index 0000000..8b6b237
--- /dev/null
+++ b/test/Sema/format-strings-enum-fixed-type.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -x c++ -std=c++11 -verify %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -x objective-c -verify %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -x objective-c++ -verify %s
+
+#ifdef __cplusplus
+# define EXTERN_C extern "C"
+#else
+# define EXTERN_C extern
+#endif
+
+EXTERN_C int printf(const char *,...);
+
+typedef enum : short { Constant = 0 } TestEnum;
+// Note that in C (and Objective-C), the type of 'Constant' is 'short'.
+// In C++ (and Objective-C++) it is 'TestEnum'.
+// This is why we don't check for that in the expected output.
+
+void test(TestEnum input) {
+ printf("%hhd", input); // expected-warning{{format specifies type 'char' but the argument has type 'TestEnum'}}
+ printf("%hhd", Constant); // expected-warning{{format specifies type 'char'}}
+
+ printf("%hd", input); // no-warning
+ printf("%hd", Constant); // no-warning
+
+ // While these are less correct, they are still safe.
+ printf("%d", input); // no-warning
+ printf("%d", Constant); // no-warning
+
+ printf("%lld", input); // expected-warning{{format specifies type 'long long' but the argument has type 'TestEnum'}}
+ printf("%lld", Constant); // expected-warning{{format specifies type 'long long'}}
+}
+
+
+typedef enum : unsigned long { LongConstant = ~0UL } LongEnum;
+
+void testLong(LongEnum input) {
+ printf("%u", input); // expected-warning{{format specifies type 'unsigned int' but the argument has type 'LongEnum'}}
+ printf("%u", LongConstant); // expected-warning{{format specifies type 'unsigned int'}}
+
+ printf("%lu", input);
+ printf("%lu", LongConstant);
+}
+
+
+typedef short short_t;
+typedef enum : short_t { ShortConstant = 0 } ShortEnum;
+
+void testUnderlyingTypedef(ShortEnum input) {
+ printf("%hhd", input); // expected-warning{{format specifies type 'char' but the argument has type 'ShortEnum'}}
+ printf("%hhd", ShortConstant); // expected-warning{{format specifies type 'char'}}
+
+ printf("%hd", input); // no-warning
+ printf("%hd", ShortConstant); // no-warning
+
+ // While these are less correct, they are still safe.
+ printf("%d", input); // no-warning
+ printf("%d", ShortConstant); // no-warning
+
+ printf("%lld", input); // expected-warning{{format specifies type 'long long' but the argument has type 'ShortEnum'}}
+ printf("%lld", ShortConstant); // expected-warning{{format specifies type 'long long'}}
+}
+
+
+typedef ShortEnum ShortEnum2;
+
+void testTypedefChain(ShortEnum2 input) {
+ printf("%hhd", input); // expected-warning{{format specifies type 'char' but the argument has type 'ShortEnum2' (aka 'ShortEnum')}}
+ printf("%hd", input); // no-warning
+ printf("%d", input); // no-warning
+ printf("%lld", input); // expected-warning{{format specifies type 'long long' but the argument has type 'ShortEnum2' (aka 'ShortEnum')}}
+}
+
+
+typedef enum : char { CharConstant = 'a' } CharEnum;
+
+// %hhd is deliberately not required to be signed, because 'char' isn't either.
+// This is a separate code path in FormatString.cpp.
+void testChar(CharEnum input) {
+ printf("%hhd", input); // no-warning
+ printf("%hhd", CharConstant); // no-warning
+
+ // This is not correct but it is safe. We warn because '%hd' shows intent.
+ printf("%hd", input); // expected-warning{{format specifies type 'short' but the argument has type 'CharEnum'}}
+ printf("%hd", CharConstant); // expected-warning{{format specifies type 'short'}}
+
+ // This is not correct but it matches the promotion rules (and is safe).
+ printf("%d", input); // no-warning
+ printf("%d", CharConstant); // no-warning
+
+ printf("%lld", input); // expected-warning{{format specifies type 'long long' but the argument has type 'CharEnum'}}
+ printf("%lld", CharConstant); // expected-warning{{format specifies type 'long long'}}
+}
diff --git a/test/Sema/format-strings-enum.c b/test/Sema/format-strings-enum.c
new file mode 100644
index 0000000..a6c27d0
--- /dev/null
+++ b/test/Sema/format-strings-enum.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x c++ -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x c++ -std=c++11 -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c++ -std=c++11 -verify %s
+
+#ifdef __cplusplus
+# define EXTERN_C extern "C"
+#else
+# define EXTERN_C extern
+#endif
+
+EXTERN_C int printf(const char *,...);
+
+typedef enum { Constant = 0 } TestEnum;
+// Note that in C, the type of 'Constant' is 'int'. In C++ it is 'TestEnum'.
+// This is why we don't check for that in the expected output.
+
+void test(TestEnum input) {
+ printf("%d", input); // no-warning
+ printf("%d", Constant); // no-warning
+
+ printf("%lld", input); // expected-warning{{format specifies type 'long long' but the argument has type 'TestEnum'}}
+ printf("%lld", Constant); // expected-warning{{format specifies type 'long long'}}
+}
+
+
+typedef enum { LongConstant = ~0UL } LongEnum;
+
+void testLong(LongEnum input) {
+ printf("%u", input); // expected-warning{{format specifies type 'unsigned int' but the argument has type 'LongEnum'}}
+ printf("%u", LongConstant); // expected-warning{{format specifies type 'unsigned int'}}
+
+ printf("%lu", input);
+ printf("%lu", LongConstant);
+}
diff --git a/test/Sema/format-strings-fixit.c b/test/Sema/format-strings-fixit.c
index 800691e..15ac713 100644
--- a/test/Sema/format-strings-fixit.c
+++ b/test/Sema/format-strings-fixit.c
@@ -64,6 +64,18 @@ void test() {
printf("%f", (uintmax_t) 42);
printf("%f", (ptrdiff_t) 42);
+ // Look beyond the first typedef.
+ typedef size_t my_size_type;
+ typedef intmax_t my_intmax_type;
+ typedef uintmax_t my_uintmax_type;
+ typedef ptrdiff_t my_ptrdiff_type;
+ typedef int my_int_type;
+ printf("%f", (my_size_type) 42);
+ printf("%f", (my_intmax_type) 42);
+ printf("%f", (my_uintmax_type) 42);
+ printf("%f", (my_ptrdiff_type) 42);
+ printf("%f", (my_int_type) 42);
+
// string
printf("%ld", "foo");
@@ -122,6 +134,18 @@ void test2() {
scanf("%f", &uIntmaxVar);
scanf("%f", &ptrdiffVar);
+ // Look beyond the first typedef for named integer types.
+ typedef size_t my_size_type;
+ typedef intmax_t my_intmax_type;
+ typedef uintmax_t my_uintmax_type;
+ typedef ptrdiff_t my_ptrdiff_type;
+ typedef int my_int_type;
+ scanf("%f", (my_size_type*)&sizeVar);
+ scanf("%f", (my_intmax_type*)&intmaxVar);
+ scanf("%f", (my_uintmax_type*)&uIntmaxVar);
+ scanf("%f", (my_ptrdiff_type*)&ptrdiffVar);
+ scanf("%f", (my_int_type*)&intVar);
+
// Preserve the original formatting.
scanf("%o", &longVar);
scanf("%u", &longVar);
@@ -162,6 +186,11 @@ void test2() {
// CHECK: printf("%jd", (intmax_t) 42);
// CHECK: printf("%ju", (uintmax_t) 42);
// CHECK: printf("%td", (ptrdiff_t) 42);
+// CHECK: printf("%zu", (my_size_type) 42);
+// CHECK: printf("%jd", (my_intmax_type) 42);
+// CHECK: printf("%ju", (my_uintmax_type) 42);
+// CHECK: printf("%td", (my_ptrdiff_type) 42);
+// CHECK: printf("%d", (my_int_type) 42);
// CHECK: printf("%s", "foo");
// CHECK: printf("%lo", (long) 42);
// CHECK: printf("%lu", (long) 42);
@@ -193,6 +222,11 @@ void test2() {
// CHECK: scanf("%jd", &intmaxVar);
// CHECK: scanf("%ju", &uIntmaxVar);
// CHECK: scanf("%td", &ptrdiffVar);
+// CHECK: scanf("%zu", (my_size_type*)&sizeVar);
+// CHECK: scanf("%jd", (my_intmax_type*)&intmaxVar);
+// CHECK: scanf("%ju", (my_uintmax_type*)&uIntmaxVar);
+// CHECK: scanf("%td", (my_ptrdiff_type*)&ptrdiffVar);
+// CHECK: scanf("%d", (my_int_type*)&intVar);
// CHECK: scanf("%lo", &longVar);
// CHECK: scanf("%lu", &longVar);
// CHECK: scanf("%lx", &longVar);
diff --git a/test/Sema/format-strings-scanf.c b/test/Sema/format-strings-scanf.c
index e94af5a..235ac11 100644
--- a/test/Sema/format-strings-scanf.c
+++ b/test/Sema/format-strings-scanf.c
@@ -33,6 +33,12 @@ void test(const char *s, int *i) {
scanf("%*d", i); // // expected-warning{{data argument not used by format string}}
scanf("%*d", i); // // expected-warning{{data argument not used by format string}}
scanf("%*d%1$d", i); // no-warning
+
+ scanf("%s", (char*)0); // no-warning
+ scanf("%s", (volatile char*)0); // no-warning
+ scanf("%s", (signed char*)0); // no-warning
+ scanf("%s", (unsigned char*)0); // no-warning
+ scanf("%hhu", (signed char*)0); // no-warning
}
void bad_length_modifiers(char *s, void *p, wchar_t *ws, long double *ld) {
@@ -121,3 +127,52 @@ void test_quad(int *x, long long *llx) {
scanf("%qd", x); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
scanf("%qd", llx); // no-warning
}
+
+void test_writeback(int *x) {
+ scanf("%n", (void*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'void *'}}
+ scanf("%n %c", x, x); // expected-warning{{format specifies type 'char *' but the argument has type 'int *'}}
+
+ scanf("%hhn", (signed char*)0); // no-warning
+ scanf("%hhn", (char*)0); // no-warning
+ scanf("%hhn", (unsigned char*)0); // no-warning
+ scanf("%hhn", (int*)0); // expected-warning{{format specifies type 'signed char *' but the argument has type 'int *'}}
+
+ scanf("%hn", (short*)0); // no-warning
+ scanf("%hn", (unsigned short*)0); // no-warning
+ scanf("%hn", (int*)0); // expected-warning{{format specifies type 'short *' but the argument has type 'int *'}}
+
+ scanf("%n", (int*)0); // no-warning
+ scanf("%n", (unsigned int*)0); // no-warning
+ scanf("%n", (char*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'char *'}}
+
+ scanf("%ln", (long*)0); // no-warning
+ scanf("%ln", (unsigned long*)0); // no-warning
+ scanf("%ln", (int*)0); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}
+
+ scanf("%lln", (long long*)0); // no-warning
+ scanf("%lln", (unsigned long long*)0); // no-warning
+ scanf("%lln", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
+
+ scanf("%qn", (long long*)0); // no-warning
+ scanf("%qn", (unsigned long long*)0); // no-warning
+ scanf("%qn", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
+
+}
+
+void test_qualifiers(const int *cip, volatile int* vip,
+ const char *ccp, volatile char* vcp,
+ const volatile int *cvip) {
+ scanf("%d", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
+ scanf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
+ scanf("%s", ccp); // expected-warning{{format specifies type 'char *' but the argument has type 'const char *'}}
+ scanf("%d", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}}
+
+ scanf("%d", vip); // No warning.
+ scanf("%n", vip); // No warning.
+ scanf("%c", vcp); // No warning.
+
+ typedef int* ip_t;
+ typedef const int* cip_t;
+ scanf("%d", (ip_t)0); // No warning.
+ scanf("%d", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}}
+}
diff --git a/test/Sema/format-strings-size_t.c b/test/Sema/format-strings-size_t.c
index 7f88ff3..5058a76 100644
--- a/test/Sema/format-strings-size_t.c
+++ b/test/Sema/format-strings-size_t.c
@@ -13,3 +13,16 @@ void test(void) {
// ptrdiff_t
printf("%td", (double)42); // expected-warning {{format specifies type 'ptrdiff_t' (aka 'long') but the argument has type 'double'}}
}
+
+void test_writeback(void) {
+ printf("%jn", (long*)0); // no-warning
+ printf("%jn", (unsigned long*)0); // no-warning
+ printf("%jn", (int*)0); // expected-warning{{format specifies type 'intmax_t *' (aka 'long *') but the argument has type 'int *'}}
+
+ printf("%zn", (long*)0); // no-warning
+ // FIXME: Warn about %zn with non-ssize_t argument.
+
+ printf("%tn", (long*)0); // no-warning
+ printf("%tn", (unsigned long*)0); // no-warning
+ printf("%tn", (int*)0); // expected-warning{{format specifies type 'ptrdiff_t *' (aka 'long *') but the argument has type 'int *'}}
+}
diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c
index 086c5c6..86b9296 100644
--- a/test/Sema/format-strings.c
+++ b/test/Sema/format-strings.c
@@ -1,7 +1,10 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s
+#define __need_wint_t
#include <stdarg.h>
-typedef __typeof(sizeof(int)) size_t;
+#include <stddef.h> // For wint_t and wchar_t
+
typedef struct _FILE FILE;
int fprintf(FILE *, const char *restrict, ...);
int printf(const char *restrict, ...); // expected-note{{passing argument to parameter here}}
@@ -85,9 +88,35 @@ void check_writeback_specifier()
{
int x;
char *b;
+ printf("%n", b); // expected-warning{{format specifies type 'int *' but the argument has type 'char *'}}
+ printf("%n", &x); // no-warning
+
+ printf("%hhn", (signed char*)0); // no-warning
+ printf("%hhn", (char*)0); // no-warning
+ printf("%hhn", (unsigned char*)0); // no-warning
+ printf("%hhn", (int*)0); // expected-warning{{format specifies type 'signed char *' but the argument has type 'int *'}}
+
+ printf("%hn", (short*)0); // no-warning
+ printf("%hn", (unsigned short*)0); // no-warning
+ printf("%hn", (int*)0); // expected-warning{{format specifies type 'short *' but the argument has type 'int *'}}
+
+ printf("%n", (int*)0); // no-warning
+ printf("%n", (unsigned int*)0); // no-warning
+ printf("%n", (char*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'char *'}}
+
+ printf("%ln", (long*)0); // no-warning
+ printf("%ln", (unsigned long*)0); // no-warning
+ printf("%ln", (int*)0); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}
+
+ printf("%lln", (long long*)0); // no-warning
+ printf("%lln", (unsigned long long*)0); // no-warning
+ printf("%lln", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
+
+ printf("%qn", (long long*)0); // no-warning
+ printf("%qn", (unsigned long long*)0); // no-warning
+ printf("%qn", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
- printf("%n",&x); // expected-warning {{'%n' in format string discouraged}}
- sprintf(b,"%d%%%n",1, &x); // expected-warning {{'%n' in format string dis}}
+ printf("%Ln", 0); // expected-warning{{length modifier 'L' results in undefined behavior or no effect with 'n' conversion specifier}}
}
void check_invalid_specifier(FILE* fp, char *buf)
@@ -164,7 +193,6 @@ void test9(char *P) {
int x;
printf(P); // expected-warning {{format string is not a string literal (potentially insecure)}}
printf(P, 42);
- printf("%n", &x); // expected-warning {{use of '%n' in format string discouraged }}
}
void torture(va_list v8) {
@@ -182,7 +210,6 @@ void test10(int x, float f, int i, long long lli) {
printf("%*d\n", f, x); // expected-warning{{field width should have type 'int', but argument has type 'double'}}
printf("%*.*d\n", x, f, x); // expected-warning{{field precision should have type 'int', but argument has type 'double'}}
printf("%**\n"); // expected-warning{{invalid conversion specifier '*'}}
- printf("%n", &i); // expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
printf("%d%d\n", x); // expected-warning{{more '%' conversions than data arguments}}
printf("%d\n", x, x); // expected-warning{{data argument not used by format string}}
printf("%W%d%Z\n", x, x, x); // expected-warning{{invalid conversion specifier 'W'}} expected-warning{{invalid conversion specifier 'Z'}}
@@ -258,7 +285,6 @@ void f0(int_t x) { printf("%d\n", x); }
// Unicode test cases. These are possibly specific to Mac OS X. If so, they should
// eventually be moved into a separate test.
-typedef __WCHAR_TYPE__ wchar_t;
void test_unicode_conversions(wchar_t *s) {
printf("%S", s); // no-warning
@@ -314,14 +340,14 @@ void bug7377_bad_length_mod_usage() {
// Bad flag usage
printf("%#p", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'p' conversion specifier}}
printf("%0d", -1); // no-warning
- printf("%#n", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'n' conversion specifier}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
- printf("%-n", (void *) 0); // expected-warning{{flag '-' results in undefined behavior with 'n' conversion specifier}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+ printf("%#n", (int *) 0); // expected-warning{{flag '#' results in undefined behavior with 'n' conversion specifier}}
+ printf("%-n", (int *) 0); // expected-warning{{flag '-' results in undefined behavior with 'n' conversion specifier}}
printf("%-p", (void *) 0); // no-warning
// Bad optional amount use
printf("%.2c", 'a'); // expected-warning{{precision used with 'c' conversion specifier, resulting in undefined behavior}}
- printf("%1n", (void *) 0); // expected-warning{{field width used with 'n' conversion specifier, resulting in undefined behavior}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
- printf("%.9n", (void *) 0); // expected-warning{{precision used with 'n' conversion specifier, resulting in undefined behavior}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+ printf("%1n", (int *) 0); // expected-warning{{field width used with 'n' conversion specifier, resulting in undefined behavior}}
+ printf("%.9n", (int *) 0); // expected-warning{{precision used with 'n' conversion specifier, resulting in undefined behavior}}
// Ignored flags
printf("% +f", 1.23); // expected-warning{{flag ' ' is ignored when flag '+' is present}}
@@ -332,17 +358,18 @@ void bug7377_bad_length_mod_usage() {
}
// PR 7981 - handle '%lc' (wint_t)
-#ifndef wint_t
-typedef int __darwin_wint_t;
-typedef __darwin_wint_t wint_t;
-#endif
void pr7981(wint_t c, wchar_t c2) {
printf("%lc", c); // no-warning
printf("%lc", 1.0); // expected-warning{{the argument has type 'double'}}
printf("%lc", (char) 1); // no-warning
- printf("%lc", &c); // expected-warning{{the argument has type 'wint_t *' (aka 'int *')}}
+ printf("%lc", &c); // expected-warning{{the argument has type 'wint_t *'}}
+ // If wint_t and wchar_t are the same width and wint_t is signed where
+ // wchar_t is unsigned, an implicit conversion isn't possible.
+#if defined(__WINT_UNSIGNED__) || !defined(__WCHAR_UNSIGNED__) || \
+ __WINT_WIDTH__ > __WCHAR_WIDTH__
printf("%lc", c2); // no-warning
+#endif
}
// <rdar://problem/8269537> -Wformat-security says NULL is not a string literal
@@ -432,10 +459,6 @@ void pr9751() {
printf(kFormat2, 1, "foo"); // expected-warning{{data argument position '18' exceeds the number of data arguments (2)}}
printf("%18$s\n", 1, "foo"); // expected-warning{{data argument position '18' exceeds the number of data arguments (2)}}
- const char kFormat3[] = "%n"; // expected-note{{format string is defined here}}
- printf(kFormat3, "as"); // expected-warning{{use of '%n' in format string discouraged}}
- printf("%n", "as"); // expected-warning{{use of '%n' in format string discouraged}}
-
const char kFormat4[] = "%y"; // expected-note{{format string is defined here}}
printf(kFormat4, 5); // expected-warning{{invalid conversion specifier 'y'}}
printf("%y", 5); // expected-warning{{invalid conversion specifier 'y'}}
@@ -497,6 +520,15 @@ void pr9751() {
printf(kFormat17, (int[]){0}); // expected-warning{{format specifies type 'unsigned short' but the argument}}
printf("%a", (long double)0); // expected-warning{{format specifies type 'double' but the argument has type 'long double'}}
+
+ // Test braced char[] initializers.
+ const char kFormat18[] = { "%lld" }; // expected-note{{format string is defined here}}
+ printf(kFormat18, 0); // expected-warning{{format specifies type}}
+
+ // Make sure we point at the offending argument rather than the format string.
+ const char kFormat19[] = "%d"; // expected-note{{format string is defined here}}
+ printf(kFormat19,
+ 0.0); // expected-warning{{format specifies}}
}
// PR 9466: clang: doesn't know about %Lu, %Ld, and %Lx
@@ -521,3 +553,48 @@ void test_other_formats() {
dateformat(""); // expected-warning{{format string is empty}}
dateformat(str); // no-warning (using strftime non literal is not unsafe)
}
+
+// Do not warn about unused arguments coming from system headers.
+// <rdar://problem/11317765>
+#include <format-unused-system-args.h>
+void test_unused_system_args(int x) {
+ PRINT1("%d\n", x); // no-warning{{extra argument is system header is OK}}
+}
+
+void pr12761(char c) {
+ // This should not warn even with -fno-signed-char.
+ printf("%hhx", c);
+}
+
+
+// Test that we correctly merge the format in both orders.
+extern void test14_foo(const char *, const char *, ...)
+ __attribute__((__format__(__printf__, 1, 3)));
+extern void test14_foo(const char *, const char *, ...)
+ __attribute__((__format__(__scanf__, 2, 3)));
+
+extern void test14_bar(const char *, const char *, ...)
+ __attribute__((__format__(__scanf__, 2, 3)));
+extern void test14_bar(const char *, const char *, ...)
+ __attribute__((__format__(__printf__, 1, 3)));
+
+void test14_zed(int *p) {
+ test14_foo("%", "%d", p); // expected-warning{{incomplete format specifier}}
+ test14_bar("%", "%d", p); // expected-warning{{incomplete format specifier}}
+}
+
+void test_qualifiers(volatile int *vip, const int *cip,
+ const volatile int *cvip) {
+ printf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
+ printf("%n", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}}
+
+ printf("%n", vip); // No warning.
+ printf("%p", cip); // No warning.
+ printf("%p", cvip); // No warning.
+
+
+ typedef int* ip_t;
+ typedef const int* cip_t;
+ printf("%n", (ip_t)0); // No warning.
+ printf("%n", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}}
+}
diff --git a/test/Sema/fpack-struct.c b/test/Sema/fpack-struct.c
index 37c8444..63766e9 100644
--- a/test/Sema/fpack-struct.c
+++ b/test/Sema/fpack-struct.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -DEXPECTED_STRUCT_SIZE=5 -fpack-struct 1 %s
-// RUN: %clang_cc1 -DEXPECTED_STRUCT_SIZE=6 -fpack-struct 2 %s
+// RUN: %clang_cc1 -DEXPECTED_STRUCT_SIZE=5 -fpack-struct=1 %s
+// RUN: %clang_cc1 -DEXPECTED_STRUCT_SIZE=6 -fpack-struct=2 %s
struct s0 {
int x;
diff --git a/test/Sema/implicit-builtin-decl.c b/test/Sema/implicit-builtin-decl.c
index 8cdd365..d7ec169 100644
--- a/test/Sema/implicit-builtin-decl.c
+++ b/test/Sema/implicit-builtin-decl.c
@@ -55,3 +55,5 @@ void snprintf() { }
// PR8316
void longjmp(); // expected-warning{{declaration of built-in function 'longjmp' requires inclusion of the header <setjmp.h>}}
+
+extern float fmaxf(float, float);
diff --git a/test/Sema/inline.c b/test/Sema/inline.c
index 3c99f24..c27c00e 100644
--- a/test/Sema/inline.c
+++ b/test/Sema/inline.c
@@ -1,6 +1,78 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if defined(INCLUDE)
+// -------
+// This section acts like a header file.
+// -------
+
+// Check the use of static variables in non-static inline functions.
+static int staticVar; // expected-note + {{'staticVar' declared here}}
+static int staticFunction(); // expected-note + {{'staticFunction' declared here}}
+static struct { int x; } staticStruct; // expected-note + {{'staticStruct' declared here}}
+
+inline int useStatic () { // expected-note 3 {{use 'static' to give inline function 'useStatic' internal linkage}}
+ staticFunction(); // expected-warning{{static function 'staticFunction' is used in an inline function with external linkage}}
+ (void)staticStruct.x; // expected-warning{{static variable 'staticStruct' is used in an inline function with external linkage}}
+ return staticVar; // expected-warning{{static variable 'staticVar' is used in an inline function with external linkage}}
+}
+
+extern inline int useStaticFromExtern () { // no suggestions
+ staticFunction(); // expected-warning{{static function 'staticFunction' is used in an inline function with external linkage}}
+ return staticVar; // expected-warning{{static variable 'staticVar' is used in an inline function with external linkage}}
+}
+
+static inline int useStaticFromStatic () {
+ staticFunction(); // no-warning
+ return staticVar; // no-warning
+}
+
+extern inline int useStaticInlineFromExtern () {
+ // Heuristic: if the function we're using is also inline, don't warn.
+ // This can still be wrong (in this case, we end up inlining calls to
+ // staticFunction and staticVar) but this got very noisy even using
+ // standard headers.
+ return useStaticFromStatic(); // no-warning
+}
+
+static int constFunction() __attribute__((const));
+
+inline int useConst () {
+ return constFunction(); // no-warning
+}
+
+#else
+// -------
+// This is the main source file.
+// -------
+
+#define INCLUDE
+#include "inline.c"
+
// Check that we don't allow illegal uses of inline
inline int a; // expected-error{{'inline' can only appear on functions}}
typedef inline int b; // expected-error{{'inline' can only appear on functions}}
int d(inline int a); // expected-error{{'inline' can only appear on functions}}
+
+// Check that the warnings from the "header file" aren't on by default in
+// the main source file.
+
+inline int useStaticMainFile () {
+ staticFunction(); // no-warning
+ return staticVar; // no-warning
+}
+
+// Check that the warnings show up when explicitly requested.
+
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wstatic-in-inline"
+
+inline int useStaticAgain () { // expected-note 2 {{use 'static' to give inline function 'useStaticAgain' internal linkage}}
+ staticFunction(); // expected-warning{{static function 'staticFunction' is used in an inline function with external linkage}}
+ return staticVar; // expected-warning{{static variable 'staticVar' is used in an inline function with external linkage}}
+}
+
+#pragma clang diagnostic pop
+
+#endif
+
+
diff --git a/test/Sema/invalid-decl.c b/test/Sema/invalid-decl.c
index a5e7ad3..2699b25 100644
--- a/test/Sema/invalid-decl.c
+++ b/test/Sema/invalid-decl.c
@@ -20,3 +20,12 @@ zend_module_entry openssl_module_entry = {
sizeof(zend_module_entry)
};
+// <rdar://problem/11067144>
+typedef int (FunctionType)(int *value);
+typedef struct {
+ UndefinedType undef; // expected-error {{unknown type name 'UndefinedType'}}
+ FunctionType fun; // expected-error {{field 'fun' declared as a function}}
+} StructType;
+void f(StructType *buf) {
+ buf->fun = 0;
+}
diff --git a/test/Sema/knr-def-call.c b/test/Sema/knr-def-call.c
index f41275d..80ad0d8 100644
--- a/test/Sema/knr-def-call.c
+++ b/test/Sema/knr-def-call.c
@@ -36,6 +36,6 @@ void proto(x)
}
void use_proto() {
- proto(42.1); // expected-warning{{implicit conversion turns literal floating-point number into integer}}
- (&proto)(42.1); // expected-warning{{implicit conversion turns literal floating-point number into integer}}
+ proto(42.1); // expected-warning{{implicit conversion from 'double' to 'int' changes value from 42.1 to 42}}
+ (&proto)(42.1); // expected-warning{{implicit conversion from 'double' to 'int' changes value from 42.1 to 42}}
}
diff --git a/test/Sema/ms_class_layout.cpp b/test/Sema/ms_class_layout.cpp
index ea3f899..f484947 100644
--- a/test/Sema/ms_class_layout.cpp
+++ b/test/Sema/ms_class_layout.cpp
@@ -97,6 +97,48 @@ struct P : public M, public virtual L {
struct R {};
+class IA {
+public:
+ virtual ~IA(){}
+ virtual void ia() = 0;
+};
+
+class ICh : public virtual IA {
+public:
+ virtual ~ICh(){}
+ virtual void ia(){}
+ virtual void iCh(){}
+};
+
+struct f {
+ virtual int asd() {return -90;}
+};
+
+struct s : public virtual f {
+ virtual ~s(){}
+ int r;
+ virtual int asd() {return -9;}
+};
+
+struct sd : virtual s, virtual ICh {
+ virtual ~sd(){}
+ int q;
+ char y;
+ virtual int asd() {return -1;}
+};
+struct AV {
+ virtual void foo();
+};
+struct BV : AV {
+};
+struct CV : virtual BV {
+ CV();
+ virtual void foo();
+};
+struct DV : BV {
+};
+struct EV : CV, DV {
+};
#pragma pack(pop)
// This needs only for building layouts.
@@ -113,6 +155,8 @@ int main() {
O* o;
P* p;
R* r;
+ sd *h;
+ EV *j;
return 0;
}
@@ -333,3 +377,132 @@ int main() {
// CHECK-NEXT: nvsize=0, nvalign=1
//CHECK: %struct.R = type { i8 }
+
+// CHECK: 0 | struct f
+// CHECK-NEXT: 0 | (f vftable pointer)
+// CHECK-NEXT: sizeof=4, dsize=4, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+
+// CHECK: 0 | struct s
+// CHECK-NEXT: 0 | (s vftable pointer)
+// CHECK-NEXT: 4 | (s vbtable pointer)
+// CHECK-NEXT: 8 | int r
+// CHECK-NEXT: 12 | (vtordisp for vbase f)
+// CHECK-NEXT: 16 | struct f (virtual base)
+// CHECK-NEXT: 16 | (f vftable pointer)
+// CHECK-NEXT: sizeof=20, dsize=20, align=4
+// CHECK-NEXT: nvsize=12, nvalign=4
+
+// CHECK: 0 | class IA
+// CHECK-NEXT: 0 | (IA vftable pointer)
+// CHECK-NEXT: sizeof=4, dsize=4, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+
+// CHECK: 0 | class ICh
+// CHECK-NEXT: 0 | (ICh vftable pointer)
+// CHECK-NEXT: 4 | (ICh vbtable pointer)
+// CHECK-NEXT: 8 | (vtordisp for vbase IA)
+// CHECK-NEXT: 12 | class IA (virtual base)
+// CHECK-NEXT: 12 | (IA vftable pointer)
+// CHECK-NEXT: sizeof=16, dsize=16, align=4
+// CHECK-NEXT: nvsize=8, nvalign=4
+
+// CHECK: 0 | struct sd
+// CHECK-NEXT: 0 | (sd vbtable pointer)
+// CHECK-NEXT: 4 | int q
+// CHECK-NEXT: 8 | char y
+// CHECK-NEXT: 12 | (vtordisp for vbase f)
+// CHECK-NEXT: 16 | struct f (virtual base)
+// CHECK-NEXT: 16 | (f vftable pointer)
+// CHECK-NEXT: 20 | struct s (virtual base)
+// CHECK-NEXT: 20 | (s vftable pointer)
+// CHECK-NEXT: 24 | (s vbtable pointer)
+// CHECK-NEXT: 28 | int r
+// CHECK-NEXT: 32 | (vtordisp for vbase IA)
+// CHECK-NEXT: 36 | class IA (virtual base)
+// CHECK-NEXT: 36 | (IA vftable pointer)
+// CHECK-NEXT: 40 | class ICh (virtual base)
+// CHECK-NEXT: 40 | (ICh vftable pointer)
+// CHECK-NEXT: 44 | (ICh vbtable pointer)
+// CHECK-NEXT: sizeof=48, dsize=48, align=4
+// CHECK-NEXT: nvsize=12, nvalign=4
+
+// CHECK: %struct.f = type { i32 (...)** }
+// CHECK: %struct.s = type { i32 (...)**, i32*, i32, [4 x i8], %struct.f }
+// CHECK: %class.IA = type { i32 (...)** }
+// CHECK: %class.ICh = type { i32 (...)**, i32*, [4 x i8], %class.IA }
+// CHECK: %struct.sd = type { i32*, i32, i8, [7 x i8], %struct.f, %struct.s.base, [4 x i8], %class.IA, %class.ICh.base }
+
+// CHECK: 0 | struct AV
+// CHECK-NEXT: 0 | (AV vftable pointer)
+// CHECK-NEXT: sizeof=4, dsize=4, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+
+
+// CHECK: 0 | struct BV
+// CHECK-NEXT: 0 | struct AV (primary base)
+// CHECK-NEXT: 0 | (AV vftable pointer)
+// CHECK-NEXT: sizeof=4, dsize=4, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+
+
+// CHECK: 0 | struct CV
+// CHECK-NEXT: 0 | (CV vbtable pointer)
+// CHECK-NEXT: 4 | (vtordisp for vbase BV)
+// CHECK-NEXT: 8 | struct BV (virtual base)
+// CHECK-NEXT: 8 | struct AV (primary base)
+// CHECK-NEXT: 8 | (AV vftable pointer)
+// CHECK-NEXT: sizeof=12, dsize=12, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+
+// CHECK: %struct.AV = type { i32 (...)** }
+// CHECK: %struct.BV = type { %struct.AV }
+// CHECK: %struct.CV = type { i32*, [4 x i8], %struct.BV }
+// CHECK: %struct.CV.base = type { i32* }
+
+// CHECK: 0 | struct DV
+// CHECK-NEXT: 0 | struct BV (primary base)
+// CHECK-NEXT: 0 | struct AV (primary base)
+// CHECK-NEXT: 0 | (AV vftable pointer)
+// CHECK-NEXT: sizeof=4, dsize=4, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+
+// CHECK: %struct.DV = type { %struct.BV }
+
+// CHECK: 0 | struct EV
+// CHECK-NEXT: 4 | struct CV (base)
+// CHECK-NEXT: 4 | (CV vbtable pointer)
+// CHECK-NEXT: 0 | struct DV (primary base)
+// CHECK-NEXT: 0 | struct BV (primary base)
+// CHECK-NEXT: 0 | struct AV (primary base)
+// CHECK-NEXT: 0 | (AV vftable pointer)
+// CHECK-NEXT: 8 | (vtordisp for vbase BV)
+// CHECK-NEXT: 12 | struct BV (virtual base)
+// CHECK-NEXT: 12 | struct AV (primary base)
+// CHECK-NEXT: 12 | (AV vftable pointer)
+// CHECK-NEXT: sizeof=16, dsize=16, align=4
+// CHECK-NEXT: nvsize=8, nvalign=4
+
+// CHECK: %struct.EV = type { %struct.DV, %struct.CV.base, [4 x i8], %struct.BV }
+// CHECK: %struct.EV.base = type { %struct.DV, %struct.CV.base }
+
+// Overriding a method means that all the vbases containing that
+// method need a vtordisp.
+namespace test1 {
+ struct A { virtual void foo(); };
+ struct B : A {};
+ struct C : virtual A, virtual B { C(); virtual void foo(); };
+ void test() { C *c; }
+
+// CHECK: 0 | struct test1::C
+// CHECK-NEXT: 0 | (C vbtable pointer)
+// CHECK-NEXT: 4 | (vtordisp for vbase A)
+// CHECK-NEXT: 8 | struct test1::A (virtual base)
+// CHECK-NEXT: 8 | (A vftable pointer)
+// CHECK-NEXT: 12 | (vtordisp for vbase B)
+// CHECK-NEXT: 16 | struct test1::B (virtual base)
+// CHECK-NEXT: 16 | struct test1::A (primary base)
+// CHECK-NEXT: 16 | (A vftable pointer)
+// CHECK-NEXT: sizeof=20, dsize=20, align=4
+// CHECK-NEXT: nvsize=4, nvalign=4
+}
diff --git a/test/Sema/ms_wide_predefined_expr.cpp b/test/Sema/ms_wide_predefined_expr.cpp
new file mode 100644
index 0000000..8e816e0
--- /dev/null
+++ b/test/Sema/ms_wide_predefined_expr.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 %s -fsyntax-only -Wno-unused-value -Wmicrosoft -verify -fms-extensions
+
+// Wide character predefined identifiers
+#define _STR2WSTR(str) L##str
+#define STR2WSTR(str) _STR2WSTR(str)
+void abcdefghi12(void) {
+ const wchar_t (*ss)[12] = &STR2WSTR(__FUNCTION__);
+ static int arr[sizeof(STR2WSTR(__FUNCTION__))==12*sizeof(wchar_t) ? 1 : -1];
+}
+
+namespace PR13206 {
+void foo(const wchar_t *);
+
+template<class T> class A {
+public:
+ void method() {
+ foo(L__FUNCTION__);
+ }
+};
+
+void bar() {
+ A<int> x;
+ x.method();
+}
+}
diff --git a/test/Sema/pragma-pack-6.c b/test/Sema/pragma-pack-6.c
new file mode 100644
index 0000000..40659c2
--- /dev/null
+++ b/test/Sema/pragma-pack-6.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple i686-apple-darwin9 %s -fsyntax-only -verify
+
+// Pragma pack handling with tag declarations
+
+struct X;
+
+#pragma pack(2)
+struct X { int x; };
+struct Y;
+#pragma pack()
+
+struct Y { int y; };
+
+extern int check[__alignof(struct X) == 2 ? 1 : -1];
+extern int check[__alignof(struct Y) == 4 ? 1 : -1];
+
diff --git a/test/Sema/switch.c b/test/Sema/switch.c
index a7a7f60..e37d5da 100644
--- a/test/Sema/switch.c
+++ b/test/Sema/switch.c
@@ -9,7 +9,7 @@ void foo(int X) {
switch (X) {
case 42: ; // expected-note {{previous case}}
case 5000000000LL: // expected-warning {{overflow}}
- case 42: // expected-error {{duplicate case value}}
+ case 42: // expected-error {{duplicate case value '42'}}
;
case 100 ... 99: ; // expected-warning {{empty case range}}
@@ -320,3 +320,32 @@ void rdar110822110(Ints i)
break;
}
}
+
+// PR9243
+#define TEST19MACRO 5
+void test19(int i) {
+ enum {
+ kTest19Enum1 = 7,
+ kTest19Enum2 = kTest19Enum1
+ };
+ const int a = 3;
+ switch (i) {
+ case 5: // expected-note {{previous case}}
+ case TEST19MACRO: // expected-error {{duplicate case value '5'}}
+
+ case 7: // expected-note {{previous case}}
+ case kTest19Enum1: // expected-error {{duplicate case value: '7' and 'kTest19Enum1' both equal '7'}} \
+ // expected-note {{previous case}}
+ case kTest19Enum1: // expected-error {{duplicate case value 'kTest19Enum1'}} \
+ // expected-note {{previous case}}
+ case kTest19Enum2: // expected-error {{duplicate case value: 'kTest19Enum1' and 'kTest19Enum2' both equal '7'}} \
+ // expected-note {{previous case}}
+ case (int)kTest19Enum2: //expected-error {{duplicate case value 'kTest19Enum2'}}
+
+ case 3: // expected-note {{previous case}}
+ case a: // expected-error {{duplicate case value: '3' and 'a' both equal '3'}} \
+ // expected-note {{previous case}}
+ case a: // expected-error {{duplicate case value 'a'}}
+ break;
+ }
+}
diff --git a/test/Sema/thread-specifier.c b/test/Sema/thread-specifier.c
index 9ec88c5..0d439b1 100644
--- a/test/Sema/thread-specifier.c
+++ b/test/Sema/thread-specifier.c
@@ -1,19 +1,21 @@
-// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -verify -pedantic %s
__thread int t1;
-__thread extern int t2;
-__thread static int t3;
+__thread extern int t2; // expected-warning {{'__thread' before 'extern'}}
+__thread static int t3; // expected-warning {{'__thread' before 'static'}}
__thread __private_extern__ int t4;
struct t5 { __thread int x; }; // expected-error {{type name does not allow storage class to be specified}}
__thread int t6(); // expected-error {{'__thread' is only allowed on variable declarations}}
+
int f(__thread int t7) { // expected-error {{'__thread' is only allowed on variable declarations}}
__thread int t8; // expected-error {{'__thread' variables must have global storage}}
- __thread extern int t9;
- __thread static int t10;
+ extern __thread int t9;
+ static __thread int t10;
__thread __private_extern__ int t11;
__thread auto int t12; // expected-error {{'__thread' variables must have global storage}}
__thread register int t13; // expected-error {{'__thread' variables must have global storage}}
}
+
__thread typedef int t14; // expected-error {{'__thread' is only allowed on variable declarations}}
__thread int t15; // expected-note {{previous definition is here}}
int t15; // expected-error {{non-thread-local declaration of 't15' follows thread-local declaration}}
diff --git a/test/Sema/tls.c b/test/Sema/tls.c
new file mode 100644
index 0000000..3b2a441
--- /dev/null
+++ b/test/Sema/tls.c
@@ -0,0 +1,20 @@
+// Test that TLS is correctly considered supported or unsupported for the
+// different targets.
+
+// Linux supports TLS.
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only %s
+// RUN: %clang_cc1 -triple i386-pc-linux-gnu -fsyntax-only %s
+
+// Darwin supports TLS since 10.7.
+// RUN: not %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only %s
+// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -fsyntax-only %s
+
+// FIXME: I thought it was supported actually?
+// RUN: not %clang_cc1 -triple x86_64-pc-win32 -fsyntax-only %s
+// RUN: not %clang_cc1 -triple i386-pc-win32 -fsyntax-only %s
+
+// OpenBSD does not suppport TLS.
+// RUN: not %clang_cc1 -triple x86_64-pc-openbsd -fsyntax-only %s
+// RUN: not %clang_cc1 -triple i386-pc-openbsd -fsyntax-only %s
+
+__thread int x;
diff --git a/test/Sema/typeof-use-deprecated.c b/test/Sema/typeof-use-deprecated.c
index 238e501..1518c83 100644
--- a/test/Sema/typeof-use-deprecated.c
+++ b/test/Sema/typeof-use-deprecated.c
@@ -1,25 +1,25 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
-struct s { int a; } __attribute__((deprecated)) x; // expected-warning {{'s' is deprecated}}
+struct s { int a; } __attribute__((deprecated)) x; // expected-warning {{'s' is deprecated}} expected-note 2 {{'s' declared here}}
typeof(x) y; // expected-warning {{'s' is deprecated}}
-union un{ int a; } __attribute__((deprecated)) u; // expected-warning {{'un' is deprecated}}
+union un{ int a; } __attribute__((deprecated)) u; // expected-warning {{'un' is deprecated}} expected-note 2 {{'un' declared here}}
typeof( u) z; // expected-warning {{'un' is deprecated}}
-enum E{ one} __attribute__((deprecated)) e; // expected-warning {{'E' is deprecated}}
+enum E{ one} __attribute__((deprecated)) e; // expected-warning {{'E' is deprecated}} expected-note 2 {{'E' declared here}}
typeof( e) w; // expected-warning {{'E' is deprecated}}
-struct foo { int x; } __attribute__((deprecated));
-typedef struct foo bar __attribute__((deprecated));
+struct foo { int x; } __attribute__((deprecated)); // expected-note {{'foo' declared here}}
+typedef struct foo bar __attribute__((deprecated)); // expected-note {{'bar' declared here}}
bar x1; // expected-warning {{'bar' is deprecated}}
int main() { typeof(x1) y; } // expected-warning {{'foo' is deprecated}}
struct gorf { int x; };
-typedef struct gorf T __attribute__((deprecated));
+typedef struct gorf T __attribute__((deprecated)); // expected-note {{'T' declared here}}
T t; // expected-warning {{'T' is deprecated}}
void wee() { typeof(t) y; }
diff --git a/test/Sema/uninit-variables.c b/test/Sema/uninit-variables.c
index d62186d..634ae0d 100644
--- a/test/Sema/uninit-variables.c
+++ b/test/Sema/uninit-variables.c
@@ -39,17 +39,19 @@ int test6() {
int test7(int y) {
int x; // expected-note{{initialize the variable 'x' to silence this warning}}
- if (y)
+ if (y) // expected-warning{{variable 'x' is used uninitialized whenever 'if' condition is false}} \
+ // expected-note{{remove the 'if' if its condition is always true}}
x = 1;
- return x; // expected-warning{{variable 'x' may be uninitialized when used here}}
+ return x; // expected-note{{uninitialized use occurs here}}
}
int test7b(int y) {
int x = x; // expected-note{{variable 'x' is declared here}}
if (y)
x = 1;
- // Warn with "may be uninitialized" here (not "is uninitialized"), since the
- // self-initialization is intended to suppress a -Wuninitialized warning.
+ // Warn with "may be uninitialized" here (not "is sometimes uninitialized"),
+ // since the self-initialization is intended to suppress a -Wuninitialized
+ // warning.
return x; // expected-warning{{variable 'x' may be uninitialized when used here}}
}
@@ -150,15 +152,15 @@ int test19() {
int test20() {
int z; // expected-note{{initialize the variable 'z' to silence this warning}}
- if ((test19_aux1() + test19_aux2() && test19_aux1()) || test19_aux3(&z))
- return z; // expected-warning{{variable 'z' may be uninitialized when used here}}
+ if ((test19_aux1() + test19_aux2() && test19_aux1()) || test19_aux3(&z)) // expected-warning {{variable 'z' is used uninitialized whenever '||' condition is true}} expected-note {{remove the '||' if its condition is always false}}
+ return z; // expected-note {{uninitialized use occurs here}}
return 0;
}
int test21(int x, int y) {
int z; // expected-note{{initialize the variable 'z' to silence this warning}}
- if ((x && y) || test19_aux3(&z) || test19_aux2())
- return z; // expected-warning{{variable 'z' may be uninitialized when used here}}
+ if ((x && y) || test19_aux3(&z) || test19_aux2()) // expected-warning {{variable 'z' is used uninitialized whenever '||' condition is true}} expected-note {{remove the '||' if its condition is always false}}
+ return z; // expected-note {{uninitialized use occurs here}}
return 0;
}
@@ -293,8 +295,9 @@ int test40(int x) {
int test41(int x) {
int y; // expected-note{{initialize the variable 'y' to silence this warning}}
- if (x) y = 1; // no-warning
- return y; // expected-warning {{variable 'y' may be uninitialized when used here}}
+ if (x) y = 1; // expected-warning{{variable 'y' is used uninitialized whenever 'if' condition is false}} \
+ // expected-note{{remove the 'if' if its condition is always true}}
+ return y; // expected-note{{uninitialized use occurs here}}
}
void test42() {
@@ -424,3 +427,84 @@ void rdar9432305(float *P) {
for (; i < 10000; ++i) // expected-warning {{variable 'i' is uninitialized when used here}}
P[i] = 0.0f;
}
+
+// Test that fixits are not emitted inside macros.
+#define UNINIT(T, x, y) T x; T y = x;
+#define ASSIGN(T, x, y) T y = x;
+void test54() {
+ UNINIT(int, a, b); // expected-warning {{variable 'a' is uninitialized when used here}} \
+ // expected-note {{variable 'a' is declared here}}
+ int c; // expected-note {{initialize the variable 'c' to silence this warning}}
+ ASSIGN(int, c, d); // expected-warning {{variable 'c' is uninitialized when used here}}
+}
+
+// Taking the address is fine
+struct { struct { void *p; } a; } test55 = { { &test55.a }}; // no-warning
+struct { struct { void *p; } a; } test56 = { { &(test56.a) }}; // no-warning
+
+void uninit_in_loop() {
+ int produce(void);
+ void consume(int);
+ for (int n = 0; n < 100; ++n) {
+ int k; // expected-note {{initialize}}
+ consume(k); // expected-warning {{variable 'k' is uninitialized}}
+ k = produce();
+ }
+}
+
+void uninit_in_loop_goto() {
+ int produce(void);
+ void consume(int);
+ for (int n = 0; n < 100; ++n) {
+ goto skip_decl;
+ int k; // expected-note {{initialize}}
+skip_decl:
+ // FIXME: This should produce the 'is uninitialized' diagnostic, but we
+ // don't have enough information in the CFG to easily tell that the
+ // variable's scope has been left and re-entered.
+ consume(k); // expected-warning {{variable 'k' may be uninitialized}}
+ k = produce();
+ }
+}
+
+typedef char jmp_buf[256];
+extern int setjmp(jmp_buf env); // implicitly returns_twice
+
+void do_stuff_and_longjmp(jmp_buf env, int *result) __attribute__((noreturn));
+
+int returns_twice() {
+ int a; // expected-note {{initialize}}
+ if (!a) { // expected-warning {{variable 'a' is uninitialized}}
+ jmp_buf env;
+ int b;
+ if (setjmp(env) == 0) {
+ do_stuff_and_longjmp(env, &b);
+ } else {
+ a = b; // no warning
+ }
+ }
+ return a;
+}
+
+int compound_assign(int *arr, int n) {
+ int sum; // expected-note {{initialize}}
+ for (int i = 0; i < n; ++i)
+ sum += arr[i]; // expected-warning {{variable 'sum' is uninitialized}}
+ return sum / n;
+}
+
+int compound_assign_2() {
+ int x; // expected-note {{initialize}}
+ return x += 1; // expected-warning {{variable 'x' is uninitialized}}
+}
+
+int compound_assign_3() {
+ int x; // expected-note {{initialize}}
+ x *= 0; // expected-warning {{variable 'x' is uninitialized}}
+ return x;
+}
+
+int self_init_in_cond(int *p) {
+ int n = ((p && (0 || 1)) && (n = *p)) ? n : -1; // ok
+ return n;
+}
diff --git a/test/Sema/unused-expr.c b/test/Sema/unused-expr.c
index 9761116..056d09a 100644
--- a/test/Sema/unused-expr.c
+++ b/test/Sema/unused-expr.c
@@ -82,7 +82,7 @@ void t5() {
int fn1() __attribute__ ((warn_unused_result));
int fn2() __attribute__ ((pure));
-int fn3() __attribute__ ((const));
+int fn3() __attribute__ ((__const));
// rdar://6587766
int t6() {
if (fn1() < 0 || fn2(2,1) < 0 || fn3(2) < 0) // no warnings
@@ -92,6 +92,7 @@ int t6() {
fn2(92, 21); // expected-warning {{ignoring return value of function declared with pure attribute}}
fn3(42); // expected-warning {{ignoring return value of function declared with const attribute}}
__builtin_fabsf(0); // expected-warning {{ignoring return value of function declared with const attribute}}
+ (void)0, fn1(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}}
return 0;
}
diff --git a/test/Sema/vector-ops.c b/test/Sema/vector-ops.c
index 3ab75d0..652a076 100644
--- a/test/Sema/vector-ops.c
+++ b/test/Sema/vector-ops.c
@@ -13,7 +13,7 @@ void test1(v2u v2ua, v2s v2sa, v2f v2fa) {
(void)(~v2fa); // expected-error{{invalid argument type 'v2f' to unary}}
// Comparison operators
- v2ua = (v2ua==v2sa); // expected-warning{{incompatible vector types assigning to 'v2u' from 'int __attribute__((ext_vector_type(2)))'}}
+ v2ua = (v2ua==v2sa); // expected-warning{{incompatible vector types assigning to 'v2u' from 'int __attribute__((ext_vector_type(2)))'}}
v2sa = (v2ua==v2sa);
// Arrays
diff --git a/test/Sema/warn-documentation-almost-trailing.c b/test/Sema/warn-documentation-almost-trailing.c
new file mode 100644
index 0000000..fa17cac
--- /dev/null
+++ b/test/Sema/warn-documentation-almost-trailing.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: cp %s %t
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -fixit %t
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -Werror %t
+
+struct a {
+ int x; //< comment // expected-warning {{not a Doxygen trailing comment}}
+ int y; /*< comment */ // expected-warning {{not a Doxygen trailing comment}}
+};
+
+// CHECK: fix-it:"{{.*}}":{8:10-8:13}:"///<"
+// CHECK: fix-it:"{{.*}}":{9:10-9:13}:"/**<"
+
diff --git a/test/Sema/warn-documentation-fixits.cpp b/test/Sema/warn-documentation-fixits.cpp
new file mode 100644
index 0000000..732b44d
--- /dev/null
+++ b/test/Sema/warn-documentation-fixits.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// expected-warning@+1 {{parameter 'ZZZZZZZZZZ' not found in the function declaration}} expected-note@+1 {{did you mean 'a'?}}
+/// \param ZZZZZZZZZZ Blah blah.
+int test1(int a);
+
+// expected-warning@+1 {{parameter 'aab' not found in the function declaration}} expected-note@+1 {{did you mean 'aaa'?}}
+/// \param aab Blah blah.
+int test2(int aaa, int bbb);
+
+// expected-warning@+1 {{template parameter 'ZZZZZZZZZZ' not found in the template declaration}} expected-note@+1 {{did you mean 'T'?}}
+/// \tparam ZZZZZZZZZZ Aaa
+template<typename T>
+void test3(T aaa);
+
+// expected-warning@+1 {{template parameter 'SomTy' not found in the template declaration}} expected-note@+1 {{did you mean 'SomeTy'?}}
+/// \tparam SomTy Aaa
+/// \tparam OtherTy Bbb
+template<typename SomeTy, typename OtherTy>
+void test4(SomeTy aaa, OtherTy bbb);
+
+// CHECK: fix-it:"{{.*}}":{5:12-5:22}:"a"
+// CHECK: fix-it:"{{.*}}":{9:12-9:15}:"aaa"
+// CHECK: fix-it:"{{.*}}":{13:13-13:23}:"T"
+// CHECK: fix-it:"{{.*}}":{18:13-18:18}:"SomeTy"
+
diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp
new file mode 100644
index 0000000..16ba429
--- /dev/null
+++ b/test/Sema/warn-documentation.cpp
@@ -0,0 +1,670 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wdocumentation -Wdocumentation-pedantic -verify %s
+
+// This file contains lots of corner cases, so ensure that XML we generate is not invalid.
+// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s | FileCheck %s -check-prefix=WRONG
+// WRONG-NOT: CommentXMLInvalid
+
+// expected-warning@+1 {{expected quoted string after equals sign}}
+/// <a href=>
+int test_html1(int);
+
+// expected-warning@+1 {{expected quoted string after equals sign}}
+/// <a href==>
+int test_html2(int);
+
+// expected-warning@+2 {{expected quoted string after equals sign}}
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/// <a href= blah
+int test_html3(int);
+
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/// <a =>
+int test_html4(int);
+
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/// <a "aaa">
+int test_html5(int);
+
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/// <a a="b" =>
+int test_html6(int);
+
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/// <a a="b" "aaa">
+int test_html7(int);
+
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/// <a a="b" =
+int test_html8(int);
+
+// expected-warning@+2 {{HTML start tag prematurely ended, expected attribute name or '>'}} expected-note@+1 {{HTML tag started here}}
+/** Aaa bbb<ccc ddd eee
+ * fff ggg.
+ */
+int test_html9(int);
+
+// expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
+/** Aaa bbb<ccc ddd eee 42%
+ * fff ggg.
+ */
+int test_html10(int);
+
+// expected-warning@+1 {{HTML end tag 'br' is forbidden}}
+/// <br></br>
+int test_html11(int);
+
+/// <blockquote>Meow</blockquote>
+int test_html_nesting1(int);
+
+/// <b><i>Meow</i></b>
+int test_html_nesting2(int);
+
+/// <p>Aaa<br>
+/// Bbb</p>
+int test_html_nesting3(int);
+
+/// <p>Aaa<br />
+/// Bbb</p>
+int test_html_nesting4(int);
+
+// expected-warning@+1 {{HTML end tag does not match any start tag}}
+/// <b><i>Meow</a>
+int test_html_nesting5(int);
+
+// expected-warning@+2 {{HTML start tag 'i' closed by 'b'}}
+// expected-warning@+1 {{HTML end tag does not match any start tag}}
+/// <b><i>Meow</b></b>
+int test_html_nesting6(int);
+
+// expected-warning@+2 {{HTML start tag 'i' closed by 'b'}}
+// expected-warning@+1 {{HTML end tag does not match any start tag}}
+/// <b><i>Meow</b></i>
+int test_html_nesting7(int);
+
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\returns Aaa
+int test_block_command1(int);
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief \returns Aaa
+int test_block_command2(int);
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief
+/// \returns Aaa
+int test_block_command3(int);
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief
+///
+/// \returns Aaa
+int test_block_command4(int);
+
+// There is trailing whitespace on one of the following lines, don't remove it!
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief
+///
+/// \returns Aaa
+int test_block_command5(int);
+
+/// \brief \c Aaa
+int test_block_command6(int);
+
+// expected-warning@+5 {{duplicated command '\brief'}} expected-note@+1 {{previous command '\brief' here}}
+/// \brief Aaa
+///
+/// Bbb
+///
+/// \brief Ccc
+int test_duplicate_brief1(int);
+
+// expected-warning@+5 {{duplicated command '\short'}} expected-note@+1 {{previous command '\short' here}}
+/// \short Aaa
+///
+/// Bbb
+///
+/// \short Ccc
+int test_duplicate_brief2(int);
+
+// expected-warning@+5 {{duplicated command '\brief'}} expected-note@+1 {{previous command '\short' (an alias of '\brief') here}}
+/// \short Aaa
+///
+/// Bbb
+///
+/// \brief Ccc
+int test_duplicate_brief3(int);
+
+
+// expected-warning@+5 {{duplicated command '\return'}} expected-note@+1 {{previous command '\return' here}}
+/// \return Aaa
+///
+/// Bbb
+///
+/// \return Ccc
+int test_duplicate_returns1(int);
+
+// expected-warning@+5 {{duplicated command '\returns'}} expected-note@+1 {{previous command '\returns' here}}
+/// \returns Aaa
+///
+/// Bbb
+///
+/// \returns Ccc
+int test_duplicate_returns2(int);
+
+// expected-warning@+5 {{duplicated command '\result'}} expected-note@+1 {{previous command '\result' here}}
+/// \result Aaa
+///
+/// Bbb
+///
+/// \result Ccc
+int test_duplicate_returns3(int);
+
+// expected-warning@+5 {{duplicated command '\return'}} expected-note@+1 {{previous command '\returns' (an alias of '\return') here}}
+/// \returns Aaa
+///
+/// Bbb
+///
+/// \return Ccc
+int test_duplicate_returns4(int);
+
+
+// expected-warning@+1 {{'\param' command used in a comment that is not attached to a function declaration}}
+/// \param a Blah blah.
+int test_param1;
+
+// expected-warning@+1 {{empty paragraph passed to '\param' command}}
+/// \param
+/// \param a Blah blah.
+int test_param2(int a);
+
+// expected-warning@+1 {{empty paragraph passed to '\param' command}}
+/// \param a
+int test_param3(int a);
+
+/// \param a Blah blah.
+int test_param4(int a);
+
+/// \param [in] a Blah blah.
+int test_param5(int a);
+
+/// \param [out] a Blah blah.
+int test_param6(int a);
+
+/// \param [in,out] a Blah blah.
+int test_param7(int a);
+
+// expected-warning@+1 {{whitespace is not allowed in parameter passing direction}}
+/// \param [ in ] a Blah blah.
+int test_param8(int a);
+
+// expected-warning@+1 {{whitespace is not allowed in parameter passing direction}}
+/// \param [in, out] a Blah blah.
+int test_param9(int a);
+
+// expected-warning@+1 {{unrecognized parameter passing direction, valid directions are '[in]', '[out]' and '[in,out]'}}
+/// \param [ junk] a Blah blah.
+int test_param10(int a);
+
+// expected-warning@+1 {{parameter 'a' not found in the function declaration}}
+/// \param a Blah blah.
+int test_param11();
+
+// expected-warning@+1 {{parameter 'A' not found in the function declaration}} expected-note@+1 {{did you mean 'a'?}}
+/// \param A Blah blah.
+int test_param12(int a);
+
+// expected-warning@+1 {{parameter 'aab' not found in the function declaration}} expected-note@+1 {{did you mean 'aaa'?}}
+/// \param aab Blah blah.
+int test_param13(int aaa, int bbb);
+
+// expected-warning@+1 {{parameter 'aab' not found in the function declaration}}
+/// \param aab Blah blah.
+int test_param14(int bbb, int ccc);
+
+class C {
+ // expected-warning@+1 {{parameter 'aaa' not found in the function declaration}}
+ /// \param aaa Blah blah.
+ C(int bbb, int ccc);
+
+ // expected-warning@+1 {{parameter 'aaa' not found in the function declaration}}
+ /// \param aaa Blah blah.
+ int test_param15(int bbb, int ccc);
+};
+
+// expected-warning@+1 {{parameter 'aab' not found in the function declaration}}
+/// \param aab Blah blah.
+template<typename T>
+void test_param16(int bbb, int ccc);
+
+// expected-warning@+3 {{parameter 'a' is already documented}}
+// expected-note@+1 {{previous documentation}}
+/// \param a Aaa.
+/// \param a Aaa.
+int test_param17(int a);
+
+// expected-warning@+4 {{parameter 'x2' is already documented}}
+// expected-note@+2 {{previous documentation}}
+/// \param x1 Aaa.
+/// \param x2 Bbb.
+/// \param x2 Ccc.
+int test_param18(int x1, int x2, int x3);
+
+
+// expected-warning@+1 {{'\tparam' command used in a comment that is not attached to a template declaration}}
+/// \tparam T Aaa
+int test_tparam1;
+
+// expected-warning@+1 {{'\tparam' command used in a comment that is not attached to a template declaration}}
+/// \tparam T Aaa
+void test_tparam2(int aaa);
+
+// expected-warning@+1 {{empty paragraph passed to '\tparam' command}}
+/// \tparam
+/// \param aaa Blah blah
+template<typename T>
+void test_tparam3(T aaa);
+
+// expected-warning@+1 {{template parameter 'T' not found in the template declaration}} expected-note@+1 {{did you mean 'TT'?}}
+/// \tparam T Aaa
+template<typename TT>
+void test_tparam4(TT aaa);
+
+// expected-warning@+1 {{template parameter 'T' not found in the template declaration}} expected-note@+1 {{did you mean 'TT'?}}
+/// \tparam T Aaa
+template<typename TT>
+class test_tparam5 {
+ // expected-warning@+1 {{template parameter 'T' not found in the template declaration}} expected-note@+1 {{did you mean 'TTT'?}}
+ /// \tparam T Aaa
+ template<typename TTT>
+ void test_tparam6(TTT aaa);
+};
+
+/// \tparam T1 Aaa
+/// \tparam T2 Bbb
+template<typename T1, typename T2>
+void test_tparam7(T1 aaa, T2 bbb);
+
+// expected-warning@+1 {{template parameter 'SomTy' not found in the template declaration}} expected-note@+1 {{did you mean 'SomeTy'?}}
+/// \tparam SomTy Aaa
+/// \tparam OtherTy Bbb
+template<typename SomeTy, typename OtherTy>
+void test_tparam8(SomeTy aaa, OtherTy bbb);
+
+// expected-warning@+2 {{template parameter 'T1' is already documented}} expected-note@+1 {{previous documentation}}
+/// \tparam T1 Aaa
+/// \tparam T1 Bbb
+template<typename T1, typename T2>
+void test_tparam9(T1 aaa, T2 bbb);
+
+/// \tparam T Aaa
+/// \tparam TT Bbb
+template<template<typename T> class TT>
+void test_tparam10(TT<int> aaa);
+
+/// \tparam T Aaa
+/// \tparam TT Bbb
+/// \tparam TTT Ccc
+template<template<template<typename T> class TT, class C> class TTT>
+void test_tparam11();
+
+/// \tparam I Aaa
+template<int I>
+void test_tparam12();
+
+template<typename T, typename U>
+class test_tparam13 { };
+
+/// \tparam T Aaa
+template<typename T>
+using test_tparam14 = test_tparam13<T, int>;
+
+// expected-warning@+1 {{template parameter 'U' not found in the template declaration}} expected-note@+1 {{did you mean 'T'?}}
+/// \tparam U Aaa
+template<typename T>
+using test_tparam15 = test_tparam13<T, int>;
+
+// no-warning
+/// \returns Aaa
+int test_returns_right_decl_1(int);
+
+class test_returns_right_decl_2 {
+ // no-warning
+ /// \returns Aaa
+ int test_returns_right_decl_3(int);
+};
+
+// no-warning
+/// \returns Aaa
+template<typename T>
+int test_returns_right_decl_4(T aaa);
+
+// no-warning
+/// \returns Aaa
+template<>
+int test_returns_right_decl_4(int aaa);
+
+/// \returns Aaa
+template<typename T>
+T test_returns_right_decl_5(T aaa);
+
+// expected-warning@+1 {{'\returns' command used in a comment that is not attached to a function or method declaration}}
+/// \returns Aaa
+int test_returns_wrong_decl_1;
+
+// expected-warning@+1 {{'\return' command used in a comment that is not attached to a function or method declaration}}
+/// \return Aaa
+int test_returns_wrong_decl_2;
+
+// expected-warning@+1 {{'\result' command used in a comment that is not attached to a function or method declaration}}
+/// \result Aaa
+int test_returns_wrong_decl_3;
+
+// expected-warning@+1 {{'\returns' command used in a comment that is attached to a function returning void}}
+/// \returns Aaa
+void test_returns_wrong_decl_4(int);
+
+// expected-warning@+1 {{'\returns' command used in a comment that is attached to a function returning void}}
+/// \returns Aaa
+template<typename T>
+void test_returns_wrong_decl_5(T aaa);
+
+// expected-warning@+1 {{'\returns' command used in a comment that is attached to a function returning void}}
+/// \returns Aaa
+template<>
+void test_returns_wrong_decl_5(int aaa);
+
+// expected-warning@+1 {{'\returns' command used in a comment that is not attached to a function or method declaration}}
+/// \returns Aaa
+struct test_returns_wrong_decl_6 { };
+
+// expected-warning@+1 {{'\returns' command used in a comment that is not attached to a function or method declaration}}
+/// \returns Aaa
+class test_returns_wrong_decl_7 {
+ // expected-warning@+1 {{'\returns' command used in a comment that is attached to a constructor}}
+ /// \returns Aaa
+ test_returns_wrong_decl_7();
+
+ // expected-warning@+1 {{'\returns' command used in a comment that is attached to a destructor}}
+ /// \returns Aaa
+ ~test_returns_wrong_decl_7();
+};
+
+// expected-warning@+1 {{'\returns' command used in a comment that is not attached to a function or method declaration}}
+/// \returns Aaa
+enum test_returns_wrong_decl_8 {
+ // expected-warning@+1 {{'\returns' command used in a comment that is not attached to a function or method declaration}}
+ /// \returns Aaa
+ test_returns_wrong_decl_9
+};
+
+// expected-warning@+1 {{'\returns' command used in a comment that is not attached to a function or method declaration}}
+/// \returns Aaa
+namespace test_returns_wrong_decl_10 { };
+
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+int test1; ///< \brief\author Aaa
+
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+int test2, ///< \brief\author Aaa
+ test3; ///< \brief\author Aaa
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+int test4; ///< \brief
+ ///< \author Aaa
+
+
+// Check that we attach the comment to the declaration during parsing in the
+// following cases. The test is based on the fact that we don't parse
+// documentation comments that are not attached to anything.
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+int test_attach1;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+int test_attach2(int);
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+struct test_attach3 {
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ int test_attach4;
+
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ int test_attach5; ///< \brief\author Aaa
+
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ int test_attach6(int);
+};
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+class test_attach7 {
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ int test_attach8;
+
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ int test_attach9; ///< \brief\author Aaa
+
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ int test_attach10(int);
+};
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+enum test_attach9 {
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ test_attach10,
+
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ test_attach11 ///< \brief\author Aaa
+};
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+struct test_noattach12 *test_attach13;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+typedef struct test_noattach14 *test_attach15;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+typedef struct test_attach16 { int a; } test_attach17;
+
+struct S { int a; };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+struct S *test_attach18;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+typedef struct S *test_attach19;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+struct test_attach20;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+typedef struct test_attach21 {
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ int test_attach22;
+} test_attach23;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+namespace test_attach24 {
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ namespace test_attach25 {
+ }
+}
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T>
+void test_attach26(T aaa);
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T, typename U>
+void test_attach27(T aaa, U bbb);
+
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<>
+void test_attach27(int aaa, int bbb);
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T>
+class test_attach28 {
+ T aaa;
+};
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+using test_attach29 = test_attach28<int>;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T, typename U>
+class test_attach30 { };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T>
+class test_attach30<T, int> { };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+template<>
+class test_attach30<int, int> { };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+template<typename T>
+using test_attach31 = test_attach30<T, int>;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T, typename U, typename V>
+class test_attach32 { };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T, typename U>
+class test_attach32<T, U, int> { };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T>
+class test_attach32<T, int, int> { };
+
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<>
+class test_attach32<int, int, int> { };
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+class test_attach33 {
+ // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+ /// \brief\author Aaa
+ /// \tparam T Aaa
+ template<typename T, typename U>
+ void test_attach34(T aaa, U bbb);
+};
+
+template<typename T>
+class test_attach35 {
+ // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+ // expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
+ /// \brief\author Aaa
+ /// \tparam T Aaa
+ template<typename TT, typename UU>
+ void test_attach36(TT aaa, UU bbb);
+};
+
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<> template<>
+void test_attach35<int>::test_attach36(int aaa, int bbb) {}
+
+template<typename T>
+class test_attach37 {
+ // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+ // expected-warning@+2 {{'\tparam' command used in a comment that is not attached to a template declaration}}
+ /// \brief\author Aaa
+ /// \tparam T Aaa
+ void test_attach38(int aaa, int bbb);
+};
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<typename T>
+void test_attach37<T>::test_attach38(int aaa, int bbb) {}
+
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+// expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\author Aaa
+/// \tparam T Aaa
+template<>
+void test_attach37<int>::test_attach38(int aaa, int bbb) {}
+
+
+// PR13411, reduced. We used to crash on this.
+/**
+ * @code Aaa.
+ */
+void test_nocrash1(int);
+
+// We used to crash on this.
+// expected-warning@+2 {{empty paragraph passed to '\param' command}}
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \param\brief
+void test_nocrash2(int);
+
+// PR13593
+
+/**
+* Bla.
+*/
+template <typename>
+void test_nocrash3();
+
+/// Foo
+template <typename, typename>
+void test_nocrash4() { }
+
+template <typename>
+void test_nocrash3()
+{
+}
diff --git a/test/Sema/warn-documentation.m b/test/Sema/warn-documentation.m
new file mode 100644
index 0000000..d6af6ed
--- /dev/null
+++ b/test/Sema/warn-documentation.m
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-objc-root-class -Wdocumentation -Wdocumentation-pedantic -verify %s
+
+@class NSString;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@interface Test1
+// expected-warning@+2 {{empty paragraph passed to '\brief' command}}
+/**
+ * \brief\author Aaa
+ * \param aaa Aaa
+ * \param bbb Bbb
+ */
++ (NSString *)test1:(NSString *)aaa suffix:(NSString *)bbb;
+
+// expected-warning@+2 {{parameter 'aab' not found in the function declaration}} expected-note@+2 {{did you mean 'aaa'?}}
+/**
+ * \param aab Aaa
+ */
++ (NSString *)test2:(NSString *)aaa;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@property int test3; // a property: ObjCPropertyDecl
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@property int test4; // a property: ObjCPropertyDecl
+@end
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@interface Test1()
+@end
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@implementation Test1 // a class implementation : ObjCImplementationDecl
++ (NSString *)test1:(NSString *)aaa suffix:(NSString *)bbb {
+ return 0;
+}
+
++ (NSString *)test2:(NSString *)aaa {
+ return 0;
+}
+
+@synthesize test3; // a property implementation: ObjCPropertyImplDecl
+@dynamic test4; // a property implementation: ObjCPropertyImplDecl
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+NSString *_test5;
+@end
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@interface Test1(Test1Category) // a category: ObjCCategoryDecl
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
++ (NSString *)test3:(NSString *)aaa;
+@end
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@implementation Test1(Test1Category) // a category implementation: ObjCCategoryImplDecl
++ (NSString *)test3:(NSString *)aaa {
+ return 0;
+}
+@end
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@protocol TestProto1 // a protocol: ObjCProtocolDecl
+@end
+
+int a;
+
+// expected-warning@+1 {{empty paragraph passed to '\brief' command}}
+/// \brief\author Aaa
+@interface Test4
+@end
+
+int b;
+
+@interface TestReturns1
+/// \returns Aaa
+- (int)test1:(NSString *)aaa;
+
+// expected-warning@+1 {{'\returns' command used in a comment that is attached to a method returning void}}
+/// \returns Aaa
+- (void)test2:(NSString *)aaa;
+@end
+
diff --git a/test/Sema/warn-outof-range-assign-enum.c b/test/Sema/warn-outof-range-assign-enum.c
new file mode 100644
index 0000000..2e79e66
--- /dev/null
+++ b/test/Sema/warn-outof-range-assign-enum.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wassign-enum %s
+// rdar://11824807
+
+typedef enum CCTestEnum
+{
+ One,
+ Two=4,
+ Three
+} CCTestEnum;
+
+CCTestEnum test = 50; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+CCTestEnum test1 = -50; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+
+CCTestEnum foo(CCTestEnum r) {
+ return 20; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+}
+
+enum Test2 { K_zero, K_one };
+enum Test2 test2(enum Test2 *t) {
+ *t = 20; // expected-warning {{integer constant not in range of enumerated type 'enum Test2'}}
+ return 10; // expected-warning {{integer constant not in range of enumerated type 'enum Test2'}}
+}
+
+int main() {
+ CCTestEnum test = 1; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+ test = 600; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+ foo(2); // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+ foo(-1); // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
+ foo(4);
+ foo(Two+1);
+}
+
diff --git a/test/Sema/warn-self-assign-field.mm b/test/Sema/warn-self-assign-field.mm
new file mode 100644
index 0000000..3ba8d62
--- /dev/null
+++ b/test/Sema/warn-self-assign-field.mm
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-objc-root-class -verify %s
+
+class S {
+ public:
+ int a_;
+ void s(int a) {
+ a_ = a_; // expected-warning {{assigning field to itself}}
+
+ // Don't really care about this one either way.
+ this->a_ = a_; // expected-warning {{assigning field to itself}}
+
+ a_ += a_; // Shouldn't warn.
+ }
+};
+
+void f0(S* s) {
+ // Would be nice to have, but not important.
+ s->a_ = s->a_;
+}
+
+void f1(S* s, S* t) {
+ // Shouldn't warn.
+ t->a_ = s->a_;
+}
+
+struct T {
+ S* s_;
+};
+
+void f2(T* t) {
+ // Would be nice to have, but even less important.
+ t->s_->a_ = t->s_->a_;
+}
+
+void f3(T* t, T* t2) {
+ // Shouldn't warn.
+ t2->s_->a_ = t->s_->a_;
+}
+
+void f4(int i) {
+ // This is a common pattern to silence "parameter unused". Shouldn't warn.
+ i = i;
+
+ int j = 0;
+ j = j; // Likewise.
+}
+
+@interface I {
+ int a_;
+}
+@end
+
+@implementation I
+- (void)setA:(int)a {
+ a_ = a_; // expected-warning {{assigning instance variable to itself}}
+}
+
+- (void)foo:(I*)i {
+ // Don't care much about this warning.
+ i->a_ = i->a_; // expected-warning {{assigning instance variable to itself}}
+
+ // Shouldn't warn.
+ a_ = i->a_;
+ i->a_ = a_;
+}
+@end
diff --git a/test/Sema/warn-strncat-size.c b/test/Sema/warn-strncat-size.c
index 7157edf..dcc3367 100644
--- a/test/Sema/warn-strncat-size.c
+++ b/test/Sema/warn-strncat-size.c
@@ -59,7 +59,7 @@ void size_1() {
char z[1];
char str[] = "hi";
- strncat(z, str, sizeof(z)); // expected-warning{{the value of the size argument in 'strncat' is too large, might lead to a buffer overflow}}
+ strncat(z, str, sizeof(z)); // expected-warning{{the value of the size argument to 'strncat' is wrong}}
}
// Support VLAs.
@@ -69,3 +69,8 @@ void vlas(int size) {
strncat(z, str, sizeof(str)); // expected-warning {{size argument in 'strncat' call appears to be size of the source}} expected-note {{change the argument to be the free space in the destination buffer minus the terminating null byte}}
}
+
+// Non-array type gets a different error message.
+void f(char* s, char* d) {
+ strncat(d, s, sizeof(d)); // expected-warning {{the value of the size argument to 'strncat' is wrong}}
+}
diff --git a/test/SemaCXX/MicrosoftCompatibility.cpp b/test/SemaCXX/MicrosoftCompatibility.cpp
index 3634fa3..6a48f36 100644
--- a/test/SemaCXX/MicrosoftCompatibility.cpp
+++ b/test/SemaCXX/MicrosoftCompatibility.cpp
@@ -1,6 +1,10 @@
// RUN: %clang_cc1 %s -triple i686-pc-win32 -fsyntax-only -std=c++11 -Wmicrosoft -verify -fms-compatibility -fexceptions -fcxx-exceptions
+typedef unsigned short char16_t;
+typedef unsigned int char32_t;
+
+typename decltype(3) a; // expected-warning {{expected a qualified name after 'typename'}}
namespace ms_conversion_rules {
diff --git a/test/SemaCXX/MicrosoftCompatibilityNoExceptions.cpp b/test/SemaCXX/MicrosoftCompatibilityNoExceptions.cpp
new file mode 100644
index 0000000..d932b5d
--- /dev/null
+++ b/test/SemaCXX/MicrosoftCompatibilityNoExceptions.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -fms-compatibility
+
+// PR13153
+namespace std {}
+class type_info {};
+void f() {
+ (void)typeid(int);
+}
diff --git a/test/SemaCXX/PR10243.cpp b/test/SemaCXX/PR10243.cpp
index 129ff80..19a8b04 100644
--- a/test/SemaCXX/PR10243.cpp
+++ b/test/SemaCXX/PR10243.cpp
@@ -9,12 +9,12 @@ struct T0 {
struct T1 {
S s; // expected-error{{field has incomplete type 'S'}}
- T1(T1&) = default;
+ T1(const T1&) = default;
};
struct T2 {
S s; // expected-error{{field has incomplete type 'S'}}
- T2& operator=(T2&) = default;
+ T2& operator=(const T2&) = default;
};
struct T3 {
diff --git a/test/SemaCXX/abstract.cpp b/test/SemaCXX/abstract.cpp
index b164d9e..e20a890 100644
--- a/test/SemaCXX/abstract.cpp
+++ b/test/SemaCXX/abstract.cpp
@@ -259,3 +259,17 @@ namespace pr9247 {
};
};
}
+
+namespace pr12658 {
+ class C {
+ public:
+ C(int v){}
+ virtual void f() = 0; // expected-note {{unimplemented pure virtual method 'f' in 'C'}}
+ };
+
+ void foo( C& c ) {}
+
+ void bar( void ) {
+ foo(C(99)); // expected-error {{allocating an object of abstract class type 'pr12658::C'}}
+ }
+}
diff --git a/test/SemaCXX/address-of-temporary.cpp b/test/SemaCXX/address-of-temporary.cpp
index eb5dee5..bb6cba3 100644
--- a/test/SemaCXX/address-of-temporary.cpp
+++ b/test/SemaCXX/address-of-temporary.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -Wno-error=address-of-temporary -verify %s
-struct X {
+// RUN: %clang_cc1 -fsyntax-only -Wno-error=address-of-temporary -verify -std=gnu++11 %s
+struct X {
X();
X(int);
X(int, int);
@@ -10,3 +10,39 @@ void f1() { (void)&X(1); } // expected-warning{{taking the address of a temporar
void f2() { (void)&X(1, 2); } // expected-warning{{taking the address of a temporary object}}
void f3() { (void)&(X)1; } // expected-warning{{taking the address of a temporary object}}
+
+namespace PointerToArrayDecay {
+ struct Y {
+ int a[4];
+ };
+
+ typedef int A[4];
+
+ template<typename T> void consume(T);
+ struct S { int *p; };
+
+ void g0() { int *p = Y().a; } // expected-warning{{pointer is initialized by a temporary array}}
+ void g1() { int *p = Y{}.a; } // expected-warning{{pointer is initialized by a temporary array}}
+ void g2() { int *p = A{}; } // expected-warning{{pointer is initialized by a temporary array}}
+ void g3() { int *p = (A){}; } // expected-warning{{pointer is initialized by a temporary array}}
+
+ void h0() { consume(Y().a); }
+ void h1() { consume(Y{}.a); }
+ void h2() { consume(A{}); }
+ void h3() { consume((A){}); }
+
+ void i0() { S s = { Y().a }; } // expected-warning{{pointer is initialized by a temporary array}}
+ void i1() { S s = { Y{}.a }; } // expected-warning{{pointer is initialized by a temporary array}}
+ void i2() { S s = { A{} }; } // expected-warning{{pointer is initialized by a temporary array}}
+ void i3() { S s = { (A){} }; } // expected-warning{{pointer is initialized by a temporary array}}
+
+ void j0() { (void)S { Y().a }; }
+ void j1() { (void)S { Y{}.a }; }
+ void j2() { (void)S { A{} }; }
+ void j3() { (void)S { (A){} }; }
+
+ void k0() { consume(S { Y().a }); }
+ void k1() { consume(S { Y{}.a }); }
+ void k2() { consume(S { A{} }); }
+ void k3() { consume(S { (A){} }); }
+}
diff --git a/test/SemaCXX/alias-template.cpp b/test/SemaCXX/alias-template.cpp
index 484dd33..4bf79f8 100644
--- a/test/SemaCXX/alias-template.cpp
+++ b/test/SemaCXX/alias-template.cpp
@@ -145,3 +145,30 @@ namespace Curried {
template<typename T, typename U> struct S;
template<typename T> template<typename U> using SS = S<T, U>; // expected-error {{extraneous template parameter list in alias template declaration}}
}
+
+// PR12647
+namespace SFINAE {
+ template<bool> struct enable_if; // expected-note 2{{here}}
+ template<> struct enable_if<true> { using type = void; };
+
+ template<typename T> struct is_enum { static constexpr bool value = __is_enum(T); };
+
+ template<typename T> using EnableIf = typename enable_if<T::value>::type; // expected-error {{undefined template}}
+ template<typename T> using DisableIf = typename enable_if<!T::value>::type; // expected-error {{undefined template}}
+
+ template<typename T> EnableIf<is_enum<T>> f();
+ template<typename T> DisableIf<is_enum<T>> f();
+
+ enum E { e };
+
+ int main() {
+ f<int>();
+ f<E>();
+ }
+
+ template<typename T, typename U = EnableIf<is_enum<T>>> struct fail1 {}; // expected-note {{here}}
+ template<typename T> struct fail2 : DisableIf<is_enum<T>> {}; // expected-note {{here}}
+
+ fail1<int> f1; // expected-note {{here}}
+ fail2<E> f2; // expected-note {{here}}
+}
diff --git a/test/SemaCXX/altivec.cpp b/test/SemaCXX/altivec.cpp
index 39421b7..9de1f04 100644
--- a/test/SemaCXX/altivec.cpp
+++ b/test/SemaCXX/altivec.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -faltivec -fno-lax-vector-conversions -triple powerpc-unknown-unknown -verify %s
+// RUN: %clang_cc1 -faltivec -fno-lax-vector-conversions -triple powerpc-unknown-unknown -fcxx-exceptions -verify %s
typedef int V4i __attribute__((vector_size(16)));
@@ -76,3 +76,8 @@ namespace LValueToRValueConversions {
vector float initFloat = (vector float)(Struct().f); // expected-error {{did you mean to call it}}
vector int initInt = (vector int)(Struct().n); // expected-error {{did you mean to call it}}
}
+
+void f() {
+ try {}
+ catch (vector pixel px) {}
+};
diff --git a/test/SemaCXX/anonymous-union-cxx11.cpp b/test/SemaCXX/anonymous-union-cxx11.cpp
new file mode 100644
index 0000000..8e682eb
--- /dev/null
+++ b/test/SemaCXX/anonymous-union-cxx11.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -pedantic %s
+
+namespace PR12866 {
+ struct bar {
+ union {
+ int member;
+ };
+ };
+
+ void foo( void ) {
+ (void)sizeof(bar::member);
+ }
+}
diff --git a/test/SemaCXX/array-bound-merge.cpp b/test/SemaCXX/array-bound-merge.cpp
index 74f58fa1..8fb2ec5 100644
--- a/test/SemaCXX/array-bound-merge.cpp
+++ b/test/SemaCXX/array-bound-merge.cpp
@@ -7,3 +7,5 @@ extern int b[10];
int b[];
extern int c[1];
int c[] = {1,2}; // expected-error {{excess elements in array initializer}}
+
+int d[1][]; // expected-error {{array has incomplete element type 'int []'}}
diff --git a/test/SemaCXX/arrow-operator.cpp b/test/SemaCXX/arrow-operator.cpp
index 6535a0a..173ff72 100644
--- a/test/SemaCXX/arrow-operator.cpp
+++ b/test/SemaCXX/arrow-operator.cpp
@@ -36,3 +36,31 @@ void f()
Line_Segment(node1->Location()); // expected-error {{not a structure or union}}
}
}
+
+
+namespace arrow_suggest {
+
+template <typename T>
+class wrapped_ptr {
+ public:
+ wrapped_ptr(T* ptr) : ptr_(ptr) {}
+ T* operator->() { return ptr_; }
+ void Check(); // expected-note {{'Check' declared here}}
+ private:
+ T *ptr_;
+};
+
+class Worker {
+ public:
+ void DoSomething();
+ void Chuck();
+};
+
+void test() {
+ wrapped_ptr<Worker> worker(new Worker);
+ worker.DoSomething(); // expected-error {{no member named 'DoSomething' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'; did you mean to use '->' instead of '.'?}}
+ worker.DoSamething(); // expected-error {{no member named 'DoSamething' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'}}
+ worker.Chuck(); // expected-error {{no member named 'Chuck' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'; did you mean 'Check'?}}
+}
+
+} // namespace arrow_suggest
diff --git a/test/SemaCXX/attr-deprecated.cpp b/test/SemaCXX/attr-deprecated.cpp
index 46568aa..f3d818a 100644
--- a/test/SemaCXX/attr-deprecated.cpp
+++ b/test/SemaCXX/attr-deprecated.cpp
@@ -1,10 +1,10 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
class A {
- void f() __attribute__((deprecated));
+ void f() __attribute__((deprecated)); // expected-note 2 {{declared here}}
void g(A* a);
void h(A* a) __attribute__((deprecated));
- int b __attribute__((deprecated));
+ int b __attribute__((deprecated)); // expected-note 2 {{declared here}}
};
void A::g(A* a)
@@ -26,7 +26,7 @@ void A::h(A* a)
}
struct B {
- virtual void f() __attribute__((deprecated));
+ virtual void f() __attribute__((deprecated)); // expected-note 4 {{declared here}}
void g();
};
@@ -68,20 +68,20 @@ void f(D* d) {
// Overloaded namespace members.
namespace test1 {
- void foo(int) __attribute__((deprecated));
+ void foo(int) __attribute__((deprecated)); // expected-note {{declared here}}
void test1() { foo(10); } // expected-warning {{deprecated}}
- void foo(short) __attribute__((deprecated));
+ void foo(short) __attribute__((deprecated)); // expected-note {{declared here}}
void test2(short s) { foo(s); } // expected-warning {{deprecated}}
void foo(long);
void test3(long l) { foo(l); }
struct A {
- friend void foo(A*) __attribute__((deprecated));
+ friend void foo(A*) __attribute__((deprecated)); // expected-note {{declared here}}
};
void test4(A *a) { foo(a); } // expected-warning {{deprecated}}
namespace ns {
struct Foo {};
- void foo(const Foo &f) __attribute__((deprecated));
+ void foo(const Foo &f) __attribute__((deprecated)); // expected-note {{declared here}}
}
void test5() {
foo(ns::Foo()); // expected-warning {{deprecated}}
@@ -91,9 +91,9 @@ namespace test1 {
// Overloaded class members.
namespace test2 {
struct A {
- void foo(int) __attribute__((deprecated));
+ void foo(int) __attribute__((deprecated)); // expected-note 2 {{declared here}}
void foo(long);
- static void bar(int) __attribute__((deprecated));
+ static void bar(int) __attribute__((deprecated)); // expected-note 3 {{declared here}}
static void bar(long);
void test2(int i, long l);
@@ -120,12 +120,12 @@ namespace test2 {
namespace test3 {
struct A {
void operator*(const A &);
- void operator*(int) __attribute__((deprecated));
+ void operator*(int) __attribute__((deprecated)); // expected-note {{declared here}}
void operator-(const A &) const;
};
void operator+(const A &, const A &);
- void operator+(const A &, int) __attribute__((deprecated));
- void operator-(const A &, int) __attribute__((deprecated));
+ void operator+(const A &, int) __attribute__((deprecated)); // expected-note {{declared here}}
+ void operator-(const A &, int) __attribute__((deprecated)); // expected-note {{declared here}}
void test() {
A a, b;
@@ -143,9 +143,9 @@ namespace test4 {
struct A {
typedef void (*intfn)(int);
typedef void (*unintfn)(unsigned);
- operator intfn() __attribute__((deprecated));
+ operator intfn() __attribute__((deprecated)); // expected-note {{declared here}}
operator unintfn();
- void operator ()(A &) __attribute__((deprecated));
+ void operator ()(A &) __attribute__((deprecated)); // expected-note {{declared here}}
void operator ()(const A &);
};
@@ -163,7 +163,7 @@ namespace test4 {
namespace test5 {
struct A {
- operator int() __attribute__((deprecated));
+ operator int() __attribute__((deprecated)); // expected-note 3 {{declared here}}
operator long();
};
void test1(A a) {
@@ -193,8 +193,8 @@ namespace test5 {
// rdar://problem/8518751
namespace test6 {
- enum __attribute__((deprecated)) A {
- a0
+ enum __attribute__((deprecated)) A { // expected-note {{declared here}}
+ a0 // expected-note {{declared here}}
};
void testA() {
A x; // expected-warning {{'A' is deprecated}}
@@ -202,7 +202,7 @@ namespace test6 {
}
enum B {
- b0 __attribute__((deprecated)),
+ b0 __attribute__((deprecated)), // expected-note {{declared here}}
b1
};
void testB() {
@@ -212,8 +212,8 @@ namespace test6 {
}
template <class T> struct C {
- enum __attribute__((deprecated)) Enum {
- c0
+ enum __attribute__((deprecated)) Enum { // expected-note {{declared here}}
+ c0 // expected-note {{declared here}}
};
};
void testC() {
@@ -224,7 +224,7 @@ namespace test6 {
template <class T> struct D {
enum Enum {
d0,
- d1 __attribute__((deprecated)),
+ d1 __attribute__((deprecated)), // expected-note {{declared here}}
};
};
void testD() {
diff --git a/test/SemaCXX/attr-visibility.cpp b/test/SemaCXX/attr-visibility.cpp
new file mode 100644
index 0000000..05aa5a3
--- /dev/null
+++ b/test/SemaCXX/attr-visibility.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <class Element>
+void foo() {
+}
+template <>
+ __attribute__((visibility("hidden"))) // expected-note {{previous attribute is here}}
+void foo<int>();
+
+template <>
+void foo<int>();
+
+template <>
+ __attribute__((visibility("default"))) // expected-error {{visibility does not match previous declaration}}
+void foo<int>() {
+}
+
+struct x3 {
+ static int y;
+} __attribute((visibility("default"))); // expected-warning {{attribute 'visibility' after definition is ignored}}
diff --git a/test/SemaCXX/bool.cpp b/test/SemaCXX/bool.cpp
index 2b3ab68..f027186 100644
--- a/test/SemaCXX/bool.cpp
+++ b/test/SemaCXX/bool.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-constant-conversion %s
// Bool literals can be enum values.
enum {
diff --git a/test/SemaCXX/condition.cpp b/test/SemaCXX/condition.cpp
index 993f8e1..ec5eb17 100644
--- a/test/SemaCXX/condition.cpp
+++ b/test/SemaCXX/condition.cpp
@@ -7,7 +7,7 @@ void test() {
typedef int arr[10];
while (arr x=0) ; // expected-error {{an array type is not allowed here}} expected-error {{array initializer must be an initializer list}}
- while (int f()=0) ; // expected-warning {{interpreted as a function declaration}} expected-note {{initializer}} expected-error {{a function type is not allowed here}}
+ while (int f()=0) ; // expected-error {{a function type is not allowed here}}
struct S {} s;
if (s) ++x; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}}
diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp
index 4aee913..a80eda4 100644
--- a/test/SemaCXX/conditional-expr.cpp
+++ b/test/SemaCXX/conditional-expr.cpp
@@ -2,7 +2,7 @@
// C++ rules for ?: are a lot stricter than C rules, and have to take into
// account more conversion options.
-// This test runs in C++0x mode for the contextual conversion of the condition.
+// This test runs in C++11 mode for the contextual conversion of the condition.
struct ToBool { explicit operator bool(); };
@@ -328,3 +328,27 @@ namespace PR9236 {
(void)(true ? (void*)0 : A()); // expected-error{{incompatible operand types}}
}
}
+
+namespace DR587 {
+ template<typename T>
+ const T *f(bool b) {
+ static T t1 = T();
+ static const T t2 = T();
+ return &(b ? t1 : t2);
+ }
+ struct S {};
+ template const int *f(bool);
+ template const S *f(bool);
+
+ extern bool b;
+ int i = 0;
+ const int ci = 0;
+ volatile int vi = 0;
+ const volatile int cvi = 0;
+
+ const int &cir = b ? i : ci;
+ volatile int &vir = b ? vi : i;
+ const volatile int &cvir1 = b ? ci : cvi;
+ const volatile int &cvir2 = b ? cvi : vi;
+ const volatile int &cvir3 = b ? ci : vi; // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
+}
diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp
index 9f80e71..a3ead79 100644
--- a/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i686-linux -Wno-string-plus-int -fsyntax-only -verify -std=c++11 -pedantic %s -Wno-comment
+// RUN: %clang_cc1 -triple i686-linux -Wno-string-plus-int -fsyntax-only -fcxx-exceptions -verify -std=c++11 -pedantic %s -Wno-comment
namespace StaticAssertFoldTest {
@@ -494,6 +494,27 @@ struct ArrayRVal {
};
static_assert(ArrayRVal().elems[3].f() == 0, "");
+constexpr int selfref[2][2][2] = {
+ selfref[1][1][1] + 1, selfref[0][0][0] + 1,
+ selfref[1][0][1] + 1, selfref[0][1][0] + 1,
+ selfref[1][0][0] + 1, selfref[0][1][1] + 1 };
+static_assert(selfref[0][0][0] == 1, "");
+static_assert(selfref[0][0][1] == 2, "");
+static_assert(selfref[0][1][0] == 1, "");
+static_assert(selfref[0][1][1] == 2, "");
+static_assert(selfref[1][0][0] == 1, "");
+static_assert(selfref[1][0][1] == 3, "");
+static_assert(selfref[1][1][0] == 0, "");
+static_assert(selfref[1][1][1] == 0, "");
+
+struct TrivialDefCtor { int n; };
+typedef TrivialDefCtor TDCArray[2][2];
+static_assert(TDCArray{}[1][1].n == 0, "");
+
+struct NonAggregateTDC : TrivialDefCtor {};
+typedef NonAggregateTDC NATDCArray[2][2];
+static_assert(NATDCArray{}[1][1].n == 0, "");
+
}
namespace DependentValues {
@@ -689,10 +710,16 @@ static_assert(&ok2 == pok2, "");
static_assert((Base2*)(Derived*)(Base*)pb1 == pok2, "");
static_assert((Derived*)(Base*)pb1 == (Derived*)pok2, "");
-constexpr Base *nullB = 42 - 6 * 7;
+constexpr Base *nullB = 42 - 6 * 7; // expected-warning {{expression which evaluates to zero treated as a null pointer constant of type 'Class::Base *const'}}
static_assert((Bottom*)nullB == 0, "");
static_assert((Derived*)nullB == 0, "");
static_assert((void*)(Bottom*)nullB == (void*)(Derived*)nullB, "");
+Base * nullB2 = '\0'; // expected-warning {{expression which evaluates to zero treated as a null pointer constant of type 'Class::Base *'}}
+Base * nullB3 = (0);
+// We suppress the warning in unevaluated contexts to workaround some gtest
+// behavior. Once this becomes an error this isn't a problem anymore.
+static_assert(nullB == (1 - 1), "");
+
namespace ConversionOperators {
@@ -1213,6 +1240,17 @@ namespace RecursiveOpaqueExpr {
constexpr int arr2[] = { 1, 0, 0, 3, 0, 2, 0, 4, 0, 5 };
static_assert(LastNonzero(begin(arr2), end(arr2)) == 5, "");
+
+ constexpr int arr3[] = {
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static_assert(LastNonzero(begin(arr3), end(arr3)) == 2, "");
}
namespace VLASizeof {
@@ -1248,3 +1286,91 @@ namespace Vector {
}
constexpr auto v2 = g(4);
}
+
+// PR12626, redux
+namespace InvalidClasses {
+ void test0() {
+ struct X; // expected-note {{forward declaration}}
+ struct Y { bool b; X x; }; // expected-error {{field has incomplete type}}
+ Y y;
+ auto& b = y.b;
+ }
+}
+
+// Constructors can be implicitly constexpr, even for a non-literal type.
+namespace ImplicitConstexpr {
+ struct Q { Q() = default; Q(const Q&) = default; Q(Q&&) = default; ~Q(); }; // expected-note 3{{here}}
+ struct R { constexpr R() noexcept; constexpr R(const R&) noexcept; constexpr R(R&&) noexcept; ~R() noexcept; };
+ struct S { R r; }; // expected-note 3{{here}}
+ struct T { T(const T&) noexcept; T(T &&) noexcept; ~T() noexcept; };
+ struct U { T t; }; // expected-note 3{{here}}
+ static_assert(!__is_literal_type(Q), "");
+ static_assert(!__is_literal_type(R), "");
+ static_assert(!__is_literal_type(S), "");
+ static_assert(!__is_literal_type(T), "");
+ static_assert(!__is_literal_type(U), "");
+ struct Test {
+ friend Q::Q() noexcept; // expected-error {{follows constexpr}}
+ friend Q::Q(Q&&) noexcept; // expected-error {{follows constexpr}}
+ friend Q::Q(const Q&) noexcept; // expected-error {{follows constexpr}}
+ friend S::S() noexcept; // expected-error {{follows constexpr}}
+ friend S::S(S&&) noexcept; // expected-error {{follows constexpr}}
+ friend S::S(const S&) noexcept; // expected-error {{follows constexpr}}
+ friend constexpr U::U() noexcept; // expected-error {{follows non-constexpr}}
+ friend constexpr U::U(U&&) noexcept; // expected-error {{follows non-constexpr}}
+ friend constexpr U::U(const U&) noexcept; // expected-error {{follows non-constexpr}}
+ };
+}
+
+// Indirectly test that an implicit lvalue to xvalue conversion performed for
+// an NRVO move operation isn't implemented as CK_LValueToRValue.
+namespace PR12826 {
+ struct Foo {};
+ constexpr Foo id(Foo x) { return x; }
+ constexpr Foo res(id(Foo()));
+}
+
+namespace PR13273 {
+ struct U {
+ int t;
+ U() = default;
+ };
+
+ struct S : U {
+ S() = default;
+ };
+
+ // S's default constructor isn't constexpr, because U's default constructor
+ // doesn't initialize 't', but it's trivial, so value-initialization doesn't
+ // actually call it.
+ static_assert(S{}.t == 0, "");
+}
+
+namespace PR12670 {
+ struct S {
+ constexpr S(int a0) : m(a0) {}
+ constexpr S() : m(6) {}
+ int m;
+ };
+ constexpr S x[3] = { {4}, 5 };
+ static_assert(x[0].m == 4, "");
+ static_assert(x[1].m == 5, "");
+ static_assert(x[2].m == 6, "");
+}
+
+// Indirectly test that an implicit lvalue-to-rvalue conversion is performed
+// when a conditional operator has one argument of type void and where the other
+// is a glvalue of class type.
+namespace ConditionalLValToRVal {
+ struct A {
+ constexpr A(int a) : v(a) {}
+ int v;
+ };
+
+ constexpr A f(const A &a) {
+ return a.v == 0 ? throw a : a;
+ }
+
+ constexpr A a(4);
+ static_assert(f(a).v == 4, "");
+}
diff --git a/test/SemaCXX/constant-expression.cpp b/test/SemaCXX/constant-expression.cpp
index 23a4dda..ec50cb7 100644
--- a/test/SemaCXX/constant-expression.cpp
+++ b/test/SemaCXX/constant-expression.cpp
@@ -115,5 +115,12 @@ int array2[recurse2]; // expected-warning {{variable length array}} expected-war
namespace FloatConvert {
typedef int a[(int)42.3];
typedef int a[(int)42.997];
- typedef int b[(int)4e10]; // expected-warning {{variable length}} expected-error {{variable length}}
+ typedef int b[(long long)4e20]; // expected-warning {{variable length}} expected-error {{variable length}} expected-warning {{'long long' is an extension}}
+}
+
+// PR12626
+namespace test3 {
+ struct X; // expected-note {{forward declaration of 'test3::X'}}
+ struct Y { bool b; X x; }; // expected-error {{field has incomplete type 'test3::X'}}
+ int f() { return Y().b; }
}
diff --git a/test/SemaCXX/constexpr-many-arguments.cpp b/test/SemaCXX/constexpr-many-arguments.cpp
new file mode 100644
index 0000000..3b5e974
--- /dev/null
+++ b/test/SemaCXX/constexpr-many-arguments.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s
+// PR13197
+
+struct type1
+{
+ constexpr type1(int a0) : my_data{a0} {}
+ int my_data[1];
+};
+
+struct type2
+{
+ typedef type1 T;
+ constexpr type2(T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09,
+ T a10, T a11, T a12, T a13, T a14, T a15, T a16, T a17, T a18, T a19,
+ T a20, T a21, T a22)
+ : my_data{a00, a01, a02, a03, a04, a05, a06, a07, a08, a09,
+ a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,
+ a20, a21, a22}
+ {}
+ type1 my_data[23];
+};
+
+struct type3
+{
+ constexpr type3(type2 a0, type2 a1) : my_data{a0, a1} {}
+ type2 my_data[2];
+};
+
+constexpr type3 g
+{
+ {
+ {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
+ {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
+ {0},{0},{0}
+ },
+ {
+ {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
+ {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
+ {0},{0},{0}
+ }
+};
+
diff --git a/test/SemaCXX/constructor-initializer.cpp b/test/SemaCXX/constructor-initializer.cpp
index e8b7f0b..f503d01 100644
--- a/test/SemaCXX/constructor-initializer.cpp
+++ b/test/SemaCXX/constructor-initializer.cpp
@@ -126,21 +126,24 @@ struct Q {
// A silly class used to demonstrate field-is-uninitialized in constructors with
// multiple params.
+int IntParam(int i) { return 0; };
class TwoInOne { public: TwoInOne(TwoInOne a, TwoInOne b) {} };
class InitializeUsingSelfTest {
bool A;
char* B;
int C;
TwoInOne D;
- InitializeUsingSelfTest(int E)
+ int E;
+ InitializeUsingSelfTest(int F)
: A(A), // expected-warning {{field is uninitialized when used here}}
B((((B)))), // expected-warning {{field is uninitialized when used here}}
C(A && InitializeUsingSelfTest::C), // expected-warning {{field is uninitialized when used here}}
D(D, // expected-warning {{field is uninitialized when used here}}
- D) {} // expected-warning {{field is uninitialized when used here}}
+ D), // expected-warning {{field is uninitialized when used here}}
+ E(IntParam(E)) {} // expected-warning {{field is uninitialized when used here}}
};
-int IntWrapper(int i) { return 0; };
+int IntWrapper(int &i) { return 0; };
class InitializeUsingSelfExceptions {
int A;
int B;
@@ -229,13 +232,13 @@ namespace PR7402 {
// <rdar://problem/8308215>: don't crash.
// Lots of questionable recovery here; errors can change.
namespace test3 {
- class A : public std::exception {}; // expected-error {{undeclared identifier}} expected-error {{expected class name}} expected-note 3 {{candidate}} expected-note {{passing argument}}
+ class A : public std::exception {}; // expected-error {{undeclared identifier}} expected-error {{expected class name}} expected-note 2 {{candidate}}
class B : public A {
public:
B(const String& s, int e=0) // expected-error {{unknown type name}}
: A(e), m_String(s) , m_ErrorStr(__null) {} // expected-error {{no matching constructor}} expected-error {{does not name}}
B(const B& e)
- : A(e), m_String(e.m_String), m_ErrorStr(__null) { // expected-error {{no viable conversion}} expected-error {{does not name}}
+ : A(e), m_String(e.m_String), m_ErrorStr(__null) { // expected-error {{does not name}} expected-error {{no member named 'm_String' in 'test3::B'}}
}
};
}
diff --git a/test/SemaCXX/conversion-function.cpp b/test/SemaCXX/conversion-function.cpp
index a7a1782..6fca050 100644
--- a/test/SemaCXX/conversion-function.cpp
+++ b/test/SemaCXX/conversion-function.cpp
@@ -392,3 +392,14 @@ namespace PR8800 {
A& a4 = (A&)c;
}
}
+
+namespace PR12712 {
+ struct A {};
+ struct B {
+ operator A();
+ operator A() const;
+ };
+ struct C : B {};
+
+ A f(const C c) { return c; }
+}
diff --git a/test/SemaCXX/conversion.cpp b/test/SemaCXX/conversion.cpp
index a64b187..ac235cc 100644
--- a/test/SemaCXX/conversion.cpp
+++ b/test/SemaCXX/conversion.cpp
@@ -65,19 +65,69 @@ void test3() {
int c = ((((NULL)))); // expected-warning {{implicit conversion of NULL constant to 'int'}}
int d;
d = ((((NULL)))); // expected-warning {{implicit conversion of NULL constant to 'int'}}
- bool bl = NULL; // FIXME: this should warn but we currently suppress a bunch of conversion-to-bool warnings including this one
+ bool bl = NULL; // expected-warning {{implicit conversion of NULL constant to 'bool'}}
char ch = NULL; // expected-warning {{implicit conversion of NULL constant to 'char'}}
unsigned char uch = NULL; // expected-warning {{implicit conversion of NULL constant to 'unsigned char'}}
short sh = NULL; // expected-warning {{implicit conversion of NULL constant to 'short'}}
+ double dbl = NULL; // expected-warning {{implicit conversion of NULL constant to 'double'}}
// Use FileCheck to ensure we don't get any unnecessary macro-expansion notes
// (that don't appear as 'real' notes & can't be seen/tested by -verify)
// CHECK-NOT: note:
- // CHECK: note: expanded from macro 'FNULL'
-#define FNULL NULL
- int a2 = FNULL; // expected-warning {{implicit conversion of NULL constant to 'int'}}
- // CHECK-NOT: note:
// CHECK: note: expanded from macro 'FINIT'
#define FINIT int a3 = NULL;
FINIT // expected-warning {{implicit conversion of NULL constant to 'int'}}
+
+ // we don't catch the case of #define FOO NULL ... int i = FOO; but that seems a bit narrow anyway
+ // and avoiding that helps us skip these cases:
+#define NULL_COND(cond) ((cond) ? &a : NULL)
+ bool bl2 = NULL_COND(true); // don't warn on NULL conversion through the conditional operator across a macro boundary
+ if (NULL_COND(true))
+ ;
+ while (NULL_COND(true))
+ ;
+ for (; NULL_COND(true); )
+ ;
+ do ;
+ while(NULL_COND(true));
+ int *ip = NULL;
+ int (*fp)() = NULL;
+ struct foo {
+ int n;
+ void func();
+ };
+ int foo::*datamem = NULL;
+ int (foo::*funmem)() = NULL;
+}
+
+namespace test4 {
+ // FIXME: We should warn for non-dependent args (only when the param type is also non-dependent) only once
+ // not once for the template + once for every instantiation
+ template<typename T>
+ void tmpl(char c = NULL, // expected-warning 4 {{implicit conversion of NULL constant to 'char'}}
+ T a = NULL, // expected-warning {{implicit conversion of NULL constant to 'char'}} \
+ expected-warning 2 {{implicit conversion of NULL constant to 'int'}}
+ T b = 1024) { // expected-warning {{implicit conversion from 'int' to 'char' changes value from 1024 to 0}}
+ }
+
+ template<typename T>
+ void tmpl2(T t = NULL) {
+ }
+
+ void func() {
+ tmpl<char>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<char>' required here}}
+ tmpl<int>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<int>' required here}}
+ // FIXME: We should warn only once for each template instantiation - not once for each call
+ tmpl<int>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<int>' required here}}
+ tmpl2<int*>();
+ }
+}
+
+namespace test5 {
+ template<int I>
+ void func() {
+ bool b = I;
+ }
+
+ template void func<3>();
}
diff --git a/test/SemaCXX/crashes.cpp b/test/SemaCXX/crashes.cpp
index b77248e..d02704c 100644
--- a/test/SemaCXX/crashes.cpp
+++ b/test/SemaCXX/crashes.cpp
@@ -104,3 +104,35 @@ namespace PR10270 {
return;
}
}
+
+namespace rdar11806334 {
+
+class cc_YCbCr;
+
+class cc_rgb
+{
+ public:
+ cc_rgb( uint p ); // expected-error {{unknown type name}}
+ cc_rgb( cc_YCbCr v_in );
+};
+
+class cc_hsl
+{
+ public:
+ cc_rgb rgb();
+ cc_YCbCr YCbCr();
+};
+
+class cc_YCbCr
+{
+ public:
+ cc_YCbCr( const cc_rgb v_in );
+};
+
+cc_YCbCr cc_hsl::YCbCr()
+{
+ cc_YCbCr v_out = cc_YCbCr( rgb());
+ return v_out;
+}
+
+}
diff --git a/test/SemaCXX/cxx0x-cursory-default-delete.cpp b/test/SemaCXX/cxx0x-cursory-default-delete.cpp
index 3290595..641760e 100644
--- a/test/SemaCXX/cxx0x-cursory-default-delete.cpp
+++ b/test/SemaCXX/cxx0x-cursory-default-delete.cpp
@@ -7,11 +7,14 @@ struct non_copiable {
};
struct non_const_copy {
- non_const_copy(non_const_copy&) = default; // expected-note {{not viable}}
- non_const_copy& operator = (non_const_copy&) & = default; // expected-note {{not viable}}
- non_const_copy& operator = (non_const_copy&) && = default; // expected-note {{not viable}}
+ non_const_copy(non_const_copy&);
+ non_const_copy& operator = (non_const_copy&) &;
+ non_const_copy& operator = (non_const_copy&) &&;
non_const_copy() = default; // expected-note {{not viable}}
};
+non_const_copy::non_const_copy(non_const_copy&) = default; // expected-note {{not viable}}
+non_const_copy& non_const_copy::operator = (non_const_copy&) & = default; // expected-note {{not viable}}
+non_const_copy& non_const_copy::operator = (non_const_copy&) && = default; // expected-note {{not viable}}
void fn1 () {
non_copiable nc;
@@ -21,7 +24,8 @@ void fn1 () {
non_const_copy ncc;
non_const_copy ncc2 = ncc;
ncc = ncc2;
- const non_const_copy cncc;
+ const non_const_copy cncc{};
+ const non_const_copy cncc1; // expected-error {{default initialization of an object of const type 'const non_const_copy' requires a user-provided default constructor}}
non_const_copy ncc3 = cncc; // expected-error {{no matching}}
ncc = cncc; // expected-error {{no viable overloaded}}
};
@@ -32,9 +36,9 @@ struct non_const_derived : non_const_copy {
};
struct bad_decls {
- bad_decls(volatile bad_decls&) = default; // expected-error {{may not be volatile}}
- bad_decls&& operator = (bad_decls) = default; // expected-error 2{{lvalue reference}}
- bad_decls& operator = (volatile bad_decls&) = default; // expected-error {{may not be volatile}}
+ bad_decls(volatile bad_decls&) = default; // expected-error {{may not be volatile}} expected-error {{must be defaulted outside the class}}
+ bad_decls&& operator = (bad_decls) = default; // expected-error {{lvalue reference}} expected-error {{must return 'bad_decls &'}}
+ bad_decls& operator = (volatile bad_decls&) = default; // expected-error {{may not be volatile}} expected-error {{must be defaulted outside the class}}
bad_decls& operator = (const bad_decls&) const = default; // expected-error {{may not have 'const', 'constexpr' or 'volatile' qualifiers}}
};
@@ -66,10 +70,9 @@ struct except_spec_d_mismatch : except_spec_a, except_spec_b {
};
struct except_spec_d_match : except_spec_a, except_spec_b {
except_spec_d_match() throw(A, B) = default;
-};
+};
// gcc-compatibility: allow attributes on default definitions
// (but not normal definitions)
struct S { S(); };
S::S() __attribute((pure)) = default;
-
diff --git a/test/SemaCXX/cxx0x-defaulted-functions.cpp b/test/SemaCXX/cxx0x-defaulted-functions.cpp
index 2e4107c..ce7ee67 100644
--- a/test/SemaCXX/cxx0x-defaulted-functions.cpp
+++ b/test/SemaCXX/cxx0x-defaulted-functions.cpp
@@ -6,26 +6,26 @@ struct foo {
foo() = default;
foo(const foo&) = default;
- foo(foo&) = default;
+ foo(foo&&) = default;
foo& operator = (const foo&) = default;
- foo& operator = (foo&) = default;
+ foo& operator = (foo&&) = default;
~foo() = default;
};
struct bar {
bar();
bar(const bar&);
- bar(bar&);
+ bar(bar&&);
bar& operator = (const bar&);
- bar& operator = (bar&);
+ bar& operator = (bar&&);
~bar();
};
bar::bar() = default;
bar::bar(const bar&) = default;
-bar::bar(bar&) = default;
+bar::bar(bar&&) = default;
bar& bar::operator = (const bar&) = default;
-bar& bar::operator = (bar&) = default;
+bar& bar::operator = (bar&&) = default;
bar::~bar() = default;
static_assert(__is_trivial(foo), "foo should be trivial");
@@ -51,3 +51,101 @@ template<typename T> struct S : T {
struct lit { constexpr lit() {} };
S<lit> s_lit; // ok
S<bar> s_bar; // ok
+
+struct Friends {
+ friend S<bar>::S();
+ friend S<bar>::S(const S&);
+ friend S<bar>::S(S&&);
+};
+
+namespace DefaultedFnExceptionSpec {
+ // DR1330: The exception-specification of an implicitly-declared special
+ // member function is evaluated as needed.
+ template<typename T> T &&declval();
+ template<typename T> struct pair {
+ pair(const pair&) noexcept(noexcept(T(declval<T>())));
+ };
+
+ struct Y;
+ struct X { X(); X(const Y&); };
+ struct Y { pair<X> p; };
+
+ template<typename T>
+ struct A {
+ pair<T> p;
+ };
+ struct B {
+ B();
+ B(const A<B>&);
+ };
+
+ // Don't crash here.
+ void f() {
+ X x = X();
+ (void)noexcept(B(declval<B>()));
+ }
+
+ template<typename T>
+ struct Error {
+ // FIXME: Type canonicalization causes all the errors to point at the first
+ // declaration which has the type 'void () noexcept (T::error)'. We should
+ // get one error for 'Error<int>::Error()' and one for 'Error<int>::~Error()'.
+ void f() noexcept(T::error); // expected-error 2{{has no members}}
+
+ Error() noexcept(T::error);
+ Error(const Error&) noexcept(T::error);
+ Error(Error&&) noexcept(T::error);
+ Error &operator=(const Error&) noexcept(T::error);
+ Error &operator=(Error&&) noexcept(T::error);
+ ~Error() noexcept(T::error);
+ };
+
+ struct DelayImplicit {
+ Error<int> e;
+ };
+
+ // Don't instantiate the exception specification here.
+ void test1(decltype(declval<DelayImplicit>() = DelayImplicit(DelayImplicit())));
+ void test2(decltype(declval<DelayImplicit>() = declval<const DelayImplicit>()));
+ void test3(decltype(DelayImplicit(declval<const DelayImplicit>())));
+
+ // Any odr-use causes the exception specification to be evaluated.
+ struct OdrUse { // \
+ expected-note {{instantiation of exception specification for 'Error'}} \
+ expected-note {{instantiation of exception specification for '~Error'}}
+ Error<int> e;
+ };
+ OdrUse use; // expected-note {{implicit default constructor for 'DefaultedFnExceptionSpec::OdrUse' first required here}}
+}
+
+namespace PR13527 {
+ struct X {
+ X() = delete; // expected-note {{here}}
+ X(const X&) = delete; // expected-note {{here}}
+ X(X&&) = delete; // expected-note {{here}}
+ X &operator=(const X&) = delete; // expected-note {{here}}
+ X &operator=(X&&) = delete; // expected-note {{here}}
+ ~X() = delete; // expected-note {{here}}
+ };
+ X::X() = default; // expected-error {{redefinition}}
+ X::X(const X&) = default; // expected-error {{redefinition}}
+ X::X(X&&) = default; // expected-error {{redefinition}}
+ X &X::operator=(const X&) = default; // expected-error {{redefinition}}
+ X &X::operator=(X&&) = default; // expected-error {{redefinition}}
+ X::~X() = default; // expected-error {{redefinition}}
+
+ struct Y {
+ Y() = default;
+ Y(const Y&) = default;
+ Y(Y&&) = default;
+ Y &operator=(const Y&) = default;
+ Y &operator=(Y&&) = default;
+ ~Y() = default;
+ };
+ Y::Y() = default; // expected-error {{definition of explicitly defaulted}}
+ Y::Y(const Y&) = default; // expected-error {{definition of explicitly defaulted}}
+ Y::Y(Y&&) = default; // expected-error {{definition of explicitly defaulted}}
+ Y &Y::operator=(const Y&) = default; // expected-error {{definition of explicitly defaulted}}
+ Y &Y::operator=(Y&&) = default; // expected-error {{definition of explicitly defaulted}}
+ Y::~Y() = default; // expected-error {{definition of explicitly defaulted}}
+}
diff --git a/test/SemaCXX/cxx0x-initializer-aggregates.cpp b/test/SemaCXX/cxx0x-initializer-aggregates.cpp
index 801a82f..c83058a 100644
--- a/test/SemaCXX/cxx0x-initializer-aggregates.cpp
+++ b/test/SemaCXX/cxx0x-initializer-aggregates.cpp
@@ -87,3 +87,32 @@ namespace array_explicit_conversion {
(void)test4{{{1}}}; // expected-note {{in instantiation of template class 'array_explicit_conversion::A<-1>' requested here}}
}
}
+
+namespace sub_constructor {
+ struct DefaultConstructor { // expected-note 2 {{not viable}}
+ DefaultConstructor(); // expected-note {{not viable}}
+ int x;
+ };
+ struct NoDefaultConstructor1 { // expected-note 2 {{not viable}}
+ NoDefaultConstructor1(int); // expected-note {{not viable}}
+ int x;
+ };
+ struct NoDefaultConstructor2 { // expected-note 4 {{not viable}}
+ NoDefaultConstructor2(int,int); // expected-note 2 {{not viable}}
+ int x;
+ };
+
+ struct Aggr {
+ DefaultConstructor a;
+ NoDefaultConstructor1 b;
+ NoDefaultConstructor2 c;
+ };
+
+ Aggr ok1 { {}, {0} , {0,0} };
+ Aggr ok2 = { {}, {0} , {0,0} };
+ Aggr too_many { {0} , {0} , {0,0} }; // expected-error {{no matching constructor for initialization}}
+ Aggr too_few { {} , {0} , {0} }; // expected-error {{no matching constructor for initialization}}
+ Aggr invalid { {} , {&ok1} , {0,0} }; // expected-error {{no matching constructor for initialization}}
+ NoDefaultConstructor2 array_ok[] = { {0,0} , {0,1} };
+ NoDefaultConstructor2 array_error[] = { {0,0} , {0} }; // expected-error {{no matching constructor for initialization}}
+} \ No newline at end of file
diff --git a/test/SemaCXX/cxx0x-initializer-constructor.cpp b/test/SemaCXX/cxx0x-initializer-constructor.cpp
index 09aca24..223e140 100644
--- a/test/SemaCXX/cxx0x-initializer-constructor.cpp
+++ b/test/SemaCXX/cxx0x-initializer-constructor.cpp
@@ -279,5 +279,28 @@ namespace PR12498 {
{
c->foo({ nullptr, 1 }); // expected-error{{initialization of incomplete type 'const PR12498::ArrayRef'}}
}
+}
+
+namespace explicit_default {
+ struct A {
+ explicit A(); // expected-note{{here}}
+ };
+ A a {}; // ok
+ // This is copy-list-initialization, and we choose an explicit constructor
+ // (even though we do so via value-initialization), so the initialization is
+ // ill-formed.
+ A b = {}; // expected-error{{chosen constructor is explicit}}
+}
+namespace init_list_default {
+ struct A {
+ A(std::initializer_list<int>);
+ };
+ A a {}; // calls initializer list constructor
+
+ struct B {
+ B();
+ B(std::initializer_list<int>) = delete;
+ };
+ B b {}; // calls default constructor
}
diff --git a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp
index 7384309..f11e19a 100644
--- a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp
+++ b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp
@@ -175,3 +175,15 @@ namespace PR12436 {
X x({}, 17);
}
+
+namespace rdar11948732 {
+ template<typename T> struct X {};
+
+ struct XCtorInit {
+ XCtorInit(std::initializer_list<X<int>>);
+ };
+
+ void f(X<int> &xi) {
+ XCtorInit xc = { xi, xi };
+ }
+}
diff --git a/test/SemaCXX/cxx98-compat-pedantic.cpp b/test/SemaCXX/cxx98-compat-pedantic.cpp
index 00532d5..c07f64e 100644
--- a/test/SemaCXX/cxx98-compat-pedantic.cpp
+++ b/test/SemaCXX/cxx98-compat-pedantic.cpp
@@ -9,7 +9,7 @@
#line 32768 // expected-warning {{#line number greater than 32767 is incompatible with C++98}}
#define VA_MACRO(x, ...) x // expected-warning {{variadic macros are incompatible with C++98}}
-VA_MACRO(,x) // expected-warning {{empty macro argument list is incompatible with C++98}}
+VA_MACRO(,x) // expected-warning {{empty macro arguments are incompatible with C++98}}
; // expected-warning {{extra ';' outside of a function is incompatible with C++98}}
diff --git a/test/SemaCXX/cxx98-compat.cpp b/test/SemaCXX/cxx98-compat.cpp
index 82a9dc8..37341f8 100644
--- a/test/SemaCXX/cxx98-compat.cpp
+++ b/test/SemaCXX/cxx98-compat.cpp
@@ -1,7 +1,15 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wc++98-compat -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s
-namespace std { struct type_info; }
+namespace std {
+ struct type_info;
+ using size_t = decltype(sizeof(0)); // expected-warning {{decltype}} expected-warning {{alias}}
+ template<typename T> struct initializer_list {
+ initializer_list(T*, size_t);
+ T *p;
+ size_t n;
+ };
+}
template<typename ...T> // expected-warning {{variadic templates are incompatible with C++98}}
class Variadic1 {};
@@ -39,6 +47,14 @@ void Lambda() {
[]{}(); // expected-warning {{lambda expressions are incompatible with C++98}}
}
+struct Ctor {
+ Ctor(int, char);
+ Ctor(double, long);
+};
+struct InitListCtor {
+ InitListCtor(std::initializer_list<bool>);
+};
+
int InitList(int i = {}) { // expected-warning {{generalized initializer lists are incompatible with C++98}} \
// expected-warning {{scalar initialized from empty initializer list is incompatible with C++98}}
(void)new int {}; // expected-warning {{generalized initializer lists are incompatible with C++98}} \
@@ -48,6 +64,14 @@ int InitList(int i = {}) { // expected-warning {{generalized initializer lists a
int x { 0 }; // expected-warning {{generalized initializer lists are incompatible with C++98}}
S<int> s = {}; // ok, aggregate
s = {}; // expected-warning {{generalized initializer lists are incompatible with C++98}}
+ std::initializer_list<int> xs = { 1, 2, 3 }; // expected-warning {{initialization of initializer_list object is incompatible with C++98}}
+ auto ys = { 1, 2, 3 }; // expected-warning {{initialization of initializer_list object is incompatible with C++98}} \
+ // expected-warning {{'auto' type specifier is incompatible with C++98}}
+ Ctor c1 = { 1, 2 }; // expected-warning {{constructor call from initializer list is incompatible with C++98}}
+ Ctor c2 = { 3.0, 4l }; // expected-warning {{constructor call from initializer list is incompatible with C++98}}
+ InitListCtor ilc = { true, false }; // expected-warning {{initialization of initializer_list object is incompatible with C++98}}
+ const int &r = { 0 }; // expected-warning {{reference initialized from initializer list is incompatible with C++98}}
+ struct { int a; const int &r; } rr = { 0, {{0}} }; // expected-warning {{reference initialized from initializer list is incompatible with C++98}}
return { 0 }; // expected-warning {{generalized initializer lists are incompatible with C++98}}
}
struct DelayedDefaultArgumentParseInitList {
@@ -303,3 +327,38 @@ namespace NonTypeTemplateArgs {
S<const int&, k> s1; // expected-warning {{non-type template argument referring to object 'k' with internal linkage is incompatible with C++98}}
S<void(&)(), f> s2; // expected-warning {{non-type template argument referring to function 'f' with internal linkage is incompatible with C++98}}
}
+
+namespace NullPointerTemplateArg {
+ struct A {};
+ template<int*> struct X {};
+ template<int A::*> struct Y {};
+ X<(int*)0> x; // expected-warning {{use of null pointer as non-type template argument is incompatible with C++98}}
+ Y<(int A::*)0> y; // expected-warning {{use of null pointer as non-type template argument is incompatible with C++98}}
+}
+
+namespace PR13480 {
+ struct basic_iterator {
+ basic_iterator(const basic_iterator &it) {}
+ basic_iterator(basic_iterator &it) {} // expected-note {{because type 'PR13480::basic_iterator' has a user-declared copy constructor}}
+ };
+
+ union test {
+ basic_iterator it; // expected-warning {{union member 'it' with a non-trivial copy constructor is incompatible with C++98}}
+ };
+}
+
+namespace AssignOpUnion {
+ struct a {
+ void operator=(const a &it) {}
+ void operator=(a &it) {} // expected-note {{because type 'AssignOpUnion::a' has a user-declared copy assignment operator}}
+ };
+
+ struct b {
+ void operator=(const b &it) {} // expected-note {{because type 'AssignOpUnion::b' has a user-declared copy assignment operator}}
+ };
+
+ union test1 {
+ a x; // expected-warning {{union member 'x' with a non-trivial copy assignment operator is incompatible with C++98}}
+ b y; // expected-warning {{union member 'y' with a non-trivial copy assignment operator is incompatible with C++98}}
+ };
+}
diff --git a/test/SemaCXX/dcl_ambig_res.cpp b/test/SemaCXX/dcl_ambig_res.cpp
index fa71b11..08867c0 100644
--- a/test/SemaCXX/dcl_ambig_res.cpp
+++ b/test/SemaCXX/dcl_ambig_res.cpp
@@ -10,9 +10,9 @@ int returns_an_int();
void foo(double a)
{
- S w(int(a)); // expected-warning{{disambiguated}}
+ S w(int(a)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}}
w(17);
- S x1(int()); // expected-warning{{disambiguated}}
+ S x1(int()); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}}
x1(&returns_an_int);
S y((int)a);
y.bar();
@@ -69,7 +69,7 @@ struct S5 {
static bool const value = false;
};
int foo8() {
- int v(int(S5::value)); // expected-warning{{disambiguated}} expected-error{{parameter declarator cannot be qualified}}
+ int v(int(S5::value)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}} expected-error{{parameter declarator cannot be qualified}}
}
template<typename T>
diff --git a/test/SemaCXX/dcl_init_aggr.cpp b/test/SemaCXX/dcl_init_aggr.cpp
index bd3de9e..8c5e654 100644
--- a/test/SemaCXX/dcl_init_aggr.cpp
+++ b/test/SemaCXX/dcl_init_aggr.cpp
@@ -15,7 +15,7 @@ struct NonAggregate {
};
NonAggregate non_aggregate_test = { 1, 2 }; // expected-error{{non-aggregate type 'NonAggregate' cannot be initialized with an initializer list}}
-NonAggregate non_aggregate_test2[2] = { { 1, 2 }, { 3, 4 } }; // expected-error 2 {{initialization of non-aggregate type 'NonAggregate' with an initializer list}}
+NonAggregate non_aggregate_test2[2] = { { 1, 2 }, { 3, 4 } }; // expected-error 2 {{non-aggregate type 'NonAggregate' cannot be initialized with an initializer list}}
// C++ [dcl.init.aggr]p3
diff --git a/test/SemaCXX/decl-expr-ambiguity.cpp b/test/SemaCXX/decl-expr-ambiguity.cpp
index 6f4d08c..0980c40 100644
--- a/test/SemaCXX/decl-expr-ambiguity.cpp
+++ b/test/SemaCXX/decl-expr-ambiguity.cpp
@@ -22,10 +22,10 @@ void f() {
(int())1; // expected-error {{C-style cast from 'int' to 'int ()' is not allowed}}
// Declarations.
- int fd(T(a)); // expected-warning {{parentheses were disambiguated as a function declarator}}
- T(*d)(int(p)); // expected-warning {{parentheses were disambiguated as a function declarator}} expected-note {{previous definition is here}}
- typedef T(*td)(int(p));
- extern T(*tp)(int(p));
+ int fd(T(a)); // expected-warning {{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}}
+ T(*d)(int(p)); // expected-note {{previous}}
+ typedef T td(int(p));
+ extern T tp(int(p));
T d3(); // expected-warning {{empty parentheses interpreted as a function declaration}} expected-note {{replace parentheses with an initializer}}
T d3v(void);
typedef T d3t();
@@ -49,6 +49,7 @@ struct RAII {
};
void func();
+void func2(short);
namespace N {
struct S;
@@ -59,6 +60,10 @@ namespace N {
S s(); // expected-warning {{function declaration}}
}
+ void nonEmptyParens() {
+ int f = 0, // g = 0; expected-note {{change this ',' to a ';' to call 'func2'}}
+ func2(short(f)); // expected-warning {{function declaration}} expected-note {{add a pair of parentheses}}
+ }
}
class C { };
@@ -70,3 +75,23 @@ void foo() {
fn(1); // expected-error {{no matching function}}
fn(g); // OK
}
+
+namespace PR11874 {
+void foo(); // expected-note 3 {{class 'foo' is hidden by a non-type declaration of 'foo' here}}
+class foo {};
+class bar {
+ bar() {
+ const foo* f1 = 0; // expected-error {{must use 'class' tag to refer to type 'foo' in this scope}}
+ foo* f2 = 0; // expected-error {{must use 'class' tag to refer to type 'foo' in this scope}}
+ foo f3; // expected-error {{must use 'class' tag to refer to type 'foo' in this scope}}
+ }
+};
+
+int baz; // expected-note 2 {{class 'baz' is hidden by a non-type declaration of 'baz' here}}
+class baz {};
+void fizbin() {
+ const baz* b1 = 0; // expected-error {{must use 'class' tag to refer to type 'baz' in this scope}}
+ baz* b2; // expected-error {{use of undeclared identifier 'b2'}}
+ baz b3; // expected-error {{must use 'class' tag to refer to type 'baz' in this scope}}
+}
+}
diff --git a/test/SemaCXX/default1.cpp b/test/SemaCXX/default1.cpp
index ae6ef97..c8c197e 100644
--- a/test/SemaCXX/default1.cpp
+++ b/test/SemaCXX/default1.cpp
@@ -47,6 +47,13 @@ int i () {
void j (int f = 4);
{
void j (int f); // expected-note{{'j' declared here}}
- j(); // expected-error{{too few arguments to function call, expected 1, have 0}}
+ j(); // expected-error{{too few arguments to function call, single argument 'f' was not specified}}
+ }
+}
+
+int i2() {
+ void j(int f = 4); // expected-note{{'j' declared here}}
+ {
+ j(2, 3); // expected-error{{too many arguments to function call, expected at most single argument 'f', have 2}}
}
}
diff --git a/test/SemaCXX/deleted-function.cpp b/test/SemaCXX/deleted-function.cpp
index d13fd0e..e78e7ed 100644
--- a/test/SemaCXX/deleted-function.cpp
+++ b/test/SemaCXX/deleted-function.cpp
@@ -55,3 +55,13 @@ struct Z : virtual DelDtor {
~Z() {} // expected-error {{attempt to use a deleted function}}
};
DelDtor dd; // expected-error {{attempt to use a deleted function}}
+
+template<typename> void test2() = delete;
+template void test2<int>();
+
+template<typename> void test3() = delete;
+template<typename> void test3();
+template void test3<int>();
+
+void test4() {} // expected-note {{previous definition is here}}
+void test4() = delete; // expected-error {{redefinition of 'test4'}}
diff --git a/test/SemaCXX/deleted-operator.cpp b/test/SemaCXX/deleted-operator.cpp
index 0e0282a..9f53e71 100644
--- a/test/SemaCXX/deleted-operator.cpp
+++ b/test/SemaCXX/deleted-operator.cpp
@@ -8,8 +8,8 @@ struct PR10757 {
int PR10757f() {
PR10757 a1;
// FIXME: We get a ridiculous number of "built-in candidate" notes here...
- if(~a1) {} // expected-error {{overload resolution selected deleted operator}} expected-note 6 {{built-in candidate}}
- if(a1==a1) {} // expected-error {{overload resolution selected deleted operator}} expected-note 81 {{built-in candidate}}
+ if(~a1) {} // expected-error {{overload resolution selected deleted operator}} expected-note 8 {{built-in candidate}}
+ if(a1==a1) {} // expected-error {{overload resolution selected deleted operator}} expected-note 121 {{built-in candidate}}
}
struct DelOpDel {
diff --git a/test/SemaCXX/elaborated-type-specifier.cpp b/test/SemaCXX/elaborated-type-specifier.cpp
index 760079f..1b1770a 100644
--- a/test/SemaCXX/elaborated-type-specifier.cpp
+++ b/test/SemaCXX/elaborated-type-specifier.cpp
@@ -19,7 +19,7 @@ bool test_elab(S1 *s1, struct S2 *s2, struct S3 *s3) {
namespace NS {
class X {
public:
- void test_elab2(struct S4 *s4);
+ void test_elab2(struct S4 *s4); // expected-note{{'NS::S4' declared here}}
};
void X::test_elab2(S4 *s4) { } // expected-note{{passing argument to parameter 's4' here}}
@@ -35,8 +35,7 @@ namespace NS {
}
void test_S5_scope() {
- S4 *s4; // expected-error{{use of undeclared identifier 'S4'}} \
- // expected-error{{use of undeclared identifier 's4'}}
+ S4 *s4; // expected-error{{unknown type name 'S4'; did you mean 'NS::S4'?}}
}
int test_funcparam_scope(struct S5 * s5) {
@@ -44,5 +43,3 @@ int test_funcparam_scope(struct S5 * s5) {
if (s5 == s5_2) return 1; // expected-error {{comparison of distinct pointer types ('struct S5 *' and 'struct S5 *')}}
return 0;
}
-
-
diff --git a/test/SemaCXX/enum-scoped.cpp b/test/SemaCXX/enum-scoped.cpp
index ebe9245..a1f911d 100644
--- a/test/SemaCXX/enum-scoped.cpp
+++ b/test/SemaCXX/enum-scoped.cpp
@@ -245,3 +245,10 @@ namespace test10 {
int m = g<int>();
int n = g<short>(); // expected-note {{here}}
}
+
+namespace pr13128 {
+ // This should compile cleanly
+ class C {
+ enum class E { C };
+ };
+}
diff --git a/test/SemaCXX/expressions.cpp b/test/SemaCXX/expressions.cpp
index 355833e..2635fb8 100644
--- a/test/SemaCXX/expressions.cpp
+++ b/test/SemaCXX/expressions.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-constant-conversion %s
void choice(int);
int choice(bool);
diff --git a/test/SemaCXX/format-strings-0x.cpp b/test/SemaCXX/format-strings-0x.cpp
index e7c5904..7b3aef1 100644
--- a/test/SemaCXX/format-strings-0x.cpp
+++ b/test/SemaCXX/format-strings-0x.cpp
@@ -12,4 +12,16 @@ void f(char **sp, float *fp) {
scanf("%afoobar", fp);
printf(nullptr);
printf(*sp); // expected-warning {{not a string literal}}
+
+ // PR13099
+ printf(
+ R"foobar(%)foobar"
+ R"bazquux(d)bazquux" // expected-warning {{more '%' conversions than data arguments}}
+ R"xyzzy()xyzzy");
+
+ printf(u8"this is %d test", 0); // ok
+ printf(u8R"foo(
+ \u1234\U0010fffe
+ %d)foo" // expected-warning {{more '%' conversions than data arguments}}
+ );
}
diff --git a/test/SemaCXX/function-extern-c.cpp b/test/SemaCXX/function-extern-c.cpp
index f20cd38..16dbbb2 100644
--- a/test/SemaCXX/function-extern-c.cpp
+++ b/test/SemaCXX/function-extern-c.cpp
@@ -36,3 +36,5 @@ extern "C" void f7( U u );
extern "C" double f8(void);
extern "C" long long f11( void );
extern "C" A *f10( void );
+
+extern "C" struct mypodstruct f12(); // expected-warning {{'f12' has C-linkage specified, but returns incomplete type 'struct mypodstruct' which could be incompatible with C}}
diff --git a/test/SemaCXX/function-redecl.cpp b/test/SemaCXX/function-redecl.cpp
index 0eb109d..b9d1f23 100644
--- a/test/SemaCXX/function-redecl.cpp
+++ b/test/SemaCXX/function-redecl.cpp
@@ -76,12 +76,9 @@ class Crash {
void GetCart(int count) const;
};
// This out-of-line definition was fine...
-void Crash::cart(int count) const {} // expected-error {{out-of-line definition of 'cart' does not match any declaration in 'Crash'}} \
- // expected-note {{'cart' declared here}} \
- // expected-note {{previous definition is here}}
+void Crash::cart(int count) const {} // expected-error {{out-of-line definition of 'cart' does not match any declaration in 'Crash'}}
// ...while this one crashed clang
-void Crash::chart(int count) const {} // expected-error {{out-of-line definition of 'chart' does not match any declaration in 'Crash'; did you mean 'cart'?}} \
- // expected-error {{redefinition of 'cart'}}
+void Crash::chart(int count) const {} // expected-error {{out-of-line definition of 'chart' does not match any declaration in 'Crash'}}
class TestConst {
public:
@@ -98,3 +95,24 @@ void TestConst::setit(int) const { // expected-error {{out-of-line definition of
struct J { int typo() const; };
int J::typo_() { return 3; } // expected-error {{out-of-line definition of 'typo_' does not match any declaration in 'J'}}
+
+// Ensure we correct the redecl of Foo::isGood to Bar::Foo::isGood and not
+// Foo::IsGood even though Foo::IsGood is technically a closer match since it
+// already has a body. Also make sure Foo::beEvil is corrected to Foo::BeEvil
+// since it is a closer match than Bar::Foo::beEvil and neither have a body.
+namespace redecl_typo {
+namespace Foo {
+ bool IsGood() { return false; }
+ void BeEvil(); // expected-note {{'BeEvil' declared here}}
+}
+namespace Bar {
+ namespace Foo {
+ bool isGood(); // expected-note {{'Bar::Foo::isGood' declared here}}
+ void beEvil();
+ }
+}
+bool Foo::isGood() { // expected-error {{out-of-line definition of 'isGood' does not match any declaration in namespace 'redecl_typo::Foo'; did you mean 'Bar::Foo::isGood'?}}
+ return true;
+}
+void Foo::beEvil() {} // expected-error {{out-of-line definition of 'beEvil' does not match any declaration in namespace 'redecl_typo::Foo'; did you mean 'BeEvil'?}}
+}
diff --git a/test/SemaCXX/implicit-exception-spec.cpp b/test/SemaCXX/implicit-exception-spec.cpp
index 25316f8..b29cff5 100644
--- a/test/SemaCXX/implicit-exception-spec.cpp
+++ b/test/SemaCXX/implicit-exception-spec.cpp
@@ -17,7 +17,7 @@ namespace InClassInitializers {
// is false.
bool ThrowSomething() noexcept(false);
struct ConstExpr {
- bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{exception specification is not available until end of class definition}}
+ bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{cannot be used by non-static data member initializer}}
};
// We can use it now.
bool w = noexcept(ConstExpr());
@@ -25,18 +25,27 @@ namespace InClassInitializers {
// Much more obviously broken: we can't parse the initializer without already
// knowing whether it produces a noexcept expression.
struct TemplateArg {
- int n = ExceptionIf<noexcept(TemplateArg())>::f(); // expected-error {{exception specification is not available until end of class definition}}
+ int n = ExceptionIf<noexcept(TemplateArg())>::f(); // expected-error {{cannot be used by non-static data member initializer}}
};
bool x = noexcept(TemplateArg());
// And within a nested class.
+ // FIXME: The diagnostic location is terrible here.
struct Nested {
struct Inner {
- int n = ExceptionIf<noexcept(Nested())>::f(); // expected-error {{exception specification is not available until end of class definition}}
- } inner;
+ int n = ExceptionIf<noexcept(Nested())>::f();
+ } inner; // expected-error {{cannot be used by non-static data member initializer}}
};
bool y = noexcept(Nested());
bool z = noexcept(Nested::Inner());
+
+ struct Nested2 {
+ struct Inner;
+ int n = Inner().n; // expected-error {{cannot be used by non-static data member initializer}}
+ struct Inner {
+ int n = ExceptionIf<noexcept(Nested())>::f();
+ } inner;
+ };
}
namespace ExceptionSpecification {
diff --git a/test/SemaCXX/invalid-member-expr.cpp b/test/SemaCXX/invalid-member-expr.cpp
index 287d9af..87da79a 100644
--- a/test/SemaCXX/invalid-member-expr.cpp
+++ b/test/SemaCXX/invalid-member-expr.cpp
@@ -37,3 +37,34 @@ namespace test3 {
string::iterator i = s.foo(); // expected-error {{no member named 'foo'}}
}
}
+
+
+// Make sure we don't crash.
+namespace rdar11293995 {
+
+struct Length {
+ explicit Length(PassRefPtr<CalculationValue>); // expected-error {{unknown type name}} \
+ expected-error {{expected ')'}} \
+ expected-note {{to match this '('}}
+};
+
+struct LengthSize {
+ Length m_width;
+ Length m_height;
+};
+
+enum EFillSizeType { Contain, Cover, SizeLength, SizeNone };
+
+struct FillSize {
+ EFillSizeType type;
+ LengthSize size;
+};
+
+class FillLayer {
+public:
+ void setSize(FillSize f) { m_sizeType = f.type;}
+private:
+ unsigned m_sizeType : 2;
+};
+
+}
diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp
index e91dee9..0fd6345 100644
--- a/test/SemaCXX/lambda-expressions.cpp
+++ b/test/SemaCXX/lambda-expressions.cpp
@@ -17,74 +17,71 @@ namespace ExplicitCapture {
[this](){(void)Member;};
[this]{[this]{};};
[]{[this]{};};// expected-error {{'this' cannot be implicitly captured in this context}}
- []{Overload(3);};
- []{Overload();}; // expected-error {{'this' cannot be implicitly captured in this context}}
+ []{Overload(3);};
+ []{Overload();}; // expected-error {{'this' cannot be implicitly captured in this context}}
[]{(void)typeid(Overload());};
- []{(void)typeid(Overload(.5f));};// expected-error {{'this' cannot be implicitly captured in this context}}
+ []{(void)typeid(Overload(.5f));};// expected-error {{'this' cannot be implicitly captured in this context}}
}
};
void f() {
- [this] () {}; // expected-error {{'this' cannot be captured in this context}}
+ [this] () {}; // expected-error {{'this' cannot be captured in this context}}
}
}
namespace ReturnDeduction {
void test() {
- [](){ return 1; };
- [](){ return 1; };
- [](){ return ({return 1; 1;}); };
- [](){ return ({return 'c'; 1;}); }; // expected-error {{must match previous return type}} \
- // expected-warning{{omitted result type}}
- []()->int{ return 'c'; return 1; };
+ [](){ return 1; };
+ [](){ return 1; };
+ [](){ return ({return 1; 1;}); };
+ [](){ return ({return 'c'; 1;}); }; // expected-error {{must match previous return type}}
+ []()->int{ return 'c'; return 1; };
[](){ return 'c'; return 1; }; // expected-error {{must match previous return type}}
- []() { return; return (void)0; };
- [](){ return 1; return 1; }; // expected-warning{{omitted result type}}
+ []() { return; return (void)0; };
+ [](){ return 1; return 1; };
}
}
namespace ImplicitCapture {
void test() {
int a = 0; // expected-note 5 {{declared}}
- []() { return a; }; // expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{begins here}}
- [&]() { return a; };
- [=]() { return a; };
- [=]() { int* b = &a; }; // expected-error {{cannot initialize a variable of type 'int *' with an rvalue of type 'const int *'}}
+ []() { return a; }; // expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{begins here}}
+ [&]() { return a; };
+ [=]() { return a; };
+ [=]() { int* b = &a; }; // expected-error {{cannot initialize a variable of type 'int *' with an rvalue of type 'const int *'}}
[=]() { return [&]() { return a; }; };
- []() { return [&]() { return a; }; }; // expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
- []() { return ^{ return a; }; };// expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
- []() { return [&a] { return a; }; }; // expected-error 2 {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note 2 {{lambda expression begins here}}
- [=]() { return [&a] { return a; }; }; //
+ []() { return [&]() { return a; }; }; // expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
+ []() { return ^{ return a; }; };// expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
+ []() { return [&a] { return a; }; }; // expected-error 2 {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note 2 {{lambda expression begins here}}
+ [=]() { return [&a] { return a; }; }; //
const int b = 2;
- []() { return b; };
+ []() { return b; };
union { // expected-note {{declared}}
int c;
float d;
};
d = 3;
- [=]() { return c; }; // expected-error {{unnamed variable cannot be implicitly captured in a lambda expression}}
+ [=]() { return c; }; // expected-error {{unnamed variable cannot be implicitly captured in a lambda expression}}
__block int e; // expected-note 3 {{declared}}
- [&]() { return e; }; // expected-error {{__block variable 'e' cannot be captured in a lambda expression}}
- [&e]() { return e; }; // expected-error 2 {{__block variable 'e' cannot be captured in a lambda expression}}
+ [&]() { return e; }; // expected-error {{__block variable 'e' cannot be captured in a lambda expression}}
+ [&e]() { return e; }; // expected-error 2 {{__block variable 'e' cannot be captured in a lambda expression}}
int f[10]; // expected-note {{declared}}
- [&]() { return f[2]; };
+ [&]() { return f[2]; };
(void) ^{ return []() { return f[2]; }; }; // expected-error {{variable 'f' cannot be implicitly captured in a lambda with no capture-default specified}} \
// expected-note{{lambda expression begins here}}
struct G { G(); G(G&); int a; }; // expected-note 6 {{not viable}}
G g;
- [=]() { const G* gg = &g; return gg->a; }; // expected-warning{{omitted result type}}
- [=]() { return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error {{no matching constructor for initialization of 'ImplicitCapture::G'}} \
- // expected-warning{{omitted result type}}
- (void)^{ return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error 2 {{no matching constructor for initialization of 'const ImplicitCapture::G'}} \
- // expected-warning{{omitted result type}}
+ [=]() { const G* gg = &g; return gg->a; };
+ [=]() { return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error {{no matching constructor for initialization of 'ImplicitCapture::G'}}
+ (void)^{ return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error 2 {{no matching constructor for initialization of 'const ImplicitCapture::G'}}
const int h = a; // expected-note {{declared}}
- []() { return h; }; // expected-error {{variable 'h' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
+ []() { return h; }; // expected-error {{variable 'h' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
// The exemption for variables which can appear in constant expressions
// applies only to objects (and not to references).
@@ -120,16 +117,16 @@ namespace NullPtr {
const int m = 0;
[=] {
- int &k = f(m); // a null pointer constant
+ int &k = f(m); // expected-warning{{expression which evaluates to zero treated as a null pointer constant of type 'int *'}}
} ();
[=] () -> bool {
- int &k = f(m); // a null pointer constant
+ int &k = f(m); // expected-warning{{expression which evaluates to zero treated as a null pointer constant of type 'int *'}}
return &m == 0;
} ();
[m] {
- int &k = f(m); // a null pointer constant
+ int &k = f(m); // expected-warning{{expression which evaluates to zero treated as a null pointer constant of type 'int *'}}
} ();
}
}
@@ -148,3 +145,79 @@ namespace ModifyingCapture {
};
}
}
+
+namespace VariadicPackExpansion {
+ template<typename T, typename U> using Fst = T;
+ template<typename...Ts> bool g(Fst<bool, Ts> ...bools);
+ template<typename...Ts> bool f(Ts &&...ts) {
+ return g<Ts...>([&ts] {
+ if (!ts)
+ return false;
+ --ts;
+ return true;
+ } () ...);
+ }
+ void h() {
+ int a = 5, b = 2, c = 3;
+ while (f(a, b, c)) {
+ }
+ }
+
+ struct sink {
+ template<typename...Ts> sink(Ts &&...) {}
+ };
+
+ template<typename...Ts> void local_class() {
+ sink {
+ [] (Ts t) {
+ struct S : Ts {
+ void f(Ts t) {
+ Ts &that = *this;
+ that = t;
+ }
+ Ts g() { return *this; };
+ };
+ S s;
+ s.f(t);
+ return s;
+ } (Ts()).g() ...
+ };
+ };
+ struct X {}; struct Y {};
+ template void local_class<X, Y>();
+
+ template<typename...Ts> void nested(Ts ...ts) {
+ f(
+ // Each expansion of this lambda implicitly captures all of 'ts', because
+ // the inner lambda also expands 'ts'.
+ [&] {
+ return ts + [&] { return f(ts...); } ();
+ } () ...
+ );
+ }
+ template void nested(int, int, int);
+
+ template<typename...Ts> void nested2(Ts ...ts) { // expected-note 2{{here}}
+ // Capture all 'ts', use only one.
+ f([&ts...] { return ts; } ()...);
+ // Capture each 'ts', use it.
+ f([&ts] { return ts; } ()...);
+ // Capture all 'ts', use all of them.
+ f([&ts...] { return (int)f(ts...); } ());
+ // Capture each 'ts', use all of them. Ill-formed. In more detail:
+ //
+ // We instantiate two lambdas here; the first captures ts$0, the second
+ // captures ts$1. Both of them reference both ts parameters, so both are
+ // ill-formed because ts can't be implicitly captured.
+ //
+ // FIXME: This diagnostic does not explain what's happening. We should
+ // specify which 'ts' we're referring to in its diagnostic name. We should
+ // also say which slice of the pack expansion is being performed in the
+ // instantiation backtrace.
+ f([&ts] { return (int)f(ts...); } ()...); // \
+ // expected-error 2{{'ts' cannot be implicitly captured}} \
+ // expected-note 2{{lambda expression begins here}}
+ }
+ template void nested2(int); // ok
+ template void nested2(int, int); // expected-note {{in instantiation of}}
+}
diff --git a/test/SemaCXX/literal-operators.cpp b/test/SemaCXX/literal-operators.cpp
index 7f68cd3..f4c5c35 100644
--- a/test/SemaCXX/literal-operators.cpp
+++ b/test/SemaCXX/literal-operators.cpp
@@ -41,3 +41,4 @@ void operator "" _cv_good (volatile const char *, const size_t); // expected-err
template <char...> void operator "" _good ();
// FIXME: Test some invalid decls that might crop up.
+template <typename...> void operator "" _invalid(); // expected-error {{parameter declaration for literal operator 'operator "" _invalid' is not valid}}
diff --git a/test/SemaCXX/long-virtual-inheritance-chain.cpp b/test/SemaCXX/long-virtual-inheritance-chain.cpp
new file mode 100644
index 0000000..8599597
--- /dev/null
+++ b/test/SemaCXX/long-virtual-inheritance-chain.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -fsyntax-only %s
+
+class test0 { virtual void f(); };
+class test1 : virtual test0 { virtual void f(); };
+class test2 : virtual test1 { virtual void f(); };
+class test3 : virtual test2 { virtual void f(); };
+class test4 : virtual test3 { virtual void f(); };
+class test5 : virtual test4 { virtual void f(); };
+class test6 : virtual test5 { virtual void f(); };
+class test7 : virtual test6 { virtual void f(); };
+class test8 : virtual test7 { virtual void f(); };
+class test9 : virtual test8 { virtual void f(); };
+class test10 : virtual test9 { virtual void f(); };
+class test11 : virtual test10 { virtual void f(); };
+class test12 : virtual test11 { virtual void f(); };
+class test13 : virtual test12 { virtual void f(); };
+class test14 : virtual test13 { virtual void f(); };
+class test15 : virtual test14 { virtual void f(); };
+class test16 : virtual test15 { virtual void f(); };
+class test17 : virtual test16 { virtual void f(); };
+class test18 : virtual test17 { virtual void f(); };
+class test19 : virtual test18 { virtual void f(); };
+class test20 : virtual test19 { virtual void f(); };
+class test21 : virtual test20 { virtual void f(); };
+class test22 : virtual test21 { virtual void f(); };
+class test23 : virtual test22 { virtual void f(); };
+class test24 : virtual test23 { virtual void f(); };
+class test25 : virtual test24 { virtual void f(); };
+class test26 : virtual test25 { virtual void f(); };
+class test27 : virtual test26 { virtual void f(); };
+class test28 : virtual test27 { virtual void f(); };
+class test29 : virtual test28 { virtual void f(); };
+class test30 : virtual test29 { virtual void f(); };
+class test31 : virtual test30 { virtual void f(); };
+class test32 : virtual test31 { virtual void f(); };
+class test33 : virtual test32 { virtual void f(); };
+class test34 : virtual test33 { virtual void f(); };
+class test35 : virtual test34 { virtual void f(); };
+class test36 : virtual test35 { virtual void f(); };
+class test37 : virtual test36 { virtual void f(); };
+class test38 : virtual test37 { virtual void f(); };
+class test39 : virtual test38 { virtual void f(); };
+class test40 : virtual test39 { virtual void f(); };
+class test41 : virtual test40 { virtual void f(); };
+class test42 : virtual test41 { virtual void f(); };
+class test43 : virtual test42 { virtual void f(); };
+class test44 : virtual test43 { virtual void f(); };
+class test45 : virtual test44 { virtual void f(); };
+class test46 : virtual test45 { virtual void f(); };
+class test47 : virtual test46 { virtual void f(); };
+class test48 : virtual test47 { virtual void f(); };
+class test49 : virtual test48 { virtual void f(); };
+class test50 : virtual test49 { virtual void f(); };
diff --git a/test/SemaCXX/member-expr.cpp b/test/SemaCXX/member-expr.cpp
index dbddd1c..763f9c7 100644
--- a/test/SemaCXX/member-expr.cpp
+++ b/test/SemaCXX/member-expr.cpp
@@ -157,3 +157,13 @@ namespace FuncInMemberExpr {
Vec fun3(int x = 0);
int test3() { return fun3.size(); } // expected-error {{base of member reference is a function; perhaps you meant to call it with no arguments}}
}
+
+namespace DotForSemiTypo {
+void f(int i) {
+ // If the programmer typo'd '.' for ';', make sure we point at the '.' rather
+ // than the "field name" (whatever the first token on the next line happens to
+ // be).
+ int j = i. // expected-error {{member reference base type 'int' is not a structure or union}}
+ j = 0;
+}
+}
diff --git a/test/SemaCXX/member-init.cpp b/test/SemaCXX/member-init.cpp
index c93c85b..a13941f 100644
--- a/test/SemaCXX/member-init.cpp
+++ b/test/SemaCXX/member-init.cpp
@@ -14,7 +14,7 @@ public:
bool b();
int k;
struct Recurse {
- int &n = b() ? Recurse().n : k; // ok
+ int &n = b() ? Recurse().n : k; // expected-error {{defaulted default constructor of 'Recurse' cannot be used by non-static data member initializer which appears before end of class definition}}
};
struct UnknownBound {
@@ -28,7 +28,7 @@ template<> struct T<2> { template<int C, int D> using B = int; };
const int C = 0, D = 0;
struct S {
int as[] = { decltype(x)::B<C, D>(0) }; // expected-error {{array bound cannot be deduced from an in-class initializer}}
- T<sizeof(as) / sizeof(int)> x; // expected-error {{requires a type specifier}}
+ T<sizeof(as) / sizeof(int)> x;
// test that we handle invalid array bound deductions without crashing when the declarator name is itself invalid
operator int[](){}; // expected-error {{'operator int' cannot be the name of a variable or data member}} \
// expected-error {{array bound cannot be deduced from an in-class initializer}}
diff --git a/test/SemaCXX/member-operator-expr.cpp b/test/SemaCXX/member-operator-expr.cpp
index ae5f8bb..c98ef73 100644
--- a/test/SemaCXX/member-operator-expr.cpp
+++ b/test/SemaCXX/member-operator-expr.cpp
@@ -27,3 +27,8 @@ void test2() {
x->operator float(); // expected-error{{no member named 'operator float'}}
x->operator; // expected-error{{expected a type}}
}
+
+namespace pr13157 {
+ class A { public: void operator()(int x, int y = 2, ...) {} };
+ void f() { A()(1); }
+} \ No newline at end of file
diff --git a/test/SemaCXX/microsoft-cxx0x.cpp b/test/SemaCXX/microsoft-cxx0x.cpp
index 3b9bbef..79bd7c3 100644
--- a/test/SemaCXX/microsoft-cxx0x.cpp
+++ b/test/SemaCXX/microsoft-cxx0x.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -triple i686-pc-win32 -fsyntax-only -Wc++11-narrowing -Wmicrosoft -verify -fms-extensions -std=c++11
+// RUN: %clang_cc1 %s -triple i686-pc-win32 -fsyntax-only -Wc++11-narrowing -Wmicrosoft -verify -fms-extensions -std=c++11 -fms-compatibility -DMS_COMPAT
struct A {
@@ -6,3 +7,16 @@ struct A {
};
int b = 3;
A var = { b }; // expected-warning {{ cannot be narrowed }} expected-note {{override}}
+
+
+namespace PR13433 {
+ struct S;
+ S make();
+
+ template<typename F> auto x(F f) -> decltype(f(make()));
+#ifndef MS_COMPAT
+// expected-error@-2{{calling 'make' with incomplete return type 'PR13433::S'}}
+// expected-note@-5{{'make' declared here}}
+// expected-note@-7{{forward declaration of 'PR13433::S'}}
+#endif
+}
diff --git a/test/SemaCXX/neon-vector-types.cpp b/test/SemaCXX/neon-vector-types.cpp
index aa82b11..336fcd4 100644
--- a/test/SemaCXX/neon-vector-types.cpp
+++ b/test/SemaCXX/neon-vector-types.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify "-triple" "thumbv7-apple-ios3.0.0" %s
// rdar://9208404
typedef int MP4Err;
@@ -25,3 +25,20 @@ MP4Err autoCorrelation2nd_Neon(Float32 *alphar, Float32 *alphai,
return 0;
}
+namespace rdar11688587 {
+ typedef float float32_t;
+ typedef __attribute__((neon_vector_type(4))) float32_t float32x4_t;
+
+ template<int I>
+ float test()
+ {
+ extern float32x4_t vec;
+ return __extension__ ({
+ float32x4_t __a = (vec);
+ (float32_t)__builtin_neon_vgetq_lane_f32(__a, I); // expected-error{{argument should be a value from 0 to 3}}
+ });
+ }
+
+ template float test<1>();
+ template float test<4>(); // expected-note{{in instantiation of function template specialization 'rdar11688587::test<4>' requested here}}
+}
diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp
index b317634..4e1abc5 100644
--- a/test/SemaCXX/nested-name-spec.cpp
+++ b/test/SemaCXX/nested-name-spec.cpp
@@ -143,7 +143,7 @@ namespace A {
void g(int&); // expected-note{{type of 1st parameter of member declaration does not match definition ('int &' vs 'const int &')}}
}
-void A::f() {} // expected-error{{out-of-line definition of 'f' does not match any declaration in namespace 'A'}}
+void A::f() {} // expected-error-re{{out-of-line definition of 'f' does not match any declaration in namespace 'A'$}}
void A::g(const int&) { } // expected-error{{out-of-line definition of 'g' does not match any declaration in namespace 'A'}}
@@ -286,3 +286,15 @@ protected:
template <typename T>
struct A2<T>::B::C; // expected-error {{no struct named 'C'}}
}
+
+namespace PR13033 {
+namespace NS {
+ int a; // expected-note {{'NS::a' declared here}}
+ int longer_b; //expected-note {{'NS::longer_b' declared here}}
+}
+
+// Suggest adding a namespace qualifier to both variable names even though one
+// is only a single character long.
+int foobar = a + longer_b; // expected-error {{use of undeclared identifier 'a'; did you mean 'NS::a'?}} \
+ // expected-error {{use of undeclared identifier 'longer_b'; did you mean 'NS::longer_b'?}}
+}
diff --git a/test/SemaCXX/no-rtti.cpp b/test/SemaCXX/no-rtti.cpp
new file mode 100644
index 0000000..7516705
--- /dev/null
+++ b/test/SemaCXX/no-rtti.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fno-rtti %s
+
+namespace std {
+ class type_info;
+}
+
+void f()
+{
+ (void)typeid(int); // expected-error {{cannot use typeid with -fno-rtti}}
+}
diff --git a/test/SemaCXX/nullptr.cpp b/test/SemaCXX/nullptr.cpp
index e313603..d148f76 100644
--- a/test/SemaCXX/nullptr.cpp
+++ b/test/SemaCXX/nullptr.cpp
@@ -33,8 +33,10 @@ nullptr_t f(nullptr_t null)
// Operators
(void)(null == nullptr);
(void)(null <= nullptr);
+ (void)(null == 0);
(void)(null == (void*)0);
(void)((void*)0 == nullptr);
+ (void)(null <= 0);
(void)(null <= (void*)0);
(void)((void*)0 <= nullptr);
(void)(0 == nullptr);
@@ -44,7 +46,7 @@ nullptr_t f(nullptr_t null)
(void)(1 > nullptr); // expected-error {{invalid operands to binary expression}}
(void)(1 != nullptr); // expected-error {{invalid operands to binary expression}}
(void)(1 + nullptr); // expected-error {{invalid operands to binary expression}}
- (void)(0 ? nullptr : 0); // expected-error {{non-pointer operand type 'int' incompatible with nullptr}}
+ (void)(0 ? nullptr : 0);
(void)(0 ? nullptr : (void*)0);
(void)(0 ? nullptr : A()); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
(void)(0 ? A() : nullptr); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
diff --git a/test/SemaCXX/offsetof-0x.cpp b/test/SemaCXX/offsetof-0x.cpp
new file mode 100644
index 0000000..610d919
--- /dev/null
+++ b/test/SemaCXX/offsetof-0x.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -fsyntax-only -std=c++11 -verify %s -Winvalid-offsetof
+
+struct NonPOD {
+ virtual void f();
+ int m;
+};
+
+struct P {
+ NonPOD fieldThatPointsToANonPODType;
+};
+
+void f() {
+ int i = __builtin_offsetof(P, fieldThatPointsToANonPODType.m); // expected-warning{{offset of on non-standard-layout type 'P'}}
+}
+
+struct StandardLayout {
+ int x;
+ StandardLayout() {}
+};
+int o = __builtin_offsetof(StandardLayout, x); // no-warning
diff --git a/test/SemaCXX/overload-call.cpp b/test/SemaCXX/overload-call.cpp
index b5e1214..615b10a 100644
--- a/test/SemaCXX/overload-call.cpp
+++ b/test/SemaCXX/overload-call.cpp
@@ -233,7 +233,7 @@ float* intref(const int&);
void intref_test() {
float* ir1 = intref(5);
- float* ir2 = intref(5.5); // expected-warning{{implicit conversion turns literal floating-point number into integer}}
+ float* ir2 = intref(5.5); // expected-warning{{implicit conversion from 'double' to 'int' changes value from 5.5 to 5}}
}
void derived5(C&); // expected-note{{candidate function not viable: cannot bind base class object of type 'A' to derived class reference 'C &' for 1st argument}}
@@ -260,14 +260,12 @@ struct Z : X, Y { };
int& cvqual_subsume(X&); // expected-note{{candidate function}}
float& cvqual_subsume(const Y&); // expected-note{{candidate function}}
-int& cvqual_subsume2(const X&); // expected-note{{candidate function}}
-float& cvqual_subsume2(const volatile Y&); // expected-note{{candidate function}}
-
-Z get_Z();
+int& cvqual_subsume2(X&); // expected-note{{candidate function}}
+float& cvqual_subsume2(volatile Y&); // expected-note{{candidate function}}
void cvqual_subsume_test(Z z) {
cvqual_subsume(z); // expected-error{{call to 'cvqual_subsume' is ambiguous}}
- int& x = cvqual_subsume2(get_Z()); // expected-error{{call to 'cvqual_subsume2' is ambiguous}}
+ cvqual_subsume2(z); // expected-error{{call to 'cvqual_subsume2' is ambiguous}}
}
// Test overloading with cv-qualification differences in reference
@@ -319,14 +317,20 @@ namespace PR5756 {
namespace test1 {
template <class T> void foo(T t, unsigned N); // expected-note {{candidate function [with T = int] not viable: no known conversion from 'const char [6]' to 'unsigned int' for 2nd argument}}
void foo(int n, char N); // expected-note {{candidate function not viable: no known conversion from 'const char [6]' to 'char' for 2nd argument}}
- void foo(int n); // expected-note {{candidate function not viable: requires 1 argument, but 2 were provided}}
- void foo(unsigned n = 10); // expected-note {{candidate function not viable: requires at most 1 argument, but 2 were provided}}
void foo(int n, const char *s, int t); // expected-note {{candidate function not viable: requires 3 arguments, but 2 were provided}}
void foo(int n, const char *s, int t, ...); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
void foo(int n, const char *s, int t, int u = 0); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
+ // PR 11857
+ void foo(int n); // expected-note {{candidate function not viable: requires single argument 'n', but 2 arguments were provided}}
+ void foo(unsigned n = 10); // expected-note {{candidate function not viable: allows at most single argument 'n', but 2 arguments were provided}}
+ void bar(int n, int u = 0); // expected-note {{candidate function not viable: requires at least argument 'n', but no arguments were provided}}
+ void baz(int n = 0, int u = 0); // expected-note {{candidate function not viable: requires at most 2 arguments, but 3 were provided}}
+
void test() {
foo(4, "hello"); //expected-error {{no matching function for call to 'foo'}}
+ bar(); //expected-error {{no matching function for call to 'bar'}}
+ baz(3, 4, 5); // expected-error {{no matching function for call to 'baz'}}
}
}
@@ -438,10 +442,10 @@ namespace PR6078 {
namespace PR6177 {
struct String { String(char const*); };
- void f(bool const volatile&); // expected-note{{passing argument to parameter here}}
- void f(String);
+ void f(bool const volatile&);
+ int &f(String);
- void g() { f(""); } // expected-error{{volatile lvalue reference to type 'const volatile bool' cannot bind to a value of unrelated type 'const char [1]'}}
+ void g() { int &r = f(""); }
}
namespace PR7095 {
@@ -568,3 +572,11 @@ namespace PR12142 {
void fun(int (*x)[10]); // expected-note{{candidate function not viable: 1st argument ('const int (*)[10]') would lose const qualifier}}
void g() { fun((const int(*)[10])0); } // expected-error{{no matching function for call to 'fun'}}
}
+
+// DR1152: Take 'volatile' into account when handling reference bindings in
+// overload resolution.
+namespace PR12931 {
+ void f(const int &, ...);
+ void f(const volatile int &, int);
+ void g() { f(0, 0); }
+}
diff --git a/test/SemaCXX/overload-member-call.cpp b/test/SemaCXX/overload-member-call.cpp
index 37c9552..0958620 100644
--- a/test/SemaCXX/overload-member-call.cpp
+++ b/test/SemaCXX/overload-member-call.cpp
@@ -72,8 +72,6 @@ namespace test1 {
class A {
template <class T> void foo(T t, unsigned N); // expected-note {{candidate function [with T = int] not viable: no known conversion from 'const char [6]' to 'unsigned int' for 2nd argument}}
void foo(int n, char N); // expected-note {{candidate function not viable: no known conversion from 'const char [6]' to 'char' for 2nd argument}}
- void foo(int n); // expected-note {{candidate function not viable: requires 1 argument, but 2 were provided}}
- void foo(unsigned n = 10); // expected-note {{candidate function not viable: requires at most 1 argument, but 2 were provided}}
void foo(int n, const char *s, int t); // expected-note {{candidate function not viable: requires 3 arguments, but 2 were provided}}
void foo(int n, const char *s, int t, ...); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
void foo(int n, const char *s, int t, int u = 0); // expected-note {{candidate function not viable: requires at least 3 arguments, but 2 were provided}}
@@ -83,6 +81,14 @@ namespace test1 {
void baz(A &d); // expected-note {{candidate function not viable: 1st argument ('const test1::A') would lose const qualifier}}
void baz(int i); // expected-note {{candidate function not viable: no known conversion from 'const test1::A' to 'int' for 1st argument}}
+
+ // PR 11857
+ void foo(int n); // expected-note {{candidate function not viable: requires single argument 'n', but 2 arguments were provided}}
+ void foo(unsigned n = 10); // expected-note {{candidate function not viable: allows at most single argument 'n', but 2 arguments were provided}}
+ void rab(double n, int u = 0); // expected-note {{candidate function not viable: requires at least argument 'n', but no arguments were provided}}
+ void rab(int n, int u = 0); // expected-note {{candidate function not viable: requires at least argument 'n', but no arguments were provided}}
+ void zab(double n = 0.0, int u = 0); // expected-note {{candidate function not viable: requires at most 2 arguments, but 3 were provided}}
+ void zab(int n = 0, int u = 0); // expected-note {{candidate function not viable: requires at most 2 arguments, but 3 were provided}}
};
void test() {
@@ -93,6 +99,9 @@ namespace test1 {
b.bar(0); //expected-error {{no matching member function for call to 'bar'}}
a.baz(b); //expected-error {{no matching member function for call to 'baz'}}
+
+ a.rab(); //expected-error {{no matching member function for call to 'rab'}}
+ a.zab(3, 4, 5); //expected-error {{no matching member function for call to 'zab'}}
}
}
diff --git a/test/SemaCXX/overloaded-builtin-operators.cpp b/test/SemaCXX/overloaded-builtin-operators.cpp
index b3c0808..ac110a3 100644
--- a/test/SemaCXX/overloaded-builtin-operators.cpp
+++ b/test/SemaCXX/overloaded-builtin-operators.cpp
@@ -174,7 +174,7 @@ void test_dr425(A a) {
// FIXME: lots of candidates here!
(void)(1.0f * a); // expected-error{{ambiguous}} \
// expected-note 4{{candidate}} \
- // expected-note {{remaining 77 candidates omitted; pass -fshow-overloads=all to show them}}
+ // expected-note {{remaining 117 candidates omitted; pass -fshow-overloads=all to show them}}
}
// pr5432
@@ -237,3 +237,34 @@ namespace PR7851 {
(void)(x - x);
}
}
+
+namespace PR12854 {
+ enum { size = 1 };
+ void plus_equals() {
+ int* __restrict py;
+ py += size;
+ }
+
+ struct RestrictInt {
+ operator int* __restrict &();
+ };
+
+ void user_conversions(RestrictInt ri) {
+ ++ri;
+ --ri;
+ ri++;
+ ri--;
+ }
+}
+
+namespace PR12964 {
+ struct X { operator __int128() const; } x;
+ bool a = x == __int128(0);
+ bool b = x == 0;
+
+ struct Y { operator unsigned __int128() const; } y;
+ bool c = y == __int128(0);
+ bool d = y == 0;
+
+ bool e = x == y;
+}
diff --git a/test/SemaCXX/pr13353.cpp b/test/SemaCXX/pr13353.cpp
new file mode 100644
index 0000000..8fb5443
--- /dev/null
+++ b/test/SemaCXX/pr13353.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only %s
+struct foo {
+ virtual void bar() ;
+};
+template<typename T>
+class zed : public foo {
+};
+template<typename T>
+class bah : public zed<T> {
+ void f() {
+ const_cast<foo *>(this->g())->bar();
+ }
+};
diff --git a/test/SemaCXX/pr13394-crash-on-invalid.cpp b/test/SemaCXX/pr13394-crash-on-invalid.cpp
new file mode 100644
index 0000000..413c52a
--- /dev/null
+++ b/test/SemaCXX/pr13394-crash-on-invalid.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// Don't crash (PR13394).
+
+namespace stretch_v1 {
+ struct closure_t {
+ const stretch_v1::ops_t* d_methods; // expected-error {{no type named 'ops_t' in namespace 'stretch_v1'}}
+ };
+}
+namespace gatekeeper_v1 {
+ namespace gatekeeper_factory_v1 {
+ struct closure_t { // expected-note {{'closure_t' declared here}}
+ gatekeeper_v1::closure_t* create(); // expected-error {{no type named 'closure_t' in namespace 'gatekeeper_v1'; did you mean 'closure_t'?}}
+ };
+ }
+ gatekeeper_v1::closure_t *x; // expected-error {{no type named 'closure_t' in namespace 'gatekeeper_v1}}
+}
diff --git a/test/SemaCXX/printf-block.cpp b/test/SemaCXX/printf-block.cpp
new file mode 100644
index 0000000..4a58003
--- /dev/null
+++ b/test/SemaCXX/printf-block.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Wformat -verify %s -Wno-error=non-pod-varargs
+
+int (^block) (int, const char *,...) __attribute__((__format__(__printf__,2,3))) = ^ __attribute__((__format__(__printf__,2,3))) (int arg, const char *format,...) {return 5;};
+
+class HasNoCStr {
+ const char *str;
+ public:
+ HasNoCStr(const char *s): str(s) { }
+ const char *not_c_str() {return str;}
+};
+
+void test_block() {
+ const char str[] = "test";
+ HasNoCStr hncs(str);
+ int n = 4;
+ block(n, "%s %d", str, n); // no-warning
+ block(n, "%s %s", hncs, n); // expected-warning{{cannot pass non-POD object of type 'HasNoCStr' to variadic block; expected type from format string was 'char *'}} expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+}
diff --git a/test/SemaCXX/printf-cstr.cpp b/test/SemaCXX/printf-cstr.cpp
new file mode 100644
index 0000000..a7eeb06
--- /dev/null
+++ b/test/SemaCXX/printf-cstr.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -fsyntax-only -Wformat -verify %s -Wno-error=non-pod-varargs
+
+#include <stdarg.h>
+
+extern "C" {
+extern int printf(const char *restrict, ...);
+extern int sprintf(char *, const char *restrict, ...);
+}
+
+class HasCStr {
+ const char *str;
+ public:
+ HasCStr(const char *s): str(s) { }
+ const char *c_str() {return str;}
+};
+
+class HasNoCStr {
+ const char *str;
+ public:
+ HasNoCStr(const char *s): str(s) { }
+ const char *not_c_str() {return str;}
+};
+
+extern const char extstr[16];
+void pod_test() {
+ char str[] = "test";
+ char dest[32];
+ char formatString[] = "non-const %s %s";
+ HasCStr hcs(str);
+ HasNoCStr hncs(str);
+ int n = 10;
+
+ printf("%d: %s\n", n, hcs.c_str());
+ printf("%d: %s\n", n, hcs); // expected-warning{{cannot pass non-POD object of type 'HasCStr' to variadic function; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}}
+ printf("%d: %s\n", n, hncs); // expected-warning{{cannot pass non-POD object of type 'HasNoCStr' to variadic function; expected type from format string was 'char *'}}
+ sprintf(str, "%d: %s", n, hcs); // expected-warning{{cannot pass non-POD object of type 'HasCStr' to variadic function; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}}
+
+ printf(formatString, hcs, hncs); // expected-warning{{cannot pass object of non-POD type 'HasCStr' through variadic function}} expected-warning{{cannot pass object of non-POD type 'HasNoCStr' through variadic function}}
+ printf(extstr, hcs, n); // expected-warning{{cannot pass object of non-POD type 'HasCStr' through variadic function}}
+}
+
+struct Printf {
+ Printf();
+ Printf(const Printf&);
+ Printf(const char *,...) __attribute__((__format__(__printf__,2,3)));
+};
+
+void constructor_test() {
+ const char str[] = "test";
+ HasCStr hcs(str);
+ Printf p("%s %d %s", str, 10, 10); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}}
+ Printf q("%s %d", hcs, 10); // expected-warning {{cannot pass non-POD object of type 'HasCStr' to variadic constructor; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}}
+}
diff --git a/test/SemaCXX/qualified-id-lookup.cpp b/test/SemaCXX/qualified-id-lookup.cpp
index d65a468..a14193c 100644
--- a/test/SemaCXX/qualified-id-lookup.cpp
+++ b/test/SemaCXX/qualified-id-lookup.cpp
@@ -86,14 +86,15 @@ namespace a {
namespace a {
namespace a { // A1
namespace a { // A2
- int i;
+ int i; // expected-note{{'::a::a::a::i' declared here}}
}
}
}
void test_a() {
- a::a::i = 3; // expected-error{{no member named 'i'}}
+ a::a::i = 3; // expected-error{{no member named 'i' in namespace 'a::a'; did you mean '::a::a::a::i'?}}
a::a::a::i = 4;
+ a::a::j = 3; // expected-error-re{{no member named 'j' in namespace 'a::a'$}}
}
struct Undef { // expected-note{{definition of 'Undef' is not complete until the closing '}'}}
diff --git a/test/SemaCXX/reinterpret-cast.cpp b/test/SemaCXX/reinterpret-cast.cpp
index 7f41b93..a4bc432 100644
--- a/test/SemaCXX/reinterpret-cast.cpp
+++ b/test/SemaCXX/reinterpret-cast.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -ffreestanding -Wundefined-reinterpret-cast %s
+// RUN: %clang_cc1 -fsyntax-only -verify -ffreestanding -Wundefined-reinterpret-cast -Wno-unused-volatile-lvalue %s
#include <stdint.h>
diff --git a/test/SemaCXX/static-assert.cpp b/test/SemaCXX/static-assert.cpp
index 364e4e4..4a7560b 100644
--- a/test/SemaCXX/static-assert.cpp
+++ b/test/SemaCXX/static-assert.cpp
@@ -34,3 +34,17 @@ static_assert(false, u"\U000317FF"); // expected-error {{static_assert failed u"
static_assert(false, u8"Ω"); // expected-error {{static_assert failed u8"\316\251"}}
static_assert(false, L"\u1234"); // expected-error {{static_assert failed L"\x1234"}}
static_assert(false, L"\x1ff" "0\x123" "fx\xfffff" "goop"); // expected-error {{static_assert failed L"\x1FF""0\x123""fx\xFFFFFgoop"}}
+
+template<typename T> struct AlwaysFails {
+ // Only give one error here.
+ static_assert(false, ""); // expected-error {{static_assert failed}}
+};
+AlwaysFails<int> alwaysFails;
+
+template<typename T> struct StaticAssertProtected {
+ static_assert(__is_literal(T), ""); // expected-error {{static_assert failed}}
+ static constexpr T t = {}; // no error here
+};
+struct X { ~X(); };
+StaticAssertProtected<int> sap1;
+StaticAssertProtected<X> sap2; // expected-note {{instantiation}}
diff --git a/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp b/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp
new file mode 100644
index 0000000..14ffcef
--- /dev/null
+++ b/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 -Wimplicit-fallthrough %s
+
+
+int fallthrough(int n) {
+ switch (n / 10) {
+ if (n - 1) {
+ n = 100;
+ } else if (n - 2) {
+ n = 101;
+ } else if (n - 3) {
+ n = 102;
+ }
+ case -1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ ;
+ case 0: {// expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ }
+ case 1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ n += 100 ;
+ case 3: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ if (n > 0)
+ n += 200;
+ case 4: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ if (n < 0)
+ ;
+ case 5: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ switch (n) {
+ case 111:
+ break;
+ case 112:
+ break;
+ case 113:
+ break ;
+ }
+ case 6: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ n += 300;
+ }
+ switch (n / 30) {
+ case 11:
+ case 12: // no warning here, intended fall-through, no statement between labels
+ n += 1600;
+ }
+ switch (n / 40) {
+ case 13:
+ if (n % 2 == 0) {
+ return 1;
+ } else {
+ return 2;
+ }
+ case 15: // no warning here, there's no fall-through
+ n += 3200;
+ }
+ switch (n / 50) {
+ case 17: {
+ if (n % 2 == 0) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+ case 19: { // no warning here, there's no fall-through
+ n += 6400;
+ return 3;
+ }
+ case 21: { // no warning here, there's no fall-through
+ break;
+ }
+ case 23: // no warning here, there's no fall-through
+ n += 128000;
+ break;
+ case 25: // no warning here, there's no fall-through
+ break;
+ }
+
+ return n;
+}
+
+class ClassWithDtor {
+public:
+ ~ClassWithDtor() {}
+};
+
+void fallthrough2(int n) {
+ switch (n) {
+ case 0:
+ {
+ ClassWithDtor temp;
+ break;
+ }
+ default: // no warning here, there's no fall-through
+ break;
+ }
+}
+
+#define MY_SWITCH(X, Y, Z, U, V) switch (X) { case Y: Z; case U: V; }
+#define MY_SWITCH2(X, Y, Z) switch (X) { Y; Z; }
+#define MY_CASE(X, Y) case X: Y
+#define MY_CASE2(X, Y, U, V) case X: Y; case U: V
+
+int fallthrough_macro1(int n) {
+ MY_SWITCH(n, 13, n *= 2, 14, break) // expected-warning{{unannotated fall-through between switch labels}}
+
+ switch (n + 1) {
+ MY_CASE(33, n += 2);
+ MY_CASE(44, break); // expected-warning{{unannotated fall-through between switch labels}}
+ MY_CASE(55, n += 3);
+ }
+
+ switch (n + 3) {
+ MY_CASE(333, return 333);
+ MY_CASE2(444, n += 44, 4444, break); // expected-warning{{unannotated fall-through between switch labels}}
+ MY_CASE(555, n += 33);
+ }
+
+ MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break)) // expected-warning{{unannotated fall-through between switch labels}}
+
+ MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break)) // expected-warning{{unannotated fall-through between switch labels}}
+
+ return n;
+}
diff --git a/test/SemaCXX/switch-implicit-fallthrough-per-method.cpp b/test/SemaCXX/switch-implicit-fallthrough-per-method.cpp
new file mode 100644
index 0000000..7c52e51
--- /dev/null
+++ b/test/SemaCXX/switch-implicit-fallthrough-per-method.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wimplicit-fallthrough-per-function %s
+
+
+int fallthrough(int n) {
+ switch (n / 10) {
+ case 0:
+ n += 100;
+ case 1: // expected-warning{{unannotated fall-through}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ switch (n) {
+ case 111:
+ n += 111;
+ [[clang::fallthrough]];
+ case 112:
+ n += 112;
+ case 113: // expected-warning{{unannotated fall-through}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ n += 113;
+ break ;
+ }
+ }
+ return n;
+}
+
+int fallthrough2(int n) {
+ switch (n / 10) {
+ case 0:
+ n += 100;
+ case 1: // no warning, as we didn't "opt-in" for it in this method
+ switch (n) {
+ case 111:
+ n += 111;
+ case 112: // no warning, as we didn't "opt-in" for it in this method
+ n += 112;
+ case 113: // no warning, as we didn't "opt-in" for it in this method
+ n += 113;
+ break ;
+ }
+ }
+ return n;
+}
+
+void unscoped(int n) {
+ switch (n % 2) {
+ case 0:
+ // FIXME: This should be typo-corrected, probably.
+ [[fallthrough]];
+ case 2: // expected-warning{{unannotated fall-through}} expected-note{{clang::fallthrough}} expected-note{{break;}}
+ [[clang::fallthrough]];
+ case 1:
+ break;
+ }
+}
diff --git a/test/SemaCXX/switch-implicit-fallthrough.cpp b/test/SemaCXX/switch-implicit-fallthrough.cpp
new file mode 100644
index 0000000..cfc29c2
--- /dev/null
+++ b/test/SemaCXX/switch-implicit-fallthrough.cpp
@@ -0,0 +1,197 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wimplicit-fallthrough %s
+
+
+int fallthrough(int n) {
+ switch (n / 10) {
+ if (n - 1) {
+ n = 100;
+ } else if (n - 2) {
+ n = 101;
+ } else if (n - 3) {
+ n = 102;
+ }
+ case -1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ ;
+ case 0: {// expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ }
+ case 1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ n += 100 ;
+ case 3: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ if (n > 0)
+ n += 200;
+ case 4: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ if (n < 0)
+ ;
+ case 5: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ switch (n) {
+ case 111:
+ break;
+ case 112:
+ break;
+ case 113:
+ break ;
+ }
+ case 6: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ n += 300;
+ case 66: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}}
+ break;
+ }
+ switch (n / 20) {
+ case 7:
+ n += 400;
+ [[clang::fallthrough]];
+ case 9: // no warning here, intended fall-through marked with an attribute
+ n += 800;
+ [[clang::fallthrough]];
+ default: { // no warning here, intended fall-through marked with an attribute
+ if (n % 2 == 0) {
+ return 1;
+ } else {
+ [[clang::fallthrough]];
+ }
+ }
+ case 10: // no warning here, intended fall-through marked with an attribute
+ if (n % 3 == 0) {
+ n %= 3;
+ } else {
+ [[clang::fallthrough]];
+ }
+ case 110: // expected-warning{{unannotated fall-through between switch labels}} but no fix-it hint as we have one fall-through annotation!
+ n += 800;
+ }
+ switch (n / 30) {
+ case 11:
+ case 12: // no warning here, intended fall-through, no statement between labels
+ n += 1600;
+ }
+ switch (n / 40) {
+ case 13:
+ if (n % 2 == 0) {
+ return 1;
+ } else {
+ return 2;
+ }
+ case 15: // no warning here, there's no fall-through
+ n += 3200;
+ }
+ switch (n / 50) {
+ case 17: {
+ if (n % 2 == 0) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+ case 19: { // no warning here, there's no fall-through
+ n += 6400;
+ return 3;
+ }
+ case 21: { // no warning here, there's no fall-through
+ break;
+ }
+ case 23: // no warning here, there's no fall-through
+ n += 128000;
+ break;
+ case 25: // no warning here, there's no fall-through
+ break;
+ }
+
+ return n;
+}
+
+class ClassWithDtor {
+public:
+ ~ClassWithDtor() {}
+};
+
+void fallthrough2(int n) {
+ switch (n) {
+ case 0:
+ {
+ ClassWithDtor temp;
+ break;
+ }
+ default: // no warning here, there's no fall-through
+ break;
+ }
+}
+
+#define MY_SWITCH(X, Y, Z, U, V) switch (X) { case Y: Z; case U: V; }
+#define MY_SWITCH2(X, Y, Z) switch (X) { Y; Z; }
+#define MY_CASE(X, Y) case X: Y
+#define MY_CASE2(X, Y, U, V) case X: Y; case U: V
+
+int fallthrough_macro1(int n) {
+ MY_SWITCH(n, 13, n *= 2, 14, break) // expected-warning{{unannotated fall-through between switch labels}}
+
+ switch (n + 1) {
+ MY_CASE(33, n += 2);
+ MY_CASE(44, break); // expected-warning{{unannotated fall-through between switch labels}}
+ MY_CASE(55, n += 3);
+ }
+
+ switch (n + 3) {
+ MY_CASE(333, return 333);
+ MY_CASE2(444, n += 44, 4444, break); // expected-warning{{unannotated fall-through between switch labels}}
+ MY_CASE(555, n += 33);
+ }
+
+ MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break)) // expected-warning{{unannotated fall-through between switch labels}}
+
+ MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break)) // expected-warning{{unannotated fall-through between switch labels}}
+
+ return n;
+}
+
+int fallthrough_position(int n) {
+ switch (n) {
+ [[clang::fallthrough]]; // expected-warning{{fallthrough annotation in unreachable code}}
+ case 221:
+ [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}}
+ return 1;
+ [[clang::fallthrough]]; // expected-warning{{fallthrough annotation in unreachable code}}
+ case 222:
+ [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}}
+ n += 400;
+ case 223: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}}
+ [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}}
+ }
+
+ // TODO: uncomment this test after CFG gets more options to deal with
+ // unreachable code:
+ // http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20120507/057370.html
+#if 0
+ long p = static_cast<long>(n) * n;
+ switch (sizeof(p)) {
+ case 9: // this test will not work on compilers with 72-bit long
+ n += static_cast<int>(p >> 32);
+ [[clang::fallthrough]]; // no warning here
+ case 5: // it is not intended to work on compilers with 40-bit long as well
+ n += static_cast<int>(p);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ return n;
+}
+
+int fallthrough_targets(int n) {
+ [[clang::fallthrough]]; // expected-error{{fallthrough annotation is outside switch statement}}
+
+ [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}}
+ switch (n) {
+ case 121:
+ n += 400;
+ [[clang::fallthrough]]; // no warning here, correct target
+ case 123:
+ [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}}
+ n += 800;
+ break;
+ [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} expected-note{{did you forget ';'?}}
+ case 125:
+ n += 1600;
+ }
+ return n;
+}
diff --git a/test/SemaCXX/type-traits.cpp b/test/SemaCXX/type-traits.cpp
index f53939a..54294bc 100644
--- a/test/SemaCXX/type-traits.cpp
+++ b/test/SemaCXX/type-traits.cpp
@@ -131,25 +131,25 @@ void is_pod()
{ int arr[T(__is_pod(HasAnonymousUnion))]; }
{ int arr[T(__is_pod(Vector))]; }
{ int arr[T(__is_pod(VectorExt))]; }
+ { int arr[T(__is_pod(Derives))]; }
+ { int arr[T(__is_pod(DerivesAr))]; }
+ { int arr[T(__is_pod(DerivesArNB))]; }
+ { int arr[T(__is_pod(DerivesEmpty))]; }
+ { int arr[T(__is_pod(HasPriv))]; }
+ { int arr[T(__is_pod(HasProt))]; }
+ { int arr[T(__is_pod(DerivesHasPriv))]; }
+ { int arr[T(__is_pod(DerivesHasProt))]; }
- { int arr[F(__is_pod(Derives))]; }
- { int arr[F(__is_pod(DerivesAr))]; }
- { int arr[F(__is_pod(DerivesArNB))]; }
- { int arr[F(__is_pod(DerivesEmpty))]; }
{ int arr[F(__is_pod(HasCons))]; }
{ int arr[F(__is_pod(HasCopyAssign))]; }
{ int arr[F(__is_pod(HasMoveAssign))]; }
{ int arr[F(__is_pod(HasDest))]; }
- { int arr[F(__is_pod(HasPriv))]; }
- { int arr[F(__is_pod(HasProt))]; }
{ int arr[F(__is_pod(HasRef))]; }
{ int arr[F(__is_pod(HasVirt))]; }
{ int arr[F(__is_pod(DerivesHasCons))]; }
{ int arr[F(__is_pod(DerivesHasCopyAssign))]; }
{ int arr[F(__is_pod(DerivesHasMoveAssign))]; }
{ int arr[F(__is_pod(DerivesHasDest))]; }
- { int arr[F(__is_pod(DerivesHasPriv))]; }
- { int arr[F(__is_pod(DerivesHasProt))]; }
{ int arr[F(__is_pod(DerivesHasRef))]; }
{ int arr[F(__is_pod(DerivesHasVirt))]; }
{ int arr[F(__is_pod(NonPOD))]; }
@@ -1223,10 +1223,10 @@ void has_trivial_copy_constructor() {
{ int arr[T(__has_trivial_copy(const Int))]; }
{ int arr[T(__has_trivial_copy(AllDefaulted))]; }
{ int arr[T(__has_trivial_copy(AllDeleted))]; }
+ { int arr[T(__has_trivial_copy(DerivesAr))]; }
{ int arr[F(__has_trivial_copy(HasCopy))]; }
{ int arr[F(__has_trivial_copy(HasTemplateCons))]; }
- { int arr[F(__has_trivial_copy(DerivesAr))]; }
{ int arr[F(__has_trivial_copy(VirtAr))]; }
{ int arr[F(__has_trivial_copy(void))]; }
{ int arr[F(__has_trivial_copy(cvoid))]; }
@@ -1250,13 +1250,13 @@ void has_trivial_copy_assignment() {
{ int arr[T(__has_trivial_assign(HasMoveAssign))]; }
{ int arr[T(__has_trivial_assign(AllDefaulted))]; }
{ int arr[T(__has_trivial_assign(AllDeleted))]; }
+ { int arr[T(__has_trivial_assign(DerivesAr))]; }
{ int arr[F(__has_trivial_assign(IntRef))]; }
{ int arr[F(__has_trivial_assign(HasCopyAssign))]; }
{ int arr[F(__has_trivial_assign(const Int))]; }
{ int arr[F(__has_trivial_assign(ConstIntAr))]; }
{ int arr[F(__has_trivial_assign(ConstIntArAr))]; }
- { int arr[F(__has_trivial_assign(DerivesAr))]; }
{ int arr[F(__has_trivial_assign(VirtAr))]; }
{ int arr[F(__has_trivial_assign(void))]; }
{ int arr[F(__has_trivial_assign(cvoid))]; }
@@ -1338,6 +1338,7 @@ void has_nothrow_assign() {
{ int arr[T(__has_nothrow_assign(HasVirtDest))]; }
{ int arr[T(__has_nothrow_assign(AllPrivate))]; }
{ int arr[T(__has_nothrow_assign(UsingAssign))]; }
+ { int arr[T(__has_nothrow_assign(DerivesAr))]; }
{ int arr[F(__has_nothrow_assign(IntRef))]; }
{ int arr[F(__has_nothrow_assign(HasCopyAssign))]; }
@@ -1345,7 +1346,6 @@ void has_nothrow_assign() {
{ int arr[F(__has_nothrow_assign(const Int))]; }
{ int arr[F(__has_nothrow_assign(ConstIntAr))]; }
{ int arr[F(__has_nothrow_assign(ConstIntArAr))]; }
- { int arr[F(__has_nothrow_assign(DerivesAr))]; }
{ int arr[F(__has_nothrow_assign(VirtAr))]; }
{ int arr[F(__has_nothrow_assign(void))]; }
{ int arr[F(__has_nothrow_assign(cvoid))]; }
@@ -1375,18 +1375,15 @@ void has_nothrow_copy() {
{ int arr[T(__has_nothrow_copy(HasVirtDest))]; }
{ int arr[T(__has_nothrow_copy(HasTemplateCons))]; }
{ int arr[T(__has_nothrow_copy(AllPrivate))]; }
+ { int arr[T(__has_nothrow_copy(DerivesAr))]; }
{ int arr[F(__has_nothrow_copy(HasCopy))]; }
{ int arr[F(__has_nothrow_copy(HasMultipleCopy))]; }
- { int arr[F(__has_nothrow_copy(DerivesAr))]; }
{ int arr[F(__has_nothrow_copy(VirtAr))]; }
{ int arr[F(__has_nothrow_copy(void))]; }
{ int arr[F(__has_nothrow_copy(cvoid))]; }
}
-template<bool b> struct assert_expr;
-template<> struct assert_expr<true> {};
-
void has_nothrow_constructor() {
{ int arr[T(__has_nothrow_constructor(Int))]; }
{ int arr[T(__has_nothrow_constructor(IntAr))]; }
@@ -1415,11 +1412,6 @@ void has_nothrow_constructor() {
{ int arr[F(__has_nothrow_constructor(void))]; }
{ int arr[F(__has_nothrow_constructor(cvoid))]; }
{ int arr[F(__has_nothrow_constructor(HasTemplateCons))]; }
-
- // While parsing an in-class initializer, the constructor is not known to be
- // non-throwing yet.
- struct HasInClassInit { int n = (assert_expr<!__has_nothrow_constructor(HasInClassInit)>(), 0); };
- { int arr[T(__has_nothrow_constructor(HasInClassInit))]; }
}
void has_virtual_destructor() {
@@ -1582,6 +1574,8 @@ struct X0 {
template<typename U> X0(const X0<U>&);
};
+struct Abstract { virtual void f() = 0; };
+
void is_convertible_to() {
{ int arr[T(__is_convertible_to(Int, Int))]; }
{ int arr[F(__is_convertible_to(Int, IntAr))]; }
@@ -1606,6 +1600,7 @@ void is_convertible_to() {
{ int arr[F(__is_convertible_to(Function, Function))]; }
{ int arr[F(__is_convertible_to(PrivateCopy, PrivateCopy))]; }
{ int arr[T(__is_convertible_to(X0<int>, X0<float>))]; }
+ { int arr[F(__is_convertible_to(Abstract, Abstract))]; }
}
namespace is_convertible_to_instantiate {
diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp
index b1e8d91..893f08a 100644
--- a/test/SemaCXX/typo-correction.cpp
+++ b/test/SemaCXX/typo-correction.cpp
@@ -190,3 +190,47 @@ namespace test1 {
};
test1::FooBar *b; // expected-error{{no type named 'FooBar' in namespace 'test1'; did you mean 'Foobar'?}}
}
+
+namespace ImplicitInt {
+ void f(int, unsinged); // expected-error{{did you mean 'unsigned'}}
+ struct S {
+ unsinged : 4; // expected-error{{did you mean 'unsigned'}}
+ };
+}
+
+namespace PR12951 {
+// If there are two corrections that have the same identifier and edit distance
+// and only differ by their namespaces, don't suggest either as a correction
+// since both are equally likely corrections.
+namespace foobar { struct Thing {}; }
+namespace bazquux { struct Thing {}; }
+void f() { Thing t; } // expected-error{{unknown type name 'Thing'}}
+}
+
+namespace PR13051 {
+ template<typename T> struct S {
+ template<typename U> void f();
+ operator bool() const;
+ };
+
+ void f() {
+ f(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}}
+ f(&S<int>::opeartor bool); // expected-error{{did you mean 'operator'?}}
+ f(&S<int>::foo); // expected-error-re{{no member named 'foo' in 'PR13051::S<int>'$}}
+ }
+}
+
+namespace PR6325 {
+class foo { }; // expected-note{{'foo' declared here}}
+// Note that for this example (pulled from the PR), if keywords are not excluded
+// as correction candidates then no suggestion would be given; correcting
+// 'boo' to 'bool' is the same edit distance as correcting 'boo' to 'foo'.
+class bar : boo { }; // expected-error{{unknown class name 'boo'; did you mean 'foo'?}}
+}
+
+namespace bogus_keyword_suggestion {
+void test() {
+ status = "OK"; // expected-error-re{{use of undeclared identifier 'status'$}}
+ return status; // expected-error-re{{use of undeclared identifier 'status'$}}
+ }
+}
diff --git a/test/SemaCXX/uninit-variables.cpp b/test/SemaCXX/uninit-variables.cpp
index eb6428d..687bfd2 100644
--- a/test/SemaCXX/uninit-variables.cpp
+++ b/test/SemaCXX/uninit-variables.cpp
@@ -141,3 +141,9 @@ void test_bitcasts_2() {
int y = (float &)x; // expected-warning {{uninitialized when used here}}
}
+void consume_const_ref(const int &n);
+int test_const_ref() {
+ int n; // expected-note {{variable}}
+ consume_const_ref(n);
+ return n; // expected-warning {{uninitialized when used here}}
+}
diff --git a/test/SemaCXX/uninitialized.cpp b/test/SemaCXX/uninitialized.cpp
index 7879e7c..13d287bf 100644
--- a/test/SemaCXX/uninitialized.cpp
+++ b/test/SemaCXX/uninitialized.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -std=c++11 -verify %s
int foo(int x);
int bar(int* x);
@@ -10,9 +10,6 @@ int far(const int& x);
int a = a; // no-warning: used to signal intended lack of initialization.
int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}}
int c = (c + c); // expected-warning 2 {{variable 'c' is uninitialized when used within its own initialization}}
-void test() {
- int d = ({ d + d ;}); // expected-warning {{variable 'd' is uninitialized when used within its own initialization}}
-}
int e = static_cast<long>(e) + 1; // expected-warning {{variable 'e' is uninitialized when used within its own initialization}}
int f = foo(f); // expected-warning {{variable 'f' is uninitialized when used within its own initialization}}
@@ -24,6 +21,51 @@ int i = boo(i);
int j = far(j);
int k = __alignof__(k);
+int l = k ? l : l; // expected-warning 2{{variable 'l' is uninitialized when used within its own initialization}}
+int m = 1 + (k ? m : m); // expected-warning 2{{variable 'm' is uninitialized when used within its own initialization}}
+int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}}
+
+void test_stuff () {
+ int a = a; // no-warning: used to signal intended lack of initialization.
+ int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}}
+ int c = (c + c); // expected-warning {{variable 'c' is uninitialized when used within its own initialization}}
+ int d = ({ d + d ;}); // expected-warning {{variable 'd' is uninitialized when used within its own initialization}}
+ int e = static_cast<long>(e) + 1; // expected-warning {{variable 'e' is uninitialized when used within its own initialization}}
+ int f = foo(f); // expected-warning {{variable 'f' is uninitialized when used within its own initialization}}
+
+ // Thes don't warn as they don't require the value.
+ int g = sizeof(g);
+ void* ptr = &ptr;
+ int h = bar(&h);
+ int i = boo(i);
+ int j = far(j);
+ int k = __alignof__(k);
+
+ int l = k ? l : l; // FIXME: warn here
+ int m = 1 + (k ? m : m); // FIXME: warn here
+ int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}}
+
+ for (;;) {
+ int a = a; // no-warning: used to signal intended lack of initialization.
+ int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}}
+ int c = (c + c); // expected-warning {{variable 'c' is uninitialized when used within its own initialization}}
+ int d = ({ d + d ;}); // expected-warning {{variable 'd' is uninitialized when used within its own initialization}}
+ int e = static_cast<long>(e) + 1; // expected-warning {{variable 'e' is uninitialized when used within its own initialization}}
+ int f = foo(f); // expected-warning {{variable 'f' is uninitialized when used within its own initialization}}
+
+ // Thes don't warn as they don't require the value.
+ int g = sizeof(g);
+ void* ptr = &ptr;
+ int h = bar(&h);
+ int i = boo(i);
+ int j = far(j);
+ int k = __alignof__(k);
+
+ int l = k ? l : l; // FIXME: warn here
+ int m = 1 + (k ? m : m); // FIXME: warn here
+ int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}}
+ }
+}
// Test self-references with record types.
class A {
@@ -48,8 +90,9 @@ class A {
A getA() { return A(); }
A getA(int x) { return A(); }
A getA(A* a) { return A(); }
+A getA(A a) { return A(); }
-void setupA() {
+void setupA(bool x) {
A a1;
a1.set(a1.get());
A a2(a1.get());
@@ -69,8 +112,33 @@ void setupA() {
A a15 = getA(a15.num); // expected-warning {{variable 'a15' is uninitialized when used within its own initialization}}
A a16(&a16.num); // expected-warning {{variable 'a16' is uninitialized when used within its own initialization}}
A a17(a17.get2()); // expected-warning {{variable 'a17' is uninitialized when used within its own initialization}}
+ A a18 = x ? a18 : a17; // expected-warning {{variable 'a18' is uninitialized when used within its own initialization}}
+ A a19 = getA(x ? a19 : a17); // expected-warning {{variable 'a19' is uninitialized when used within its own initialization}}
}
+bool x;
+
+A a1;
+A a2(a1.get());
+A a3(a1);
+A a4(&a4);
+A a5(a5.zero());
+A a6(a6.ONE);
+A a7 = getA();
+A a8 = getA(a8.TWO);
+A a9 = getA(&a9);
+A a10(a10.count);
+
+A a11(a11); // expected-warning {{variable 'a11' is uninitialized when used within its own initialization}}
+A a12(a12.get()); // expected-warning {{variable 'a12' is uninitialized when used within its own initialization}}
+A a13(a13.num); // expected-warning {{variable 'a13' is uninitialized when used within its own initialization}}
+A a14 = A(a14); // expected-warning {{variable 'a14' is uninitialized when used within its own initialization}}
+A a15 = getA(a15.num); // expected-warning {{variable 'a15' is uninitialized when used within its own initialization}}
+A a16(&a16.num); // expected-warning {{variable 'a16' is uninitialized when used within its own initialization}}
+A a17(a17.get2()); // expected-warning {{variable 'a17' is uninitialized when used within its own initialization}}
+A a18 = x ? a18 : a17; // expected-warning {{variable 'a18' is uninitialized when used within its own initialization}}
+A a19 = getA(x ? a19 : a17); // expected-warning {{variable 'a19' is uninitialized when used within its own initialization}}
+
struct B {
// POD struct.
int x;
@@ -97,6 +165,7 @@ void setupB() {
B b7(b7); // expected-warning {{variable 'b7' is uninitialized when used within its own initialization}}
B b8 = getB(b8.x); // expected-warning {{variable 'b8' is uninitialized when used within its own initialization}}
B b9 = getB(b9.y); // expected-warning {{variable 'b9' is uninitialized when used within its own initialization}}
+ B b10 = getB(-b10.x); // expected-warning {{variable 'b10' is uninitialized when used within its own initialization}}
}
// Also test similar constructs in a field's initializer.
@@ -106,9 +175,9 @@ struct S {
S(bool (*)[1]) : x(x) {} // expected-warning {{field is uninitialized when used here}}
S(bool (*)[2]) : x(x + 1) {} // expected-warning {{field is uninitialized when used here}}
- S(bool (*)[3]) : x(x + x) {} // expected-warning {{field is uninitialized when used here}}
+ S(bool (*)[3]) : x(x + x) {} // expected-warning 2{{field is uninitialized when used here}}
S(bool (*)[4]) : x(static_cast<long>(x) + 1) {} // expected-warning {{field is uninitialized when used here}}
- S(bool (*)[5]) : x(foo(x)) {} // FIXME: This should warn!
+ S(bool (*)[5]) : x(foo(x)) {} // expected-warning {{field is uninitialized when used here}}
// These don't actually require the value of x and so shouldn't warn.
S(char (*)[1]) : x(sizeof(x)) {} // rdar://8610363
@@ -164,6 +233,86 @@ int pr12325(int params) {
// Test lambda expressions with -Wuninitialized
int test_lambda() {
- auto f1 = [] (int x, int y) { int z; return x + y + z; }; // expected-warning {{C++11 requires lambda with omitted result type to consist of a single return statement}} expected-warning{{variable 'z' is uninitialized when used here}} expected-note {{initialize the variable 'z' to silence this warning}}
+ auto f1 = [] (int x, int y) { int z; return x + y + z; }; // expected-warning{{variable 'z' is uninitialized when used here}} expected-note {{initialize the variable 'z' to silence this warning}}
return f1(1, 2);
}
+
+namespace {
+ struct A {
+ enum { A1 };
+ static int A2() {return 5;}
+ int A3;
+ int A4() { return 5;}
+ };
+
+ struct B {
+ A a;
+ };
+
+ struct C {
+ C() {}
+ C(int x) {}
+ static A a;
+ B b;
+ };
+ A C::a = A();
+
+ // Accessing non-static members will give a warning.
+ struct D {
+ C c;
+ D(char (*)[1]) : c(c.b.a.A1) {}
+ D(char (*)[2]) : c(c.b.a.A2()) {}
+ D(char (*)[3]) : c(c.b.a.A3) {} // expected-warning {{field is uninitialized when used here}}
+ D(char (*)[4]) : c(c.b.a.A4()) {} // expected-warning {{field is uninitialized when used here}}
+
+ // c::a is static, so it is already initialized
+ D(char (*)[5]) : c(c.a.A1) {}
+ D(char (*)[6]) : c(c.a.A2()) {}
+ D(char (*)[7]) : c(c.a.A3) {}
+ D(char (*)[8]) : c(c.a.A4()) {}
+ };
+
+ struct E {
+ int a, b, c;
+ E(char (*)[1]) : a(a ? b : c) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[2]) : a(b ? a : a) {} // expected-warning 2{{field is uninitialized when used here}}
+ E(char (*)[3]) : a(b ? (a) : c) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[4]) : a(b ? c : (a+c)) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[5]) : a(b ? c : b) {}
+
+ E(char (*)[6]) : a(a ?: a) {} // expected-warning 2{{field is uninitialized when used here}}
+ E(char (*)[7]) : a(b ?: a) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[8]) : a(a ?: c) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[9]) : a(b ?: c) {}
+
+ E(char (*)[10]) : a((a, a, b)) {}
+ E(char (*)[11]) : a((c + a, a + 1, b)) {} // expected-warning 2{{field is uninitialized when used here}}
+ E(char (*)[12]) : a((b + c, c, a)) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[13]) : a((a, a, a, a)) {} // expected-warning {{field is uninitialized when used here}}
+ E(char (*)[14]) : a((b, c, c)) {}
+ };
+
+ struct F {
+ int a;
+ F* f;
+ F(int) {}
+ F() {}
+ };
+
+ int F::*ptr = &F::a;
+ F* F::*f_ptr = &F::f;
+ struct G {
+ F f1, f2;
+ F *f3, *f4;
+ G(char (*)[1]) : f1(f1) {} // expected-warning {{field is uninitialized when used here}}
+ G(char (*)[2]) : f2(f1) {}
+ G(char (*)[3]) : f2(F()) {}
+
+ G(char (*)[4]) : f1(f1.*ptr) {} // expected-warning {{field is uninitialized when used here}}
+ G(char (*)[5]) : f2(f1.*ptr) {}
+
+ G(char (*)[6]) : f3(f3) {} // expected-warning {{field is uninitialized when used here}}
+ G(char (*)[7]) : f3(f3->*f_ptr) {} // expected-warning {{field is uninitialized when used here}}
+ G(char (*)[8]) : f3(new F(f3->*ptr)) {} // expected-warning {{field is uninitialized when used here}}
+ };
+}
diff --git a/test/SemaCXX/unknown-type-name.cpp b/test/SemaCXX/unknown-type-name.cpp
index 5f8d8ca..893e0cc 100644
--- a/test/SemaCXX/unknown-type-name.cpp
+++ b/test/SemaCXX/unknown-type-name.cpp
@@ -20,6 +20,13 @@ struct A {
typedef T type;
type f();
+
+ type g();
+
+ static int n;
+ static type m;
+ static int h(T::type, int); // expected-error{{missing 'typename'}}
+ static int h(T::type x, char); // expected-error{{missing 'typename'}}
};
template<typename T>
@@ -27,3 +34,52 @@ A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
template<typename T>
A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(T::type) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void g(T::type x) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(T::type, int) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(T::type x, char) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(int, T::type) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(char, T::type x) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(int, T::type, int) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(int, T::type x, char) { } // expected-error{{missing 'typename'}}
+
+template<typename T> int A<T>::n(T::value); // ok
+template<typename T>
+A<T>::type // expected-error{{missing 'typename'}}
+A<T>::m(T::value, 0); // ok
+
+template<typename T> int A<T>::h(T::type, int) {} // expected-error{{missing 'typename'}}
+template<typename T> int A<T>::h(T::type x, char) {} // expected-error{{missing 'typename'}}
+
+template<typename T> int h(T::type, int); // expected-error{{missing 'typename'}}
+template<typename T> int h(T::type x, char); // expected-error{{missing 'typename'}}
+
+template<typename T> int junk1(T::junk); // expected-error{{declared as a template}}
+template<typename T> int junk2(T::junk) throw(); // expected-error{{missing 'typename'}}
+template<typename T> int junk3(T::junk) = delete; // expected-error{{missing 'typename'}} expected-warning{{C++11}}
+template<typename T> int junk4(T::junk j); // expected-error{{missing 'typename'}}
+
+// FIXME: We can tell this was intended to be a function because it does not
+// have a dependent nested name specifier.
+template<typename T> int i(T::type, int()); // expected-error{{variable 'i' declared as a template}}
+
+// FIXME: We know which type specifier should have been specified here. Provide
+// a fix-it to add 'typename A<T>::type'
+template<typename T>
+A<T>::g() { } // expected-error{{requires a type specifier}}
diff --git a/test/SemaCXX/unused.cpp b/test/SemaCXX/unused.cpp
index 88783ce..54898c8 100644
--- a/test/SemaCXX/unused.cpp
+++ b/test/SemaCXX/unused.cpp
@@ -1,24 +1,36 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
+
// PR4103 : Make sure we don't get a bogus unused expression warning
-class APInt {
- char foo;
-};
-class APSInt : public APInt {
- char bar;
-public:
- APSInt &operator=(const APSInt &RHS);
-};
+namespace PR4103 {
+ class APInt {
+ char foo;
+ };
+ class APSInt : public APInt {
+ char bar;
+ public:
+ APSInt &operator=(const APSInt &RHS);
+ };
-APSInt& APSInt::operator=(const APSInt &RHS) {
- APInt::operator=(RHS);
- return *this;
-}
+ APSInt& APSInt::operator=(const APSInt &RHS) {
+ APInt::operator=(RHS);
+ return *this;
+ }
-template<typename T>
-struct X {
- X();
-};
+ template<typename T>
+ struct X {
+ X();
+ };
+
+ void test() {
+ X<int>();
+ }
+}
-void test() {
- X<int>();
+namespace derefvolatile {
+ void f(volatile char* x) {
+ *x; // expected-warning {{expression result unused; assign into a variable to force a volatile load}}
+ (void)*x; // expected-warning {{expression result unused; assign into a variable to force a volatile load}}
+ volatile char y = 10;
+ (void)y; // don't warn here, because it's a common pattern.
+ }
}
diff --git a/test/SemaCXX/user-defined-conversions.cpp b/test/SemaCXX/user-defined-conversions.cpp
index 43ec5a3..284a310 100644
--- a/test/SemaCXX/user-defined-conversions.cpp
+++ b/test/SemaCXX/user-defined-conversions.cpp
@@ -69,7 +69,7 @@ void test_conversion(ConvertibleToBase ctb, ConvertibleToDerived ctd,
}
struct X1 {
- X1(X1&); // expected-note{{candidate constructor not viable: no known conversion from 'X1' to 'X1 &' for 1st argument}}
+ X1(X1&); // expected-note{{candidate constructor not viable: expects an l-value for 1st argument}}
};
struct X2 {
diff --git a/test/SemaCXX/virtuals.cpp b/test/SemaCXX/virtuals.cpp
index ea7d203..a340e9d 100644
--- a/test/SemaCXX/virtuals.cpp
+++ b/test/SemaCXX/virtuals.cpp
@@ -30,7 +30,7 @@ A fn(A) // expected-error{{parameter type 'A' is an abstract class}} \
// expected-error{{return type 'A' is an abstract class}}
{
A a; // expected-error{{variable type 'A' is an abstract class}}
- (void)static_cast<A>(0);
+ (void)static_cast<A>(0); // expected-error{{allocating an object of abstract class type 'A'}}
try {
} catch(A) { // expected-error{{variable type 'A' is an abstract class}}
}
diff --git a/test/SemaCXX/warn-deprecated-header.cpp b/test/SemaCXX/warn-deprecated-header.cpp
index f6ac2cb..015e775 100644
--- a/test/SemaCXX/warn-deprecated-header.cpp
+++ b/test/SemaCXX/warn-deprecated-header.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -fdeprecated-macro -verify %s
// RUN: %clang_cc1 -fsyntax-only -Werror %s
+// expected-warning@+2 {{This file is deprecated.}}
#ifdef __DEPRECATED
-#warning This file is deprecated. // expected-warning {{This file is deprecated.}}
+#warning This file is deprecated.
#endif
diff --git a/test/SemaCXX/warn-literal-conversion.cpp b/test/SemaCXX/warn-literal-conversion.cpp
index 5fcae5d..d7bec4c 100644
--- a/test/SemaCXX/warn-literal-conversion.cpp
+++ b/test/SemaCXX/warn-literal-conversion.cpp
@@ -5,29 +5,29 @@ void foo(int y);
// Warn when a literal float or double is assigned or bound to an integer.
void test0() {
// Float
- int y0 = 1.2222F; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y1 = (1.2222F); // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y2 = (((1.2222F))); // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y3 = 12E-1F; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y4 = 1.23E1F; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ int y0 = 1.2222F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2222 to 1}}
+ int y1 = (1.2222F); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2222 to 1}}
+ int y2 = (((1.2222F))); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2222 to 1}}
+ int y3 = 12E-1F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2 to 1}}
+ int y4 = 1.23E1F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from 12.3 to 12}}
// Double
- int y5 = 1.2222; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y6 = 12E-1; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y7 = 1.23E1; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
- int y8 = (1.23E1); // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ int y5 = 1.2222; // expected-warning {{implicit conversion from 'double' to 'int' changes value from 1.2222 to 1}}
+ int y6 = 12E-1; // expected-warning {{implicit conversion from 'double' to 'int' changes value from 1.2 to 1}}
+ int y7 = 1.23E1; // expected-warning {{implicit conversion from 'double' to 'int' changes value from 12.3 to 12}}
+ int y8 = (1.23E1); // expected-warning {{implicit conversion from 'double' to 'int' changes value from 12.3 to 12}}
// Test assignment to an existing variable.
- y8 = 2.22F; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ y8 = 2.22F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from 2.22 to 2}}
// Test direct initialization.
- int y9(1.23F); // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ int y9(1.23F); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.23 to 1}}
// Test passing a literal floating-point value to a function that takes an integer.
- foo(1.2F); // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ foo(1.2F); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2 to 1}}
- int y10 = -1.2F; // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ int y10 = -1.2F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2 to 1}}
- // -Wconversion-literal does NOT catch const values.
+ // -Wliteral-conversion does NOT catch const values.
// (-Wconversion DOES catch them.)
static const float sales_tax_rate = .095F;
int z = sales_tax_rate;
diff --git a/test/SemaCXX/warn-loop-analysis.cpp b/test/SemaCXX/warn-loop-analysis.cpp
new file mode 100644
index 0000000..627bc51
--- /dev/null
+++ b/test/SemaCXX/warn-loop-analysis.cpp
@@ -0,0 +1,154 @@
+// RUN: %clang_cc1 -fsyntax-only -Wloop-analysis -verify %s
+
+struct S {
+ bool stop() { return false; }
+ bool keep_running;
+};
+
+void by_ref(int &value) { }
+void by_value(int value) { }
+void by_pointer(int *value) {}
+
+void test1() {
+ S s;
+ for (; !s.stop();) {}
+ for (; s.keep_running;) {}
+ for (int i; i < 1; ++i) {}
+ for (int i; i < 1; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (int i; i < 1; ) { ++i; }
+ for (int i; i < 1; ) { return; }
+ for (int i; i < 1; ) { break; }
+ for (int i; i < 1; ) { goto exit_loop; }
+exit_loop:
+ for (int i; i < 1; ) { by_ref(i); }
+ for (int i; i < 1; ) { by_value(i); } // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (int i; i < 1; ) { by_pointer(&i); }
+
+ for (int i; i < 1; ++i)
+ for (int j; j < 1; ++j)
+ { }
+ for (int i; i < 1; ++i)
+ for (int j; j < 1; ++i) // expected-warning {{variable 'j' used in loop condition not modified in loop body}}
+ { }
+ for (int i; i < 1; ++i)
+ for (int j; i < 1; ++j) // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ { }
+
+ for (int *i, *j; i < j; ++i) {}
+ for (int *i, *j; i < j;) {} // expected-warning {{variables 'i' and 'j' used in loop condition not modified in loop body}}
+
+ // Dereferencing pointers is ignored for now.
+ for (int *i; *i; ) {}
+}
+
+void test2() {
+ int i, j, k;
+ int *ptr;
+
+ // Testing CastExpr
+ for (; i; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i; ) { i = 5; }
+
+ // Testing BinaryOperator
+ for (; i < j; ) {} // expected-warning {{variables 'i' and 'j' used in loop condition not modified in loop body}}
+ for (; i < j; ) { i = 5; }
+ for (; i < j; ) { j = 5; }
+
+ // Testing IntegerLiteral
+ for (; i < 5; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i < 5; ) { i = 5; }
+
+ // Testing FloatingLiteral
+ for (; i < 5.0; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i < 5.0; ) { i = 5; }
+
+ // Testing CharacterLiteral
+ for (; i == 'a'; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i == 'a'; ) { i = 5; }
+
+ // Testing CXXBoolLiteralExpr
+ for (; i == true; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i == true; ) { i = 5; }
+
+ // Testing GNUNullExpr
+ for (; ptr == __null; ) {} // expected-warning {{variable 'ptr' used in loop condition not modified in loop body}}
+ for (; ptr == __null; ) { ptr = &i; }
+
+ // Testing UnaryOperator
+ for (; -i > 5; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; -i > 5; ) { ++i; }
+
+ // Testing ImaginaryLiteral
+ for (; i != 3i; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i != 3i; ) { ++i; }
+
+ // Testing ConditionalOperator
+ for (; i ? j : k; ) {} // expected-warning {{variables 'i', 'j', and 'k' used in loop condition not modified in loop body}}
+ for (; i ? j : k; ) { ++i; }
+ for (; i ? j : k; ) { ++j; }
+ for (; i ? j : k; ) { ++k; }
+ for (; i; ) { j = i ? i : i; } // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i; ) { j = (i = 1) ? i : i; }
+ for (; i; ) { j = i ? i : ++i; }
+
+ // Testing BinaryConditionalOperator
+ for (; i ?: j; ) {} // expected-warning {{variables 'i' and 'j' used in loop condition not modified in loop body}}
+ for (; i ?: j; ) { ++i; }
+ for (; i ?: j; ) { ++j; }
+ for (; i; ) { j = i ?: i; } // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+
+ // Testing ParenExpr
+ for (; (i); ) { } // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; (i); ) { ++i; }
+
+ // Testing non-evaluated variables
+ for (; i < sizeof(j); ) { } // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i < sizeof(j); ) { ++j; } // expected-warning {{variable 'i' used in loop condition not modified in loop body}}
+ for (; i < sizeof(j); ) { ++i; }
+}
+
+// False positive and how to silence.
+void test3() {
+ int x;
+ int *ptr = &x;
+ for (;x<5;) { *ptr = 6; } // expected-warning {{variable 'x' used in loop condition not modified in loop body}}
+
+ for (;x<5;) {
+ *ptr = 6;
+ (void)x;
+ }
+}
+
+// Check ordering and printing of variables. Max variables is currently 4.
+void test4() {
+ int a, b, c, d, e, f;
+ for (; a;); // expected-warning {{variable 'a' used in loop condition not modified in loop body}}
+ for (; a + b;); // expected-warning {{variables 'a' and 'b' used in loop condition not modified in loop body}}
+ for (; a + b + c;); // expected-warning {{variables 'a', 'b', and 'c' used in loop condition not modified in loop body}}
+ for (; a + b + c + d;); // expected-warning {{variables 'a', 'b', 'c', and 'd' used in loop condition not modified in loop body}}
+ for (; a + b + c + d + e;); // expected-warning {{variables used in loop condition not modified in loop body}}
+ for (; a + b + c + d + e + f;); // expected-warning {{variables used in loop condition not modified in loop body}}
+ for (; a + c + d + b;); // expected-warning {{variables 'a', 'c', 'd', and 'b' used in loop condition not modified in loop body}}
+ for (; d + c + b + a;); // expected-warning {{variables 'd', 'c', 'b', and 'a' used in loop condition not modified in loop body}}
+}
+
+// Ensure that the warning doesn't fail when lots of variables are used
+// in the conditional.
+void test5() {
+ for (int a; a+a+a+a+a+a+a+a+a+a;); // \
+ // expected-warning {{variable 'a' used in loop condition not modified in loop body}}
+ for (int a; a+a+a+a+a+a+a+a+a+a+a;); // \
+ // expected-warning {{variable 'a' used in loop condition not modified in loop body}}
+ for (int a; a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;); // \
+ // expected-warning {{variable 'a' used in loop condition not modified in loop body}}
+ for (int a; a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;);//\
+ // expected-warning {{variable 'a' used in loop condition not modified in loop body}}
+}
+
+// Ignore global variables and static variables.
+int x6;
+void test6() {
+ static int y;
+ for (;x6;);
+ for (;y;);
+}
diff --git a/test/SemaCXX/warn-memset-bad-sizeof.cpp b/test/SemaCXX/warn-memset-bad-sizeof.cpp
index e0d28da..e388634 100644
--- a/test/SemaCXX/warn-memset-bad-sizeof.cpp
+++ b/test/SemaCXX/warn-memset-bad-sizeof.cpp
@@ -35,27 +35,27 @@ void f(Mat m, const Foo& const_foo, char *buffer) {
/* Should warn */
memset(&s, 0, sizeof(&s)); // \
- // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}}
+ // expected-warning {{'memset' call operates on objects of type 'S' while the size is based on a different type 'S *'}} expected-note{{did you mean to remove the addressof in the argument to 'sizeof' (and multiply it by the number of elements)?}}
memset(ps, 0, sizeof(ps)); // \
- // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}}
+ // expected-warning {{'memset' call operates on objects of type 'S' while the size is based on a different type 'S *'}} expected-note{{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
memset(ps2, 0, sizeof(ps2)); // \
- // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}}
+ // expected-warning {{'memset' call operates on objects of type 'S' while the size is based on a different type 'PS' (aka 'S *')}} expected-note{{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
memset(ps2, 0, sizeof(typeof(ps2))); // \
// expected-warning {{argument to 'sizeof' in 'memset' call is the same pointer type}}
memset(ps2, 0, sizeof(PS)); // \
// expected-warning {{argument to 'sizeof' in 'memset' call is the same pointer type}}
memset(heap_buffer, 0, sizeof(heap_buffer)); // \
- // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}}
+ // expected-warning {{'memset' call operates on objects of type 'char' while the size is based on a different type 'char *'}} expected-note{{did you mean to provide an explicit length?}}
memcpy(&s, 0, sizeof(&s)); // \
- // expected-warning {{argument to 'sizeof' in 'memcpy' call is the same expression as the destination}}
+ // expected-warning {{'memcpy' call operates on objects of type 'S' while the size is based on a different type 'S *'}} expected-note{{did you mean to remove the addressof in the argument to 'sizeof' (and multiply it by the number of elements)?}}
memcpy(0, &s, sizeof(&s)); // \
- // expected-warning {{argument to 'sizeof' in 'memcpy' call is the same expression as the source}}
+ // expected-warning {{'memcpy' call operates on objects of type 'S' while the size is based on a different type 'S *'}} expected-note{{did you mean to remove the addressof in the argument to 'sizeof' (and multiply it by the number of elements)?}}
memmove(ps, 0, sizeof(ps)); // \
- // expected-warning {{argument to 'sizeof' in 'memmove' call is the same expression as the destination}}
+ // expected-warning {{'memmove' call operates on objects of type 'S' while the size is based on a different type 'S *'}} expected-note{{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
memcmp(ps, 0, sizeof(ps)); // \
- // expected-warning {{argument to 'sizeof' in 'memcmp' call is the same expression as the destination}}
+ // expected-warning {{'memcmp' call operates on objects of type 'S' while the size is based on a different type 'S *'}} expected-note{{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
/* Shouldn't warn */
memset((void*)&s, 0, sizeof(&s));
@@ -132,14 +132,14 @@ void strcpy_and_friends() {
const char* BAR = "<- this, too";
strncmp(FOO, BAR, sizeof(FOO)); // \
- // expected-warning {{argument to 'sizeof' in 'strncmp' call is the same expression as the destination}}
+ // expected-warning {{'strncmp' call operates on objects of type 'const char' while the size is based on a different type 'const char *'}} expected-note{{did you mean to provide an explicit length?}}
strncasecmp(FOO, BAR, sizeof(FOO)); // \
- // expected-warning {{argument to 'sizeof' in 'strncasecmp' call is the same expression as the destination}}
+ // expected-warning {{'strncasecmp' call operates on objects of type 'const char' while the size is based on a different type 'const char *'}} expected-note{{did you mean to provide an explicit length?}}
char buff[80];
strncpy(buff, BAR, sizeof(BAR)); // \
- // expected-warning {{argument to 'sizeof' in 'strncpy' call is the same expression as the source}}
+ // expected-warning {{'strncpy' call operates on objects of type 'const char' while the size is based on a different type 'const char *'}} expected-note{{did you mean to provide an explicit length?}}
strndup(FOO, sizeof(FOO)); // \
- // expected-warning {{argument to 'sizeof' in 'strndup' call is the same expression as the source}}
+ // expected-warning {{'strndup' call operates on objects of type 'const char' while the size is based on a different type 'const char *'}} expected-note{{did you mean to provide an explicit length?}}
}
diff --git a/test/SemaCXX/warn-static-function-inheader.cpp b/test/SemaCXX/warn-static-function-inheader.cpp
new file mode 100644
index 0000000..30386d9
--- /dev/null
+++ b/test/SemaCXX/warn-static-function-inheader.cpp
@@ -0,0 +1,12 @@
+#include "warn-static-function-inheader.h"
+// RUN: %clang_cc1 -fsyntax-only -verify -Wall %s
+// rdar://11202617
+
+static void another(void) { // expected-warning {{function 'another' is not needed and will not be emitted}}
+}
+
+template <typename T>
+void foo(void) {
+ thing();
+ another();
+}
diff --git a/test/SemaCXX/warn-static-function-inheader.h b/test/SemaCXX/warn-static-function-inheader.h
new file mode 100644
index 0000000..1c9e00b
--- /dev/null
+++ b/test/SemaCXX/warn-static-function-inheader.h
@@ -0,0 +1,3 @@
+static void thing(void) { // expected-warning {{'static' function 'thing' declared in header file should be declared 'static inline'}}
+}
+
diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp
index 566e5c1..17a1931 100644
--- a/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1,4 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 %s
+
+// FIXME: should also run %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s
+// FIXME: should also run %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
#define LOCKABLE __attribute__ ((lockable))
#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
@@ -48,6 +51,30 @@ class __attribute__((scoped_lockable)) ReaderMutexLock {
~ReaderMutexLock() __attribute__((unlock_function));
};
+class SCOPED_LOCKABLE ReleasableMutexLock {
+ public:
+ ReleasableMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu);
+ ~ReleasableMutexLock() UNLOCK_FUNCTION();
+
+ void Release() UNLOCK_FUNCTION();
+};
+
+
+template<class T>
+class SmartPtr {
+public:
+ SmartPtr(T* p) : ptr_(p) { }
+ SmartPtr(const SmartPtr<T>& p) : ptr_(p.ptr_) { }
+ ~SmartPtr();
+
+ T* get() const { return ptr_; }
+ T* operator->() const { return ptr_; }
+ T& operator*() const { return *ptr_; }
+
+private:
+ T* ptr_;
+};
+
Mutex sls_mu;
@@ -177,14 +204,11 @@ void sls_fun_bad_3() {
void sls_fun_bad_4() {
if (getBool())
- sls_mu.Lock(); // \
- expected-warning{{mutex 'sls_mu2' is not locked on every path through here}} \
- expected-note{{mutex acquired here}}
-
+ sls_mu.Lock(); // expected-note{{mutex acquired here}}
else
- sls_mu2.Lock(); // \
- expected-note{{mutex acquired here}}
-} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}}
+ sls_mu2.Lock(); // expected-note{{mutex acquired here}}
+} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}} \
+ // expected-warning{{mutex 'sls_mu2' is not locked on every path through here}}
void sls_fun_bad_5() {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
@@ -225,15 +249,14 @@ void sls_fun_bad_7() {
void sls_fun_bad_8() {
sls_mu.Lock(); // expected-note{{mutex acquired here}}
- // FIXME: TERRIBLE SOURCE LOCATION!
- do { // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
- sls_mu.Unlock();
+ do {
+ sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
} while (getBool());
}
void sls_fun_bad_9() {
do {
- sls_mu.Lock(); // \
+ sls_mu.Lock(); // \
// expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} \
// expected-note{{mutex acquired here}}
} while (getBool());
@@ -241,15 +264,15 @@ void sls_fun_bad_9() {
}
void sls_fun_bad_10() {
- sls_mu.Lock(); // expected-note 2{{mutex acquired here}}
- while(getBool()) {
- sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ sls_mu.Lock(); // expected-note 2{{mutex acquired here}}
+ while(getBool()) { // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ sls_mu.Unlock();
}
} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}}
void sls_fun_bad_11() {
while (getBool()) { // \
- expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
sls_mu.Lock(); // expected-note {{mutex acquired here}}
}
sls_mu.Unlock(); // \
@@ -506,7 +529,7 @@ void late_bad_0() {
LateFoo fooB;
fooA.mu.Lock();
fooB.a = 5; // \
- // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ // expected-warning{{writing variable 'a' requires locking 'fooB.mu' exclusively}}
fooA.mu.Unlock();
}
@@ -516,7 +539,7 @@ void late_bad_1() {
b1.mu1_.Lock();
int res = b1.a_ + b3->b_;
b3->b_ = *b1.q; // \
- // expected-warning{{reading the value pointed to by 'q' requires locking 'mu'}}
+ // expected-warning{{reading the value pointed to by 'q' requires locking 'b1.mu'}}
b1.mu1_.Unlock();
b1.b_ = res;
mu.Unlock();
@@ -526,7 +549,7 @@ void late_bad_2() {
LateBar BarA;
BarA.FooPointer->mu.Lock();
BarA.Foo.a = 2; // \
- // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ // expected-warning{{writing variable 'a' requires locking 'BarA.Foo.mu' exclusively}}
BarA.FooPointer->mu.Unlock();
}
@@ -534,7 +557,7 @@ void late_bad_3() {
LateBar BarA;
BarA.Foo.mu.Lock();
BarA.FooPointer->a = 2; // \
- // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ // expected-warning{{writing variable 'a' requires locking 'BarA.FooPointer->mu' exclusively}}
BarA.Foo.mu.Unlock();
}
@@ -542,7 +565,7 @@ void late_bad_4() {
LateBar BarA;
BarA.Foo.mu.Lock();
BarA.Foo2.a = 2; // \
- // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ // expected-warning{{writing variable 'a' requires locking 'BarA.Foo2.mu' exclusively}}
BarA.Foo.mu.Unlock();
}
@@ -1176,13 +1199,13 @@ void main()
{
Foo f1, *f2;
f1.mu_.Lock();
- f1.bar(); // expected-warning {{cannot call function 'bar' while mutex 'mu_' is locked}}
+ f1.bar(); // expected-warning {{cannot call function 'bar' while mutex 'f1.mu_' is locked}}
mu2.Lock();
f1.foo();
mu2.Unlock();
f1.mu_.Unlock();
f2->mu_.Lock();
- f2->bar(); // expected-warning {{cannot call function 'bar' while mutex 'mu_' is locked}}
+ f2->bar(); // expected-warning {{cannot call function 'bar' while mutex 'f2->mu_' is locked}}
f2->mu_.Unlock();
mu2.Lock();
w = 2;
@@ -1210,7 +1233,7 @@ void func()
{
b1->MyLock();
b1->a_ = 5;
- b2->a_ = 3; // expected-warning {{writing variable 'a_' requires locking 'mu1_' exclusively}}
+ b2->a_ = 3; // expected-warning {{writing variable 'a_' requires locking 'b2->mu1_' exclusively}}
b2->MyLock();
b2->MyUnlock();
b1->MyUnlock();
@@ -1239,12 +1262,12 @@ int func(int i)
{
int x;
b3->mu1_.Lock();
- res = b1.a_ + b3->b_; // expected-warning {{reading variable 'a_' requires locking 'mu1_'}} \
+ res = b1.a_ + b3->b_; // expected-warning {{reading variable 'a_' requires locking 'b1.mu1_'}} \
// expected-warning {{writing variable 'res' requires locking 'mu' exclusively}}
*p = i; // expected-warning {{reading variable 'p' requires locking 'mu'}} \
// expected-warning {{writing the value pointed to by 'p' requires locking 'mu' exclusively}}
b1.a_ = res + b3->b_; // expected-warning {{reading variable 'res' requires locking 'mu'}} \
- // expected-warning {{writing variable 'a_' requires locking 'mu1_' exclusively}}
+ // expected-warning {{writing variable 'a_' requires locking 'b1.mu1_' exclusively}}
b3->b_ = *b1.q; // expected-warning {{reading the value pointed to by 'q' requires locking 'mu'}}
b3->mu1_.Unlock();
b1.b_ = res; // expected-warning {{reading variable 'res' requires locking 'mu'}}
@@ -1269,8 +1292,8 @@ class Foo {
child->Func(new_foo); // There shouldn't be any warning here as the
// acquired lock is not in child.
- child->bar(7); // expected-warning {{calling function 'bar' requires exclusive lock on 'lock_'}}
- child->a_ = 5; // expected-warning {{writing variable 'a_' requires locking 'lock_' exclusively}}
+ child->bar(7); // expected-warning {{calling function 'bar' requires exclusive lock on 'child->lock_'}}
+ child->a_ = 5; // expected-warning {{writing variable 'a_' requires locking 'child->lock_' exclusively}}
lock_.Unlock();
}
@@ -1307,7 +1330,7 @@ void Foo::Func(Foo* child) {
lock_.Lock();
child->lock_.Lock();
- child->Func(new_foo); // expected-warning {{cannot call function 'Func' while mutex 'lock_' is locked}}
+ child->Func(new_foo); // expected-warning {{cannot call function 'Func' while mutex 'child->lock_' is locked}}
child->bar(7);
child->a_ = 5;
child->lock_.Unlock();
@@ -1355,8 +1378,8 @@ Foo *foo;
void func()
{
- foo->f1(); // expected-warning {{calling function 'f1' requires exclusive lock on 'mu2'}} \
- // expected-warning {{calling function 'f1' requires exclusive lock on 'mu1'}}
+ foo->f1(); // expected-warning {{calling function 'f1' requires exclusive lock on 'foo->mu2'}} \
+ // expected-warning {{calling function 'f1' requires exclusive lock on 'foo->mu1'}}
}
} // end namespace thread_annot_lock_42
@@ -1379,14 +1402,14 @@ void main() {
Child *c;
Base *b = c;
- b->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'mu_'}}
+ b->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'b->mu_'}}
b->mu_.Lock();
- b->func2(); // expected-warning {{cannot call function 'func2' while mutex 'mu_' is locked}}
+ b->func2(); // expected-warning {{cannot call function 'func2' while mutex 'b->mu_' is locked}}
b->mu_.Unlock();
- c->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'mu_'}}
+ c->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'c->mu_'}}
c->mu_.Lock();
- c->func2(); // expected-warning {{cannot call function 'func2' while mutex 'mu_' is locked}}
+ c->func2(); // expected-warning {{cannot call function 'func2' while mutex 'c->mu_' is locked}}
c->mu_.Unlock();
}
} // end namespace thread_annot_lock_46
@@ -1413,9 +1436,9 @@ int Foo::method1(int i) {
void main()
{
Foo a;
- a.method1(1); // expected-warning {{calling function 'method1' requires shared lock on 'mu1'}} \
+ a.method1(1); // expected-warning {{calling function 'method1' requires shared lock on 'a.mu1'}} \
// expected-warning {{calling function 'method1' requires shared lock on 'mu'}} \
- // expected-warning {{calling function 'method1' requires shared lock on 'mu2'}} \
+ // expected-warning {{calling function 'method1' requires shared lock on 'a.mu2'}} \
// expected-warning {{calling function 'method1' requires shared lock on 'mu3'}}
}
} // end namespace thread_annot_lock_67_modified
@@ -1461,14 +1484,14 @@ namespace substitution_test {
DataLocker dlr;
dlr.lockData(d1); // expected-note {{mutex acquired here}}
dlr.unlockData(d2); // \
- // expected-warning {{unlocking 'mu' that was not locked}}
- } // expected-warning {{mutex 'mu' is still locked at the end of function}}
+ // expected-warning {{unlocking 'd2->mu' that was not locked}}
+ } // expected-warning {{mutex 'd1->mu' is still locked at the end of function}}
void bar4(MyData* d1, MyData* d2) {
DataLocker dlr;
dlr.lockData(d1);
foo(d2); // \
- // expected-warning {{calling function 'foo' requires exclusive lock on 'mu'}}
+ // expected-warning {{calling function 'foo' requires exclusive lock on 'd2->mu'}}
dlr.unlockData(d1);
}
};
@@ -1527,7 +1550,7 @@ namespace template_member_test {
struct IndirectLock {
int DoNaughtyThings(T *t) {
u->n = 0; // expected-warning {{reading variable 'u' requires locking 'm'}}
- return t->s->n; // expected-warning {{reading variable 's' requires locking 'm'}}
+ return t->s->n; // expected-warning {{reading variable 's' requires locking 't->m'}}
}
};
@@ -1543,7 +1566,7 @@ namespace template_member_test {
template<typename U> struct W {
V v;
void f(U u) {
- v.p->f(u); // expected-warning {{reading variable 'p' requires locking 'm'}}
+ v.p->f(u); // expected-warning {{reading variable 'p' requires locking 'v.m'}}
}
};
template struct W<int>; // expected-note {{here}}
@@ -1581,7 +1604,7 @@ struct TestScopedLockable {
MutexLock mulock_a(&mu1);
MutexLock mulock_b(&mu1); // \
// expected-warning {{locking 'mu1' that is already locked}}
- } // expected-warning {{unlocking 'mu1' that was not locked}}
+ }
void foo4() {
MutexLock mulock1(&mu1), mulock2(&mu2);
@@ -1606,7 +1629,7 @@ Foo fooObj;
void foo() EXCLUSIVE_LOCKS_REQUIRED(fooObj.mu_);
void bar() {
- foo(); // expected-warning {{calling function 'foo' requires exclusive lock on 'mu_'}}
+ foo(); // expected-warning {{calling function 'foo' requires exclusive lock on 'fooObj.mu_'}}
fooObj.mu_.Lock();
foo();
fooObj.mu_.Unlock();
@@ -1679,7 +1702,7 @@ struct TestTryLock {
bool b2 = b;
if (cond)
b = true;
- if (b) { // b should be unknown at this point, becuase of the join point
+ if (b) { // b should be unknown at this point, because of the join point
a = 8; // expected-warning {{writing variable 'a' requires locking 'mu' exclusively}}
}
if (b2) { // b2 should be known at this point.
@@ -1806,7 +1829,7 @@ void test() {
f1.mu_.Unlock();
bt.barTD(&f1); // \
- // expected-warning {{calling function 'barTD' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'barTD' requires exclusive lock on 'f1.mu_'}}
bt.fooBase.mu_.Unlock();
bt.fooBaseT.mu_.Unlock();
@@ -1814,7 +1837,7 @@ void test() {
Cell<int> cell;
cell.data = 0; // \
- // expected-warning {{writing variable 'data' requires locking 'mu_' exclusively}}
+ // expected-warning {{writing variable 'data' requires locking 'cell.mu_' exclusively}}
cell.foo();
cell.mu_.Lock();
cell.fooEx();
@@ -1883,7 +1906,7 @@ void Foo::foo1(Foo *f_defined) {
void test() {
Foo myfoo;
myfoo.foo1(&myfoo); // \
- // expected-warning {{calling function 'foo1' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'foo1' requires exclusive lock on 'myfoo.mu_'}}
myfoo.mu_.Lock();
myfoo.foo1(&myfoo);
myfoo.mu_.Unlock();
@@ -1998,29 +2021,28 @@ void test() {
Foo myFoo;
myFoo.foo2(); // \
- // expected-warning {{calling function 'foo2' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'foo2' requires exclusive lock on 'myFoo.mu_'}}
myFoo.foo3(&myFoo); // \
- // expected-warning {{calling function 'foo3' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'foo3' requires exclusive lock on 'myFoo.mu_'}}
myFoo.fooT1(dummy); // \
- // expected-warning {{calling function 'fooT1' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'fooT1' requires exclusive lock on 'myFoo.mu_'}}
- // FIXME: uncomment with template instantiation of attributes patch
- // myFoo.fooT2(dummy); // expected warning
+ myFoo.fooT2(dummy); // \
+ // expected-warning {{calling function 'fooT2' requires exclusive lock on 'myFoo.mu_'}}
fooF1(&myFoo); // \
- // expected-warning {{calling function 'fooF1' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'fooF1' requires exclusive lock on 'myFoo.mu_'}}
fooF2(&myFoo); // \
- // expected-warning {{calling function 'fooF2' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'fooF2' requires exclusive lock on 'myFoo.mu_'}}
fooF3(&myFoo); // \
- // expected-warning {{calling function 'fooF3' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'fooF3' requires exclusive lock on 'myFoo.mu_'}}
myFoo.mu_.Lock();
myFoo.foo2();
myFoo.foo3(&myFoo);
myFoo.fooT1(dummy);
- // FIXME: uncomment with template instantiation of attributes patch
- // myFoo.fooT2(dummy);
+ myFoo.fooT2(dummy);
fooF1(&myFoo);
fooF2(&myFoo);
@@ -2029,7 +2051,7 @@ void test() {
FooT<int> myFooT;
myFooT.foo(); // \
- // expected-warning {{calling function 'foo' requires exclusive lock on 'mu_'}}
+ // expected-warning {{calling function 'foo' requires exclusive lock on 'myFooT.mu_'}}
}
} // end namespace FunctionDefinitionTest
@@ -2213,31 +2235,887 @@ void test() {
bar.getFoo().mu_.Lock();
bar.getFooey().a = 0; // \
- // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ // expected-warning {{writing variable 'a' requires locking 'bar.getFooey().mu_' exclusively}}
bar.getFoo().mu_.Unlock();
bar.getFoo2(a).mu_.Lock();
bar.getFoo2(b).a = 0; // \
- // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ // expected-warning {{writing variable 'a' requires locking 'bar.getFoo2(b).mu_' exclusively}}
bar.getFoo2(a).mu_.Unlock();
bar.getFoo3(a, b).mu_.Lock();
bar.getFoo3(a, c).a = 0; // \
- // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ // expected-warning {{writing variable 'a' requires locking 'bar.getFoo3(a,c).mu_' exclusively}}
bar.getFoo3(a, b).mu_.Unlock();
getBarFoo(bar, a).mu_.Lock();
getBarFoo(bar, b).a = 0; // \
- // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ // expected-warning {{writing variable 'a' requires locking 'getBarFoo(bar,b).mu_' exclusively}}
getBarFoo(bar, a).mu_.Unlock();
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
(a > 0 ? fooArray[b] : fooArray[c]).a = 0; // \
- // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ // expected-warning {{writing variable 'a' requires locking '((a#_)#_#fooArray[b]).mu_' exclusively}}
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
}
-} // end namespace
+} // end namespace MoreLockExpressions
+
+
+namespace TrylockJoinPoint {
+
+class Foo {
+ Mutex mu;
+ bool c;
+
+ void foo() {
+ if (c) {
+ if (!mu.TryLock())
+ return;
+ } else {
+ mu.Lock();
+ }
+ mu.Unlock();
+ }
+};
+
+} // end namespace TrylockJoinPoint
+
+
+namespace LockReturned {
+
+class Foo {
+public:
+ int a GUARDED_BY(mu_);
+ void foo() EXCLUSIVE_LOCKS_REQUIRED(mu_);
+ void foo2(Foo* f) EXCLUSIVE_LOCKS_REQUIRED(mu_, f->mu_);
+
+ static void sfoo(Foo* f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_);
+
+ Mutex* getMu() LOCK_RETURNED(mu_);
+
+ Mutex mu_;
+
+ static Mutex* getMu(Foo* f) LOCK_RETURNED(f->mu_);
+};
+
+
+// Calls getMu() directly to lock and unlock
+void test1(Foo* f1, Foo* f2) {
+ f1->a = 0; // expected-warning {{writing variable 'a' requires locking 'f1->mu_' exclusively}}
+ f1->foo(); // expected-warning {{calling function 'foo' requires exclusive lock on 'f1->mu_'}}
+
+ f1->foo2(f2); // expected-warning {{calling function 'foo2' requires exclusive lock on 'f1->mu_'}} \
+ // expected-warning {{calling function 'foo2' requires exclusive lock on 'f2->mu_'}}
+ Foo::sfoo(f1); // expected-warning {{calling function 'sfoo' requires exclusive lock on 'f1->mu_'}}
+
+ f1->getMu()->Lock();
+
+ f1->a = 0;
+ f1->foo();
+ f1->foo2(f2); // expected-warning {{calling function 'foo2' requires exclusive lock on 'f2->mu_'}}
+
+ Foo::getMu(f2)->Lock();
+ f1->foo2(f2);
+ Foo::getMu(f2)->Unlock();
+
+ Foo::sfoo(f1);
+
+ f1->getMu()->Unlock();
+}
+
+
+Mutex* getFooMu(Foo* f) LOCK_RETURNED(Foo::getMu(f));
+
+class Bar : public Foo {
+public:
+ int b GUARDED_BY(getMu());
+ void bar() EXCLUSIVE_LOCKS_REQUIRED(getMu());
+ void bar2(Bar* g) EXCLUSIVE_LOCKS_REQUIRED(getMu(this), g->getMu());
+
+ static void sbar(Bar* g) EXCLUSIVE_LOCKS_REQUIRED(g->getMu());
+ static void sbar2(Bar* g) EXCLUSIVE_LOCKS_REQUIRED(getFooMu(g));
+};
+
+
+
+// Use getMu() within other attributes.
+// This requires at lest levels of substitution, more in the case of
+void test2(Bar* b1, Bar* b2) {
+ b1->b = 0; // expected-warning {{writing variable 'b' requires locking 'b1->mu_' exclusively}}
+ b1->bar(); // expected-warning {{calling function 'bar' requires exclusive lock on 'b1->mu_'}}
+ b1->bar2(b2); // expected-warning {{calling function 'bar2' requires exclusive lock on 'b1->mu_'}} \
+ // expected-warning {{calling function 'bar2' requires exclusive lock on 'b2->mu_'}}
+ Bar::sbar(b1); // expected-warning {{calling function 'sbar' requires exclusive lock on 'b1->mu_'}}
+ Bar::sbar2(b1); // expected-warning {{calling function 'sbar2' requires exclusive lock on 'b1->mu_'}}
+
+ b1->getMu()->Lock();
+
+ b1->b = 0;
+ b1->bar();
+ b1->bar2(b2); // expected-warning {{calling function 'bar2' requires exclusive lock on 'b2->mu_'}}
+
+ b2->getMu()->Lock();
+ b1->bar2(b2);
+
+ b2->getMu()->Unlock();
+
+ Bar::sbar(b1);
+ Bar::sbar2(b1);
+
+ b1->getMu()->Unlock();
+}
+
+
+// Sanity check -- lock the mutex directly, but use attributes that call getMu()
+// Also lock the mutex using getFooMu, which calls a lock_returned function.
+void test3(Bar* b1, Bar* b2) {
+ b1->mu_.Lock();
+ b1->b = 0;
+ b1->bar();
+
+ getFooMu(b2)->Lock();
+ b1->bar2(b2);
+ getFooMu(b2)->Unlock();
+
+ Bar::sbar(b1);
+ Bar::sbar2(b1);
+
+ b1->mu_.Unlock();
+}
+
+} // end namespace LockReturned
+
+
+namespace ReleasableScopedLock {
+
+class Foo {
+ Mutex mu_;
+ bool c;
+ int a GUARDED_BY(mu_);
+
+ void test1();
+ void test2();
+ void test3();
+ void test4();
+ void test5();
+};
+
+
+void Foo::test1() {
+ ReleasableMutexLock rlock(&mu_);
+ rlock.Release();
+}
+
+void Foo::test2() {
+ ReleasableMutexLock rlock(&mu_);
+ if (c) { // test join point -- held/not held during release
+ rlock.Release();
+ }
+}
+
+void Foo::test3() {
+ ReleasableMutexLock rlock(&mu_);
+ a = 0;
+ rlock.Release();
+ a = 1; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+}
+
+void Foo::test4() {
+ ReleasableMutexLock rlock(&mu_);
+ rlock.Release();
+ rlock.Release(); // expected-warning {{unlocking 'mu_' that was not locked}}
+}
+
+void Foo::test5() {
+ ReleasableMutexLock rlock(&mu_);
+ if (c) {
+ rlock.Release();
+ }
+ // no warning on join point for managed lock.
+ rlock.Release(); // expected-warning {{unlocking 'mu_' that was not locked}}
+}
+
+
+} // end namespace ReleasableScopedLock
+
+
+namespace TrylockFunctionTest {
+
+class Foo {
+public:
+ Mutex mu1_;
+ Mutex mu2_;
+ bool c;
+
+ bool lockBoth() EXCLUSIVE_TRYLOCK_FUNCTION(true, mu1_, mu2_);
+};
+
+bool Foo::lockBoth() {
+ if (!mu1_.TryLock())
+ return false;
+
+ mu2_.Lock();
+ if (!c) {
+ mu1_.Unlock();
+ mu2_.Unlock();
+ return false;
+ }
+
+ return true;
+}
+
+
+} // end namespace TrylockFunctionTest
+
+
+
+namespace DoubleLockBug {
+
+class Foo {
+public:
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+
+ void foo1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
+ int foo2() SHARED_LOCKS_REQUIRED(mu_);
+};
+
+
+void Foo::foo1() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ a = 0;
+}
+
+int Foo::foo2() SHARED_LOCKS_REQUIRED(mu_) {
+ return a;
+}
+
+}
+
+
+
+namespace UnlockBug {
+
+class Foo {
+public:
+ Mutex mutex_;
+
+ void foo1() EXCLUSIVE_LOCKS_REQUIRED(mutex_) { // expected-note {{mutex acquired here}}
+ mutex_.Unlock();
+ } // expected-warning {{expecting mutex 'mutex_' to be locked at the end of function}}
+
+
+ void foo2() SHARED_LOCKS_REQUIRED(mutex_) { // expected-note {{mutex acquired here}}
+ mutex_.Unlock();
+ } // expected-warning {{expecting mutex 'mutex_' to be locked at the end of function}}
+};
+
+} // end namespace UnlockBug
+
+
+
+namespace FoolishScopedLockableBug {
+
+class SCOPED_LOCKABLE WTF_ScopedLockable {
+public:
+ WTF_ScopedLockable(Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu);
+
+ // have to call release() manually;
+ ~WTF_ScopedLockable();
+
+ void release() UNLOCK_FUNCTION();
+};
+
+
+class Foo {
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+ bool c;
+
+ void doSomething();
+
+ void test1() {
+ WTF_ScopedLockable wtf(&mu_);
+ wtf.release();
+ }
+
+ void test2() {
+ WTF_ScopedLockable wtf(&mu_); // expected-note {{mutex acquired here}}
+ } // expected-warning {{mutex 'mu_' is still locked at the end of function}}
+
+ void test3() {
+ if (c) {
+ WTF_ScopedLockable wtf(&mu_);
+ wtf.release();
+ }
+ }
+
+ void test4() {
+ if (c) {
+ doSomething();
+ }
+ else {
+ WTF_ScopedLockable wtf(&mu_);
+ wtf.release();
+ }
+ }
+
+ void test5() {
+ if (c) {
+ WTF_ScopedLockable wtf(&mu_); // expected-note {{mutex acquired here}}
+ }
+ } // expected-warning {{mutex 'mu_' is not locked on every path through here}}
+
+ void test6() {
+ if (c) {
+ doSomething();
+ }
+ else {
+ WTF_ScopedLockable wtf(&mu_); // expected-note {{mutex acquired here}}
+ }
+ } // expected-warning {{mutex 'mu_' is not locked on every path through here}}
+};
+
+
+} // end namespace FoolishScopedLockableBug
+
+
+
+namespace TemporaryCleanupExpr {
+
+class Foo {
+ int a GUARDED_BY(getMutexPtr().get());
+
+ SmartPtr<Mutex> getMutexPtr();
+
+ void test();
+};
+
+
+void Foo::test() {
+ {
+ ReaderMutexLock lock(getMutexPtr().get());
+ int b = a;
+ }
+ int b = a; // expected-warning {{reading variable 'a' requires locking 'getMutexPtr()'}}
+}
+
+} // end namespace TemporaryCleanupExpr
+
+
+
+namespace SmartPointerTests {
+
+class Foo {
+public:
+ SmartPtr<Mutex> mu_;
+ int a GUARDED_BY(mu_);
+ int b GUARDED_BY(mu_.get());
+ int c GUARDED_BY(*mu_);
+
+ void Lock() EXCLUSIVE_LOCK_FUNCTION(mu_);
+ void Unlock() UNLOCK_FUNCTION(mu_);
+
+ void test0();
+ void test1();
+ void test2();
+ void test3();
+ void test4();
+ void test5();
+ void test6();
+ void test7();
+ void test8();
+};
+
+void Foo::test0() {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ b = 0; // expected-warning {{writing variable 'b' requires locking 'mu_' exclusively}}
+ c = 0; // expected-warning {{writing variable 'c' requires locking 'mu_' exclusively}}
+}
+
+void Foo::test1() {
+ mu_->Lock();
+ a = 0;
+ b = 0;
+ c = 0;
+ mu_->Unlock();
+}
+
+void Foo::test2() {
+ (*mu_).Lock();
+ a = 0;
+ b = 0;
+ c = 0;
+ (*mu_).Unlock();
+}
+
+
+void Foo::test3() {
+ mu_.get()->Lock();
+ a = 0;
+ b = 0;
+ c = 0;
+ mu_.get()->Unlock();
+}
+
+
+void Foo::test4() {
+ MutexLock lock(mu_.get());
+ a = 0;
+ b = 0;
+ c = 0;
+}
+
+
+void Foo::test5() {
+ MutexLock lock(&(*mu_));
+ a = 0;
+ b = 0;
+ c = 0;
+}
+
+
+void Foo::test6() {
+ Lock();
+ a = 0;
+ b = 0;
+ c = 0;
+ Unlock();
+}
+
+
+void Foo::test7() {
+ {
+ Lock();
+ mu_->Unlock();
+ }
+ {
+ mu_->Lock();
+ Unlock();
+ }
+ {
+ mu_.get()->Lock();
+ mu_->Unlock();
+ }
+ {
+ mu_->Lock();
+ mu_.get()->Unlock();
+ }
+ {
+ mu_.get()->Lock();
+ (*mu_).Unlock();
+ }
+ {
+ (*mu_).Lock();
+ mu_->Unlock();
+ }
+}
+
+
+void Foo::test8() {
+ mu_->Lock();
+ mu_.get()->Lock(); // expected-warning {{locking 'mu_' that is already locked}}
+ (*mu_).Lock(); // expected-warning {{locking 'mu_' that is already locked}}
+ mu_.get()->Unlock();
+ Unlock(); // expected-warning {{unlocking 'mu_' that was not locked}}
+}
+
+
+class Bar {
+ SmartPtr<Foo> foo;
+
+ void test0();
+ void test1();
+ void test2();
+ void test3();
+};
+
+
+void Bar::test0() {
+ foo->a = 0; // expected-warning {{writing variable 'a' requires locking 'foo->mu_' exclusively}}
+ (*foo).b = 0; // expected-warning {{writing variable 'b' requires locking 'foo->mu_' exclusively}}
+ foo.get()->c = 0; // expected-warning {{writing variable 'c' requires locking 'foo->mu_' exclusively}}
+}
+
+
+void Bar::test1() {
+ foo->mu_->Lock();
+ foo->a = 0;
+ (*foo).b = 0;
+ foo.get()->c = 0;
+ foo->mu_->Unlock();
+}
+
+
+void Bar::test2() {
+ (*foo).mu_->Lock();
+ foo->a = 0;
+ (*foo).b = 0;
+ foo.get()->c = 0;
+ foo.get()->mu_->Unlock();
+}
+
+
+void Bar::test3() {
+ MutexLock lock(foo->mu_.get());
+ foo->a = 0;
+ (*foo).b = 0;
+ foo.get()->c = 0;
+}
+
+} // end namespace SmartPointerTests
+
+
+
+namespace DuplicateAttributeTest {
+
+class LOCKABLE Foo {
+public:
+ Mutex mu1_;
+ Mutex mu2_;
+ Mutex mu3_;
+ int a GUARDED_BY(mu1_);
+ int b GUARDED_BY(mu2_);
+ int c GUARDED_BY(mu3_);
+
+ void lock() EXCLUSIVE_LOCK_FUNCTION();
+ void unlock() UNLOCK_FUNCTION();
+
+ void lock1() EXCLUSIVE_LOCK_FUNCTION(mu1_);
+ void slock1() SHARED_LOCK_FUNCTION(mu1_);
+ void lock3() EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_);
+ void locklots()
+ EXCLUSIVE_LOCK_FUNCTION(mu1_)
+ EXCLUSIVE_LOCK_FUNCTION(mu2_)
+ EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_);
+
+ void unlock1() UNLOCK_FUNCTION(mu1_);
+ void unlock3() UNLOCK_FUNCTION(mu1_, mu2_, mu3_);
+ void unlocklots()
+ UNLOCK_FUNCTION(mu1_)
+ UNLOCK_FUNCTION(mu2_)
+ UNLOCK_FUNCTION(mu1_, mu2_, mu3_);
+};
+
+
+void Foo::lock() EXCLUSIVE_LOCK_FUNCTION() { }
+void Foo::unlock() UNLOCK_FUNCTION() { }
+
+void Foo::lock1() EXCLUSIVE_LOCK_FUNCTION(mu1_) {
+ mu1_.Lock();
+}
+
+void Foo::slock1() SHARED_LOCK_FUNCTION(mu1_) {
+ mu1_.Lock();
+}
+
+void Foo::lock3() EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_) {
+ mu1_.Lock();
+ mu2_.Lock();
+ mu3_.Lock();
+}
+
+void Foo::locklots()
+ EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_)
+ EXCLUSIVE_LOCK_FUNCTION(mu2_, mu3_) {
+ mu1_.Lock();
+ mu2_.Lock();
+ mu3_.Lock();
+}
+
+void Foo::unlock1() UNLOCK_FUNCTION(mu1_) {
+ mu1_.Unlock();
+}
+
+void Foo::unlock3() UNLOCK_FUNCTION(mu1_, mu2_, mu3_) {
+ mu1_.Unlock();
+ mu2_.Unlock();
+ mu3_.Unlock();
+}
+
+void Foo::unlocklots()
+ UNLOCK_FUNCTION(mu1_, mu2_)
+ UNLOCK_FUNCTION(mu2_, mu3_) {
+ mu1_.Unlock();
+ mu2_.Unlock();
+ mu3_.Unlock();
+}
+
+
+void test0() {
+ Foo foo;
+ foo.lock();
+ foo.unlock();
+
+ foo.lock();
+ foo.lock(); // expected-warning {{locking 'foo' that is already locked}}
+ foo.unlock();
+ foo.unlock(); // expected-warning {{unlocking 'foo' that was not locked}}
+}
+
+
+void test1() {
+ Foo foo;
+ foo.lock1();
+ foo.a = 0;
+ foo.unlock1();
+
+ foo.lock1();
+ foo.lock1(); // expected-warning {{locking 'foo.mu1_' that is already locked}}
+ foo.a = 0;
+ foo.unlock1();
+ foo.unlock1(); // expected-warning {{unlocking 'foo.mu1_' that was not locked}}
+}
+
+
+int test2() {
+ Foo foo;
+ foo.slock1();
+ int d1 = foo.a;
+ foo.unlock1();
+
+ foo.slock1();
+ foo.slock1(); // expected-warning {{locking 'foo.mu1_' that is already locked}}
+ int d2 = foo.a;
+ foo.unlock1();
+ foo.unlock1(); // expected-warning {{unlocking 'foo.mu1_' that was not locked}}
+ return d1 + d2;
+}
+
+
+void test3() {
+ Foo foo;
+ foo.lock3();
+ foo.a = 0;
+ foo.b = 0;
+ foo.c = 0;
+ foo.unlock3();
+
+ foo.lock3();
+ foo.lock3(); // \
+ // expected-warning {{locking 'foo.mu1_' that is already locked}} \
+ // expected-warning {{locking 'foo.mu2_' that is already locked}} \
+ // expected-warning {{locking 'foo.mu3_' that is already locked}}
+ foo.a = 0;
+ foo.b = 0;
+ foo.c = 0;
+ foo.unlock3();
+ foo.unlock3(); // \
+ // expected-warning {{unlocking 'foo.mu1_' that was not locked}} \
+ // expected-warning {{unlocking 'foo.mu2_' that was not locked}} \
+ // expected-warning {{unlocking 'foo.mu3_' that was not locked}}
+}
+
+
+void testlots() {
+ Foo foo;
+ foo.locklots();
+ foo.a = 0;
+ foo.b = 0;
+ foo.c = 0;
+ foo.unlocklots();
+
+ foo.locklots();
+ foo.locklots(); // \
+ // expected-warning {{locking 'foo.mu1_' that is already locked}} \
+ // expected-warning {{locking 'foo.mu2_' that is already locked}} \
+ // expected-warning {{locking 'foo.mu3_' that is already locked}}
+ foo.a = 0;
+ foo.b = 0;
+ foo.c = 0;
+ foo.unlocklots();
+ foo.unlocklots(); // \
+ // expected-warning {{unlocking 'foo.mu1_' that was not locked}} \
+ // expected-warning {{unlocking 'foo.mu2_' that was not locked}} \
+ // expected-warning {{unlocking 'foo.mu3_' that was not locked}}
+}
+
+} // end namespace DuplicateAttributeTest
+
+
+
+namespace TryLockEqTest {
+
+class Foo {
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+ bool c;
+
+ int tryLockMutexI() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu_);
+ Mutex* tryLockMutexP() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu_);
+ void unlock() UNLOCK_FUNCTION(mu_);
+
+ void test1();
+ void test2();
+};
+
+
+void Foo::test1() {
+ if (tryLockMutexP() == 0) {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ return;
+ }
+ a = 0;
+ unlock();
+
+ if (tryLockMutexP() != 0) {
+ a = 0;
+ unlock();
+ }
+
+ if (0 != tryLockMutexP()) {
+ a = 0;
+ unlock();
+ }
+
+ if (!(tryLockMutexP() == 0)) {
+ a = 0;
+ unlock();
+ }
+
+ if (tryLockMutexI() == 0) {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ return;
+ }
+ a = 0;
+ unlock();
+
+ if (0 == tryLockMutexI()) {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ return;
+ }
+ a = 0;
+ unlock();
+
+ if (tryLockMutexI() == 1) {
+ a = 0;
+ unlock();
+ }
+
+ if (mu_.TryLock() == false) {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ return;
+ }
+ a = 0;
+ unlock();
+
+ if (mu_.TryLock() == true) {
+ a = 0;
+ unlock();
+ }
+ else {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ }
+
+#if __has_feature(cxx_nullptr)
+ if (tryLockMutexP() == nullptr) {
+ a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ return;
+ }
+ a = 0;
+ unlock();
+#endif
+}
+
+
+void Foo::test2() {
+/* FIXME: these tests depend on changes to the CFG.
+ *
+ if (mu_.TryLock() && c) {
+ a = 0;
+ unlock();
+ }
+ else return;
+
+ if (c && mu_.TryLock()) {
+ a = 0;
+ unlock();
+ }
+ else return;
+
+ if (!(mu_.TryLock() && c))
+ return;
+ a = 0;
+ unlock();
+
+ if (!(c && mu_.TryLock()))
+ return;
+ a = 0;
+ unlock();
+
+ if (!(mu_.TryLock() == 0) && c) {
+ a = 0;
+ unlock();
+ }
+
+ if (!mu_.TryLock() || c)
+ return;
+ a = 0;
+ unlock();
+*/
+}
+
+} // end namespace TryLockEqTest
+
+
+namespace ExistentialPatternMatching {
+
+class Graph {
+public:
+ Mutex mu_;
+};
+
+void LockAllGraphs() EXCLUSIVE_LOCK_FUNCTION(&Graph::mu_);
+void UnlockAllGraphs() UNLOCK_FUNCTION(&Graph::mu_);
+
+class Node {
+public:
+ int a GUARDED_BY(&Graph::mu_);
+
+ void foo() EXCLUSIVE_LOCKS_REQUIRED(&Graph::mu_) {
+ a = 0;
+ }
+ void foo2() LOCKS_EXCLUDED(&Graph::mu_);
+};
+
+void test() {
+ Graph g1;
+ Graph g2;
+ Node n1;
+
+ n1.a = 0; // expected-warning {{writing variable 'a' requires locking '&ExistentialPatternMatching::Graph::mu_' exclusively}}
+ n1.foo(); // expected-warning {{calling function 'foo' requires exclusive lock on '&ExistentialPatternMatching::Graph::mu_'}}
+ n1.foo2();
+
+ g1.mu_.Lock();
+ n1.a = 0;
+ n1.foo();
+ n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is locked}}
+ g1.mu_.Unlock();
+
+ g2.mu_.Lock();
+ n1.a = 0;
+ n1.foo();
+ n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is locked}}
+ g2.mu_.Unlock();
+
+ LockAllGraphs();
+ n1.a = 0;
+ n1.foo();
+ n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is locked}}
+ UnlockAllGraphs();
+
+ LockAllGraphs();
+ g1.mu_.Unlock();
+
+ LockAllGraphs();
+ g2.mu_.Unlock();
+
+ LockAllGraphs();
+ g1.mu_.Lock(); // expected-warning {{locking 'g1.mu_' that is already locked}}
+ g1.mu_.Unlock();
+}
+} // end namespace ExistentialPatternMatching
diff --git a/test/SemaCXX/warn-thread-safety-parsing.cpp b/test/SemaCXX/warn-thread-safety-parsing.cpp
index c2fa1d7..3f8a735 100644
--- a/test/SemaCXX/warn-thread-safety-parsing.cpp
+++ b/test/SemaCXX/warn-thread-safety-parsing.cpp
@@ -22,7 +22,7 @@
#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis))
-class __attribute__((lockable)) Mu {
+class LOCKABLE Mutex {
public:
void Lock();
};
@@ -32,11 +32,11 @@ class UnlockableMu{
class MuWrapper {
public:
- Mu mu;
- Mu getMu() {
+ Mutex mu;
+ Mutex getMu() {
return mu;
}
- Mu * getMuPointer() {
+ Mutex * getMuPointer() {
return &mu;
}
};
@@ -50,32 +50,32 @@ class MuDoubleWrapper {
}
};
-Mu mu1;
+Mutex mu1;
UnlockableMu umu;
-Mu mu2;
+Mutex mu2;
MuWrapper muWrapper;
MuDoubleWrapper muDoubleWrapper;
-Mu* muPointer;
-Mu ** muDoublePointer = & muPointer;
-Mu& muRef = mu1;
+Mutex* muPointer;
+Mutex** muDoublePointer = & muPointer;
+Mutex& muRef = mu1;
//---------------------------------------//
// Scoping tests
//--------------------------------------//
class Foo {
- Mu foomu;
- void needLock() __attribute__((exclusive_lock_function(foomu)));
+ Mutex foomu;
+ void needLock() EXCLUSIVE_LOCK_FUNCTION(foomu);
};
class Foo2 {
- void needLock() __attribute__((exclusive_lock_function(foomu)));
- Mu foomu;
+ void needLock() EXCLUSIVE_LOCK_FUNCTION(foomu);
+ Mutex foomu;
};
class Bar {
- Mu barmu;
- Mu barmu2 __attribute__((acquired_after(barmu)));
+ Mutex barmu;
+ Mutex barmu2 ACQUIRED_AFTER(barmu);
};
@@ -90,34 +90,34 @@ class Bar {
#error "Should support no_thread_safety_analysis attribute"
#endif
-void noanal_fun() __attribute__((no_thread_safety_analysis));
+void noanal_fun() NO_THREAD_SAFETY_ANALYSIS;
void noanal_fun_args() __attribute__((no_thread_safety_analysis(1))); // \
// expected-error {{attribute takes no arguments}}
-int noanal_testfn(int y) __attribute__((no_thread_safety_analysis));
+int noanal_testfn(int y) NO_THREAD_SAFETY_ANALYSIS;
int noanal_testfn(int y) {
- int x __attribute__((no_thread_safety_analysis)) = y; // \
+ int x NO_THREAD_SAFETY_ANALYSIS = y; // \
// expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
return x;
};
-int noanal_test_var __attribute__((no_thread_safety_analysis)); // \
+int noanal_test_var NO_THREAD_SAFETY_ANALYSIS; // \
// expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
class NoanalFoo {
private:
- int test_field __attribute__((no_thread_safety_analysis)); // \
+ int test_field NO_THREAD_SAFETY_ANALYSIS; // \
// expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
- void test_method() __attribute__((no_thread_safety_analysis));
+ void test_method() NO_THREAD_SAFETY_ANALYSIS;
};
-class __attribute__((no_thread_safety_analysis)) NoanalTestClass { // \
+class NO_THREAD_SAFETY_ANALYSIS NoanalTestClass { // \
// expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
};
-void noanal_fun_params(int lvar __attribute__((no_thread_safety_analysis))); // \
+void noanal_fun_params(int lvar NO_THREAD_SAFETY_ANALYSIS); // \
// expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
@@ -129,30 +129,30 @@ void noanal_fun_params(int lvar __attribute__((no_thread_safety_analysis))); //
#error "Should support guarded_var attribute"
#endif
-int gv_var_noargs __attribute__((guarded_var));
+int gv_var_noargs GUARDED_VAR;
int gv_var_args __attribute__((guarded_var(1))); // \
// expected-error {{attribute takes no arguments}}
class GVFoo {
private:
- int gv_field_noargs __attribute__((guarded_var));
+ int gv_field_noargs GUARDED_VAR;
int gv_field_args __attribute__((guarded_var(1))); // \
// expected-error {{attribute takes no arguments}}
};
-class __attribute__((guarded_var)) GV { // \
+class GUARDED_VAR GV { // \
// expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
};
-void gv_function() __attribute__((guarded_var)); // \
+void gv_function() GUARDED_VAR; // \
// expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-void gv_function_params(int gv_lvar __attribute__((guarded_var))); // \
+void gv_function_params(int gv_lvar GUARDED_VAR); // \
// expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
int gv_testfn(int y){
- int x __attribute__((guarded_var)) = y; // \
+ int x GUARDED_VAR = y; // \
// expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
return x;
}
@@ -167,21 +167,21 @@ int gv_testfn(int y){
#error "Should support pt_guarded_var attribute"
#endif
-int *pgv_pt_var_noargs __attribute__((pt_guarded_var));
+int *pgv_pt_var_noargs PT_GUARDED_VAR;
-int pgv_var_noargs __attribute__((pt_guarded_var)); // \
+int pgv_var_noargs PT_GUARDED_VAR; // \
// expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
class PGVFoo {
private:
- int *pt_field_noargs __attribute__((pt_guarded_var));
- int field_noargs __attribute__((pt_guarded_var)); // \
+ int *pt_field_noargs PT_GUARDED_VAR;
+ int field_noargs PT_GUARDED_VAR; // \
// expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
int *gv_field_args __attribute__((pt_guarded_var(1))); // \
// expected-error {{attribute takes no arguments}}
};
-class __attribute__((pt_guarded_var)) PGV { // \
+class PT_GUARDED_VAR PGV { // \
// expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
};
@@ -189,14 +189,14 @@ int *pgv_var_args __attribute__((pt_guarded_var(1))); // \
// expected-error {{attribute takes no arguments}}
-void pgv_function() __attribute__((pt_guarded_var)); // \
+void pgv_function() PT_GUARDED_VAR; // \
// expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-void pgv_function_params(int *gv_lvar __attribute__((pt_guarded_var))); // \
+void pgv_function_params(int *gv_lvar PT_GUARDED_VAR); // \
// expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
void pgv_testfn(int y){
- int *x __attribute__((pt_guarded_var)) = new int(0); // \
+ int *x PT_GUARDED_VAR = new int(0); // \
// expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
delete x;
}
@@ -211,35 +211,35 @@ void pgv_testfn(int y){
#error "Should support lockable attribute"
#endif
-class __attribute__((lockable)) LTestClass {
+class LOCKABLE LTestClass {
};
class __attribute__((lockable (1))) LTestClass_args { // \
// expected-error {{attribute takes no arguments}}
};
-void l_test_function() __attribute__((lockable)); // \
+void l_test_function() LOCKABLE; // \
// expected-warning {{'lockable' attribute only applies to classes}}
int l_testfn(int y) {
- int x __attribute__((lockable)) = y; // \
+ int x LOCKABLE = y; // \
// expected-warning {{'lockable' attribute only applies to classes}}
return x;
}
-int l_test_var __attribute__((lockable)); // \
+int l_test_var LOCKABLE; // \
// expected-warning {{'lockable' attribute only applies to classes}}
class LFoo {
private:
- int test_field __attribute__((lockable)); // \
+ int test_field LOCKABLE; // \
// expected-warning {{'lockable' attribute only applies to classes}}
- void test_method() __attribute__((lockable)); // \
+ void test_method() LOCKABLE; // \
// expected-warning {{'lockable' attribute only applies to classes}}
};
-void l_function_params(int lvar __attribute__((lockable))); // \
+void l_function_params(int lvar LOCKABLE); // \
// expected-warning {{'lockable' attribute only applies to classes}}
@@ -251,35 +251,35 @@ void l_function_params(int lvar __attribute__((lockable))); // \
#error "Should support scoped_lockable attribute"
#endif
-class __attribute__((scoped_lockable)) SLTestClass {
+class SCOPED_LOCKABLE SLTestClass {
};
class __attribute__((scoped_lockable (1))) SLTestClass_args { // \
// expected-error {{attribute takes no arguments}}
};
-void sl_test_function() __attribute__((scoped_lockable)); // \
+void sl_test_function() SCOPED_LOCKABLE; // \
// expected-warning {{'scoped_lockable' attribute only applies to classes}}
int sl_testfn(int y) {
- int x __attribute__((scoped_lockable)) = y; // \
+ int x SCOPED_LOCKABLE = y; // \
// expected-warning {{'scoped_lockable' attribute only applies to classes}}
return x;
}
-int sl_test_var __attribute__((scoped_lockable)); // \
+int sl_test_var SCOPED_LOCKABLE; // \
// expected-warning {{'scoped_lockable' attribute only applies to classes}}
class SLFoo {
private:
- int test_field __attribute__((scoped_lockable)); // \
+ int test_field SCOPED_LOCKABLE; // \
// expected-warning {{'scoped_lockable' attribute only applies to classes}}
- void test_method() __attribute__((scoped_lockable)); // \
+ void test_method() SCOPED_LOCKABLE; // \
// expected-warning {{'scoped_lockable' attribute only applies to classes}}
};
-void sl_function_params(int lvar __attribute__((scoped_lockable))); // \
+void sl_function_params(int lvar SCOPED_LOCKABLE); // \
// expected-warning {{'scoped_lockable' attribute only applies to classes}}
@@ -295,7 +295,7 @@ void sl_function_params(int lvar __attribute__((scoped_lockable))); // \
//1. Check applied to the right types & argument number
-int gb_var_arg __attribute__((guarded_by(mu1)));
+int gb_var_arg GUARDED_BY(mu1);
int gb_var_args __attribute__((guarded_by(mu1, mu2))); // \
// expected-error {{attribute takes one argument}}
@@ -307,21 +307,21 @@ class GBFoo {
private:
int gb_field_noargs __attribute__((guarded_by)); // \
// expected-error {{attribute takes one argument}}
- int gb_field_args __attribute__((guarded_by(mu1)));
+ int gb_field_args GUARDED_BY(mu1);
};
-class __attribute__((guarded_by(mu1))) GB { // \
+class GUARDED_BY(mu1) GB { // \
// expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
};
-void gb_function() __attribute__((guarded_by(mu1))); // \
+void gb_function() GUARDED_BY(mu1); // \
// expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-void gb_function_params(int gv_lvar __attribute__((guarded_by(mu1)))); // \
+void gb_function_params(int gv_lvar GUARDED_BY(mu1)); // \
// expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
int gb_testfn(int y){
- int x __attribute__((guarded_by(mu1))) = y; // \
+ int x GUARDED_BY(mu1) = y; // \
// expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
return x;
}
@@ -329,24 +329,24 @@ int gb_testfn(int y){
//2. Check argument parsing.
// legal attribute arguments
-int gb_var_arg_1 __attribute__((guarded_by(muWrapper.mu)));
-int gb_var_arg_2 __attribute__((guarded_by(muDoubleWrapper.muWrapper->mu)));
-int gb_var_arg_3 __attribute__((guarded_by(muWrapper.getMu())));
-int gb_var_arg_4 __attribute__((guarded_by(*muWrapper.getMuPointer())));
-int gb_var_arg_5 __attribute__((guarded_by(&mu1)));
-int gb_var_arg_6 __attribute__((guarded_by(muRef)));
-int gb_var_arg_7 __attribute__((guarded_by(muDoubleWrapper.getWrapper()->getMu())));
-int gb_var_arg_8 __attribute__((guarded_by(muPointer)));
+int gb_var_arg_1 GUARDED_BY(muWrapper.mu);
+int gb_var_arg_2 GUARDED_BY(muDoubleWrapper.muWrapper->mu);
+int gb_var_arg_3 GUARDED_BY(muWrapper.getMu());
+int gb_var_arg_4 GUARDED_BY(*muWrapper.getMuPointer());
+int gb_var_arg_5 GUARDED_BY(&mu1);
+int gb_var_arg_6 GUARDED_BY(muRef);
+int gb_var_arg_7 GUARDED_BY(muDoubleWrapper.getWrapper()->getMu());
+int gb_var_arg_8 GUARDED_BY(muPointer);
// illegal attribute arguments
-int gb_var_arg_bad_1 __attribute__((guarded_by(1))); // \
+int gb_var_arg_bad_1 GUARDED_BY(1); // \
// expected-warning {{'guarded_by' attribute requires arguments that are class type or point to class type; type here is 'int'}}
-int gb_var_arg_bad_2 __attribute__((guarded_by("mu"))); // \
- // expected-warning {{'guarded_by' attribute requires arguments that are class type or point to class type; type here is 'const char [3]'}}
-int gb_var_arg_bad_3 __attribute__((guarded_by(muDoublePointer))); // \
- // expected-warning {{'guarded_by' attribute requires arguments that are class type or point to class type; type here is 'class Mu **'}}
-int gb_var_arg_bad_4 __attribute__((guarded_by(umu))); // \
+int gb_var_arg_bad_2 GUARDED_BY("mu"); // \
+ // expected-warning {{ignoring 'guarded_by' attribute because its argument is invalid}}
+int gb_var_arg_bad_3 GUARDED_BY(muDoublePointer); // \
+ // expected-warning {{'guarded_by' attribute requires arguments that are class type or point to class type; type here is 'class Mutex **'}}
+int gb_var_arg_bad_4 GUARDED_BY(umu); // \
// expected-warning {{'guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute; type here is 'class UnlockableMu'}}
//3.
@@ -366,33 +366,33 @@ int gb_var_arg_bad_4 __attribute__((guarded_by(umu))); // \
int *pgb_var_noargs __attribute__((pt_guarded_by)); // \
// expected-error {{attribute takes one argument}}
-int *pgb_ptr_var_arg __attribute__((pt_guarded_by(mu1)));
+int *pgb_ptr_var_arg PT_GUARDED_BY(mu1);
-int *pgb_ptr_var_args __attribute__((guarded_by(mu1, mu2))); // \
+int *pgb_ptr_var_args __attribute__((pt_guarded_by(mu1, mu2))); // \
// expected-error {{attribute takes one argument}}
-int pgb_var_args __attribute__((pt_guarded_by(mu1))); // \
+int pgb_var_args PT_GUARDED_BY(mu1); // \
// expected-warning {{'pt_guarded_by' only applies to pointer types; type here is 'int'}}
class PGBFoo {
private:
int *pgb_field_noargs __attribute__((pt_guarded_by)); // \
// expected-error {{attribute takes one argument}}
- int *pgb_field_args __attribute__((pt_guarded_by(mu1)));
+ int *pgb_field_args PT_GUARDED_BY(mu1);
};
-class __attribute__((pt_guarded_by(mu1))) PGB { // \
+class PT_GUARDED_BY(mu1) PGB { // \
// expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
};
-void pgb_function() __attribute__((pt_guarded_by(mu1))); // \
+void pgb_function() PT_GUARDED_BY(mu1); // \
// expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-void pgb_function_params(int gv_lvar __attribute__((pt_guarded_by(mu1)))); // \
+void pgb_function_params(int gv_lvar PT_GUARDED_BY(mu1)); // \
// expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
void pgb_testfn(int y){
- int *x __attribute__((pt_guarded_by(mu1))) = new int(0); // \
+ int *x PT_GUARDED_BY(mu1) = new int(0); // \
// expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
delete x;
}
@@ -400,24 +400,24 @@ void pgb_testfn(int y){
//2. Check argument parsing.
// legal attribute arguments
-int * pgb_var_arg_1 __attribute__((pt_guarded_by(muWrapper.mu)));
-int * pgb_var_arg_2 __attribute__((pt_guarded_by(muDoubleWrapper.muWrapper->mu)));
-int * pgb_var_arg_3 __attribute__((pt_guarded_by(muWrapper.getMu())));
-int * pgb_var_arg_4 __attribute__((pt_guarded_by(*muWrapper.getMuPointer())));
-int * pgb_var_arg_5 __attribute__((pt_guarded_by(&mu1)));
-int * pgb_var_arg_6 __attribute__((pt_guarded_by(muRef)));
-int * pgb_var_arg_7 __attribute__((pt_guarded_by(muDoubleWrapper.getWrapper()->getMu())));
-int * pgb_var_arg_8 __attribute__((pt_guarded_by(muPointer)));
+int * pgb_var_arg_1 PT_GUARDED_BY(muWrapper.mu);
+int * pgb_var_arg_2 PT_GUARDED_BY(muDoubleWrapper.muWrapper->mu);
+int * pgb_var_arg_3 PT_GUARDED_BY(muWrapper.getMu());
+int * pgb_var_arg_4 PT_GUARDED_BY(*muWrapper.getMuPointer());
+int * pgb_var_arg_5 PT_GUARDED_BY(&mu1);
+int * pgb_var_arg_6 PT_GUARDED_BY(muRef);
+int * pgb_var_arg_7 PT_GUARDED_BY(muDoubleWrapper.getWrapper()->getMu());
+int * pgb_var_arg_8 PT_GUARDED_BY(muPointer);
// illegal attribute arguments
-int * pgb_var_arg_bad_1 __attribute__((pt_guarded_by(1))); // \
+int * pgb_var_arg_bad_1 PT_GUARDED_BY(1); // \
// expected-warning {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_2 __attribute__((pt_guarded_by("mu"))); // \
+int * pgb_var_arg_bad_2 PT_GUARDED_BY("mu"); // \
+ // expected-warning {{ignoring 'pt_guarded_by' attribute because its argument is invalid}}
+int * pgb_var_arg_bad_3 PT_GUARDED_BY(muDoublePointer); // \
// expected-warning {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_3 __attribute__((pt_guarded_by(muDoublePointer))); // \
- // expected-warning {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_4 __attribute__((pt_guarded_by(umu))); // \
+int * pgb_var_arg_bad_4 PT_GUARDED_BY(umu); // \
// expected-warning {{'pt_guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -431,56 +431,56 @@ int * pgb_var_arg_bad_4 __attribute__((pt_guarded_by(umu))); // \
#error "Should support acquired_after attribute"
#endif
-Mu mu_aa __attribute__((acquired_after(mu1)));
+Mutex mu_aa ACQUIRED_AFTER(mu1);
-Mu aa_var_noargs __attribute__((acquired_after)); // \
+Mutex aa_var_noargs __attribute__((acquired_after)); // \
// expected-error {{attribute takes at least 1 argument}}
class AAFoo {
private:
- Mu aa_field_noargs __attribute__((acquired_after)); // \
+ Mutex aa_field_noargs __attribute__((acquired_after)); // \
// expected-error {{attribute takes at least 1 argument}}
- Mu aa_field_args __attribute__((acquired_after(mu1)));
+ Mutex aa_field_args ACQUIRED_AFTER(mu1);
};
-class __attribute__((acquired_after(mu1))) AA { // \
+class ACQUIRED_AFTER(mu1) AA { // \
// expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
};
-void aa_function() __attribute__((acquired_after(mu1))); // \
+void aa_function() ACQUIRED_AFTER(mu1); // \
// expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-void aa_function_params(int gv_lvar __attribute__((acquired_after(mu1)))); // \
+void aa_function_params(int gv_lvar ACQUIRED_AFTER(mu1)); // \
// expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
void aa_testfn(int y){
- Mu x __attribute__((acquired_after(mu1))) = Mu(); // \
+ Mutex x ACQUIRED_AFTER(mu1) = Mutex(); // \
// expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
}
//Check argument parsing.
// legal attribute arguments
-Mu aa_var_arg_1 __attribute__((acquired_after(muWrapper.mu)));
-Mu aa_var_arg_2 __attribute__((acquired_after(muDoubleWrapper.muWrapper->mu)));
-Mu aa_var_arg_3 __attribute__((acquired_after(muWrapper.getMu())));
-Mu aa_var_arg_4 __attribute__((acquired_after(*muWrapper.getMuPointer())));
-Mu aa_var_arg_5 __attribute__((acquired_after(&mu1)));
-Mu aa_var_arg_6 __attribute__((acquired_after(muRef)));
-Mu aa_var_arg_7 __attribute__((acquired_after(muDoubleWrapper.getWrapper()->getMu())));
-Mu aa_var_arg_8 __attribute__((acquired_after(muPointer)));
+Mutex aa_var_arg_1 ACQUIRED_AFTER(muWrapper.mu);
+Mutex aa_var_arg_2 ACQUIRED_AFTER(muDoubleWrapper.muWrapper->mu);
+Mutex aa_var_arg_3 ACQUIRED_AFTER(muWrapper.getMu());
+Mutex aa_var_arg_4 ACQUIRED_AFTER(*muWrapper.getMuPointer());
+Mutex aa_var_arg_5 ACQUIRED_AFTER(&mu1);
+Mutex aa_var_arg_6 ACQUIRED_AFTER(muRef);
+Mutex aa_var_arg_7 ACQUIRED_AFTER(muDoubleWrapper.getWrapper()->getMu());
+Mutex aa_var_arg_8 ACQUIRED_AFTER(muPointer);
// illegal attribute arguments
-Mu aa_var_arg_bad_1 __attribute__((acquired_after(1))); // \
- // expected-warning {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_2 __attribute__((acquired_after("mu"))); // \
+Mutex aa_var_arg_bad_1 ACQUIRED_AFTER(1); // \
// expected-warning {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_3 __attribute__((acquired_after(muDoublePointer))); // \
+Mutex aa_var_arg_bad_2 ACQUIRED_AFTER("mu"); // \
+ // expected-warning {{ignoring 'acquired_after' attribute because its argument is invalid}}
+Mutex aa_var_arg_bad_3 ACQUIRED_AFTER(muDoublePointer); // \
// expected-warning {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_4 __attribute__((acquired_after(umu))); // \
+Mutex aa_var_arg_bad_4 ACQUIRED_AFTER(umu); // \
// expected-warning {{'acquired_after' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-UnlockableMu aa_var_arg_bad_5 __attribute__((acquired_after(mu_aa))); // \
+UnlockableMu aa_var_arg_bad_5 ACQUIRED_AFTER(mu_aa); // \
// expected-warning {{'acquired_after' attribute can only be applied in a context annotated with 'lockable' attribute}}
//-----------------------------------------//
@@ -491,59 +491,59 @@ UnlockableMu aa_var_arg_bad_5 __attribute__((acquired_after(mu_aa))); // \
#error "Should support acquired_before attribute"
#endif
-Mu mu_ab __attribute__((acquired_before(mu1)));
+Mutex mu_ab ACQUIRED_BEFORE(mu1);
-Mu ab_var_noargs __attribute__((acquired_before)); // \
+Mutex ab_var_noargs __attribute__((acquired_before)); // \
// expected-error {{attribute takes at least 1 argument}}
class ABFoo {
private:
- Mu ab_field_noargs __attribute__((acquired_before)); // \
+ Mutex ab_field_noargs __attribute__((acquired_before)); // \
// expected-error {{attribute takes at least 1 argument}}
- Mu ab_field_args __attribute__((acquired_before(mu1)));
+ Mutex ab_field_args ACQUIRED_BEFORE(mu1);
};
-class __attribute__((acquired_before(mu1))) AB { // \
+class ACQUIRED_BEFORE(mu1) AB { // \
// expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
};
-void ab_function() __attribute__((acquired_before(mu1))); // \
+void ab_function() ACQUIRED_BEFORE(mu1); // \
// expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-void ab_function_params(int gv_lvar __attribute__((acquired_before(mu1)))); // \
+void ab_function_params(int gv_lvar ACQUIRED_BEFORE(mu1)); // \
// expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
void ab_testfn(int y){
- Mu x __attribute__((acquired_before(mu1))) = Mu(); // \
+ Mutex x ACQUIRED_BEFORE(mu1) = Mutex(); // \
// expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
}
-// Note: illegal int ab_int __attribute__((acquired_before(mu1))) will
+// Note: illegal int ab_int ACQUIRED_BEFORE(mu1) will
// be taken care of by warnings that ab__int is not lockable.
//Check argument parsing.
// legal attribute arguments
-Mu ab_var_arg_1 __attribute__((acquired_before(muWrapper.mu)));
-Mu ab_var_arg_2 __attribute__((acquired_before(muDoubleWrapper.muWrapper->mu)));
-Mu ab_var_arg_3 __attribute__((acquired_before(muWrapper.getMu())));
-Mu ab_var_arg_4 __attribute__((acquired_before(*muWrapper.getMuPointer())));
-Mu ab_var_arg_5 __attribute__((acquired_before(&mu1)));
-Mu ab_var_arg_6 __attribute__((acquired_before(muRef)));
-Mu ab_var_arg_7 __attribute__((acquired_before(muDoubleWrapper.getWrapper()->getMu())));
-Mu ab_var_arg_8 __attribute__((acquired_before(muPointer)));
+Mutex ab_var_arg_1 ACQUIRED_BEFORE(muWrapper.mu);
+Mutex ab_var_arg_2 ACQUIRED_BEFORE(muDoubleWrapper.muWrapper->mu);
+Mutex ab_var_arg_3 ACQUIRED_BEFORE(muWrapper.getMu());
+Mutex ab_var_arg_4 ACQUIRED_BEFORE(*muWrapper.getMuPointer());
+Mutex ab_var_arg_5 ACQUIRED_BEFORE(&mu1);
+Mutex ab_var_arg_6 ACQUIRED_BEFORE(muRef);
+Mutex ab_var_arg_7 ACQUIRED_BEFORE(muDoubleWrapper.getWrapper()->getMu());
+Mutex ab_var_arg_8 ACQUIRED_BEFORE(muPointer);
// illegal attribute arguments
-Mu ab_var_arg_bad_1 __attribute__((acquired_before(1))); // \
+Mutex ab_var_arg_bad_1 ACQUIRED_BEFORE(1); // \
// expected-warning {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_2 __attribute__((acquired_before("mu"))); // \
+Mutex ab_var_arg_bad_2 ACQUIRED_BEFORE("mu"); // \
+ // expected-warning {{ignoring 'acquired_before' attribute because its argument is invalid}}
+Mutex ab_var_arg_bad_3 ACQUIRED_BEFORE(muDoublePointer); // \
// expected-warning {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_3 __attribute__((acquired_before(muDoublePointer))); // \
- // expected-warning {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_4 __attribute__((acquired_before(umu))); // \
+Mutex ab_var_arg_bad_4 ACQUIRED_BEFORE(umu); // \
// expected-warning {{'acquired_before' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-UnlockableMu ab_var_arg_bad_5 __attribute__((acquired_before(mu_ab))); // \
+UnlockableMu ab_var_arg_bad_5 ACQUIRED_BEFORE(mu_ab); // \
// expected-warning {{'acquired_before' attribute can only be applied in a context annotated with 'lockable' attribute}}
@@ -557,65 +557,65 @@ UnlockableMu ab_var_arg_bad_5 __attribute__((acquired_before(mu_ab))); // \
// takes zero or more arguments, all locks (vars/fields)
-void elf_function() __attribute__((exclusive_lock_function));
+void elf_function() EXCLUSIVE_LOCK_FUNCTION();
-void elf_function_args() __attribute__((exclusive_lock_function(mu1, mu2)));
+void elf_function_args() EXCLUSIVE_LOCK_FUNCTION(mu1, mu2);
-int elf_testfn(int y) __attribute__((exclusive_lock_function));
+int elf_testfn(int y) EXCLUSIVE_LOCK_FUNCTION();
int elf_testfn(int y) {
- int x __attribute__((exclusive_lock_function)) = y; // \
+ int x EXCLUSIVE_LOCK_FUNCTION() = y; // \
// expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
return x;
};
-int elf_test_var __attribute__((exclusive_lock_function)); // \
+int elf_test_var EXCLUSIVE_LOCK_FUNCTION(); // \
// expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
class ElfFoo {
private:
- int test_field __attribute__((exclusive_lock_function)); // \
+ int test_field EXCLUSIVE_LOCK_FUNCTION(); // \
// expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((exclusive_lock_function));
+ void test_method() EXCLUSIVE_LOCK_FUNCTION();
};
-class __attribute__((exclusive_lock_function)) ElfTestClass { // \
+class EXCLUSIVE_LOCK_FUNCTION() ElfTestClass { // \
// expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
};
-void elf_fun_params(int lvar __attribute__((exclusive_lock_function))); // \
+void elf_fun_params(int lvar EXCLUSIVE_LOCK_FUNCTION()); // \
// expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
// Check argument parsing.
// legal attribute arguments
-int elf_function_1() __attribute__((exclusive_lock_function(muWrapper.mu)));
-int elf_function_2() __attribute__((exclusive_lock_function(muDoubleWrapper.muWrapper->mu)));
-int elf_function_3() __attribute__((exclusive_lock_function(muWrapper.getMu())));
-int elf_function_4() __attribute__((exclusive_lock_function(*muWrapper.getMuPointer())));
-int elf_function_5() __attribute__((exclusive_lock_function(&mu1)));
-int elf_function_6() __attribute__((exclusive_lock_function(muRef)));
-int elf_function_7() __attribute__((exclusive_lock_function(muDoubleWrapper.getWrapper()->getMu())));
-int elf_function_8() __attribute__((exclusive_lock_function(muPointer)));
-int elf_function_9(Mu x) __attribute__((exclusive_lock_function(1)));
-int elf_function_9(Mu x, Mu y) __attribute__((exclusive_lock_function(1,2)));
+int elf_function_1() EXCLUSIVE_LOCK_FUNCTION(muWrapper.mu);
+int elf_function_2() EXCLUSIVE_LOCK_FUNCTION(muDoubleWrapper.muWrapper->mu);
+int elf_function_3() EXCLUSIVE_LOCK_FUNCTION(muWrapper.getMu());
+int elf_function_4() EXCLUSIVE_LOCK_FUNCTION(*muWrapper.getMuPointer());
+int elf_function_5() EXCLUSIVE_LOCK_FUNCTION(&mu1);
+int elf_function_6() EXCLUSIVE_LOCK_FUNCTION(muRef);
+int elf_function_7() EXCLUSIVE_LOCK_FUNCTION(muDoubleWrapper.getWrapper()->getMu());
+int elf_function_8() EXCLUSIVE_LOCK_FUNCTION(muPointer);
+int elf_function_9(Mutex x) EXCLUSIVE_LOCK_FUNCTION(1);
+int elf_function_9(Mutex x, Mutex y) EXCLUSIVE_LOCK_FUNCTION(1,2);
// illegal attribute arguments
-int elf_function_bad_2() __attribute__((exclusive_lock_function("mu"))); // \
- // expected-warning {{'exclusive_lock_function' attribute requires arguments that are class type or point to class type}}
-int elf_function_bad_3() __attribute__((exclusive_lock_function(muDoublePointer))); // \
+int elf_function_bad_2() EXCLUSIVE_LOCK_FUNCTION("mu"); // \
+ // expected-warning {{ignoring 'exclusive_lock_function' attribute because its argument is invalid}}
+int elf_function_bad_3() EXCLUSIVE_LOCK_FUNCTION(muDoublePointer); // \
// expected-warning {{'exclusive_lock_function' attribute requires arguments that are class type or point to class type}}
-int elf_function_bad_4() __attribute__((exclusive_lock_function(umu))); // \
+int elf_function_bad_4() EXCLUSIVE_LOCK_FUNCTION(umu); // \
// expected-warning {{'exclusive_lock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-int elf_function_bad_1() __attribute__((exclusive_lock_function(1))); // \
+int elf_function_bad_1() EXCLUSIVE_LOCK_FUNCTION(1); // \
// expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int elf_function_bad_5(Mu x) __attribute__((exclusive_lock_function(0))); // \
+int elf_function_bad_5(Mutex x) EXCLUSIVE_LOCK_FUNCTION(0); // \
// expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int elf_function_bad_6(Mu x, Mu y) __attribute__((exclusive_lock_function(0))); // \
+int elf_function_bad_6(Mutex x, Mutex y) EXCLUSIVE_LOCK_FUNCTION(0); // \
// expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int elf_function_bad_7() __attribute__((exclusive_lock_function(0))); // \
+int elf_function_bad_7() EXCLUSIVE_LOCK_FUNCTION(0); // \
// expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
@@ -629,65 +629,65 @@ int elf_function_bad_7() __attribute__((exclusive_lock_function(0))); // \
// takes zero or more arguments, all locks (vars/fields)
-void slf_function() __attribute__((shared_lock_function));
+void slf_function() SHARED_LOCK_FUNCTION();
-void slf_function_args() __attribute__((shared_lock_function(mu1, mu2)));
+void slf_function_args() SHARED_LOCK_FUNCTION(mu1, mu2);
-int slf_testfn(int y) __attribute__((shared_lock_function));
+int slf_testfn(int y) SHARED_LOCK_FUNCTION();
int slf_testfn(int y) {
- int x __attribute__((shared_lock_function)) = y; // \
+ int x SHARED_LOCK_FUNCTION() = y; // \
// expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
return x;
};
-int slf_test_var __attribute__((shared_lock_function)); // \
+int slf_test_var SHARED_LOCK_FUNCTION(); // \
// expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-void slf_fun_params(int lvar __attribute__((shared_lock_function))); // \
+void slf_fun_params(int lvar SHARED_LOCK_FUNCTION()); // \
// expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
class SlfFoo {
private:
- int test_field __attribute__((shared_lock_function)); // \
+ int test_field SHARED_LOCK_FUNCTION(); // \
// expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((shared_lock_function));
+ void test_method() SHARED_LOCK_FUNCTION();
};
-class __attribute__((shared_lock_function)) SlfTestClass { // \
+class SHARED_LOCK_FUNCTION() SlfTestClass { // \
// expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
};
// Check argument parsing.
// legal attribute arguments
-int slf_function_1() __attribute__((shared_lock_function(muWrapper.mu)));
-int slf_function_2() __attribute__((shared_lock_function(muDoubleWrapper.muWrapper->mu)));
-int slf_function_3() __attribute__((shared_lock_function(muWrapper.getMu())));
-int slf_function_4() __attribute__((shared_lock_function(*muWrapper.getMuPointer())));
-int slf_function_5() __attribute__((shared_lock_function(&mu1)));
-int slf_function_6() __attribute__((shared_lock_function(muRef)));
-int slf_function_7() __attribute__((shared_lock_function(muDoubleWrapper.getWrapper()->getMu())));
-int slf_function_8() __attribute__((shared_lock_function(muPointer)));
-int slf_function_9(Mu x) __attribute__((shared_lock_function(1)));
-int slf_function_9(Mu x, Mu y) __attribute__((shared_lock_function(1,2)));
+int slf_function_1() SHARED_LOCK_FUNCTION(muWrapper.mu);
+int slf_function_2() SHARED_LOCK_FUNCTION(muDoubleWrapper.muWrapper->mu);
+int slf_function_3() SHARED_LOCK_FUNCTION(muWrapper.getMu());
+int slf_function_4() SHARED_LOCK_FUNCTION(*muWrapper.getMuPointer());
+int slf_function_5() SHARED_LOCK_FUNCTION(&mu1);
+int slf_function_6() SHARED_LOCK_FUNCTION(muRef);
+int slf_function_7() SHARED_LOCK_FUNCTION(muDoubleWrapper.getWrapper()->getMu());
+int slf_function_8() SHARED_LOCK_FUNCTION(muPointer);
+int slf_function_9(Mutex x) SHARED_LOCK_FUNCTION(1);
+int slf_function_9(Mutex x, Mutex y) SHARED_LOCK_FUNCTION(1,2);
// illegal attribute arguments
-int slf_function_bad_2() __attribute__((shared_lock_function("mu"))); // \
+int slf_function_bad_2() SHARED_LOCK_FUNCTION("mu"); // \
+ // expected-warning {{ignoring 'shared_lock_function' attribute because its argument is invalid}}
+int slf_function_bad_3() SHARED_LOCK_FUNCTION(muDoublePointer); // \
// expected-warning {{'shared_lock_function' attribute requires arguments that are class type or point to class type}}
-int slf_function_bad_3() __attribute__((shared_lock_function(muDoublePointer))); // \
- // expected-warning {{'shared_lock_function' attribute requires arguments that are class type or point to class type}}
-int slf_function_bad_4() __attribute__((shared_lock_function(umu))); // \
+int slf_function_bad_4() SHARED_LOCK_FUNCTION(umu); // \
// expected-warning {{'shared_lock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-int slf_function_bad_1() __attribute__((shared_lock_function(1))); // \
+int slf_function_bad_1() SHARED_LOCK_FUNCTION(1); // \
// expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int slf_function_bad_5(Mu x) __attribute__((shared_lock_function(0))); // \
+int slf_function_bad_5(Mutex x) SHARED_LOCK_FUNCTION(0); // \
// expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int slf_function_bad_6(Mu x, Mu y) __attribute__((shared_lock_function(0))); // \
+int slf_function_bad_6(Mutex x, Mutex y) SHARED_LOCK_FUNCTION(0); // \
// expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int slf_function_bad_7() __attribute__((shared_lock_function(0))); // \
+int slf_function_bad_7() SHARED_LOCK_FUNCTION(0); // \
// expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
@@ -705,62 +705,62 @@ int slf_function_bad_7() __attribute__((shared_lock_function(0))); // \
void etf_function() __attribute__((exclusive_trylock_function)); // \
// expected-error {{attribute takes at least 1 argument}}
-void etf_function_args() __attribute__((exclusive_trylock_function(1, mu2)));
+void etf_function_args() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu2);
-void etf_function_arg() __attribute__((exclusive_trylock_function(1)));
+void etf_function_arg() EXCLUSIVE_TRYLOCK_FUNCTION(1);
-int etf_testfn(int y) __attribute__((exclusive_trylock_function(1)));
+int etf_testfn(int y) EXCLUSIVE_TRYLOCK_FUNCTION(1);
int etf_testfn(int y) {
- int x __attribute__((exclusive_trylock_function(1))) = y; // \
+ int x EXCLUSIVE_TRYLOCK_FUNCTION(1) = y; // \
// expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
return x;
};
-int etf_test_var __attribute__((exclusive_trylock_function(1))); // \
+int etf_test_var EXCLUSIVE_TRYLOCK_FUNCTION(1); // \
// expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
class EtfFoo {
private:
- int test_field __attribute__((exclusive_trylock_function(1))); // \
+ int test_field EXCLUSIVE_TRYLOCK_FUNCTION(1); // \
// expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((exclusive_trylock_function(1)));
+ void test_method() EXCLUSIVE_TRYLOCK_FUNCTION(1);
};
-class __attribute__((exclusive_trylock_function(1))) EtfTestClass { // \
+class EXCLUSIVE_TRYLOCK_FUNCTION(1) EtfTestClass { // \
// expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
};
-void etf_fun_params(int lvar __attribute__((exclusive_trylock_function(1)))); // \
+void etf_fun_params(int lvar EXCLUSIVE_TRYLOCK_FUNCTION(1)); // \
// expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
// Check argument parsing.
// legal attribute arguments
-int etf_function_1() __attribute__((exclusive_trylock_function(1, muWrapper.mu)));
-int etf_function_2() __attribute__((exclusive_trylock_function(1, muDoubleWrapper.muWrapper->mu)));
-int etf_function_3() __attribute__((exclusive_trylock_function(1, muWrapper.getMu())));
-int etf_function_4() __attribute__((exclusive_trylock_function(1, *muWrapper.getMuPointer())));
-int etf_function_5() __attribute__((exclusive_trylock_function(1, &mu1)));
-int etf_function_6() __attribute__((exclusive_trylock_function(1, muRef)));
-int etf_function_7() __attribute__((exclusive_trylock_function(1, muDoubleWrapper.getWrapper()->getMu())));
-int etf_functetfn_8() __attribute__((exclusive_trylock_function(1, muPointer)));
-int etf_function_9() __attribute__((exclusive_trylock_function(true)));
+int etf_function_1() EXCLUSIVE_TRYLOCK_FUNCTION(1, muWrapper.mu);
+int etf_function_2() EXCLUSIVE_TRYLOCK_FUNCTION(1, muDoubleWrapper.muWrapper->mu);
+int etf_function_3() EXCLUSIVE_TRYLOCK_FUNCTION(1, muWrapper.getMu());
+int etf_function_4() EXCLUSIVE_TRYLOCK_FUNCTION(1, *muWrapper.getMuPointer());
+int etf_function_5() EXCLUSIVE_TRYLOCK_FUNCTION(1, &mu1);
+int etf_function_6() EXCLUSIVE_TRYLOCK_FUNCTION(1, muRef);
+int etf_function_7() EXCLUSIVE_TRYLOCK_FUNCTION(1, muDoubleWrapper.getWrapper()->getMu());
+int etf_functetfn_8() EXCLUSIVE_TRYLOCK_FUNCTION(1, muPointer);
+int etf_function_9() EXCLUSIVE_TRYLOCK_FUNCTION(true);
// illegal attribute arguments
-int etf_function_bad_1() __attribute__((exclusive_trylock_function(mu1))); // \
+int etf_function_bad_1() EXCLUSIVE_TRYLOCK_FUNCTION(mu1); // \
// expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_2() __attribute__((exclusive_trylock_function("mu"))); // \
+int etf_function_bad_2() EXCLUSIVE_TRYLOCK_FUNCTION("mu"); // \
// expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_3() __attribute__((exclusive_trylock_function(muDoublePointer))); // \
+int etf_function_bad_3() EXCLUSIVE_TRYLOCK_FUNCTION(muDoublePointer); // \
// expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_4() __attribute__((exclusive_trylock_function(1, "mu"))); // \
- // expected-warning {{'exclusive_trylock_function' attribute requires arguments that are class type or point to class type}}
-int etf_function_bad_5() __attribute__((exclusive_trylock_function(1, muDoublePointer))); // \
+int etf_function_bad_4() EXCLUSIVE_TRYLOCK_FUNCTION(1, "mu"); // \
+ // expected-warning {{ignoring 'exclusive_trylock_function' attribute because its argument is invalid}}
+int etf_function_bad_5() EXCLUSIVE_TRYLOCK_FUNCTION(1, muDoublePointer); // \
// expected-warning {{'exclusive_trylock_function' attribute requires arguments that are class type or point to class type}}
-int etf_function_bad_6() __attribute__((exclusive_trylock_function(1, umu))); // \
+int etf_function_bad_6() EXCLUSIVE_TRYLOCK_FUNCTION(1, umu); // \
// expected-warning {{'exclusive_trylock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -778,63 +778,63 @@ int etf_function_bad_6() __attribute__((exclusive_trylock_function(1, umu))); //
void stf_function() __attribute__((shared_trylock_function)); // \
// expected-error {{attribute takes at least 1 argument}}
-void stf_function_args() __attribute__((shared_trylock_function(1, mu2)));
+void stf_function_args() SHARED_TRYLOCK_FUNCTION(1, mu2);
-void stf_function_arg() __attribute__((shared_trylock_function(1)));
+void stf_function_arg() SHARED_TRYLOCK_FUNCTION(1);
-int stf_testfn(int y) __attribute__((shared_trylock_function(1)));
+int stf_testfn(int y) SHARED_TRYLOCK_FUNCTION(1);
int stf_testfn(int y) {
- int x __attribute__((shared_trylock_function(1))) = y; // \
+ int x SHARED_TRYLOCK_FUNCTION(1) = y; // \
// expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
return x;
};
-int stf_test_var __attribute__((shared_trylock_function(1))); // \
+int stf_test_var SHARED_TRYLOCK_FUNCTION(1); // \
// expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-void stf_fun_params(int lvar __attribute__((shared_trylock_function(1)))); // \
+void stf_fun_params(int lvar SHARED_TRYLOCK_FUNCTION(1)); // \
// expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
class StfFoo {
private:
- int test_field __attribute__((shared_trylock_function(1))); // \
+ int test_field SHARED_TRYLOCK_FUNCTION(1); // \
// expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((shared_trylock_function(1)));
+ void test_method() SHARED_TRYLOCK_FUNCTION(1);
};
-class __attribute__((shared_trylock_function(1))) StfTestClass { // \
+class SHARED_TRYLOCK_FUNCTION(1) StfTestClass { // \
// expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
};
// Check argument parsing.
// legal attribute arguments
-int stf_function_1() __attribute__((shared_trylock_function(1, muWrapper.mu)));
-int stf_function_2() __attribute__((shared_trylock_function(1, muDoubleWrapper.muWrapper->mu)));
-int stf_function_3() __attribute__((shared_trylock_function(1, muWrapper.getMu())));
-int stf_function_4() __attribute__((shared_trylock_function(1, *muWrapper.getMuPointer())));
-int stf_function_5() __attribute__((shared_trylock_function(1, &mu1)));
-int stf_function_6() __attribute__((shared_trylock_function(1, muRef)));
-int stf_function_7() __attribute__((shared_trylock_function(1, muDoubleWrapper.getWrapper()->getMu())));
-int stf_function_8() __attribute__((shared_trylock_function(1, muPointer)));
-int stf_function_9() __attribute__((shared_trylock_function(true)));
+int stf_function_1() SHARED_TRYLOCK_FUNCTION(1, muWrapper.mu);
+int stf_function_2() SHARED_TRYLOCK_FUNCTION(1, muDoubleWrapper.muWrapper->mu);
+int stf_function_3() SHARED_TRYLOCK_FUNCTION(1, muWrapper.getMu());
+int stf_function_4() SHARED_TRYLOCK_FUNCTION(1, *muWrapper.getMuPointer());
+int stf_function_5() SHARED_TRYLOCK_FUNCTION(1, &mu1);
+int stf_function_6() SHARED_TRYLOCK_FUNCTION(1, muRef);
+int stf_function_7() SHARED_TRYLOCK_FUNCTION(1, muDoubleWrapper.getWrapper()->getMu());
+int stf_function_8() SHARED_TRYLOCK_FUNCTION(1, muPointer);
+int stf_function_9() SHARED_TRYLOCK_FUNCTION(true);
// illegal attribute arguments
-int stf_function_bad_1() __attribute__((shared_trylock_function(mu1))); // \
+int stf_function_bad_1() SHARED_TRYLOCK_FUNCTION(mu1); // \
// expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_2() __attribute__((shared_trylock_function("mu"))); // \
+int stf_function_bad_2() SHARED_TRYLOCK_FUNCTION("mu"); // \
// expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_3() __attribute__((shared_trylock_function(muDoublePointer))); // \
+int stf_function_bad_3() SHARED_TRYLOCK_FUNCTION(muDoublePointer); // \
// expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_4() __attribute__((shared_trylock_function(1, "mu"))); // \
+int stf_function_bad_4() SHARED_TRYLOCK_FUNCTION(1, "mu"); // \
+ // expected-warning {{ignoring 'shared_trylock_function' attribute because its argument is invalid}}
+int stf_function_bad_5() SHARED_TRYLOCK_FUNCTION(1, muDoublePointer); // \
// expected-warning {{'shared_trylock_function' attribute requires arguments that are class type or point to class type}}
-int stf_function_bad_5() __attribute__((shared_trylock_function(1, muDoublePointer))); // \
- // expected-warning {{'shared_trylock_function' attribute requires arguments that are class type or point to class type}}
-int stf_function_bad_6() __attribute__((shared_trylock_function(1, umu))); // \
+int stf_function_bad_6() SHARED_TRYLOCK_FUNCTION(1, umu); // \
// expected-warning {{'shared_trylock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -848,65 +848,65 @@ int stf_function_bad_6() __attribute__((shared_trylock_function(1, umu))); // \
// takes zero or more arguments, all locks (vars/fields)
-void uf_function() __attribute__((unlock_function));
+void uf_function() UNLOCK_FUNCTION();
-void uf_function_args() __attribute__((unlock_function(mu1, mu2)));
+void uf_function_args() UNLOCK_FUNCTION(mu1, mu2);
-int uf_testfn(int y) __attribute__((unlock_function));
+int uf_testfn(int y) UNLOCK_FUNCTION();
int uf_testfn(int y) {
- int x __attribute__((unlock_function)) = y; // \
+ int x UNLOCK_FUNCTION() = y; // \
// expected-warning {{'unlock_function' attribute only applies to functions and methods}}
return x;
};
-int uf_test_var __attribute__((unlock_function)); // \
+int uf_test_var UNLOCK_FUNCTION(); // \
// expected-warning {{'unlock_function' attribute only applies to functions and methods}}
class UfFoo {
private:
- int test_field __attribute__((unlock_function)); // \
+ int test_field UNLOCK_FUNCTION(); // \
// expected-warning {{'unlock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((unlock_function));
+ void test_method() UNLOCK_FUNCTION();
};
-class __attribute__((no_thread_safety_analysis)) UfTestClass { // \
+class NO_THREAD_SAFETY_ANALYSIS UfTestClass { // \
// expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
};
-void uf_fun_params(int lvar __attribute__((unlock_function))); // \
+void uf_fun_params(int lvar UNLOCK_FUNCTION()); // \
// expected-warning {{'unlock_function' attribute only applies to functions and methods}}
// Check argument parsing.
// legal attribute arguments
-int uf_function_1() __attribute__((unlock_function(muWrapper.mu)));
-int uf_function_2() __attribute__((unlock_function(muDoubleWrapper.muWrapper->mu)));
-int uf_function_3() __attribute__((unlock_function(muWrapper.getMu())));
-int uf_function_4() __attribute__((unlock_function(*muWrapper.getMuPointer())));
-int uf_function_5() __attribute__((unlock_function(&mu1)));
-int uf_function_6() __attribute__((unlock_function(muRef)));
-int uf_function_7() __attribute__((unlock_function(muDoubleWrapper.getWrapper()->getMu())));
-int uf_function_8() __attribute__((unlock_function(muPointer)));
-int uf_function_9(Mu x) __attribute__((unlock_function(1)));
-int uf_function_9(Mu x, Mu y) __attribute__((unlock_function(1,2)));
+int uf_function_1() UNLOCK_FUNCTION(muWrapper.mu);
+int uf_function_2() UNLOCK_FUNCTION(muDoubleWrapper.muWrapper->mu);
+int uf_function_3() UNLOCK_FUNCTION(muWrapper.getMu());
+int uf_function_4() UNLOCK_FUNCTION(*muWrapper.getMuPointer());
+int uf_function_5() UNLOCK_FUNCTION(&mu1);
+int uf_function_6() UNLOCK_FUNCTION(muRef);
+int uf_function_7() UNLOCK_FUNCTION(muDoubleWrapper.getWrapper()->getMu());
+int uf_function_8() UNLOCK_FUNCTION(muPointer);
+int uf_function_9(Mutex x) UNLOCK_FUNCTION(1);
+int uf_function_9(Mutex x, Mutex y) UNLOCK_FUNCTION(1,2);
// illegal attribute arguments
-int uf_function_bad_2() __attribute__((unlock_function("mu"))); // \
- // expected-warning {{'unlock_function' attribute requires arguments that are class type or point to class type}}
-int uf_function_bad_3() __attribute__((unlock_function(muDoublePointer))); // \
+int uf_function_bad_2() UNLOCK_FUNCTION("mu"); // \
+ // expected-warning {{ignoring 'unlock_function' attribute because its argument is invalid}}
+int uf_function_bad_3() UNLOCK_FUNCTION(muDoublePointer); // \
// expected-warning {{'unlock_function' attribute requires arguments that are class type or point to class type}}
-int uf_function_bad_4() __attribute__((unlock_function(umu))); // \
+int uf_function_bad_4() UNLOCK_FUNCTION(umu); // \
// expected-warning {{'unlock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-int uf_function_bad_1() __attribute__((unlock_function(1))); // \
+int uf_function_bad_1() UNLOCK_FUNCTION(1); // \
// expected-error {{'unlock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int uf_function_bad_5(Mu x) __attribute__((unlock_function(0))); // \
+int uf_function_bad_5(Mutex x) UNLOCK_FUNCTION(0); // \
// expected-error {{'unlock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int uf_function_bad_6(Mu x, Mu y) __attribute__((unlock_function(0))); // \
+int uf_function_bad_6(Mutex x, Mutex y) UNLOCK_FUNCTION(0); // \
// expected-error {{'unlock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int uf_function_bad_7() __attribute__((unlock_function(0))); // \
+int uf_function_bad_7() UNLOCK_FUNCTION(0); // \
// expected-error {{'unlock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
@@ -923,57 +923,57 @@ int uf_function_bad_7() __attribute__((unlock_function(0))); // \
void lr_function() __attribute__((lock_returned)); // \
// expected-error {{attribute takes one argument}}
-void lr_function_arg() __attribute__((lock_returned(mu1)));
+void lr_function_arg() LOCK_RETURNED(mu1);
void lr_function_args() __attribute__((lock_returned(mu1, mu2))); // \
// expected-error {{attribute takes one argument}}
-int lr_testfn(int y) __attribute__((lock_returned(mu1)));
+int lr_testfn(int y) LOCK_RETURNED(mu1);
int lr_testfn(int y) {
- int x __attribute__((lock_returned(mu1))) = y; // \
+ int x LOCK_RETURNED(mu1) = y; // \
// expected-warning {{'lock_returned' attribute only applies to functions and methods}}
return x;
};
-int lr_test_var __attribute__((lock_returned(mu1))); // \
+int lr_test_var LOCK_RETURNED(mu1); // \
// expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-void lr_fun_params(int lvar __attribute__((lock_returned(mu1)))); // \
+void lr_fun_params(int lvar LOCK_RETURNED(mu1)); // \
// expected-warning {{'lock_returned' attribute only applies to functions and methods}}
class LrFoo {
private:
- int test_field __attribute__((lock_returned(mu1))); // \
+ int test_field LOCK_RETURNED(mu1); // \
// expected-warning {{'lock_returned' attribute only applies to functions and methods}}
- void test_method() __attribute__((lock_returned(mu1)));
+ void test_method() LOCK_RETURNED(mu1);
};
-class __attribute__((lock_returned(mu1))) LrTestClass { // \
+class LOCK_RETURNED(mu1) LrTestClass { // \
// expected-warning {{'lock_returned' attribute only applies to functions and methods}}
};
// Check argument parsing.
// legal attribute arguments
-int lr_function_1() __attribute__((lock_returned(muWrapper.mu)));
-int lr_function_2() __attribute__((lock_returned(muDoubleWrapper.muWrapper->mu)));
-int lr_function_3() __attribute__((lock_returned(muWrapper.getMu())));
-int lr_function_4() __attribute__((lock_returned(*muWrapper.getMuPointer())));
-int lr_function_5() __attribute__((lock_returned(&mu1)));
-int lr_function_6() __attribute__((lock_returned(muRef)));
-int lr_function_7() __attribute__((lock_returned(muDoubleWrapper.getWrapper()->getMu())));
-int lr_function_8() __attribute__((lock_returned(muPointer)));
+int lr_function_1() LOCK_RETURNED(muWrapper.mu);
+int lr_function_2() LOCK_RETURNED(muDoubleWrapper.muWrapper->mu);
+int lr_function_3() LOCK_RETURNED(muWrapper.getMu());
+int lr_function_4() LOCK_RETURNED(*muWrapper.getMuPointer());
+int lr_function_5() LOCK_RETURNED(&mu1);
+int lr_function_6() LOCK_RETURNED(muRef);
+int lr_function_7() LOCK_RETURNED(muDoubleWrapper.getWrapper()->getMu());
+int lr_function_8() LOCK_RETURNED(muPointer);
// illegal attribute arguments
-int lr_function_bad_1() __attribute__((lock_returned(1))); // \
+int lr_function_bad_1() LOCK_RETURNED(1); // \
// expected-warning {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_2() __attribute__((lock_returned("mu"))); // \
+int lr_function_bad_2() LOCK_RETURNED("mu"); // \
+ // expected-warning {{ignoring 'lock_returned' attribute because its argument is invalid}}
+int lr_function_bad_3() LOCK_RETURNED(muDoublePointer); // \
// expected-warning {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_3() __attribute__((lock_returned(muDoublePointer))); // \
- // expected-warning {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_4() __attribute__((lock_returned(umu))); // \
+int lr_function_bad_4() LOCK_RETURNED(umu); // \
// expected-warning {{'lock_returned' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -991,56 +991,56 @@ int lr_function_bad_4() __attribute__((lock_returned(umu))); // \
void le_function() __attribute__((locks_excluded)); // \
// expected-error {{attribute takes at least 1 argument}}
-void le_function_arg() __attribute__((locks_excluded(mu1)));
+void le_function_arg() LOCKS_EXCLUDED(mu1);
-void le_function_args() __attribute__((locks_excluded(mu1, mu2)));
+void le_function_args() LOCKS_EXCLUDED(mu1, mu2);
-int le_testfn(int y) __attribute__((locks_excluded(mu1)));
+int le_testfn(int y) LOCKS_EXCLUDED(mu1);
int le_testfn(int y) {
- int x __attribute__((locks_excluded(mu1))) = y; // \
+ int x LOCKS_EXCLUDED(mu1) = y; // \
// expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
return x;
};
-int le_test_var __attribute__((locks_excluded(mu1))); // \
+int le_test_var LOCKS_EXCLUDED(mu1); // \
// expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-void le_fun_params(int lvar __attribute__((locks_excluded(mu1)))); // \
+void le_fun_params(int lvar LOCKS_EXCLUDED(mu1)); // \
// expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
class LeFoo {
private:
- int test_field __attribute__((locks_excluded(mu1))); // \
+ int test_field LOCKS_EXCLUDED(mu1); // \
// expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
- void test_method() __attribute__((locks_excluded(mu1)));
+ void test_method() LOCKS_EXCLUDED(mu1);
};
-class __attribute__((locks_excluded(mu1))) LeTestClass { // \
+class LOCKS_EXCLUDED(mu1) LeTestClass { // \
// expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
};
// Check argument parsing.
// legal attribute arguments
-int le_function_1() __attribute__((locks_excluded(muWrapper.mu)));
-int le_function_2() __attribute__((locks_excluded(muDoubleWrapper.muWrapper->mu)));
-int le_function_3() __attribute__((locks_excluded(muWrapper.getMu())));
-int le_function_4() __attribute__((locks_excluded(*muWrapper.getMuPointer())));
-int le_function_5() __attribute__((locks_excluded(&mu1)));
-int le_function_6() __attribute__((locks_excluded(muRef)));
-int le_function_7() __attribute__((locks_excluded(muDoubleWrapper.getWrapper()->getMu())));
-int le_function_8() __attribute__((locks_excluded(muPointer)));
+int le_function_1() LOCKS_EXCLUDED(muWrapper.mu);
+int le_function_2() LOCKS_EXCLUDED(muDoubleWrapper.muWrapper->mu);
+int le_function_3() LOCKS_EXCLUDED(muWrapper.getMu());
+int le_function_4() LOCKS_EXCLUDED(*muWrapper.getMuPointer());
+int le_function_5() LOCKS_EXCLUDED(&mu1);
+int le_function_6() LOCKS_EXCLUDED(muRef);
+int le_function_7() LOCKS_EXCLUDED(muDoubleWrapper.getWrapper()->getMu());
+int le_function_8() LOCKS_EXCLUDED(muPointer);
// illegal attribute arguments
-int le_function_bad_1() __attribute__((locks_excluded(1))); // \
- // expected-warning {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_2() __attribute__((locks_excluded("mu"))); // \
+int le_function_bad_1() LOCKS_EXCLUDED(1); // \
// expected-warning {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_3() __attribute__((locks_excluded(muDoublePointer))); // \
+int le_function_bad_2() LOCKS_EXCLUDED("mu"); // \
+ // expected-warning {{ignoring 'locks_excluded' attribute because its argument is invalid}}
+int le_function_bad_3() LOCKS_EXCLUDED(muDoublePointer); // \
// expected-warning {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_4() __attribute__((locks_excluded(umu))); // \
+int le_function_bad_4() LOCKS_EXCLUDED(umu); // \
// expected-warning {{'locks_excluded' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -1058,56 +1058,56 @@ int le_function_bad_4() __attribute__((locks_excluded(umu))); // \
void elr_function() __attribute__((exclusive_locks_required)); // \
// expected-error {{attribute takes at least 1 argument}}
-void elr_function_arg() __attribute__((exclusive_locks_required(mu1)));
+void elr_function_arg() EXCLUSIVE_LOCKS_REQUIRED(mu1);
-void elr_function_args() __attribute__((exclusive_locks_required(mu1, mu2)));
+void elr_function_args() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2);
-int elr_testfn(int y) __attribute__((exclusive_locks_required(mu1)));
+int elr_testfn(int y) EXCLUSIVE_LOCKS_REQUIRED(mu1);
int elr_testfn(int y) {
- int x __attribute__((exclusive_locks_required(mu1))) = y; // \
+ int x EXCLUSIVE_LOCKS_REQUIRED(mu1) = y; // \
// expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
return x;
};
-int elr_test_var __attribute__((exclusive_locks_required(mu1))); // \
+int elr_test_var EXCLUSIVE_LOCKS_REQUIRED(mu1); // \
// expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-void elr_fun_params(int lvar __attribute__((exclusive_locks_required(mu1)))); // \
+void elr_fun_params(int lvar EXCLUSIVE_LOCKS_REQUIRED(mu1)); // \
// expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
class ElrFoo {
private:
- int test_field __attribute__((exclusive_locks_required(mu1))); // \
+ int test_field EXCLUSIVE_LOCKS_REQUIRED(mu1); // \
// expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
- void test_method() __attribute__((exclusive_locks_required(mu1)));
+ void test_method() EXCLUSIVE_LOCKS_REQUIRED(mu1);
};
-class __attribute__((exclusive_locks_required(mu1))) ElrTestClass { // \
+class EXCLUSIVE_LOCKS_REQUIRED(mu1) ElrTestClass { // \
// expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
};
// Check argument parsing.
// legal attribute arguments
-int elr_function_1() __attribute__((exclusive_locks_required(muWrapper.mu)));
-int elr_function_2() __attribute__((exclusive_locks_required(muDoubleWrapper.muWrapper->mu)));
-int elr_function_3() __attribute__((exclusive_locks_required(muWrapper.getMu())));
-int elr_function_4() __attribute__((exclusive_locks_required(*muWrapper.getMuPointer())));
-int elr_function_5() __attribute__((exclusive_locks_required(&mu1)));
-int elr_function_6() __attribute__((exclusive_locks_required(muRef)));
-int elr_function_7() __attribute__((exclusive_locks_required(muDoubleWrapper.getWrapper()->getMu())));
-int elr_function_8() __attribute__((exclusive_locks_required(muPointer)));
+int elr_function_1() EXCLUSIVE_LOCKS_REQUIRED(muWrapper.mu);
+int elr_function_2() EXCLUSIVE_LOCKS_REQUIRED(muDoubleWrapper.muWrapper->mu);
+int elr_function_3() EXCLUSIVE_LOCKS_REQUIRED(muWrapper.getMu());
+int elr_function_4() EXCLUSIVE_LOCKS_REQUIRED(*muWrapper.getMuPointer());
+int elr_function_5() EXCLUSIVE_LOCKS_REQUIRED(&mu1);
+int elr_function_6() EXCLUSIVE_LOCKS_REQUIRED(muRef);
+int elr_function_7() EXCLUSIVE_LOCKS_REQUIRED(muDoubleWrapper.getWrapper()->getMu());
+int elr_function_8() EXCLUSIVE_LOCKS_REQUIRED(muPointer);
// illegal attribute arguments
-int elr_function_bad_1() __attribute__((exclusive_locks_required(1))); // \
+int elr_function_bad_1() EXCLUSIVE_LOCKS_REQUIRED(1); // \
// expected-warning {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_2() __attribute__((exclusive_locks_required("mu"))); // \
+int elr_function_bad_2() EXCLUSIVE_LOCKS_REQUIRED("mu"); // \
+ // expected-warning {{ignoring 'exclusive_locks_required' attribute because its argument is invalid}}
+int elr_function_bad_3() EXCLUSIVE_LOCKS_REQUIRED(muDoublePointer); // \
// expected-warning {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_3() __attribute__((exclusive_locks_required(muDoublePointer))); // \
- // expected-warning {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_4() __attribute__((exclusive_locks_required(umu))); // \
+int elr_function_bad_4() EXCLUSIVE_LOCKS_REQUIRED(umu); // \
// expected-warning {{'exclusive_locks_required' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -1126,56 +1126,56 @@ int elr_function_bad_4() __attribute__((exclusive_locks_required(umu))); // \
void slr_function() __attribute__((shared_locks_required)); // \
// expected-error {{attribute takes at least 1 argument}}
-void slr_function_arg() __attribute__((shared_locks_required(mu1)));
+void slr_function_arg() SHARED_LOCKS_REQUIRED(mu1);
-void slr_function_args() __attribute__((shared_locks_required(mu1, mu2)));
+void slr_function_args() SHARED_LOCKS_REQUIRED(mu1, mu2);
-int slr_testfn(int y) __attribute__((shared_locks_required(mu1)));
+int slr_testfn(int y) SHARED_LOCKS_REQUIRED(mu1);
int slr_testfn(int y) {
- int x __attribute__((shared_locks_required(mu1))) = y; // \
+ int x SHARED_LOCKS_REQUIRED(mu1) = y; // \
// expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
return x;
};
-int slr_test_var __attribute__((shared_locks_required(mu1))); // \
+int slr_test_var SHARED_LOCKS_REQUIRED(mu1); // \
// expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-void slr_fun_params(int lvar __attribute__((shared_locks_required(mu1)))); // \
+void slr_fun_params(int lvar SHARED_LOCKS_REQUIRED(mu1)); // \
// expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
class SlrFoo {
private:
- int test_field __attribute__((shared_locks_required(mu1))); // \
+ int test_field SHARED_LOCKS_REQUIRED(mu1); // \
// expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
- void test_method() __attribute__((shared_locks_required(mu1)));
+ void test_method() SHARED_LOCKS_REQUIRED(mu1);
};
-class __attribute__((shared_locks_required(mu1))) SlrTestClass { // \
+class SHARED_LOCKS_REQUIRED(mu1) SlrTestClass { // \
// expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
};
// Check argument parsing.
// legal attribute arguments
-int slr_function_1() __attribute__((shared_locks_required(muWrapper.mu)));
-int slr_function_2() __attribute__((shared_locks_required(muDoubleWrapper.muWrapper->mu)));
-int slr_function_3() __attribute__((shared_locks_required(muWrapper.getMu())));
-int slr_function_4() __attribute__((shared_locks_required(*muWrapper.getMuPointer())));
-int slr_function_5() __attribute__((shared_locks_required(&mu1)));
-int slr_function_6() __attribute__((shared_locks_required(muRef)));
-int slr_function_7() __attribute__((shared_locks_required(muDoubleWrapper.getWrapper()->getMu())));
-int slr_function_8() __attribute__((shared_locks_required(muPointer)));
+int slr_function_1() SHARED_LOCKS_REQUIRED(muWrapper.mu);
+int slr_function_2() SHARED_LOCKS_REQUIRED(muDoubleWrapper.muWrapper->mu);
+int slr_function_3() SHARED_LOCKS_REQUIRED(muWrapper.getMu());
+int slr_function_4() SHARED_LOCKS_REQUIRED(*muWrapper.getMuPointer());
+int slr_function_5() SHARED_LOCKS_REQUIRED(&mu1);
+int slr_function_6() SHARED_LOCKS_REQUIRED(muRef);
+int slr_function_7() SHARED_LOCKS_REQUIRED(muDoubleWrapper.getWrapper()->getMu());
+int slr_function_8() SHARED_LOCKS_REQUIRED(muPointer);
// illegal attribute arguments
-int slr_function_bad_1() __attribute__((shared_locks_required(1))); // \
- // expected-warning {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_2() __attribute__((shared_locks_required("mu"))); // \
+int slr_function_bad_1() SHARED_LOCKS_REQUIRED(1); // \
// expected-warning {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_3() __attribute__((shared_locks_required(muDoublePointer))); // \
+int slr_function_bad_2() SHARED_LOCKS_REQUIRED("mu"); // \
+ // expected-warning {{ignoring 'shared_locks_required' attribute because its argument is invalid}}
+int slr_function_bad_3() SHARED_LOCKS_REQUIRED(muDoublePointer); // \
// expected-warning {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_4() __attribute__((shared_locks_required(umu))); // \
+int slr_function_bad_4() SHARED_LOCKS_REQUIRED(umu); // \
// expected-warning {{'shared_locks_required' attribute requires arguments whose type is annotated with 'lockable' attribute}}
@@ -1240,43 +1240,43 @@ struct Foomgoper {
// Parsing of member variables and function parameters
//------------------------------------------------------
-Mu gmu;
+Mutex gmu;
class StaticMu {
- static Mu statmu;
+ static Mutex statmu;
};
class FooLate {
public:
- void foo1() __attribute__((exclusive_locks_required(gmu))) { }
- void foo2() __attribute__((exclusive_locks_required(mu))) { }
- void foo3(Mu *m) __attribute__((exclusive_locks_required(m))) { }
- void foo3(FooLate *f) __attribute__((exclusive_locks_required(f->mu))) { }
- void foo4(FooLate *f) __attribute__((exclusive_locks_required(f->mu)));
+ void foo1() EXCLUSIVE_LOCKS_REQUIRED(gmu) { }
+ void foo2() EXCLUSIVE_LOCKS_REQUIRED(mu) { }
+ void foo3(Mutex *m) EXCLUSIVE_LOCKS_REQUIRED(m) { }
+ void foo3(FooLate *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu) { }
+ void foo4(FooLate *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu);
- static void foo5() __attribute__((exclusive_locks_required(mu))); // \
+ static void foo5() EXCLUSIVE_LOCKS_REQUIRED(mu); // \
// expected-error {{'this' cannot be implicitly used in a static member function declaration}}
template <class T>
- void foo6() __attribute__((exclusive_locks_required(T::statmu))) { }
+ void foo6() EXCLUSIVE_LOCKS_REQUIRED(T::statmu) { }
template <class T>
- void foo7(T* f) __attribute__((exclusive_locks_required(f->mu))) { }
+ void foo7(T* f) EXCLUSIVE_LOCKS_REQUIRED(f->mu) { }
- int a __attribute__((guarded_by(gmu)));
- int b __attribute__((guarded_by(mu)));
- int c __attribute__((guarded_by(this->mu)));
+ int a GUARDED_BY(gmu);
+ int b GUARDED_BY(mu);
+ int c GUARDED_BY(this->mu);
- Mu mu;
+ Mutex mu;
};
//-------------------------
// Empty argument lists
//-------------------------
-class __attribute__((lockable)) EmptyArgListsTest {
- void lock() __attribute__((exclusive_lock_function())) { }
- void unlock() __attribute__((unlock_function())) { }
+class LOCKABLE EmptyArgListsTest {
+ void lock() EXCLUSIVE_LOCK_FUNCTION() { }
+ void unlock() UNLOCK_FUNCTION() { }
};
@@ -1285,7 +1285,7 @@ namespace FunctionDefinitionParseTest {
class Foo {
public:
- Mu mu_;
+ Mutex mu_;
void foo1();
void foo2(Foo *f);
};
@@ -1293,17 +1293,17 @@ public:
template <class T>
class Bar {
public:
- Mu mu_;
+ Mutex mu_;
void bar();
};
-void Foo::foo1() __attribute__((exclusive_locks_required(mu_))) { }
-void Foo::foo2(Foo *f) __attribute__((exclusive_locks_required(f->mu_))) { }
+void Foo::foo1() EXCLUSIVE_LOCKS_REQUIRED(mu_) { }
+void Foo::foo2(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_) { }
template <class T>
-void Bar<T>::bar() __attribute__((exclusive_locks_required(mu_))) { }
+void Bar<T>::bar() EXCLUSIVE_LOCKS_REQUIRED(mu_) { }
-void baz(Foo *f) __attribute__((exclusive_locks_required(f->mu_))) { }
+void baz(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_) { }
} // end namespace
@@ -1312,13 +1312,15 @@ namespace TestMultiDecl {
class Foo {
public:
- int __attribute__((guarded_by(mu_))) a;
- int __attribute__((guarded_by(mu_))) b, c;
+ int GUARDED_BY(mu_) a;
+ int GUARDED_BY(mu_) b, c;
private:
- Mu mu_;
+ Mutex mu_;
};
+} // end namespace TestMultiDecl
+
namespace NestedClassLateDecl {
@@ -1331,15 +1333,100 @@ class Foo {
void bar2(Bar* b) EXCLUSIVE_LOCKS_REQUIRED(b->mu) { b->a = 0; }
void bar3(Foo* f) EXCLUSIVE_LOCKS_REQUIRED(f->fooMu) { f->a = 0; }
- Mu mu;
+ Mutex mu;
};
int a GUARDED_BY(fooMu);
- Mu fooMu;
- static Mu fooMuStatic;
+ Mutex fooMu;
+ static Mutex fooMuStatic;
};
}
-} // end namespace TestMultiDecl
+namespace PointerToMemberTest {
+
+// Empty string should be ignored.
+int testEmptyAttribute GUARDED_BY("");
+void testEmptyAttributeFunction() EXCLUSIVE_LOCKS_REQUIRED("");
+
+class Graph {
+public:
+ Mutex mu_;
+
+ static Mutex* get_static_mu() LOCK_RETURNED(&Graph::mu_);
+};
+
+class Node {
+public:
+ void foo() EXCLUSIVE_LOCKS_REQUIRED(&Graph::mu_);
+ int a GUARDED_BY(&Graph::mu_);
+};
+
+}
+
+
+namespace SmartPointerTest {
+
+template<class T>
+class smart_ptr {
+ public:
+ T* operator->() { return ptr_; }
+ T& operator*() { return ptr_; }
+
+ private:
+ T* ptr_;
+};
+
+
+Mutex gmu;
+smart_ptr<int> gdat PT_GUARDED_BY(gmu);
+
+
+class MyClass {
+public:
+ Mutex mu_;
+ smart_ptr<Mutex> smu_;
+
+
+ smart_ptr<int> a PT_GUARDED_BY(mu_);
+ int b GUARDED_BY(smu_);
+};
+
+}
+
+
+namespace InheritanceTest {
+
+class LOCKABLE Base {
+ public:
+ void lock() EXCLUSIVE_LOCK_FUNCTION();
+ void unlock() UNLOCK_FUNCTION();
+};
+
+class Base2 { };
+
+class Derived1 : public Base { };
+
+class Derived2 : public Base2, public Derived1 { };
+
+class Derived3 : public Base2 { };
+
+class Foo {
+ Derived1 mu1_;
+ Derived2 mu2_;
+ Derived3 mu3_;
+ int a GUARDED_BY(mu1_);
+ int b GUARDED_BY(mu2_);
+ int c GUARDED_BY(mu3_); // \
+ // expected-warning {{'guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute; type here is 'class InheritanceTest::Derived3'}}
+
+ void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1_, mu2_) {
+ a = 0;
+ b = 0;
+ }
+};
+
+}
+
+
diff --git a/test/SemaCXX/warn-unique-enum.cpp b/test/SemaCXX/warn-unique-enum.cpp
new file mode 100644
index 0000000..59a1278
--- /dev/null
+++ b/test/SemaCXX/warn-unique-enum.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -Wunique-enum
+enum A { A1 = 1, A2 = 1, A3 = 1 }; // expected-warning {{all elements of 'A' are initialized with literals to value 1}} \
+// expected-note {{initialize the last element with the previous element to silence this warning}}
+enum { B1 = 1, B2 = 1, B3 = 1 }; // no warning
+enum C { // expected-warning {{all elements of 'C' are initialized with literals to value 1}}
+ C1 = true,
+ C2 = true // expected-note {{initialize the last element with the previous element to silence this warning}}
+};
+enum D { D1 = 5, D2 = 5L, D3 = 5UL, D4 = 5LL, D5 = 5ULL }; // expected-warning {{all elements of 'D' are initialized with literals to value 5}} \
+// expected-note {{initialize the last element with the previous element to silence this warning}}
+
+// Don't warn on enums with less than 2 elements.
+enum E { E1 = 4 };
+enum F { F1 };
+enum G {};
+
+// Don't warn when integer literals do not initialize the elements.
+enum H { H1 = 4, H_MAX = H1, H_MIN = H1 };
+enum I { I1 = H1, I2 = 4 };
+enum J { J1 = 4, J2 = I2 };
+enum K { K1, K2, K3, K4 };
+
+// Don't crash or warn on this one.
+// rdar://11875995
+enum L {
+ L1 = 0x8000000000000000ULL, L2 = 0x0000000000000001ULL
+};
diff --git a/test/SemaCXX/warn-unused-private-field.cpp b/test/SemaCXX/warn-unused-private-field.cpp
new file mode 100644
index 0000000..661442d
--- /dev/null
+++ b/test/SemaCXX/warn-unused-private-field.cpp
@@ -0,0 +1,246 @@
+// RUN: %clang_cc1 -fsyntax-only -Wunused-private-field -Wused-but-marked-unused -verify -std=c++11 %s
+
+class NotFullyDefined {
+ public:
+ NotFullyDefined();
+ private:
+ int y;
+};
+
+class HasUndefinedNestedClass {
+ class Undefined;
+ int unused_;
+};
+
+class HasUndefinedPureVirtualDestructor {
+ virtual ~HasUndefinedPureVirtualDestructor() = 0;
+ int unused_;
+};
+
+class HasDefinedNestedClasses {
+ class DefinedHere {};
+ class DefinedOutside;
+ int unused_; // expected-warning{{private field 'unused_' is not used}}
+};
+class HasDefinedNestedClasses::DefinedOutside {};
+
+class HasUndefinedFriendFunction {
+ friend void undefinedFriendFunction();
+ int unused_;
+};
+
+class HasUndefinedFriendClass {
+ friend class NotFullyDefined;
+ friend class NotDefined;
+ int unused_;
+};
+
+class HasFriend {
+ friend class FriendClass;
+ friend void friendFunction(HasFriend f);
+ int unused_; // expected-warning{{private field 'unused_' is not used}}
+ int used_by_friend_class_;
+ int used_by_friend_function_;
+};
+
+class ClassWithTemplateFriend {
+ template <typename T> friend class TemplateFriend;
+ int used_by_friend_;
+ int unused_;
+};
+
+template <typename T> class TemplateFriend {
+public:
+ TemplateFriend(ClassWithTemplateFriend my_friend) {
+ int var = my_friend.used_by_friend_;
+ }
+};
+
+class FriendClass {
+ HasFriend my_friend_;
+ void use() {
+ my_friend_.used_by_friend_class_ = 42;
+ }
+};
+
+void friendFunction(HasFriend my_friend) {
+ my_friend.used_by_friend_function_ = 42;
+}
+
+class NonTrivialConstructor {
+ public:
+ NonTrivialConstructor() {}
+};
+
+class NonTrivialDestructor {
+ public:
+ ~NonTrivialDestructor() {}
+};
+
+class Trivial {
+ public:
+ Trivial() = default;
+ Trivial(int a) {}
+};
+
+int side_effect() {
+ return 42;
+}
+
+class A {
+ public:
+ A() : primitive_type_(42), default_initializer_(), other_initializer_(42),
+ trivial_(), user_constructor_(42),
+ initialized_with_side_effect_(side_effect()) {
+ used_ = 42;
+ attr_used_ = 42; // expected-warning{{'attr_used_' was marked unused but was used}}
+ }
+
+ A(int x, A* a) : pointer_(a) {}
+
+ private:
+ int primitive_type_; // expected-warning{{private field 'primitive_type_' is not used}}
+ A* pointer_; // expected-warning{{private field 'pointer_' is not used}}
+ int no_initializer_; // expected-warning{{private field 'no_initializer_' is not used}}
+ int default_initializer_; // expected-warning{{private field 'default_initializer_' is not used}}
+ int other_initializer_; // expected-warning{{private field 'other_initializer_' is not used}}
+ int used_, unused_; // expected-warning{{private field 'unused_' is not used}}
+ int in_class_initializer_ = 42; // expected-warning{{private field 'in_class_initializer_' is not used}}
+ int in_class_initializer_with_side_effect_ = side_effect();
+ Trivial trivial_initializer_ = Trivial(); // expected-warning{{private field 'trivial_initializer_' is not used}}
+ Trivial non_trivial_initializer_ = Trivial(42);
+ int initialized_with_side_effect_;
+ static int static_fields_are_ignored_;
+
+ Trivial trivial_; // expected-warning{{private field 'trivial_' is not used}}
+ Trivial user_constructor_;
+ NonTrivialConstructor non_trivial_constructor_;
+ NonTrivialDestructor non_trivial_destructor_;
+
+ int attr_ __attribute__((unused));
+ int attr_used_ __attribute__((unused));
+};
+
+class EverythingUsed {
+ public:
+ EverythingUsed() : as_array_index_(0), var_(by_initializer_) {
+ var_ = sizeof(sizeof_);
+ int *use = &by_reference_;
+ int test[2];
+ test[as_array_index_] = 42;
+ }
+
+ template<class T>
+ void useStuff(T t) {
+ by_template_function_ = 42;
+ }
+
+ private:
+ int var_;
+ int sizeof_;
+ int by_reference_;
+ int by_template_function_;
+ int as_array_index_;
+ int by_initializer_;
+};
+
+class HasFeatureTest {
+#if __has_feature(attribute_unused_on_fields)
+ int unused_; // expected-warning{{private field 'unused_' is not used}}
+ int unused2_ __attribute__((unused)); // no-warning
+#endif
+};
+
+namespace templates {
+class B {
+ template <typename T> void f(T t);
+ int a;
+};
+} // namespace templates
+
+namespace mutual_friends {
+// Undefined methods make mutual friends undefined.
+class A {
+ int a;
+ friend class B;
+ void doSomethingToAOrB();
+};
+class B {
+ int b;
+ friend class A;
+};
+
+// Undefined friends do not make a mutual friend undefined.
+class C {
+ int c;
+ void doSomethingElse() {}
+ friend class E;
+ friend class D;
+};
+class D {
+ int d; // expected-warning{{private field 'd' is not used}}
+ friend class C;
+};
+
+// Undefined nested classes make mutual friends undefined.
+class F {
+ int f;
+ class G;
+ friend class H;
+};
+class H {
+ int h;
+ friend class F;
+};
+} // namespace mutual_friends
+
+namespace anonymous_structs_unions {
+class A {
+ private:
+ // FIXME: Look at the DeclContext for anonymous structs/unions.
+ union {
+ int *Aligner;
+ unsigned char Data[8];
+ };
+};
+union S {
+ private:
+ int *Aligner;
+ unsigned char Data[8];
+};
+} // namespace anonymous_structs_unions
+
+namespace pr13413 {
+class A {
+ A() : p_(__null), b_(false), a_(this), p2_(nullptr) {}
+ void* p_; // expected-warning{{private field 'p_' is not used}}
+ bool b_; // expected-warning{{private field 'b_' is not used}}
+ A* a_; // expected-warning{{private field 'a_' is not used}}
+ void* p2_; // expected-warning{{private field 'p2_' is not used}}
+};
+}
+
+namespace pr13543 {
+ void f(int);
+ void f(char);
+ struct S {
+ S() : p(&f) {}
+ private:
+ void (*p)(int); // expected-warning{{private field 'p' is not used}}
+ };
+
+ struct A { int n; };
+ struct B {
+ B() : a(A()) {}
+ B(char) {}
+ B(int n) : a{n}, b{(f(n), 0)} {}
+ private:
+ A a = A(); // expected-warning{{private field 'a' is not used}}
+ A b;
+ };
+
+ struct X { ~X(); };
+ class C {
+ X x[4]; // no-warning
+ };
+}
diff --git a/test/SemaCXX/warn-unused-value.cpp b/test/SemaCXX/warn-unused-value.cpp
index 1c0263c..072ee60 100644
--- a/test/SemaCXX/warn-unused-value.cpp
+++ b/test/SemaCXX/warn-unused-value.cpp
@@ -12,7 +12,7 @@ namespace test0 {
// pointer to volatile has side effect (thus no warning)
Box* box = new Box;
box->i; // expected-warning {{expression result unused}}
- box->j;
+ box->j; // expected-warning {{expression result unused}}
}
}
diff --git a/test/SemaObjC/arc-bridged-cast.m b/test/SemaObjC/arc-bridged-cast.m
index 56efe81..b5ec36a 100644
--- a/test/SemaObjC/arc-bridged-cast.m
+++ b/test/SemaObjC/arc-bridged-cast.m
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -fblocks -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -fblocks -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
typedef const void *CFTypeRef;
CFTypeRef CFBridgingRetain(id X);
@@ -32,13 +33,32 @@ void to_cf(id obj) {
// rdar://problem/9629566 - temporary workaround
CFTypeRef cf5 = (__bridge_retain CFTypeRef)CreateSomething(); // expected-error {{unknown cast annotation __bridge_retain; did you mean __bridge_retained?}}
+ // CHECK: fix-it:"{{.*}}":{35:20-35:35}:"__bridge_retained"
}
-void fixits() {
+CFTypeRef fixits() {
id obj1 = (id)CFCreateSomething(); // expected-error{{cast of C pointer type 'CFTypeRef' (aka 'const void *') to Objective-C pointer type 'id' requires a bridged cast}} \
- // expected-note{{use __bridge to convert directly (no change in ownership)}} \
- // expected-note{{use CFBridgingRelease call to transfer ownership of a +1 'CFTypeRef' (aka 'const void *') into ARC}}
+ // expected-note{{use __bridge to convert directly (no change in ownership)}} expected-note{{use CFBridgingRelease call to transfer ownership of a +1 'CFTypeRef' (aka 'const void *') into ARC}}
+ // CHECK: fix-it:"{{.*}}":{40:17-40:17}:"CFBridgingRelease("
+ // CHECK: fix-it:"{{.*}}":{40:36-40:36}:")"
+
CFTypeRef cf1 = (CFTypeRef)CreateSomething(); // expected-error{{cast of Objective-C pointer type 'id' to C pointer type 'CFTypeRef' (aka 'const void *') requires a bridged cast}} \
// expected-note{{use __bridge to convert directly (no change in ownership)}} \
// expected-note{{use CFBridgingRetain call to make an ARC object available as a +1 'CFTypeRef' (aka 'const void *')}}
+ // CHECK: fix-it:"{{.*}}":{45:30-45:30}:"CFBridgingRetain("
+ // CHECK: fix-it:"{{.*}}":{45:47-45:47}:")"
+
+ return (obj1); // expected-error{{implicit conversion of Objective-C pointer type 'id' to C pointer type 'CFTypeRef' (aka 'const void *') requires a bridged cast}} \
+ // expected-note{{use __bridge to convert directly (no change in ownership)}} \
+ // expected-note{{use CFBridgingRetain call to make an ARC object available as a +1 'CFTypeRef' (aka 'const void *')}}
+ // CHECK: fix-it:"{{.*}}":{51:10-51:10}:"(__bridge CFTypeRef)"
+ // CHECK: fix-it:"{{.*}}":{51:10-51:10}:"CFBridgingRetain"
+}
+
+CFTypeRef fixitsWithSpace(id obj) {
+ return(obj); // expected-error{{implicit conversion of Objective-C pointer type 'id' to C pointer type 'CFTypeRef' (aka 'const void *') requires a bridged cast}} \
+ // expected-note{{use __bridge to convert directly (no change in ownership)}} \
+ // expected-note{{use CFBridgingRetain call to make an ARC object available as a +1 'CFTypeRef' (aka 'const void *')}}
+ // CHECK: fix-it:"{{.*}}":{59:9-59:9}:"(__bridge CFTypeRef)"
+ // CHECK: fix-it:"{{.*}}":{59:9-59:9}:" CFBridgingRetain"
}
diff --git a/test/SemaObjC/arc-cf.m b/test/SemaObjC/arc-cf.m
index 69662ea..5754720 100644
--- a/test/SemaObjC/arc-cf.m
+++ b/test/SemaObjC/arc-cf.m
@@ -10,11 +10,13 @@ id CFBridgingRelease(CFTypeRef);
typedef const struct __CFString *CFStringRef;
extern CFStringRef CFMakeString0(void);
+#pragma clang arc_cf_code_audited begin
extern CFStringRef CFCreateString0(void);
+#pragma clang arc_cf_code_audited end
void test0() {
id x;
x = (id) CFMakeString0(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{CFBridgingRelease call to transfer}}
- x = (id) CFCreateString0(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{CFBridgingRelease call to transfer}}
+ x = (id) CFCreateString0(); // expected-error {{requires a bridged cast}} expected-note {{CFBridgingRelease call to transfer}}
}
extern CFStringRef CFMakeString1(void) __attribute__((cf_returns_not_retained));
@@ -22,7 +24,7 @@ extern CFStringRef CFCreateString1(void) __attribute__((cf_returns_retained));
void test1() {
id x;
x = (id) CFMakeString1();
- x = (id) CFCreateString1(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{CFBridgingRelease call to transfer}}
+ x = (id) CFCreateString1(); // expected-error {{requires a bridged cast}} expected-note {{CFBridgingRelease call to transfer}}
}
#define CF_AUDIT_BEGIN _Pragma("clang arc_cf_code_audited begin")
@@ -40,6 +42,6 @@ void test2() {
id x;
x = (id) CFMakeString2();
x = (id) CFCreateString2();
- x = (id) CFMakeString3(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{CFBridgingRelease call to transfer}}
- x = (id) CFCreateString3(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{CFBridgingRelease call to transfer}}
+ x = (id) CFMakeString3(); // expected-error {{requires a bridged cast}} expected-note {{CFBridgingRelease call to transfer}}
+ x = (id) CFCreateString3(); // expected-error {{requires a bridged cast}} expected-note {{CFBridgingRelease call to transfer}}
}
diff --git a/test/SemaObjC/arc-dict-bridged-cast.m b/test/SemaObjC/arc-dict-bridged-cast.m
new file mode 100644
index 0000000..ea64840
--- /dev/null
+++ b/test/SemaObjC/arc-dict-bridged-cast.m
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// rdar://11913153
+
+typedef const struct __CFString * CFStringRef;
+typedef struct __CFString * CFMutableStringRef;
+typedef signed long CFIndex;
+typedef const struct __CFAllocator * CFAllocatorRef;
+
+extern const CFStringRef kCFBundleNameKey;
+
+@protocol NSCopying @end
+
+@interface NSDictionary
+- (id)objectForKeyedSubscript:(id<NSCopying>)key;
+@end
+
+#pragma clang arc_cf_code_audited begin
+extern
+CFMutableStringRef CFStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength);
+#pragma clang arc_cf_code_audited end
+
+typedef const void * CFTypeRef;
+
+id CFBridgingRelease(CFTypeRef __attribute__((cf_consumed)) X);
+
+@interface NSMutableString @end
+
+NSMutableString *test() {
+ NSDictionary *infoDictionary;
+ infoDictionary[kCFBundleNameKey] = 0; // expected-error {{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} \
+ // expected-error {{implicit conversion of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type '__strong id<NSCopying>' requires a bridged cast}} \
+ // expected-note {{use __bridge to convert directly (no change in ownership)}} \
+ // expected-note {{use CFBridgingRelease call to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}}
+ return infoDictionary[CFStringCreateMutable(((void*)0), 100)]; // expected-error {{indexing expression is invalid because subscript type 'CFMutableStringRef' (aka 'struct __CFString *') is not an integral or Objective-C pointer type}} \
+ // expected-error {{implicit conversion of C pointer type 'CFMutableStringRef' (aka 'struct __CFString *') to Objective-C pointer type '__strong id<NSCopying>' requires a bridged cast}} \
+ // expected-note {{use CFBridgingRelease call to transfer ownership of a +1 'CFMutableStringRef' (aka 'struct __CFString *') into ARC}}
+
+}
+
+// CHECK: fix-it:"{{.*}}":{31:18-31:18}:"(__bridge __strong id<NSCopying>)("
+// CHECK: fix-it:"{{.*}}":{31:34-31:34}:")"
+// CHECK: fix-it:"{{.*}}":{31:18-31:18}:"CFBridgingRelease("
+// CHECK: fix-it:"{{.*}}":{31:34-31:34}:")"
+// CHECK: fix-it:"{{.*}}":{35:25-35:25}:"CFBridgingRelease("
+// CHECK: fix-it:"{{.*}}":{35:63-35:63}:")"
diff --git a/test/SemaObjC/arc-no-runtime.m b/test/SemaObjC/arc-no-runtime.m
index b75064f..c5820d4 100644
--- a/test/SemaObjC/arc-no-runtime.m
+++ b/test/SemaObjC/arc-no-runtime.m
@@ -3,7 +3,7 @@
// rdar://problem/9150784
void test(void) {
__weak id x; // expected-error {{the current deployment target does not support automated __weak references}}
- __weak void *v; // expected-warning {{'__weak' only applies to objective-c object or block pointer types}}
+ __weak void *v; // expected-warning {{'__weak' only applies to Objective-C object or block pointer types}}
}
@interface A
diff --git a/test/SemaObjC/arc-property-lifetime.m b/test/SemaObjC/arc-property-lifetime.m
index fad37cc..bd393e0 100644
--- a/test/SemaObjC/arc-property-lifetime.m
+++ b/test/SemaObjC/arc-property-lifetime.m
@@ -168,3 +168,8 @@ void foo(Baz *f) {
f.realy_strong_attr_prop = [[Baz alloc] init];
f.implicit = [[Baz alloc] init];
}
+
+// rdar://11253688
+@interface Boom
+@property (readonly) const void * innerPointer __attribute__((objc_returns_inner_pointer)); // expected-error {{'objc_returns_inner_pointer' attribute only applies to methods}}
+@end
diff --git a/test/SemaObjC/arc-property.m b/test/SemaObjC/arc-property.m
index 2599fb9..41d8e87 100644
--- a/test/SemaObjC/arc-property.m
+++ b/test/SemaObjC/arc-property.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -fblocks -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -fblocks -fobjc-exceptions -verify -Wno-objc-root-class %s
// rdar://9309489
@interface MyClass {
@@ -55,3 +55,11 @@
@implementation Test2
@synthesize test2;
@end
+
+// rdar://problem/11144407
+@interface Test3
+@property (strong) id exception;
+@end
+void test3(Test3 *t3) {
+ @throw t3.exception;
+}
diff --git a/test/SemaObjC/arc-unbridged-cast.m b/test/SemaObjC/arc-unbridged-cast.m
index 7d5a6b0..6a39e70 100644
--- a/test/SemaObjC/arc-unbridged-cast.m
+++ b/test/SemaObjC/arc-unbridged-cast.m
@@ -44,10 +44,10 @@ void test1(int cond) {
x = (id) (cond ? (void*) 0 : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
x = (id) (cond ? (CFStringRef) @"help" : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) auditedCreateString(); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) (cond ? auditedCreateString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) (cond ? (void*) 0 : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) (cond ? (CFStringRef) @"help" : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) auditedCreateString(); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? auditedCreateString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? (void*) 0 : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? (CFStringRef) @"help" : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
x = (id) [object property];
x = (id) (cond ? [object property] : (void*) 0);
diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m
index 9c3b298..bd30715 100644
--- a/test/SemaObjC/arc.m
+++ b/test/SemaObjC/arc.m
@@ -641,7 +641,7 @@ void test35(void) {
test36_helper(&y);
^{ test36_helper(&y); }();
- __strong int non_objc_type; // expected-warning {{'__strong' only applies to objective-c object or block pointer types}}
+ __strong int non_objc_type; // expected-warning {{'__strong' only applies to Objective-C object or block pointer types}}
}
void test36(int first, ...) {
@@ -696,3 +696,24 @@ void _NSCalcBeze(NSColor* color, NSColor* bezelColors[]); // expected-error {{mu
}
@end
+// rdar://11814185
+@interface Radar11814185
+@property (nonatomic, weak) Radar11814185* picker1;
++ alloc;
+- init;
+@end
+
+@implementation Radar11814185
+
+@synthesize picker1;
+
+- (void)viewDidLoad
+{
+ picker1 = [[Radar11814185 alloc] init]; // expected-warning {{assigning retained object to weak variable; object will be released after assignment}}
+ self.picker1 = [[Radar11814185 alloc] init]; // expected-warning {{assigning retained object to weak property; object will be released after assignment}}
+}
+
++ alloc { return 0; }
+- init { return 0; }
+@end
+
diff --git a/test/SemaObjC/assign-rvalue-message.m b/test/SemaObjC/assign-rvalue-message.m
index 1105d5e..a90cc50 100644
--- a/test/SemaObjC/assign-rvalue-message.m
+++ b/test/SemaObjC/assign-rvalue-message.m
@@ -19,6 +19,6 @@ struct Bar {
- (void)baz {
bar.x = 0;
- [self bar].x = 10; // expected-error {{assigning to 'readonly' return result of an objective-c message not allowed}}
+ [self bar].x = 10; // expected-error {{assigning to 'readonly' return result of an Objective-C message not allowed}}
}
@end
diff --git a/test/SemaObjC/at-defs.m b/test/SemaObjC/at-defs.m
index 4c0c586f..73316ca 100644
--- a/test/SemaObjC/at-defs.m
+++ b/test/SemaObjC/at-defs.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-fragile-abi %s -fsyntax-only
+// RUN: %clang_cc1 -triple i386-unknown-unknown -fobjc-runtime=macosx-fragile-10.5 %s -fsyntax-only
@interface Test {
double a;
diff --git a/test/SemaObjC/attr-availability.m b/test/SemaObjC/attr-availability.m
index d857bda..7c9ff0f 100644
--- a/test/SemaObjC/attr-availability.m
+++ b/test/SemaObjC/attr-availability.m
@@ -1,13 +1,21 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin9.0.0 -fsyntax-only -verify %s
-@interface A
-- (void)method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2)));
+
+@protocol P
+- (void)proto_method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 2 {{method 'proto_method' declared here}}
+@end
+
+@interface A <P>
+- (void)method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note {{method 'method' declared here}}
@end
+// rdar://11475360
@interface B : A
-- (void)method;
+- (void)method; // expected-note {{method 'method' declared here}}
@end
void f(A *a, B *b) {
[a method]; // expected-warning{{'method' is deprecated: first deprecated in Mac OS X 10.2}}
- [b method];
+ [b method]; // expected-warning {{'method' is deprecated: first deprecated in Mac OS X 10.2}}
+ [a proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in Mac OS X 10.2}}
+ [b proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in Mac OS X 10.2}}
}
diff --git a/test/SemaObjC/attr-deprecated.m b/test/SemaObjC/attr-deprecated.m
index db0b958..260462a 100644
--- a/test/SemaObjC/attr-deprecated.m
+++ b/test/SemaObjC/attr-deprecated.m
@@ -1,10 +1,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
@interface A {
- int X __attribute__((deprecated));
+ int X __attribute__((deprecated)); // expected-note 2 {{declared here}}
}
-+ (void)F __attribute__((deprecated));
-- (void)f __attribute__((deprecated));
++ (void)F __attribute__((deprecated)); // expected-note 2 {{declared here}}
+- (void)f __attribute__((deprecated)); // expected-note 4 {{declared here}}
@end
@implementation A
@@ -42,7 +42,7 @@
@end
@protocol P
-- (void)p __attribute__((deprecated));
+- (void)p __attribute__((deprecated)); // expected-note {{declared here}}
@end
void t1(A *a)
@@ -71,7 +71,7 @@ void t4(Class c)
@interface Bar
-@property (assign, setter = MySetter:) int FooBar __attribute__ ((deprecated));
+@property (assign, setter = MySetter:) int FooBar __attribute__ ((deprecated)); // expected-note 2 {{declared here}}
- (void) MySetter : (int) value;
@end
@@ -83,7 +83,7 @@ int t5() {
__attribute ((deprecated))
-@interface DEPRECATED {
+@interface DEPRECATED { // expected-note 2 {{declared here}}
@public int ivar;
DEPRECATED *ivar2; // no warning.
}
@@ -107,7 +107,7 @@ __attribute ((deprecated))
@interface Test2
-@property int test2 __attribute__((deprecated));
+@property int test2 __attribute__((deprecated)); // expected-note 4 {{declared here}}
@end
void test(Test2 *foo) {
@@ -121,3 +121,16 @@ void test(Test2 *foo) {
__attribute__((deprecated))
@interface A(Blah) // expected-error{{attributes may not be specified on a category}}
@end
+
+
+typedef struct {
+ int x;
+} footype __attribute((deprecated)); // expected-note 2 {{declared here}}
+
+@interface foo {
+ footype a; // expected-warning {{'footype' is deprecated}}
+ footype b __attribute((deprecated));
+}
+@property footype c; // expected-warning {{'footype' is deprecated}}
+@property footype d __attribute((deprecated));
+@end
diff --git a/test/SemaObjC/blocks.m b/test/SemaObjC/blocks.m
index 7beec19..9926b08 100644
--- a/test/SemaObjC/blocks.m
+++ b/test/SemaObjC/blocks.m
@@ -73,3 +73,124 @@ void foo10() {
NSLog(@"%@", myBlock);
}
+
+// In C, enum constants have the type of the underlying integer type, not the
+// enumeration they are part of. We pretend the constants have enum type when
+// they are mixed with other expressions of enum type.
+enum CStyleEnum {
+ CSE_Value = 1
+};
+enum CStyleEnum getCSE();
+typedef enum CStyleEnum (^cse_block_t)();
+
+void testCStyleEnumInference(bool arg) {
+ cse_block_t a;
+
+ // No warnings here.
+ a = ^{ return getCSE(); };
+
+ a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}}
+ return 1;
+ };
+ a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}}
+ return CSE_Value;
+ };
+
+ // No warnings here.
+ a = ^{ if (arg) return CSE_Value; else return getCSE(); };
+ a = ^{ if (arg) return getCSE(); else return CSE_Value; };
+
+ // These two blocks actually return 'int'
+ a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}}
+ if (arg)
+ return 1;
+ else
+ return CSE_Value;
+ };
+
+ a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}}
+ if (arg)
+ return CSE_Value;
+ else
+ return 1;
+ };
+}
+
+
+enum FixedTypeEnum : unsigned {
+ FTE_Value = 1U
+};
+enum FixedTypeEnum getFTE();
+typedef enum FixedTypeEnum (^fte_block_t)();
+
+void testFixedTypeEnumInference(bool arg) {
+ fte_block_t a;
+
+ // No warnings here.
+ a = ^{ return getFTE(); };
+
+ // Since we fixed the underlying type of the enum, this is considered a
+ // compatible block type.
+ a = ^{
+ return 1U;
+ };
+ a = ^{
+ return FTE_Value;
+ };
+
+ // No warnings here.
+ a = ^{ if (arg) return FTE_Value; else return FTE_Value; };
+ a = ^{ if (arg) return getFTE(); else return getFTE(); };
+ a = ^{ if (arg) return FTE_Value; else return getFTE(); };
+ a = ^{ if (arg) return getFTE(); else return FTE_Value; };
+
+ // These two blocks actually return 'unsigned'.
+ a = ^{
+ if (arg)
+ return 1U;
+ else
+ return FTE_Value;
+ };
+
+ a = ^{
+ if (arg)
+ return FTE_Value;
+ else
+ return 1U;
+ };
+}
+
+
+enum {
+ AnonymousValue = 1
+};
+
+enum : short {
+ FixedAnonymousValue = 1
+};
+
+typedef enum {
+ TDE_Value
+} TypeDefEnum;
+TypeDefEnum getTDE();
+
+typedef enum : short {
+ TDFTE_Value
+} TypeDefFixedTypeEnum;
+TypeDefFixedTypeEnum getTDFTE();
+
+typedef int (^int_block_t)();
+typedef short (^short_block_t)();
+void testAnonymousEnumTypes(int arg) {
+ int_block_t IB;
+ IB = ^{ return AnonymousValue; };
+ IB = ^{ if (arg) return TDE_Value; else return getTDE(); }; // expected-error {{incompatible block pointer}}
+ IB = ^{ if (arg) return getTDE(); else return TDE_Value; }; // expected-error {{incompatible block pointer}}
+
+ // Since we fixed the underlying type of the enum, these are considered
+ // compatible block types anyway.
+ short_block_t SB;
+ SB = ^{ return FixedAnonymousValue; };
+ SB = ^{ if (arg) return TDFTE_Value; else return getTDFTE(); };
+ SB = ^{ if (arg) return getTDFTE(); else return TDFTE_Value; };
+}
diff --git a/test/SemaObjC/boxing-illegal-types.m b/test/SemaObjC/boxing-illegal-types.m
new file mode 100644
index 0000000..ad45b11
--- /dev/null
+++ b/test/SemaObjC/boxing-illegal-types.m
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wattributes %s
+
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+typedef signed char BOOL;
+
+@interface NSNumber
+@end
+@interface NSNumber (NSNumberCreation)
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
+@end
+
+typedef struct {
+ int x, y, z;
+} point;
+
+void testStruct() {
+ point p = { 0, 0, 0 };
+ id boxed = @(p); // expected-error {{illegal type 'point' used in a boxed expression}}
+}
+
+void testPointers() {
+ void *null = 0;
+ id boxed_null = @(null); // expected-error {{illegal type 'void *' used in a boxed expression}}
+ int numbers[] = { 0, 1, 2 };
+ id boxed_numbers = @(numbers); // expected-error {{illegal type 'int *' used in a boxed expression}}
+}
+
+void testInvalid() {
+ @(not_defined); // expected-error {{use of undeclared identifier 'not_defined'}}
+}
+
+enum MyEnum {
+ ME_foo
+};
+
+enum ForwE;
+
+void testEnum(void *p) {
+ enum MyEnum myen;
+ id box = @(myen);
+ box = @(ME_foo);
+ box = @(*(enum ForwE*)p); // expected-error {{incomplete type 'enum ForwE' used in a boxed expression}}
+}
diff --git a/test/SemaObjC/category-1.m b/test/SemaObjC/category-1.m
index f842278..a7e6965 100644
--- a/test/SemaObjC/category-1.m
+++ b/test/SemaObjC/category-1.m
@@ -99,3 +99,12 @@
@class I; // expected-note {{forward declaration}}
@implementation I(cat) // expected-error{{cannot find interface declaration}}
@end
+
+// <rdar://problem/11478173>
+@interface Unrelated
+- foo;
+@end
+
+@interface Blah (Blarg) // expected-error{{cannot find interface declaration for 'Blah'}}
+- foo;
+@end
diff --git a/test/SemaObjC/class-bitfield.m b/test/SemaObjC/class-bitfield.m
index ae12e04..4b13d9a 100644
--- a/test/SemaObjC/class-bitfield.m
+++ b/test/SemaObjC/class-bitfield.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -fobjc-fragile-abi -fsyntax-only -verify
+// RUN: %clang_cc1 %s -fobjc-runtime=macosx-fragile-10.5 -fsyntax-only -verify
@interface X
{
diff --git a/test/SemaObjC/cocoa-api-usage.m b/test/SemaObjC/cocoa-api-usage.m
index 85e2115..bed7ecd 100644
--- a/test/SemaObjC/cocoa-api-usage.m
+++ b/test/SemaObjC/cocoa-api-usage.m
@@ -1,9 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -fsyntax-only -Wobjc-cocoa-api -verify
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %s -fsyntax-only -Wobjc-cocoa-api -verify
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c %s.fixed -fsyntax-only
-// RUN: cp %s %t.m
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %t.m -fixit -Wobjc-cocoa-api
-// RUN: diff %s.fixed %t.m
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -x objective-c %s.fixed -fsyntax-only
// RUN: cp %s %t.m
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %t.m -fixit -Wobjc-cocoa-api
// RUN: diff %s.fixed %t.m
@@ -82,7 +78,7 @@ typedef signed char BOOL;
void foo() {
NSString *str = M([NSString stringWithString:@"foo"]); // expected-warning {{redundant}}
- str = [[NSString alloc] initWithString:@"foo"];
+ str = [[NSString alloc] initWithString:@"foo"]; // expected-warning {{redundant}}
NSArray *arr = [NSArray arrayWithArray:@[str]]; // expected-warning {{redundant}}
NSDictionary *dict = [NSDictionary dictionaryWithDictionary:@{str: arr}]; // expected-warning {{redundant}}
}
diff --git a/test/SemaObjC/cocoa-api-usage.m.fixed b/test/SemaObjC/cocoa-api-usage.m.fixed
index 55e060a..f472cf1 100644
--- a/test/SemaObjC/cocoa-api-usage.m.fixed
+++ b/test/SemaObjC/cocoa-api-usage.m.fixed
@@ -1,9 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -fsyntax-only -Wobjc-cocoa-api -verify
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %s -fsyntax-only -Wobjc-cocoa-api -verify
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c %s.fixed -fsyntax-only
-// RUN: cp %s %t.m
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %t.m -fixit -Wobjc-cocoa-api
-// RUN: diff %s.fixed %t.m
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -x objective-c %s.fixed -fsyntax-only
// RUN: cp %s %t.m
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %t.m -fixit -Wobjc-cocoa-api
// RUN: diff %s.fixed %t.m
@@ -82,7 +78,7 @@ typedef signed char BOOL;
void foo() {
NSString *str = M(@"foo"); // expected-warning {{redundant}}
- str = [[NSString alloc] initWithString:@"foo"];
+ str = @"foo"; // expected-warning {{redundant}}
NSArray *arr = @[str]; // expected-warning {{redundant}}
NSDictionary *dict = @{str: arr}; // expected-warning {{redundant}}
}
diff --git a/test/SemaObjC/conflicting-ivar-test-1.m b/test/SemaObjC/conflicting-ivar-test-1.m
index a7c1d35..a5c09d8 100644
--- a/test/SemaObjC/conflicting-ivar-test-1.m
+++ b/test/SemaObjC/conflicting-ivar-test-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-fragile-abi -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fobjc-runtime=macosx-fragile-10.5 -fsyntax-only -verify -Wno-objc-root-class %s
@interface INTF
{
diff --git a/test/SemaObjC/continuation-class-err.m b/test/SemaObjC/continuation-class-err.m
index d691f12..ceb8ee9 100644
--- a/test/SemaObjC/continuation-class-err.m
+++ b/test/SemaObjC/continuation-class-err.m
@@ -11,9 +11,9 @@
@end
@interface ReadOnly ()
-@property(readwrite, copy) id object; // expected-warning {{property attribute in continuation class does not match the primary class}}
-@property(readonly) id object1; // expected-error {{illegal redeclaration of property in continuation class 'ReadOnly' (attribute must be 'readwrite', while its primary must be 'readonly')}}
-@property (readwrite, assign) int indentLevel; // OK. assign the the default in any case.
+@property(readwrite, copy) id object; // expected-warning {{property attribute in class extension does not match the primary class}}
+@property(readonly) id object1; // expected-error {{illegal redeclaration of property in class extension 'ReadOnly' (attribute must be 'readwrite', while its primary must be 'readonly')}}
+@property (readwrite, assign) int indentLevel; // OK. assign the default in any case.
@end
@protocol Proto
@@ -31,8 +31,8 @@
@end
@interface Bar ()
-@property (copy) id foo; // expected-error {{illegal redeclaration of property in continuation class 'Bar' (attribute must be 'readwrite', while its primary must be 'readonly')}}
-@property (copy) id fee; // expected-error {{illegal redeclaration of property in continuation class 'Bar' (attribute must be 'readwrite', while its primary must be 'readonly')}}
+@property (copy) id foo; // expected-error {{illegal redeclaration of property in class extension 'Bar' (attribute must be 'readwrite', while its primary must be 'readonly')}}
+@property (copy) id fee; // expected-error {{illegal redeclaration of property in class extension 'Bar' (attribute must be 'readwrite', while its primary must be 'readonly')}}
@end
@implementation Bar
diff --git a/test/SemaObjC/continuation-class-property.m b/test/SemaObjC/continuation-class-property.m
index 7d95424..2a8e508 100644
--- a/test/SemaObjC/continuation-class-property.m
+++ b/test/SemaObjC/continuation-class-property.m
@@ -38,8 +38,8 @@ typedef struct {
@end
@interface MyClass ()
-@property (readwrite) NSString *foo; // expected-error {{type of property 'NSString *' in continuation class does not match property type in primary class}}
-@property (readwrite, strong) NSRect bar; // expected-error {{type of property 'NSRect' in continuation class does not match property type in primary class}}
+@property (readwrite) NSString *foo; // expected-error {{type of property 'NSString *' in class extension does not match property type in primary class}}
+@property (readwrite, strong) NSRect bar; // expected-error {{type of property 'NSRect' in class extension does not match property type in primary class}}
@end
// rdar://10655530
diff --git a/test/SemaObjC/dealloc.m b/test/SemaObjC/dealloc.m
new file mode 100644
index 0000000..feafafd
--- /dev/null
+++ b/test/SemaObjC/dealloc.m
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -fblocks -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-arc -fblocks -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// rdar://11987838
+
+@protocol NSObject
+- dealloc; // expected-error {{return type must be correctly specified as 'void' under ARC, instead of 'id'}}
+// CHECK: fix-it:"{{.*}}":{6:3-6:3}:"(void)"
+@end
+
+@protocol Foo <NSObject> @end
+
+@interface Root <Foo>
+@end
+
+@interface Baz : Root {
+}
+@end
+
+@implementation Baz
+- (id) dealloc { // expected-error {{return type must be correctly specified as 'void' under ARC, instead of 'id'}}
+// CHECK: fix-it:"{{.*}}":{20:5-20:7}:"void"
+}
+
+@end
+
diff --git a/test/SemaObjC/default-synthesize-1.m b/test/SemaObjC/default-synthesize-1.m
index c201e74..5aaca9a 100644
--- a/test/SemaObjC/default-synthesize-1.m
+++ b/test/SemaObjC/default-synthesize-1.m
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -fobjc-default-synthesize-properties -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-default-synthesize-properties -Wobjc-missing-property-synthesis -verify -Wno-objc-root-class %s
+// rdar://11295716
@interface NSObject
- (void) release;
@@ -7,21 +8,21 @@
@class NSString;
@interface SynthItAll : NSObject
-@property int howMany;
-@property (retain) NSString* what;
+@property int howMany; // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
+@property (retain) NSString* what; // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
@end
-@implementation SynthItAll
+@implementation SynthItAll // expected-note 2 {{detected while default synthesizing properties in class implementation}}
//@synthesize howMany, what;
@end
@interface SynthSetter : NSObject
-@property (nonatomic) int howMany; // REM: nonatomic to avoid warnings about only implementing one of the pair
-@property (nonatomic, retain) NSString* what;
+@property (nonatomic) int howMany; // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
+@property (nonatomic, retain) NSString* what; // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
@end
-@implementation SynthSetter
+@implementation SynthSetter // expected-note 2 {{detected while default synthesizing properties in class implementation}}
//@synthesize howMany, what;
- (int) howMany {
@@ -37,11 +38,11 @@
@interface SynthGetter : NSObject
-@property (nonatomic) int howMany; // REM: nonatomic to avoid warnings about only implementing one of the pair
-@property (nonatomic, retain) NSString* what;
+@property (nonatomic) int howMany; // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
+@property (nonatomic, retain) NSString* what; // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
@end
-@implementation SynthGetter
+@implementation SynthGetter // expected-note 2 {{detected while default synthesizing properties in class implementation}}
//@synthesize howMany, what;
// - (int) howMany
@@ -114,3 +115,12 @@
}
@end
+@interface rdar11333367
+@property enum A x; // expected-note {{forward declaration of 'enum A'}} expected-note {{property declared here}}
+@property struct B y; // expected-note {{forward declaration of 'struct B'}} expected-note {{property declared here}} \
+ // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
+@end
+@implementation rdar11333367 // expected-error {{cannot synthesize property 'y' with incomplete type 'struct B'}} \
+ // expected-note {{detected while default synthesizing properties in class implementation}}
+@synthesize x; // expected-error {{cannot synthesize property 'x' with incomplete type 'enum A'}}
+@end
diff --git a/test/SemaObjC/default-synthesize-2.m b/test/SemaObjC/default-synthesize-2.m
index b95f263..3756413 100644
--- a/test/SemaObjC/default-synthesize-2.m
+++ b/test/SemaObjC/default-synthesize-2.m
@@ -41,12 +41,13 @@
// Test3
@interface Test3
{
- id uid;
+ id uid; // expected-note {{ivar is declared here}}
}
-@property (readwrite, assign) id uid;
+@property (readwrite, assign) id uid; // expected-note {{property declared here}}
@end
-@implementation Test3
+// rdar://11671080
+@implementation Test3 // expected-warning {{autosynthesized property 'uid' will use synthesized instance variable '_uid', not existing instance variable 'uid'}}
// Oops, forgot to write @synthesize! will be default synthesized
- (void) myMethod {
self.uid = 0; // Use of the “setter”
@@ -114,3 +115,15 @@ int* _object;
}
@end
+// rdar://11671080
+@interface Test8
+{
+ id _y;
+ id y; // expected-note {{ivar is declared here}}
+}
+@property(copy) id y; // expected-note {{property declared here}}
+@end
+
+
+@implementation Test8 @end // expected-warning {{autosynthesized property 'y' will use instance variable '_y', not existing instance variable 'y'}}
+
diff --git a/test/SemaObjC/delay-parsing-cfunctions.m b/test/SemaObjC/delay-parsing-cfunctions.m
new file mode 100644
index 0000000..a6f66fe
--- /dev/null
+++ b/test/SemaObjC/delay-parsing-cfunctions.m
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -fsyntax-only -Werror -verify -Wno-objc-root-class %s
+// rdar://10387088
+
+@interface MyClass
+- (void)someMethod;
+@end
+
+@implementation MyClass
+- (void)someMethod {
+ [self privateMethod]; // clang already does not warn here
+}
+
+int bar(MyClass * myObject) {
+ [myObject privateMethod];
+ return gorfbar(myObject);
+}
+- (void)privateMethod { }
+
+int gorfbar(MyClass * myObject) {
+ [myObject privateMethod];
+ [myObject privateMethod1];
+ return getMe + bar(myObject);
+}
+
+int KR(myObject)
+MyClass * myObject;
+{
+ [myObject privateMethod];
+ [myObject privateMethod1];
+ return getMe + bar(myObject);
+}
+
+- (void)privateMethod1 {
+ getMe = getMe+1;
+}
+
+static int getMe;
+
+static int test() {
+ return 0;
+}
+
+@end
diff --git a/test/SemaObjC/direct-synthesized-ivar-access.m b/test/SemaObjC/direct-synthesized-ivar-access.m
index 54b7110..dc14911 100644
--- a/test/SemaObjC/direct-synthesized-ivar-access.m
+++ b/test/SemaObjC/direct-synthesized-ivar-access.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -Wnonfragile-abi2 -fsyntax-only -fobjc-default-synthesize-properties -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-default-synthesize-properties -verify -Wno-objc-root-class %s
// rdar://8673791
// rdar://9943851
diff --git a/test/SemaObjC/duplicate-property-class-extension.m b/test/SemaObjC/duplicate-property-class-extension.m
index bf48ed6..696768d 100644
--- a/test/SemaObjC/duplicate-property-class-extension.m
+++ b/test/SemaObjC/duplicate-property-class-extension.m
@@ -9,7 +9,7 @@
@interface Foo ()
@property (readwrite) char foo; // expected-note 2 {{property declared here}}
@property (readwrite) char NewProperty; // expected-note 2 {{property declared here}}
-@property (readwrite) char bar; // expected-error{{illegal redeclaration of 'readwrite' property in continuation class 'Foo' (perhaps you intended this to be a 'readwrite' redeclaration of a 'readonly' public property?)}}
+@property (readwrite) char bar; // expected-error{{illegal redeclaration of 'readwrite' property in class extension 'Foo' (perhaps you intended this to be a 'readwrite' redeclaration of a 'readonly' public property?)}}
@end
@interface Foo ()
diff --git a/test/SemaObjC/error-implicit-property.m b/test/SemaObjC/error-implicit-property.m
index ea0587a..7e795c7 100644
--- a/test/SemaObjC/error-implicit-property.m
+++ b/test/SemaObjC/error-implicit-property.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify %s
+// RUN: %clang_cc1 -Wno-objc-root-class -verify %s
// rdar://11273060
@interface I
diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m
index 987889b..7faa995 100644
--- a/test/SemaObjC/format-strings-objc.m
+++ b/test/SemaObjC/format-strings-objc.m
@@ -31,6 +31,12 @@ extern void *_NSConstantStringClassReference;
typedef const struct __CFString * CFStringRef;
extern void CFStringCreateWithFormat(CFStringRef format, ...) __attribute__((format(CFString, 1, 2)));
+#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
+
+// This function is used instead of the builtin if -fno-constant-cfstrings.
+// The definition on Mac OS X is NOT annotated with format_arg as of 10.8,
+// but clang will implicitly add the attribute if it's not written.
+extern CFStringRef __CFStringMakeConstantString(const char *);
int printf(const char * restrict, ...) ;
@@ -52,6 +58,7 @@ void rdar_7068334() {
long long test = 500;
printf("%i ",test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
NSLog(@"%i ",test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
+ CFStringCreateWithFormat(CFSTR("%i"),test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
}
// <rdar://problem/7697748>
@@ -72,7 +79,7 @@ extern void MyCFStringCreateWithFormat(CFStringRef format, ...) __attribute__((f
void check_mylog() {
MyNSLog(@"%@"); // expected-warning {{more '%' conversions than data arguments}}
- // FIXME: find a way to test CFString too, but I don't know how to create constant CFString.
+ MyCFStringCreateWithFormat(CFSTR("%@")); // expected-warning {{more '%' conversions than data arguments}}
}
// PR 10275 - format function attribute isn't checked in Objective-C methods
@@ -111,11 +118,20 @@ NSString *test_literal_propagation(void) {
}
// Do not emit warnings when using NSLocalizedString
-extern NSString *GetLocalizedString(NSString *str);
-#define NSLocalizedString(key) GetLocalizedString(key)
+#include "format-strings-system.h"
+
+// Test it inhibits diag only for macros in system headers
+#define MyNSLocalizedString(key) GetLocalizedString(key)
+#define MyNSAssert(fmt, arg) NSLog(fmt, arg, 0, 0)
void check_NSLocalizedString() {
[Foo fooWithFormat:NSLocalizedString(@"format"), @"arg"]; // no-warning
+ [Foo fooWithFormat:MyNSLocalizedString(@"format"), @"arg"]; // expected-warning {{format string is not a string literal}}}
+}
+
+void check_NSAssert() {
+ NSAssert(@"Hello %@", @"World"); // no-warning
+ MyNSAssert(@"Hello %@", @"World"); // expected-warning {{data argument not used by format string}}
}
typedef __WCHAR_TYPE__ wchar_t;
@@ -150,8 +166,12 @@ void test_percent_C() {
}
// Test that %@ works with toll-free bridging (<rdar://problem/10814120>).
-void test_toll_free_bridging(CFStringRef x) {
+void test_toll_free_bridging(CFStringRef x, id y) {
NSLog(@"%@", x); // no-warning
+ CFStringCreateWithFormat(CFSTR("%@"), x); // no-warning
+
+ NSLog(@"%@", y); // no-warning
+ CFStringCreateWithFormat(CFSTR("%@"), y); // no-warning
}
@interface Bar
@@ -186,3 +206,32 @@ int rdar11049844() {
printf("%p", x); // no-warning
}
+void test_nonBuiltinCFStrings() {
+ CFStringCreateWithFormat(__CFStringMakeConstantString("%@"), 1); // expected-warning{{format specifies type 'id' but the argument has type 'int'}}
+}
+
+
+// Don't crash on an invalid argument expression.
+// <rdar://problem/11890818>
+@interface NSDictionary : NSObject
+- (id)objectForKeyedSubscript:(id)key;
+@end
+
+void testInvalidFormatArgument(NSDictionary *dict) {
+ NSLog(@"no specifiers", dict[CFSTR("abc")]); // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}}
+ NSLog(@"%@", dict[CFSTR("abc")]); // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}}
+ NSLog(@"%@ %@", dict[CFSTR("abc")]); // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}}
+
+ [Foo fooWithFormat:@"no specifiers", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}}
+ [Foo fooWithFormat:@"%@", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}}
+ [Foo fooWithFormat:@"%@ %@", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} expected-warning{{more '%' conversions than data arguments}}
+}
+
+
+// <rdar://problem/11825593>
+void testByValueObjectInFormat(Foo *obj) {
+ printf("%d %d %d", 1L, *obj, 1L); // expected-error {{cannot pass object with interface type 'Foo' by value to variadic function; expected type from format string was 'int'}} expected-warning 2 {{format specifies type 'int' but the argument has type 'long'}}
+
+ [Bar log2:@"%d", *obj]; // expected-error {{cannot pass object with interface type 'Foo' by value to variadic method; expected type from format string was 'int'}}
+}
+
diff --git a/test/SemaObjC/format-strings-system.h b/test/SemaObjC/format-strings-system.h
new file mode 100644
index 0000000..73b7768
--- /dev/null
+++ b/test/SemaObjC/format-strings-system.h
@@ -0,0 +1,10 @@
+
+#pragma clang system_header
+
+@class NSString;
+
+// Do not emit warnings when using NSLocalizedString
+extern NSString *GetLocalizedString(NSString *str);
+#define NSLocalizedString(key) GetLocalizedString(key)
+
+#define NSAssert(fmt, arg) NSLog(fmt, arg, 0, 0)
diff --git a/test/SemaObjC/getter-setter-defined-in-category-of-parent.m b/test/SemaObjC/getter-setter-defined-in-category-of-parent.m
new file mode 100644
index 0000000..71c3237
--- /dev/null
+++ b/test/SemaObjC/getter-setter-defined-in-category-of-parent.m
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+
+@interface MyParent {
+ int X;
+}
+@end
+@implementation MyParent
+@end
+
+@interface MyParent(AA) {
+}
+@end
+@implementation MyParent (AA)
+- (void) setX: (int)in {X = in - 2;}
+- (int) X {return X;}
+@end
+
+@interface MyClass : MyParent
+@end
+@implementation MyClass
+@end
+
+int foo(MyClass *o) {
+ o.X = 2;
+ return o.X;
+} \ No newline at end of file
diff --git a/test/SemaObjC/iboutlet.m b/test/SemaObjC/iboutlet.m
new file mode 100644
index 0000000..a29915c
--- /dev/null
+++ b/test/SemaObjC/iboutlet.m
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -fsyntax-only -fobjc-default-synthesize-properties -Wno-objc-root-class -verify %s
+// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -fobjc-default-synthesize-properties -Wno-objc-root-class -verify %s
+// rdar://11448209
+
+#define READONLY readonly
+
+@class NSView;
+
+#define IBOutlet __attribute__((iboutlet))
+
+@interface I
+@property (getter = MyGetter, readonly, assign) IBOutlet NSView *myView; // expected-note {{property declared here}} \
+ // expected-note {{readonly IBOutlet property should be changed to be readwrite}}
+
+@property (readonly) IBOutlet NSView *myView1; // expected-note {{readonly IBOutlet property should be changed to be readwrite}} \
+ // expected-note {{property declared here}}
+
+@property (getter = MyGetter, READONLY) IBOutlet NSView *myView2; // expected-note {{property declared here}}
+
+@end
+
+@implementation I // expected-warning 3 {{readonly IBOutlet property when auto-synthesized may not work correctly with 'nib' loader}}
+@end
diff --git a/test/SemaObjC/id.m b/test/SemaObjC/id.m
index 27b84de..ced406e 100644
--- a/test/SemaObjC/id.m
+++ b/test/SemaObjC/id.m
@@ -16,6 +16,16 @@ void foo() {
}
// Test attempt to redefine 'id' in an incompatible fashion.
-typedef int id; // FIXME: Decide how we want to deal with this (now that 'id' is more of a built-in type).
+// rdar://11356439
+typedef int id; // expected-error {{typedef redefinition with different types ('int' vs 'id')}}
id b;
+typedef double id; // expected-error {{typedef redefinition with different types ('double' vs 'id')}}
+
+typedef char *id; // expected-error {{typedef redefinition with different types ('char *' vs 'id')}}
+
+typedef union U{ int iu; } *id; // expected-error {{typedef redefinition with different types ('union U *' vs 'id')}}
+
+void test11356439(id o) {
+ o->x; // expected-error {{member reference base type 'id' is not a structure or union}}
+}
diff --git a/test/SemaObjC/interface-1.m b/test/SemaObjC/interface-1.m
index 87c2307..79fbad8 100644
--- a/test/SemaObjC/interface-1.m
+++ b/test/SemaObjC/interface-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi %s -fsyntax-only -verify
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 %s -fsyntax-only -verify
// rdar://5957506
@interface NSWhatever :
diff --git a/test/SemaObjC/interface-layout.m b/test/SemaObjC/interface-layout.m
index a8a93f0..336605a 100644
--- a/test/SemaObjC/interface-layout.m
+++ b/test/SemaObjC/interface-layout.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -fsyntax-only -verify -triple i386-apple-darwin9 -fobjc-fragile-abi
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5
typedef struct objc_object {} *id;
typedef signed char BOOL;
typedef unsigned int NSUInteger;
diff --git a/test/SemaObjC/ivar-in-class-extension-error.m b/test/SemaObjC/ivar-in-class-extension-error.m
index cecaa33..b22b798 100644
--- a/test/SemaObjC/ivar-in-class-extension-error.m
+++ b/test/SemaObjC/ivar-in-class-extension-error.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fobjc-fragile-abi -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fobjc-runtime=macosx-fragile-10.5 -fsyntax-only -verify %s
// rdar://6812436
@interface A @end
diff --git a/test/SemaObjC/method-bad-param.m b/test/SemaObjC/method-bad-param.m
index 9505cb4..d44b536 100644
--- a/test/SemaObjC/method-bad-param.m
+++ b/test/SemaObjC/method-bad-param.m
@@ -26,7 +26,7 @@ foo somefunc2() {} // expected-error {{interface type 'foo' cannot be returned b
// rdar://6780761
void f0(foo *a0) {
extern void g0(int x, ...);
- g0(1, *(foo*)a0); // expected-error {{cannot pass object with interface type 'foo' by-value through variadic function}}
+ g0(1, *(foo*)a0); // expected-error {{cannot pass object with interface type 'foo' by value through variadic function}}
}
// rdar://8421082
diff --git a/test/SemaObjC/method-prototype-scope.m b/test/SemaObjC/method-prototype-scope.m
index 0bebd9b..c581500 100644
--- a/test/SemaObjC/method-prototype-scope.m
+++ b/test/SemaObjC/method-prototype-scope.m
@@ -7,7 +7,7 @@ int object;
@class NSString, NSArray;
@interface Test
-- Func:(int)XXXX, id object;
+- Func:(int)XXXX, id object; // expected-warning {{use of C-style parameters in Objective-C method declarations is deprecated}}
- doSomethingElseWith:(id)object;
@@ -23,7 +23,7 @@ int object;
return object; // expected-warning {{incompatible pointer types returning 'NSArray *' from a function with result type 'NSString *'}}
}
-- Func:(int)XXXX, id object { return object; }
+- Func:(int)XXXX, id object { return object; } // expected-warning {{use of C-style parameters in Objective-C method declarations is deprecated}}
- doSomethingElseWith:(id)object { return object; }
diff --git a/test/SemaObjC/mismatched-undefined-method.m b/test/SemaObjC/mismatched-undefined-method.m
new file mode 100644
index 0000000..c41d142
--- /dev/null
+++ b/test/SemaObjC/mismatched-undefined-method.m
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-deprecated-declarations -verify %s
+// rdar://11460990
+
+typedef unsigned int CGDirectDisplayID;
+
+@interface NSObject @end
+
+@interface BrightnessAssistant : NSObject {}
+- (void)BrightnessAssistantUnregisterForNotifications:(void*) observer; // expected-note {{previous definition is here}}
+@end
+@implementation BrightnessAssistant // expected-note {{implementation started here}}
+- (void)BrightnessAssistantUnregisterForNotifications:(CGDirectDisplayID) displayID, void* observer // expected-warning {{conflicting parameter types in implementation of 'BrightnessAssistantUnregisterForNotifications:': 'void *' vs 'CGDirectDisplayID'}}
+@end // expected-error {{expected method body}} // expected-error {{missing '@end'}}
diff --git a/test/SemaObjC/narrow-property-type-in-cont-class.m b/test/SemaObjC/narrow-property-type-in-cont-class.m
index 3ba848f..0f73b1e 100644
--- a/test/SemaObjC/narrow-property-type-in-cont-class.m
+++ b/test/SemaObjC/narrow-property-type-in-cont-class.m
@@ -14,6 +14,6 @@
@interface GKTurnBasedMatchMakerKVO ()
@property(nonatomic,readwrite,retain) NSMutableArray* outline;
-@property(nonatomic,readwrite,retain) NSArray* err_outline; // expected-error {{type of property 'NSArray *' in continuation class does not match property type in primary class}}
+@property(nonatomic,readwrite,retain) NSArray* err_outline; // expected-error {{type of property 'NSArray *' in class extension does not match property type in primary class}}
@end
diff --git a/test/SemaObjC/no-ivar-in-interface-block.m b/test/SemaObjC/no-ivar-in-interface-block.m
new file mode 100644
index 0000000..215db61
--- /dev/null
+++ b/test/SemaObjC/no-ivar-in-interface-block.m
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class -Wobjc-interface-ivars %s
+// rdar://10763173
+
+@interface I
+{
+ @protected int P_IVAR; // expected-warning {{declaration of ivars in the interface is deprecated}}
+
+ @public int PU_IVAR; // expected-warning {{declaration of ivars in the interface is deprecated}}
+
+ @private int PRV_IVAR; // expected-warning {{declaration of ivars in the interface is deprecated}}
+}
+@end
+
+@interface I()
+{
+ int I1;
+ int I2;
+}
+@end
+
+@interface I()
+{
+ int I3, I4;
+}
+@end
+
+@implementation I
+{
+ int I5;
+ int I6;
+}
+@end
diff --git a/test/SemaObjC/nowarn-superclass-method-mismatch.m b/test/SemaObjC/nowarn-superclass-method-mismatch.m
new file mode 100644
index 0000000..b211cde
--- /dev/null
+++ b/test/SemaObjC/nowarn-superclass-method-mismatch.m
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -Wsuper-class-method-mismatch -verify %s
+// rdar://11793793
+
+@class NSString;
+
+@interface Super
+@property (nonatomic) NSString *thingy;
+@property () __weak id PROP;
+@end
+
+@interface Sub : Super
+@end
+
+@implementation Sub
+- (void)setThingy:(NSString *)val
+{
+ [super setThingy:val];
+}
+@synthesize PROP;
+@end
diff --git a/test/SemaObjC/nsobject-attribute.m b/test/SemaObjC/nsobject-attribute.m
index f41df89..e3f2874 100644
--- a/test/SemaObjC/nsobject-attribute.m
+++ b/test/SemaObjC/nsobject-attribute.m
@@ -46,9 +46,19 @@ int main(int argc, char *argv[]) {
__attribute__((NSObject)) void * color; // expected-warning {{__attribute ((NSObject)) may be put on a typedef only, attribute is ignored}}
}
// <rdar://problem/10930507>
-@property (nonatomic, retain) __attribute__((NSObject)) void * color; // // no-warning
+@property (nonatomic, retain) __attribute__((NSObject)) CGColorRefNoNSObject color; // // no-warning
@end
void test_10453342() {
char* __attribute__((NSObject)) string2 = 0; // expected-warning {{__attribute ((NSObject)) may be put on a typedef only, attribute is ignored}}
}
+// rdar://11569860
+@interface A { int i; }
+@property(retain) __attribute__((NSObject)) int i; // expected-error {{__attribute ((NSObject)) is for pointer types only}} \
+ // expected-error {{property with 'retain (or strong)' attribute must be of object type}}
+@end
+
+@implementation A
+@synthesize i;
+@end
+
diff --git a/test/SemaObjC/objc-container-subscripting-2.m b/test/SemaObjC/objc-container-subscripting-2.m
index 3c0081b..62320fc 100644
--- a/test/SemaObjC/objc-container-subscripting-2.m
+++ b/test/SemaObjC/objc-container-subscripting-2.m
@@ -16,8 +16,8 @@ typedef unsigned int size_t;
id func() {
NSMutableArray *array;
float f;
- array[f] = array; // expected-error {{indexing expression is invalid because subscript type 'float' is not an integral or objective-C pointer type}}
- return array[3.14]; // expected-error {{indexing expression is invalid because subscript type 'double' is not an integral or objective-C pointer type}}
+ array[f] = array; // expected-error {{indexing expression is invalid because subscript type 'float' is not an integral or Objective-C pointer type}}
+ return array[3.14]; // expected-error {{indexing expression is invalid because subscript type 'double' is not an integral or Objective-C pointer type}}
}
void test_unused() {
diff --git a/test/SemaObjC/objc-container-subscripting-3.m b/test/SemaObjC/objc-container-subscripting-3.m
index 5fd1a10..2f716d6 100644
--- a/test/SemaObjC/objc-container-subscripting-3.m
+++ b/test/SemaObjC/objc-container-subscripting-3.m
@@ -14,7 +14,7 @@
int main() {
Test *array;
int i = array[10]; // expected-error {{method for accessing array element must have Objective-C object return type instead of 'int'}}
- array[2] = i; // expected-error {{cannot assign to this array because assigning method's 2nd parameter of type 'int' is not an objective-C pointer type}}
+ array[2] = i; // expected-error {{cannot assign to this array because assigning method's 2nd parameter of type 'int' is not an Objective-C pointer type}}
NSMutableDictionary *dict;
id key, val;
diff --git a/test/SemaObjC/objc-container-subscripting.m b/test/SemaObjC/objc-container-subscripting.m
index 4125bc6..3bbd7d4 100644
--- a/test/SemaObjC/objc-container-subscripting.m
+++ b/test/SemaObjC/objc-container-subscripting.m
@@ -14,15 +14,15 @@ int main() {
NSMutableArray<P> * array;
id oldObject = array[10]; // expected-error {{method index parameter type 'double' is not integral type}}
array[3] = 0; // expected-error {{method index parameter type 'void *' is not integral type}} \
- // expected-error {{cannot assign to this array because assigning method's 2nd parameter of type 'id *' is not an objective-C pointer type}}
+ // expected-error {{cannot assign to this array because assigning method's 2nd parameter of type 'id *' is not an Objective-C pointer type}}
I* iarray;
iarray[3] = 0; // expected-error {{expected method to write array element not found on object of type 'I *'}}
I* p = iarray[4]; // expected-error {{expected method to read array element not found on object of type 'I *'}}
- oldObject = array[10]++; // expected-error {{illegal operation on objective-c container subscripting}}
- oldObject = array[10]--; // expected-error {{illegal operation on objective-c container subscripting}}
- oldObject = --array[10]; // expected-error {{illegal operation on objective-c container subscripting}}
+ oldObject = array[10]++; // expected-error {{illegal operation on Objective-C container subscripting}}
+ oldObject = array[10]--; // expected-error {{illegal operation on Objective-C container subscripting}}
+ oldObject = --array[10]; // expected-error {{illegal operation on Objective-C container subscripting}}
}
@interface NSMutableDictionary
diff --git a/test/SemaObjC/objc-cstyle-args-in-methods.m b/test/SemaObjC/objc-cstyle-args-in-methods.m
index d37b589..ebc7192 100644
--- a/test/SemaObjC/objc-cstyle-args-in-methods.m
+++ b/test/SemaObjC/objc-cstyle-args-in-methods.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-deprecated-declarations -verify -Wno-objc-root-class %s
@interface Foo
- (id)test:(id)one, id two;
diff --git a/test/SemaObjC/objc-dictionary-literal.m b/test/SemaObjC/objc-dictionary-literal.m
index 2acbc39..0b6da4a 100644
--- a/test/SemaObjC/objc-dictionary-literal.m
+++ b/test/SemaObjC/objc-dictionary-literal.m
@@ -27,7 +27,7 @@ int main() {
NSDictionary *dict = @{ @"name":@666 };
dict[@"name"] = @666;
- dict["name"] = @666; // expected-error {{indexing expression is invalid because subscript type 'char *' is not an objective-C pointer}}
+ dict["name"] = @666; // expected-error {{indexing expression is invalid because subscript type 'char *' is not an Objective-C pointer}}
return 0;
}
diff --git a/test/SemaObjC/objc-literal-comparison.m b/test/SemaObjC/objc-literal-comparison.m
new file mode 100644
index 0000000..f1aa8ec
--- /dev/null
+++ b/test/SemaObjC/objc-literal-comparison.m
@@ -0,0 +1,96 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-everything -Wobjc-literal-compare "-Dnil=((id)0)" -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-everything -Wobjc-literal-compare "-Dnil=(id)0" -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-everything -Wobjc-literal-compare "-Dnil=0" -verify %s
+
+// (test the warning flag as well)
+
+typedef signed char BOOL;
+
+@interface BaseObject
++ (instancetype)new;
+@end
+
+@interface NSObject : BaseObject
+- (BOOL)isEqual:(id)other;
+@end
+
+@interface NSNumber : NSObject
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
+@end
+
+@interface NSArray : NSObject
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
+@end
+
+@interface NSDictionary : NSObject
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt;
+@end
+
+@interface NSString : NSObject
+@end
+
+void testComparisonsWithFixits(id obj) {
+ if (obj == @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (obj != @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@"" == obj) return; // expected-warning{{direct comparison of a string literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@"" == @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+
+ if (@[] == obj) return; // expected-warning{{direct comparison of an array literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@{} == obj) return; // expected-warning{{direct comparison of a dictionary literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@12 == obj) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@1.0 == obj) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@__objc_yes == obj) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@(1+1) == obj) return; // expected-warning{{direct comparison of a boxed expression has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+}
+
+
+@interface BadEqualReturnString : NSString
+- (void)isEqual:(id)other;
+@end
+
+@interface BadEqualArgString : NSString
+- (BOOL)isEqual:(int)other;
+@end
+
+
+void testComparisonsWithoutFixits() {
+ if ([BaseObject new] == @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+
+ if ([BadEqualReturnString new] == @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+ if ([BadEqualArgString new] == @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+
+ if (@"" < @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+ if (@"" > @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+ if (@"" <= @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+ if (@"" >= @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}}
+}
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-string-compare"
+
+void testWarningFlags(id obj) {
+ if (obj == @"") return; // no-warning
+ if (@"" == obj) return; // no-warning
+
+ if (obj == @1) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+ if (@1 == obj) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}}
+}
+
+#pragma clang diagnostic pop
+
+
+void testNilComparison() {
+ // Don't warn when comparing to nil in a macro.
+#define RETURN_IF_NIL(x) if (x == nil || nil == x) return
+ RETURN_IF_NIL(@"");
+ RETURN_IF_NIL(@1);
+ RETURN_IF_NIL(@1.0);
+ RETURN_IF_NIL(@[]);
+ RETURN_IF_NIL(@{});
+ RETURN_IF_NIL(@__objc_yes);
+ RETURN_IF_NIL(@(1+1));
+}
+
diff --git a/test/SemaObjC/objc-literal-nsnumber.m b/test/SemaObjC/objc-literal-nsnumber.m
index 6635bea..26bca18 100644
--- a/test/SemaObjC/objc-literal-nsnumber.m
+++ b/test/SemaObjC/objc-literal-nsnumber.m
@@ -83,3 +83,7 @@ typedef float BOOL;
BOOL radar11231426() {
return __objc_yes;
}
+
+id stringBoxingNoSuchMethod(const char *str) {
+ return @(str); // expected-error {{declaration of 'stringWithUTF8String:' is missing in NSString class}}
+}
diff --git a/test/SemaObjC/objc-literal-sig.m b/test/SemaObjC/objc-literal-sig.m
index fb5c79f..86f42d3 100644
--- a/test/SemaObjC/objc-literal-sig.m
+++ b/test/SemaObjC/objc-literal-sig.m
@@ -16,25 +16,36 @@ typedef _Bool BOOL;
+ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
+ (NSNumber *)numberWithFloat:(float)value;
+ (NSNumber *)numberWithDouble:(double)value;
-+ (int)numberWithBool:(BOOL)value; // expected-note{{method returns unexpected type 'int' (should be an object type)}}
++ (int)numberWithBool:(BOOL)value; // expected-note 2 {{method returns unexpected type 'int' (should be an object type)}}
+@end
+
+@interface NSString
++ (char)stringWithUTF8String:(const char *)value; // expected-note 2 {{method returns unexpected type 'char' (should be an object type)}}
@end
@interface NSArray
@end
@interface NSArray (NSArrayCreation)
-+ (id)arrayWithObjects:(const int [])objects // expected-note{{first parameter has unexpected type 'const int *' (should be 'const id *')}}
++ (id)arrayWithObjects:(const int [])objects // expected-note 2 {{first parameter has unexpected type 'const int *' (should be 'const id *')}}
count:(unsigned long)cnt;
@end
@interface NSDictionary
+ (id)dictionaryWithObjects:(const id [])objects
- forKeys:(const int [])keys // expected-note{{second parameter has unexpected type 'const int *' (should be 'const id *')}}
+ forKeys:(const int [])keys // expected-note 2 {{second parameter has unexpected type 'const int *' (should be 'const id *')}}
count:(unsigned long)cnt;
@end
+// All tests are doubled to make sure that a bad method is not saved
+// and then used un-checked.
void test_sig() {
(void)@__objc_yes; // expected-error{{literal construction method 'numberWithBool:' has incompatible signature}}
+ (void)@__objc_yes; // expected-error{{literal construction method 'numberWithBool:' has incompatible signature}}
id array = @[ @17 ]; // expected-error{{literal construction method 'arrayWithObjects:count:' has incompatible signature}}
+ id array2 = @[ @17 ]; // expected-error{{literal construction method 'arrayWithObjects:count:' has incompatible signature}}
id dict = @{ @"hello" : @17 }; // expected-error{{literal construction method 'dictionaryWithObjects:forKeys:count:' has incompatible signature}}
+ id dict2 = @{ @"hello" : @17 }; // expected-error{{literal construction method 'dictionaryWithObjects:forKeys:count:' has incompatible signature}}
+ id str = @("hello"); // expected-error{{literal construction method 'stringWithUTF8String:' has incompatible signature}}
+ id str2 = @("hello"); // expected-error{{literal construction method 'stringWithUTF8String:' has incompatible signature}}
}
diff --git a/test/SemaObjC/property-10.m b/test/SemaObjC/property-10.m
index 51eb39c..8cb8ec6 100644
--- a/test/SemaObjC/property-10.m
+++ b/test/SemaObjC/property-10.m
@@ -24,7 +24,7 @@
@property(unsafe_unretained, copy, retain) id p4_3; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'unsafe_unretained' and 'retain' are mutually exclusive}}
@property(unsafe_unretained, copy, strong) id s4_3; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'unsafe_unretained' and 'strong' are mutually exclusive}}
-@property id p4; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}}, expected-warning {{default property attribute 'assign' not appropriate for non-gc object}}
+@property id p4; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}}, expected-warning {{default property attribute 'assign' not appropriate for non-GC object}}
@property(nonatomic,copy) int (^includeMailboxCondition)();
@property(nonatomic,copy) int (*includeMailboxCondition2)(); // expected-error {{property with 'copy' attribute must be of object type}}
diff --git a/test/SemaObjC/property-12.m b/test/SemaObjC/property-12.m
index cd0fccf..c4a7555 100644
--- a/test/SemaObjC/property-12.m
+++ b/test/SemaObjC/property-12.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -Wreadonly-setter-attrs -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-objc-root-class -Wreadonly-setter-attrs -verify %s
@protocol P0
@property(readonly,assign) id X; // expected-warning {{property attributes 'readonly' and 'assign' are mutually exclusive}}
@@ -29,4 +29,39 @@
@end
+// rdar://11656982
+@interface I0 <P0> @end
+@implementation I0
+@synthesize X;
+@end
+
+@interface I1 <P1> @end
+@implementation I1
+@synthesize X;
+@end
+
+@interface I2 <P2> @end
+@implementation I2
+@synthesize X;
+@end
+
+@interface I3 <P3> @end
+@implementation I3
+@synthesize X;
+@end
+
+@interface I4 <P4> @end
+@implementation I4
+@synthesize X;
+@end
+
+@interface I5 <P5> @end
+@implementation I5
+@synthesize X;
+@end
+
+@interface I6 <P6> @end
+@implementation I6
+@synthesize X;
+@end
diff --git a/test/SemaObjC/property-impl-misuse.m b/test/SemaObjC/property-impl-misuse.m
index c3cedb0..939909e 100644
--- a/test/SemaObjC/property-impl-misuse.m
+++ b/test/SemaObjC/property-impl-misuse.m
@@ -30,7 +30,20 @@
@synthesize gradientStyle = _gradientStyle;
- (void)setGradientStyle:(id)value { }
-+ (void)_componentCellWithRepresentedObject {
- self.gradientStyle; // expected-error {{property 'gradientStyle' not found on object of type 'Class'}}
++ (id)_componentCellWithRepresentedObject {
+ return self.gradientStyle;
}
@end
+
+// rdar://11054153
+@interface rdar11054153
+@property int P; // expected-error {{type of property 'P' ('int') does not match type of accessor 'P' ('void')}}
+- (void)P; // expected-note {{declared here}}
+
+@property int P1; // expected-warning {{type of property 'P1' does not match type of accessor 'P1'}}
+- (double) P1; // expected-note {{declared here}}
+
+@property int P2; // expected-error {{type of property 'P2' ('int') does not match type of accessor 'P2' ('double *')}}
+- (double*)P2; // expected-note {{declared here}}
+
+@end
diff --git a/test/SemaObjC/property-ivar-mismatch.m b/test/SemaObjC/property-ivar-mismatch.m
index 6abd6e6..a0d1c9d 100644
--- a/test/SemaObjC/property-ivar-mismatch.m
+++ b/test/SemaObjC/property-ivar-mismatch.m
@@ -12,3 +12,15 @@
@synthesize prop = ivar; // expected-error {{type of property 'prop' ('int') does not match type of ivar 'ivar' ('char')}}
@end
+
+@interface Test5
+{
+ void * _P; // expected-note {{ivar is declared here}}
+}
+@property int P;
+@end
+
+@implementation Test5
+@synthesize P=_P; // expected-error {{ype of property 'P' ('int') does not match type of ivar '_P' ('void *')}}
+@end
+
diff --git a/test/SemaObjC/property-typecheck-1.m b/test/SemaObjC/property-typecheck-1.m
index f71e4a0..58d0f21 100644
--- a/test/SemaObjC/property-typecheck-1.m
+++ b/test/SemaObjC/property-typecheck-1.m
@@ -73,10 +73,10 @@ typedef void (F)(void);
NSArray* first;
}
-@property (readonly) NSArray* pieces; // expected-warning {{type of property 'pieces' does not match type of accessor 'pieces'}}
-@property (readonly) NSMutableArray* first;
+@property (readonly) NSArray* pieces; // expected-warning {{type of property 'pieces' does not match type of accessor 'pieces'}}
+@property (readonly) NSMutableArray* first;
-- (NSMutableArray*) pieces; // expected-note {{declared here}} // expected-note {{declared here}}
+- (NSMutableArray*) pieces; // expected-note 2 {{declared here}}
- (NSArray*) first;
@end
@@ -95,7 +95,7 @@ typedef void (F)(void);
- (id)firstPeice
{
- return container.first;
+ return container.first;
}
@end
diff --git a/test/SemaObjC/property-user-setter.m b/test/SemaObjC/property-user-setter.m
index 7d4e544..9ebad60 100644
--- a/test/SemaObjC/property-user-setter.m
+++ b/test/SemaObjC/property-user-setter.m
@@ -102,3 +102,55 @@ int main (void) {
abort ();
return 0;
}
+
+// rdar://11363363
+@interface rdar11363363
+{
+ id R;
+}
+@property (copy) id p;
+@property (copy) id r;
+@property (copy) id Q;
+@property (copy) id t; // expected-note 2 {{property declared here}}
+@property (copy) id T; // expected-note 2 {{property declared here}}
+@property (copy) id Pxyz; // expected-note 2 {{property declared here}}
+@property (copy) id pxyz; // expected-note 2 {{property declared here}}
+@end
+
+@implementation rdar11363363
+@synthesize p;
+@synthesize r;
+@synthesize Q;
+@synthesize t, T;
+@synthesize Pxyz, pxyz;
+- (id) Meth {
+ self.P = 0;
+ self.q = 0;
+// rdar://11528439
+ self.t = 0; // expected-error {{synthesized properties 't' and 'T' both claim setter 'setT:'}}
+ self.T = 0; // expected-error {{synthesized properties 'T' and 't' both claim setter 'setT:'}}
+ self.Pxyz = 0; // expected-error {{synthesized properties 'Pxyz' and 'pxyz' both claim setter 'setPxyz:'}}
+ self.pxyz = 0; // expected-error {{synthesized properties 'pxyz' and 'Pxyz' both claim setter 'setPxyz:'}}
+ self.R = 0;
+ return self.R; // expected-error {{expected getter method not found on object of type 'rdar11363363 *'}}
+}
+@end
+
+// rdar://11499742
+@class BridgeFormatter;
+
+@interface FMXBridgeFormatter
+
+@property(assign, readwrite, getter=formatter, setter=setFormatter:) BridgeFormatter* cppFormatter;
+
+@end
+
+@implementation FMXBridgeFormatter
+@synthesize cppFormatter;
+
+- (void) dealloc
+{
+ self.formatter = 0;
+}
+@end
+
diff --git a/test/SemaObjC/property.m b/test/SemaObjC/property.m
index a948741..d6495b4 100644
--- a/test/SemaObjC/property.m
+++ b/test/SemaObjC/property.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-fragile-abi -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -fsyntax-only -verify -Wno-objc-root-class %s
@interface I
{
@@ -6,7 +6,7 @@
int name;
}
@property int d1;
-@property id prop_id; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}}, expected-warning {{default property attribute 'assign' not appropriate for non-gc object}}
+@property id prop_id; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}}, expected-warning {{default property attribute 'assign' not appropriate for non-GC object}}
@property int name;
@end
diff --git a/test/SemaObjC/protocol-attribute.m b/test/SemaObjC/protocol-attribute.m
index 178774c..b2aecc2 100644
--- a/test/SemaObjC/protocol-attribute.m
+++ b/test/SemaObjC/protocol-attribute.m
@@ -6,7 +6,7 @@ __attribute ((unavailable))
Class <FwProto> cFw = 0; // expected-error {{'FwProto' is unavailable}}
-__attribute ((deprecated)) @protocol MyProto1
+__attribute ((deprecated)) @protocol MyProto1 // expected-note 7 {{declared here}}
@end
@protocol Proto2 <MyProto1> // expected-warning {{'MyProto1' is deprecated}}
diff --git a/test/SemaObjC/protocols.m b/test/SemaObjC/protocols.m
index ca38f20..eb27341 100644
--- a/test/SemaObjC/protocols.m
+++ b/test/SemaObjC/protocols.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-deprecated-declarations -verify %s
@interface INTF1
@required // expected-error {{directive may only be specified in protocols only}}
diff --git a/test/SemaObjC/provisional-ivar-lookup.m b/test/SemaObjC/provisional-ivar-lookup.m
index 2ec23a5..7460fc2 100644
--- a/test/SemaObjC/provisional-ivar-lookup.m
+++ b/test/SemaObjC/provisional-ivar-lookup.m
@@ -36,7 +36,7 @@
@synthesize PROP=PROP;
- (void)setPROP:(int)value {
- PROP = PROP; // OK
+ PROP = value; // OK
}
@end
diff --git a/test/SemaObjC/related-result-type-inference.m b/test/SemaObjC/related-result-type-inference.m
index 124767c..b1d77dc 100644
--- a/test/SemaObjC/related-result-type-inference.m
+++ b/test/SemaObjC/related-result-type-inference.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -verify -Wno-deprecated-declarations -Wno-objc-root-class %s
@interface Unrelated
@end
@@ -178,3 +178,9 @@ void test_inference() {
return (id)self; // expected-warning {{returning 'Fail *' from a function with incompatible result type 'id<X>'}}
}
@end
+
+// <rdar://problem/11460990>
+
+@interface WeirdNSString : NSString
+- (id)initWithCString:(const char*)string, void *blah;
+@end
diff --git a/test/SemaObjC/sizeof-interface.m b/test/SemaObjC/sizeof-interface.m
index 7304b6c..db765b7 100644
--- a/test/SemaObjC/sizeof-interface.m
+++ b/test/SemaObjC/sizeof-interface.m
@@ -23,7 +23,7 @@ void *g3(I0 *P) {
@end
// size == 4
-int g1[ sizeof(I0) // expected-error {{invalid application of 'sizeof' to interface 'I0' in non-fragile ABI}}
+int g1[ sizeof(I0) // expected-error {{application of 'sizeof' to interface 'I0' is not supported on this architecture and platform}}
== 4 ? 1 : -1];
@implementation I0
@@ -32,7 +32,7 @@ int g1[ sizeof(I0) // expected-error {{invalid application of 'sizeof' to in
// size == 4 (we do not include extended properties in the
// sizeof).
-int g2[ sizeof(I0) // expected-error {{invalid application of 'sizeof' to interface 'I0' in non-fragile ABI}}
+int g2[ sizeof(I0) // expected-error {{application of 'sizeof' to interface 'I0' is not supported on this architecture and platform}}
== 4 ? 1 : -1];
@interface I1
@@ -43,7 +43,7 @@ int g2[ sizeof(I0) // expected-error {{invalid application of 'sizeof' to inte
@synthesize p0 = _p0;
@end
-typedef struct { @defs(I1); } I1_defs; // expected-error {{invalid application of @defs in non-fragile ABI}}
+typedef struct { @defs(I1); } I1_defs; // expected-error {{use of @defs is not supported on this architecture and platform}}
// FIXME: This is currently broken due to the way the record layout we
// create is tied to whether we have seen synthesized properties. Ugh.
@@ -51,9 +51,9 @@ typedef struct { @defs(I1); } I1_defs; // expected-error {{invalid application o
// rdar://6821047
int bar(I0 *P) {
- P = P+5; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size in non-fragile ABI}}
- P = 5+P; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size in non-fragile ABI}}
- P = P-5; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size in non-fragile ABI}}
+ P = P+5; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size for this architecture and platform}}
+ P = 5+P; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size for this architecture and platform}}
+ P = P-5; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size for this architecture and platform}}
return P[4].x[2]; // expected-error {{expected method to read array element not found on object of type 'I0 *'}}
}
@@ -64,7 +64,7 @@ int bar(I0 *P) {
@interface XCAttributeRunDirectNode
{
@public
- unsigned long attributeRuns[1024 + sizeof(I)]; // expected-error {{invalid application of 'sizeof' to interface 'I' in non-fragile ABI}}
+ unsigned long attributeRuns[1024 + sizeof(I)]; // expected-error {{application of 'sizeof' to interface 'I' is not supported on this architecture and platform}}
int i;
}
@end
@@ -85,6 +85,6 @@ int foo()
Foo *f;
// Both of these crash clang nicely
- ++f; // expected-error {{arithmetic on pointer to interface 'Foo', which is not a constant size in non-fragile ABI}}
- --f; // expected-error {{arithmetic on pointer to interface 'Foo', which is not a constant size in non-fragile ABI}}
+ ++f; // expected-error {{arithmetic on pointer to interface 'Foo', which is not a constant size for this architecture and platform}}
+ --f; // expected-error {{arithmetic on pointer to interface 'Foo', which is not a constant size for this architecture and platform}}
}
diff --git a/test/SemaObjC/special-dep-unavail-warning.m b/test/SemaObjC/special-dep-unavail-warning.m
index 754bf5f..a179647 100644
--- a/test/SemaObjC/special-dep-unavail-warning.m
+++ b/test/SemaObjC/special-dep-unavail-warning.m
@@ -45,7 +45,7 @@ void test(C *c) {
// rdar://10268422
__attribute ((deprecated))
-@interface DEPRECATED
+@interface DEPRECATED // expected-note {{declared here}}
+(id)new;
@end
diff --git a/test/SemaObjC/tentative-property-decl.m b/test/SemaObjC/tentative-property-decl.m
new file mode 100644
index 0000000..f69ac6d
--- /dev/null
+++ b/test/SemaObjC/tentative-property-decl.m
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -fsyntax-only -Weverything -verify %s
+// rdar://11656982
+/** Normally, a property cannot be both 'readonly' and having a "write" attribute
+ (copy/retain/etc.). But, property declaration in primary class and protcols
+ are tentative as they may be overridden into a 'readwrite' property in class
+ extensions. Postpone diagnosing such warnings until the class implementation
+ is seen.
+*/
+
+@interface Super {
+}
+@end
+
+@class NSString;
+
+@interface MyClass : Super
+@property(nonatomic, copy, readonly) NSString *prop;
+@property(nonatomic, copy, readonly) id warnProp; // expected-warning {{property attributes 'readonly' and 'copy' are mutually exclusive}}
+@end
+
+@interface MyClass ()
+@property(nonatomic, copy, readwrite) NSString *prop;
+@end
+
+@implementation MyClass
+@synthesize prop;
+@synthesize warnProp;
+@end
+
+
+@protocol P
+@property(nonatomic, copy, readonly) NSString *prop;
+@property(nonatomic, copy, readonly) id warnProp; // expected-warning {{property attributes 'readonly' and 'copy' are mutually exclusive}}
+@end
+
+@interface YourClass : Super <P>
+@end
+
+@interface YourClass ()
+@property(nonatomic, copy, readwrite) NSString *prop;
+@end
+
+@implementation YourClass
+@synthesize prop;
+@synthesize warnProp;
+@end
+
diff --git a/test/SemaObjC/unused.m b/test/SemaObjC/unused.m
index 975b9a9..5c7542b 100644
--- a/test/SemaObjC/unused.m
+++ b/test/SemaObjC/unused.m
@@ -51,3 +51,5 @@ void test2() {
}
@end
+// rdar://10777111
+static NSString *x = @"hi"; // expected-warning {{unused variable 'x'}}
diff --git a/test/SemaObjC/warn-deprecated-implementations.m b/test/SemaObjC/warn-deprecated-implementations.m
index 919b221..5f7c2fd 100644
--- a/test/SemaObjC/warn-deprecated-implementations.m
+++ b/test/SemaObjC/warn-deprecated-implementations.m
@@ -20,7 +20,7 @@
@end
__attribute__((deprecated))
-@interface CL // expected-note 2 {{class declared here}}
+@interface CL // expected-note 2 {{class declared here}} // expected-note 2 {{declared here}}
@end
@implementation CL // expected-warning {{Implementing deprecated class}}
diff --git a/test/SemaObjC/warn-direct-ivar-access.m b/test/SemaObjC/warn-direct-ivar-access.m
new file mode 100644
index 0000000..d2295f4
--- /dev/null
+++ b/test/SemaObjC/warn-direct-ivar-access.m
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -Wdirect-ivar-access -verify -Wno-objc-root-class %s
+// rdar://6505197
+
+__attribute__((objc_root_class)) @interface MyObject {
+@public
+ id _myMaster;
+ id _isTickledPink;
+ int _myIntProp;
+}
+@property(retain) id myMaster;
+@property(assign) id isTickledPink; // expected-note {{property declared here}}
+@property int myIntProp;
+@end
+
+@implementation MyObject
+
+@synthesize myMaster = _myMaster;
+@synthesize isTickledPink = _isTickledPink; // expected-error {{existing ivar '_isTickledPink' for property 'isTickledPink'}}
+@synthesize myIntProp = _myIntProp;
+
+- (void) doSomething {
+ _myMaster = _isTickledPink; // expected-warning {{instance variable '_myMaster' is being directly accessed}} \
+ // expected-warning {{instance variable '_isTickledPink' is being directly accessed}}
+}
+
+- (id) init {
+ _myMaster=0;
+ return _myMaster;
+}
+- (void) dealloc { _myMaster = 0; }
+@end
+
+MyObject * foo ()
+{
+ MyObject* p=0;
+ p.isTickledPink = p.myMaster; // ok
+ p->_isTickledPink = (*p)._myMaster; // expected-warning {{instance variable '_isTickledPink' is being directly accessed}} \
+ // expected-warning {{instance variable '_myMaster' is being directly accessed}}
+ if (p->_myIntProp) // expected-warning {{instance variable '_myIntProp' is being directly accessed}}
+ p->_myIntProp = 0; // expected-warning {{instance variable '_myIntProp' is being directly accessed}}
+ return p->_isTickledPink; // expected-warning {{instance variable '_isTickledPink' is being directly accessed}}
+}
+
+@interface ITest32 {
+@public
+ id ivar;
+}
+@end
+
+id Test32(__weak ITest32 *x) {
+ __weak ITest32 *y;
+ x->ivar = 0; // expected-error {{dereferencing a __weak pointer is not allowed}}
+ return y ? y->ivar // expected-error {{dereferencing a __weak pointer is not allowed}}
+ : (*x).ivar; // expected-error {{dereferencing a __weak pointer is not allowed}}
+}
+
diff --git a/test/SemaObjC/id-isa-ref.m b/test/SemaObjC/warn-isa-ref.m
index c2debb0..1932a02 100644
--- a/test/SemaObjC/id-isa-ref.m
+++ b/test/SemaObjC/warn-isa-ref.m
@@ -5,6 +5,7 @@ typedef struct objc_object {
} *id;
@interface NSObject {
+ id firstobj;
struct objc_class *isa;
}
@end
@@ -33,3 +34,50 @@ static void func() {
expected-warning{{receiver type 'struct objc_class *' is not 'id' or interface pointer, consider casting it to 'id'}} \
expected-warning{{method '-self' not found (return type defaults to 'id')}}
}
+
+// rdar://11702488
+// If an ivar is (1) the first ivar in a root class and (2) named `isa`,
+// then it should get the same warnings that id->isa gets.
+
+@interface BaseClass {
+@public
+ Class isa; // expected-note 3 {{ivar is declared here}}
+}
+@end
+
+@interface OtherClass {
+@public
+ id firstIvar;
+ Class isa; // note, not first ivar;
+}
+@end
+
+@interface Subclass : BaseClass @end
+
+@interface SiblingClass : BaseClass @end
+
+@interface Root @end
+
+@interface hasIsa : Root {
+@public
+ Class isa; // note, isa is not in root class
+}
+@end
+
+@implementation Subclass
+-(void)method {
+ hasIsa *u;
+ id v;
+ BaseClass *w;
+ Subclass *x;
+ SiblingClass *y;
+ OtherClass *z;
+ (void)v->isa; // expected-warning {{direct access to objective-c's isa is deprecated}}
+ (void)w->isa; // expected-warning {{direct access to objective-c's isa is deprecated}}
+ (void)x->isa; // expected-warning {{direct access to objective-c's isa is deprecated}}
+ (void)y->isa; // expected-warning {{direct access to objective-c's isa is deprecated}}
+ (void)z->isa;
+ (void)u->isa;
+}
+@end
+
diff --git a/test/SemaObjC/warn-protocol-method-deprecated.m b/test/SemaObjC/warn-protocol-method-deprecated.m
new file mode 100644
index 0000000..928694d
--- /dev/null
+++ b/test/SemaObjC/warn-protocol-method-deprecated.m
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-objc-root-class -verify %s
+// rdar://11618852
+
+@protocol TestProtocol
+- (void)newProtocolMethod;
+- (void)deprecatedProtocolMethod __attribute__((deprecated)); // expected-note 2 {{method 'deprecatedProtocolMethod' declared here}}
+@end
+
+@interface NSObject @end
+
+@interface TestClass : NSObject <TestProtocol>
+
+- (void)newInstanceMethod;
+- (void)deprecatedInstanceMethod __attribute__((deprecated)); // expected-note {{method 'deprecatedInstanceMethod' declared here}}
+
+@end
+
+int main(int argc, const char * argv[])
+{
+
+ TestClass *testObj = (TestClass*)0;
+ [testObj newInstanceMethod];
+ [testObj deprecatedInstanceMethod]; // expected-warning {{'deprecatedInstanceMethod' is deprecated}}
+
+ [testObj newProtocolMethod];
+ [testObj deprecatedProtocolMethod]; // expected-warning {{'deprecatedProtocolMethod' is deprecated}}
+
+ id <TestProtocol> testProto = testObj;
+ [testProto newProtocolMethod];
+ [testProto deprecatedProtocolMethod]; // expected-warning {{'deprecatedProtocolMethod' is deprecated}}
+ return 0;
+}
diff --git a/test/SemaObjC/arc-retain-block-property.m b/test/SemaObjC/warn-retain-block-property.m
index 3b66d14..3a54baf 100644
--- a/test/SemaObjC/arc-retain-block-property.m
+++ b/test/SemaObjC/warn-retain-block-property.m
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -fobjc-arc -verify -Wno-objc-root-class %s
// rdar://9829425
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -Wno-objc-root-class %s
+// rdar://11761511
extern void doSomething();
diff --git a/test/SemaObjC/weak-receiver-warn.m b/test/SemaObjC/weak-receiver-warn.m
index f3955da..547f008 100644
--- a/test/SemaObjC/weak-receiver-warn.m
+++ b/test/SemaObjC/weak-receiver-warn.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -fblocks -Wreceiver-is-weak -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -fblocks -Wno-objc-root-class -Wreceiver-is-weak -verify %s
// rdar://10225276
@interface Test0
@@ -15,5 +15,66 @@ void test0(Test0 *x) {
[weakx addBlock: ^{ [x actNow]; }]; // expected-warning {{weak receiver may be unpredictably null in ARC mode}}
[weakx setBlock: ^{ [x actNow]; }]; // expected-warning {{weak receiver may be unpredictably null in ARC mode}}
- weakx.block = ^{ [x actNow]; };
+ weakx.block = ^{ [x actNow]; }; // expected-warning {{weak receiver may be unpredictably null in ARC mode}}
}
+
+@interface Test
+{
+ __weak Test* weak_prop;
+}
+- (void) Meth;
+@property __weak Test* weak_prop; // expected-note {{property declared here}}
+@property (weak, atomic) id weak_atomic_prop; // expected-note {{property declared here}}
+- (__weak id) P; // expected-note {{method 'P' declared here}}
+@end
+
+@implementation Test
+- (void) Meth {
+ if (self.weak_prop) {
+ self.weak_prop = 0;
+ }
+ if (self.weak_atomic_prop) {
+ self.weak_atomic_prop = 0;
+ }
+ [self.weak_prop Meth]; // expected-warning {{weak property may be unpredictably null in ARC mode}}
+ id pi = self.P;
+
+ [self.weak_atomic_prop Meth]; // expected-warning {{weak property may be unpredictably null in ARC mode}}
+
+ [self.P Meth]; // expected-warning {{weak implicit property may be unpredictably null in ARC mode}}
+}
+
+- (__weak id) P { return 0; }
+@dynamic weak_prop, weak_atomic_prop;
+@end
+
+
+@interface MyClass {
+ __weak MyClass *_parent;
+}
+@property (weak) MyClass *parent; // expected-note 2 {{property declared here}}
+@end
+
+@implementation MyClass
+@synthesize parent = _parent;
+
+- (void)doSomething
+{
+ [[self parent] doSomething]; // expected-warning {{weak property may be unpredictably null in ARC mode}}
+
+ (void)self.parent.doSomething; // expected-warning {{weak property may be unpredictably null in ARC mode}}
+}
+
+@end
+
+
+// Weak properties on protocols can be synthesized by an adopting class.
+@protocol MyProtocol
+@property (weak) id object; // expected-note 2 {{property declared here}}
+@end
+
+void testProtocol(id <MyProtocol> input) {
+ [[input object] Meth]; // expected-warning {{weak property may be unpredictably null in ARC mode}}
+ [input.object Meth]; // expected-warning {{weak property may be unpredictably null in ARC mode}}
+}
+
diff --git a/test/SemaObjCXX/arc-0x.mm b/test/SemaObjCXX/arc-0x.mm
index 28eec51..e24b960 100644
--- a/test/SemaObjCXX/arc-0x.mm
+++ b/test/SemaObjCXX/arc-0x.mm
@@ -11,6 +11,9 @@ void move_it(__strong id &&from) {
- init;
@end
+// <rdar://problem/12031870>: don't warn about this
+extern "C" A* MakeA();
+
// Ensure that deduction works with lifetime qualifiers.
void deduction(id obj) {
auto a = [[A alloc] init];
@@ -22,7 +25,7 @@ void deduction(id obj) {
__strong id *idp = new auto(obj);
__strong id array[17];
- for (auto x : array) {
+ for (auto x : array) { // expected-warning{{'auto' deduced as 'id' in declaration of 'x'}}
__strong id *xPtr = &x;
}
@@ -51,3 +54,30 @@ void test1c() {
(void) ^{ (void) p; };
(void) ^{ (void) v; }; // expected-error {{cannot capture __autoreleasing variable in a block}}
}
+
+
+// <rdar://problem/11319689>
+// warn when initializing an 'auto' variable with an 'id' initializer expression
+
+void testAutoId(id obj) {
+ auto x = obj; // expected-warning{{'auto' deduced as 'id' in declaration of 'x'}}
+}
+
+@interface Array
++ (instancetype)new;
+- (id)objectAtIndex:(int)index;
+@end
+
+// ...but don't warn if it's coming from a template parameter.
+template<typename T, int N>
+void autoTemplateFunction(T param, id obj, Array *arr) {
+ auto x = param; // no-warning
+ auto y = obj; // expected-warning{{'auto' deduced as 'id' in declaration of 'y'}}
+ auto z = [arr objectAtIndex:N]; // expected-warning{{'auto' deduced as 'id' in declaration of 'z'}}
+}
+
+void testAutoIdTemplate(id obj) {
+ autoTemplateFunction<id, 2>(obj, obj, [Array new]); // no-warning
+}
+
+
diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm
index 9eca846..8009272 100644
--- a/test/SemaObjCXX/arc-templates.mm
+++ b/test/SemaObjCXX/arc-templates.mm
@@ -3,6 +3,8 @@
@interface A
@end
+@class NSString;
+
template<typename T, typename U>
struct is_same {
static const bool value = false;
@@ -266,3 +268,18 @@ namespace rdar9828157 {
float &fr = (f)(ap);
}
}
+
+namespace rdar10862386 {
+ // More deduction with lifetime qualifiers.
+ template <typename T>
+ int testing(const T &) {
+ return 1;
+ }
+
+ void test() {
+ testing(1);
+ testing("hi");
+ testing<NSString *>(@"hi");
+ testing(@"hi");
+ }
+}
diff --git a/test/SemaObjCXX/arc-type-traits.mm b/test/SemaObjCXX/arc-type-traits.mm
index 9877870..67bab00 100644
--- a/test/SemaObjCXX/arc-type-traits.mm
+++ b/test/SemaObjCXX/arc-type-traits.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -verify -std=c++11 %s
// Check the results of the various type-trait query functions on
// lifetime-qualified types in ARC.
@@ -9,48 +9,75 @@
#define TRAIT_IS_TRUE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? 1 : -1]
#define TRAIT_IS_FALSE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? -1 : 1]
+#define TRAIT_IS_TRUE_2(Trait, Type1, Type2) char JOIN2(Trait,__LINE__)[Trait(Type1, Type2)? 1 : -1]
+#define TRAIT_IS_FALSE_2(Trait, Type1, Type2) char JOIN2(Trait,__LINE__)[Trait(Type1, Type2)? -1 : 1]
+struct HasStrong { id obj; };
+struct HasWeak { __weak id obj; };
+struct HasUnsafeUnretained { __unsafe_unretained id obj; };
+
// __has_nothrow_assign
TRAIT_IS_TRUE(__has_nothrow_assign, __strong id);
TRAIT_IS_TRUE(__has_nothrow_assign, __weak id);
TRAIT_IS_TRUE(__has_nothrow_assign, __autoreleasing id);
TRAIT_IS_TRUE(__has_nothrow_assign, __unsafe_unretained id);
+TRAIT_IS_TRUE(__has_nothrow_assign, HasStrong);
+TRAIT_IS_TRUE(__has_nothrow_assign, HasWeak);
+TRAIT_IS_TRUE(__has_nothrow_assign, HasUnsafeUnretained);
// __has_nothrow_copy
TRAIT_IS_TRUE(__has_nothrow_copy, __strong id);
TRAIT_IS_TRUE(__has_nothrow_copy, __weak id);
TRAIT_IS_TRUE(__has_nothrow_copy, __autoreleasing id);
TRAIT_IS_TRUE(__has_nothrow_copy, __unsafe_unretained id);
+TRAIT_IS_TRUE(__has_nothrow_copy, HasStrong);
+TRAIT_IS_TRUE(__has_nothrow_copy, HasWeak);
+TRAIT_IS_TRUE(__has_nothrow_copy, HasUnsafeUnretained);
// __has_nothrow_constructor
TRAIT_IS_TRUE(__has_nothrow_constructor, __strong id);
TRAIT_IS_TRUE(__has_nothrow_constructor, __weak id);
TRAIT_IS_TRUE(__has_nothrow_constructor, __autoreleasing id);
TRAIT_IS_TRUE(__has_nothrow_constructor, __unsafe_unretained id);
+TRAIT_IS_TRUE(__has_nothrow_constructor, HasStrong);
+TRAIT_IS_TRUE(__has_nothrow_constructor, HasWeak);
+TRAIT_IS_TRUE(__has_nothrow_constructor, HasUnsafeUnretained);
// __has_trivial_assign
TRAIT_IS_FALSE(__has_trivial_assign, __strong id);
TRAIT_IS_FALSE(__has_trivial_assign, __weak id);
TRAIT_IS_FALSE(__has_trivial_assign, __autoreleasing id);
TRAIT_IS_TRUE(__has_trivial_assign, __unsafe_unretained id);
+TRAIT_IS_FALSE(__has_trivial_assign, HasStrong);
+TRAIT_IS_FALSE(__has_trivial_assign, HasWeak);
+TRAIT_IS_TRUE(__has_trivial_assign, HasUnsafeUnretained);
// __has_trivial_copy
TRAIT_IS_FALSE(__has_trivial_copy, __strong id);
TRAIT_IS_FALSE(__has_trivial_copy, __weak id);
TRAIT_IS_FALSE(__has_trivial_copy, __autoreleasing id);
TRAIT_IS_TRUE(__has_trivial_copy, __unsafe_unretained id);
+TRAIT_IS_FALSE(__has_trivial_copy, HasStrong);
+TRAIT_IS_FALSE(__has_trivial_copy, HasWeak);
+TRAIT_IS_TRUE(__has_trivial_copy, HasUnsafeUnretained);
// __has_trivial_constructor
TRAIT_IS_FALSE(__has_trivial_constructor, __strong id);
TRAIT_IS_FALSE(__has_trivial_constructor, __weak id);
TRAIT_IS_FALSE(__has_trivial_constructor, __autoreleasing id);
TRAIT_IS_TRUE(__has_trivial_constructor, __unsafe_unretained id);
+TRAIT_IS_FALSE(__has_trivial_constructor, HasStrong);
+TRAIT_IS_FALSE(__has_trivial_constructor, HasWeak);
+TRAIT_IS_TRUE(__has_trivial_constructor, HasUnsafeUnretained);
// __has_trivial_destructor
TRAIT_IS_FALSE(__has_trivial_destructor, __strong id);
TRAIT_IS_FALSE(__has_trivial_destructor, __weak id);
TRAIT_IS_TRUE(__has_trivial_destructor, __autoreleasing id);
TRAIT_IS_TRUE(__has_trivial_destructor, __unsafe_unretained id);
+TRAIT_IS_FALSE(__has_trivial_destructor, HasStrong);
+TRAIT_IS_FALSE(__has_trivial_destructor, HasWeak);
+TRAIT_IS_TRUE(__has_trivial_destructor, HasUnsafeUnretained);
// __is_literal
TRAIT_IS_TRUE(__is_literal, __strong id);
@@ -69,12 +96,18 @@ TRAIT_IS_FALSE(__is_pod, __strong id);
TRAIT_IS_FALSE(__is_pod, __weak id);
TRAIT_IS_FALSE(__is_pod, __autoreleasing id);
TRAIT_IS_TRUE(__is_pod, __unsafe_unretained id);
+TRAIT_IS_FALSE(__is_pod, HasStrong);
+TRAIT_IS_FALSE(__is_pod, HasWeak);
+TRAIT_IS_TRUE(__is_pod, HasUnsafeUnretained);
// __is_trivial
TRAIT_IS_FALSE(__is_trivial, __strong id);
TRAIT_IS_FALSE(__is_trivial, __weak id);
TRAIT_IS_FALSE(__is_trivial, __autoreleasing id);
TRAIT_IS_TRUE(__is_trivial, __unsafe_unretained id);
+TRAIT_IS_FALSE(__is_trivial, HasStrong);
+TRAIT_IS_FALSE(__is_trivial, HasWeak);
+TRAIT_IS_TRUE(__is_trivial, HasUnsafeUnretained);
// __is_scalar
TRAIT_IS_FALSE(__is_scalar, __strong id);
@@ -88,3 +121,94 @@ TRAIT_IS_TRUE(__is_standard_layout, __weak id);
TRAIT_IS_TRUE(__is_standard_layout, __autoreleasing id);
TRAIT_IS_TRUE(__is_standard_layout, __unsafe_unretained id);
+// __is_trivally_assignable
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __strong id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __weak id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __autoreleasing id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __unsafe_unretained id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __strong id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __weak id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __autoreleasing id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __strong id&, __unsafe_unretained id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __strong id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __weak id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __autoreleasing id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __unsafe_unretained id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __strong id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __weak id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __autoreleasing id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __weak id&, __unsafe_unretained id&&);
+
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __strong id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __weak id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __autoreleasing id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __unsafe_unretained id);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __strong id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __weak id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __autoreleasing id&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, __autoreleasing id&, __unsafe_unretained id&&);
+
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __strong id);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __weak id);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __autoreleasing id);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __unsafe_unretained id);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __strong id&&);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __weak id&&);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __autoreleasing id&&);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, __unsafe_unretained id&, __unsafe_unretained id&&);
+
+TRAIT_IS_FALSE_2(__is_trivially_assignable, HasStrong&, HasStrong);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, HasStrong&, HasStrong&&);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, HasWeak&, HasWeak);
+TRAIT_IS_FALSE_2(__is_trivially_assignable, HasWeak&, HasWeak&&);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, HasUnsafeUnretained&, HasUnsafeUnretained);
+TRAIT_IS_TRUE_2(__is_trivially_assignable, HasUnsafeUnretained&, HasUnsafeUnretained&&);
+
+// __is_trivally_constructible
+TRAIT_IS_FALSE(__is_trivially_constructible, __strong id);
+TRAIT_IS_FALSE(__is_trivially_constructible, __weak id);
+TRAIT_IS_FALSE(__is_trivially_constructible, __autoreleasing id);
+TRAIT_IS_TRUE(__is_trivially_constructible, __unsafe_unretained id);
+
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __strong id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __weak id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __autoreleasing id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __unsafe_unretained id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __strong id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __weak id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __autoreleasing id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __strong id, __unsafe_unretained id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __strong id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __weak id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __autoreleasing id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __unsafe_unretained id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __strong id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __weak id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __autoreleasing id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __weak id, __unsafe_unretained id&&);
+
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __strong id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __weak id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __autoreleasing id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __unsafe_unretained id);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __strong id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __weak id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __autoreleasing id&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, __autoreleasing id, __unsafe_unretained id&&);
+
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __strong id);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __weak id);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __autoreleasing id);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __unsafe_unretained id);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __strong id&&);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __weak id&&);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __autoreleasing id&&);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, __unsafe_unretained id, __unsafe_unretained id&&);
+
+TRAIT_IS_FALSE_2(__is_trivially_constructible, HasStrong, HasStrong);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, HasStrong, HasStrong&&);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, HasWeak, HasWeak);
+TRAIT_IS_FALSE_2(__is_trivially_constructible, HasWeak, HasWeak&&);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, HasUnsafeUnretained, HasUnsafeUnretained);
+TRAIT_IS_TRUE_2(__is_trivially_constructible, HasUnsafeUnretained, HasUnsafeUnretained&&);
+
diff --git a/test/SemaObjCXX/arc-unbridged-cast.mm b/test/SemaObjCXX/arc-unbridged-cast.mm
index 0b3ba07..f7d2391 100644
--- a/test/SemaObjCXX/arc-unbridged-cast.mm
+++ b/test/SemaObjCXX/arc-unbridged-cast.mm
@@ -44,10 +44,10 @@ void test1(int cond) {
x = (id) (cond ? (void*) 0 : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
x = (id) (cond ? (CFStringRef) @"help" : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) auditedCreateString(); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) (cond ? auditedCreateString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) (cond ? (void*) 0 : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
- x = (id) (cond ? (CFStringRef) @"help" : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) auditedCreateString(); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? auditedCreateString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? (void*) 0 : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? (CFStringRef) @"help" : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use CFBridgingRelease call to}}
x = (id) [object property];
x = (id) (cond ? [object property] : (void*) 0);
diff --git a/test/SemaObjCXX/boxing-illegal-types.mm b/test/SemaObjCXX/boxing-illegal-types.mm
new file mode 100644
index 0000000..7729753
--- /dev/null
+++ b/test/SemaObjCXX/boxing-illegal-types.mm
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wattributes %s
+
+typedef long NSInteger;
+typedef unsigned long NSUInteger;
+typedef signed char BOOL;
+
+@interface NSNumber
+@end
+@interface NSNumber (NSNumberCreation)
++ (NSNumber *)numberWithChar:(char)value;
++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
++ (NSNumber *)numberWithShort:(short)value;
++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
++ (NSNumber *)numberWithLong:(long)value;
++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
++ (NSNumber *)numberWithLongLong:(long long)value;
++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
++ (NSNumber *)numberWithFloat:(float)value;
++ (NSNumber *)numberWithDouble:(double)value;
++ (NSNumber *)numberWithBool:(BOOL)value;
++ (NSNumber *)numberWithInteger:(NSInteger)value;
++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
+@end
+
+typedef struct {
+ int x, y, z;
+} point;
+
+void testStruct() {
+ point p = { 0, 0, 0 };
+ id boxed = @(p); // expected-error {{illegal type 'point' used in a boxed expression}}
+}
+
+void testPointers() {
+ void *null = 0;
+ id boxed_null = @(null); // expected-error {{illegal type 'void *' used in a boxed expression}}
+ int numbers[] = { 0, 1, 2 };
+ id boxed_numbers = @(numbers); // expected-error {{illegal type 'int *' used in a boxed expression}}
+}
+
+void testInvalid() {
+ @(not_defined); // expected-error {{use of undeclared identifier 'not_defined'}}
+}
+
+enum MyEnum {
+ ME_foo
+};
+
+enum ForwE; // expected-error {{ISO C++ forbids forward references to 'enum' types}}
+
+void testEnum(void *p) {
+ enum MyEnum myen;
+ id box = @(myen);
+ box = @(ME_foo);
+ box = @(*(enum ForwE*)p); // expected-error {{incomplete type 'enum ForwE' used in a boxed expression}}
+}
diff --git a/test/SemaObjCXX/crash.mm b/test/SemaObjCXX/crash.mm
new file mode 100644
index 0000000..345f72e
--- /dev/null
+++ b/test/SemaObjCXX/crash.mm
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -fsyntax-only %s -verify
+
+// <rdar://problem/11286701>
+namespace std {
+ template<typename T, typename U> class pair;
+}
+
+@interface NSObject
+@end
+
+@interface Test : NSObject
+@end
+
+@implementation Test
+
+struct EvilStruct {
+} // note the missing semicolon
+
+ typedef std::pair<int, int> IntegerPair; // expected-error{{typedef declarator cannot be qualified}} \
+// expected-error{{typedef name must be an identifier}} \
+// expected-error{{expected ';' after top level declarator}}
+
+@end
diff --git a/test/SemaObjCXX/delay-parsing-cfunctions.mm b/test/SemaObjCXX/delay-parsing-cfunctions.mm
new file mode 100644
index 0000000..fa65dbe
--- /dev/null
+++ b/test/SemaObjCXX/delay-parsing-cfunctions.mm
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -x objective-c++ -std=c++11 -fsyntax-only -Werror -verify -Wno-objc-root-class %s
+// rdar://10387088
+
+struct X {
+X();
+void SortWithCollator();
+};
+
+@interface MyClass
+- (void)someMethod;
+@end
+
+@implementation MyClass
+- (void)someMethod {
+ [self privateMethod]; // clang already does not warn here
+}
+
+int bar(MyClass * myObject) {
+ [myObject privateMethod];
+ return gorfbar(myObject);
+}
+- (void)privateMethod { }
+
+int gorfbar(MyClass * myObject) {
+ [myObject privateMethod];
+ [myObject privateMethod1];
+ return getMe + bar(myObject);
+}
+
+- (void)privateMethod1 {
+ getMe = getMe+1;
+}
+
+static int getMe;
+
+static int test() {
+ return 0;
+}
+
+int x{17};
+
+X::X() = default;
+void X::SortWithCollator() {}
+// pr13418
+namespace {
+ int CurrentTabId() {return 0;}
+}
+@end
diff --git a/test/SemaObjCXX/delay-parsing-cplusfuncs.mm b/test/SemaObjCXX/delay-parsing-cplusfuncs.mm
new file mode 100644
index 0000000..b022709
--- /dev/null
+++ b/test/SemaObjCXX/delay-parsing-cplusfuncs.mm
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -Werror -verify -Wno-objc-root-class %s
+// rdar://10387088
+
+@interface MyClass
+- (void)someMethod;
+@end
+
+struct S {
+ int bar(MyClass * myObject);
+
+ int gorfbar(MyClass * myObject);
+
+ S();
+ S(MyClass *O1, MyClass *O2);
+ S(MyClass *O1);
+
+ MyClass * Obj1, *Obj2;
+
+};
+
+@implementation MyClass
+- (void)someMethod {
+ [self privateMethod]; // clang already does not warn here
+}
+
+int S::bar(MyClass * myObject) {
+ [myObject privateMethod];
+ return gorfbar(myObject);
+}
+- (void)privateMethod { }
+
+int S::gorfbar(MyClass * myObject) {
+ [myObject privateMethod];
+ [myObject privateMethod1];
+ return getMe + bar(myObject);
+}
+
+S::S(MyClass *O1, MyClass *O2) : Obj1(O1), Obj2(O2) {
+ [O1 privateMethod];
+ [O2 privateMethod1];
+}
+S::S(MyClass *O1) : Obj1(O1){ Obj2 = 0; }
+
+S::S() {}
+
+- (void)privateMethod1 {
+ getMe = getMe+1;
+}
+
+static int getMe;
+
+@end
diff --git a/test/SemaObjCXX/delay-parsing-func-tryblock.mm b/test/SemaObjCXX/delay-parsing-func-tryblock.mm
new file mode 100644
index 0000000..8cf615e
--- /dev/null
+++ b/test/SemaObjCXX/delay-parsing-func-tryblock.mm
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -x objective-c++ -fcxx-exceptions -fsyntax-only -Werror -verify -Wno-objc-root-class %s
+// rdar://10387088
+
+@interface MyClass
+- (void)someMethod;
+@end
+
+struct BadReturn {
+ BadReturn(MyClass * myObject);
+ int bar(MyClass * myObject);
+ void MemFunc(MyClass * myObject);
+ int i;
+ MyClass *CObj;
+};
+
+@implementation MyClass
+- (void)someMethod {
+ [self privateMethod]; // clang already does not warn here
+}
+
+int BadReturn::bar(MyClass * myObject) {
+ [myObject privateMethod];
+ return 0;
+}
+
+BadReturn::BadReturn(MyClass * myObject) try : CObj(myObject) {
+} catch(...) {
+ try {
+ [myObject privateMethod];
+ [myObject privateMethod1];
+ getMe = bar(myObject);
+ [CObj privateMethod1];
+ } catch(int ei) {
+ i = ei;
+ } catch(...) {
+ {
+ i = 0;
+ }
+ }
+}
+
+void BadReturn::MemFunc(MyClass * myObject) try {
+} catch(...) {
+ try {
+ [myObject privateMethod];
+ [myObject privateMethod1];
+ getMe = bar(myObject);
+ [CObj privateMethod1];
+ } catch(int ei) {
+ i = ei;
+ } catch(...) {
+ {
+ i = 0;
+ }
+ }
+}
+
+- (void)privateMethod { }
+
+- (void)privateMethod1 {
+ getMe = getMe+1;
+}
+
+static int getMe;
+
+@end
diff --git a/test/SemaObjCXX/exceptions-fragile.mm b/test/SemaObjCXX/exceptions-fragile.mm
index 91b7077..54d9f83 100644
--- a/test/SemaObjCXX/exceptions-fragile.mm
+++ b/test/SemaObjCXX/exceptions-fragile.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fobjc-fragile-abi -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fobjc-runtime=macosx-fragile-10.5 -fsyntax-only -verify %s
@interface NSException @end
void opaque();
diff --git a/test/SemaObjCXX/foreach.mm b/test/SemaObjCXX/foreach.mm
new file mode 100644
index 0000000..3c4b908
--- /dev/null
+++ b/test/SemaObjCXX/foreach.mm
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -std=c++11 %s
+// rdar://9293227
+
+@class NSArray;
+
+void f(NSArray *a) {
+ id keys;
+ for (int i : a); // expected-error{{selector element type 'int' is not a valid object}}
+ for ((id)2 : a); // expected-error {{for range declaration must declare a variable}} \
+ // expected-warning {{expression result unused}}
+ for (2 : a); // expected-error {{for range declaration must declare a variable}} \
+ // expected-warning {{expression result unused}}
+
+ for (id thisKey : keys);
+}
+
+/* // rdar://9072298 */
+@protocol NSObject @end
+
+@interface NSObject <NSObject> {
+ Class isa;
+}
+@end
+
+typedef struct {
+ unsigned long state;
+ id *itemsPtr;
+ unsigned long *mutationsPtr;
+ unsigned long extra[5];
+} NSFastEnumerationState;
+
+@protocol NSFastEnumeration
+
+- (unsigned long)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(unsigned long)len;
+
+@end
+
+int main ()
+{
+ NSObject<NSFastEnumeration>* collection = 0;
+ for (id thing : collection) { }
+
+ id array;
+ for (int (^b)(void) : array) {
+ if (b() == 10000) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* rdar://problem/11068137 */
+@interface Test2
+@property (assign) id prop;
+@end
+void test2(NSObject<NSFastEnumeration> *collection) {
+ Test2 *obj;
+ for (obj.prop : collection) { // expected-error {{for range declaration must declare a variable}} \
+ // expected-warning {{property access result unused - getters should not be used for side effects}}
+ }
+}
diff --git a/test/SemaObjCXX/fragile-abi-object-assign.m b/test/SemaObjCXX/fragile-abi-object-assign.m
index 5bb3ac6..b3504e9 100644
--- a/test/SemaObjCXX/fragile-abi-object-assign.m
+++ b/test/SemaObjCXX/fragile-abi-object-assign.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-fragile-abi -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin11 -fsyntax-only -fobjc-runtime=macosx-fragile-10.5 -verify -Wno-objc-root-class %s
// rdar://10731065
@interface MyView {}
diff --git a/test/SemaObjCXX/instantiate-stmt.mm b/test/SemaObjCXX/instantiate-stmt.mm
index ff72858..7575f7a 100644
--- a/test/SemaObjCXX/instantiate-stmt.mm
+++ b/test/SemaObjCXX/instantiate-stmt.mm
@@ -38,20 +38,20 @@ template<typename T> void eat(T);
template<typename E, typename T>
void fast_enumeration_test(T collection) {
for (E element in collection) { // expected-error{{selector element type 'int' is not a valid object}} \
- // expected-error{{collection expression type 'vector' is not a valid object}}
+ // expected-error{{the type 'vector' is not a pointer to a fast-enumerable object}}
eat(element);
}
E element;
for (element in collection) // expected-error{{selector element type 'int' is not a valid object}} \
- // expected-error{{collection expression type 'vector' is not a valid object}}
+ // expected-error{{the type 'vector' is not a pointer to a fast-enumerable object}}
eat(element);
- for (NSString *str in collection) // expected-error{{collection expression type 'vector' is not a valid object}}
+ for (NSString *str in collection) // expected-error{{the type 'vector' is not a pointer to a fast-enumerable object}}
eat(str);
NSString *str;
- for (str in collection) // expected-error{{collection expression type 'vector' is not a valid object}}
+ for (str in collection) // expected-error{{the type 'vector' is not a pointer to a fast-enumerable object}}
eat(str);
}
diff --git a/test/SemaObjCXX/message.mm b/test/SemaObjCXX/message.mm
index 5ac2f40..7d8520c 100644
--- a/test/SemaObjCXX/message.mm
+++ b/test/SemaObjCXX/message.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fobjc-fragile-abi -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-fragile-10.5 -verify -Wno-objc-root-class %s
@interface I1
- (int*)method;
@end
diff --git a/test/SemaObjCXX/objc-container-subscripting.mm b/test/SemaObjCXX/objc-container-subscripting.mm
index c835cbe..537e152 100644
--- a/test/SemaObjCXX/objc-container-subscripting.mm
+++ b/test/SemaObjCXX/objc-container-subscripting.mm
@@ -32,8 +32,8 @@ template void test_dictionary_subscripts(NSMutableDictionary*, id, int); // expe
template<typename T, typename U, typename O>
void test_array_subscripts(T base, U index, O obj) {
- base[index] = obj; // expected-error {{indexing expression is invalid because subscript type 'double' is not an integral or objective-C pointer type}}
- obj = base[index]; // expected-error {{indexing expression is invalid because subscript type 'double' is not an integral or objective-C pointer type}}
+ base[index] = obj; // expected-error {{indexing expression is invalid because subscript type 'double' is not an integral or Objective-C pointer type}}
+ obj = base[index]; // expected-error {{indexing expression is invalid because subscript type 'double' is not an integral or Objective-C pointer type}}
}
template void test_array_subscripts(NSMutableArray *, int, id);
diff --git a/test/SemaObjCXX/property-synthesis-error.mm b/test/SemaObjCXX/property-synthesis-error.mm
index 4b726d8..4f64a3a 100644
--- a/test/SemaObjCXX/property-synthesis-error.mm
+++ b/test/SemaObjCXX/property-synthesis-error.mm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class -fobjc-default-synthesize-properties %s
// rdar: //8550657
@interface NSArray @end
@@ -72,3 +72,14 @@ private:
@synthesize tcppObject = _tcppObject;
@end
+
+struct IncompleteStruct; // expected-note 2 {{forward declaration of 'IncompleteStruct'}}
+struct ConvertToIncomplete { operator IncompleteStruct&(); };
+@interface SynthIncompleteRef
+@property (readonly, nonatomic) IncompleteStruct& x; // expected-note {{property declared here}}
+@property (readonly, nonatomic) IncompleteStruct& y; // expected-note {{property declared here}}
+@end
+
+@implementation SynthIncompleteRef // expected-error {{cannot synthesize property 'x' with incomplete type 'IncompleteStruct'}}
+@synthesize y; // expected-error {{cannot synthesize property 'y' with incomplete type 'IncompleteStruct'}}
+@end
diff --git a/test/SemaObjCXX/protocol-lookup.mm b/test/SemaObjCXX/protocol-lookup.mm
index bd8444c..e8abf6c 100644
--- a/test/SemaObjCXX/protocol-lookup.mm
+++ b/test/SemaObjCXX/protocol-lookup.mm
@@ -52,5 +52,5 @@
void rdar8575095(id a) {
[id<NSObject>(a) retain];
id<NSObject> x(id<NSObject>(0));
- id<NSObject> x2(id<NSObject>(y)); // expected-warning{{parentheses were disambiguated as a function declarator}}
+ id<NSObject> x2(id<NSObject>(y)); // expected-warning{{disambiguated as a function declaration}} expected-note{{add a pair of parentheses}}
}
diff --git a/test/SemaObjCXX/warn-missing-super.mm b/test/SemaObjCXX/warn-missing-super.mm
new file mode 100644
index 0000000..cd2a6cc
--- /dev/null
+++ b/test/SemaObjCXX/warn-missing-super.mm
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// PR13401
+
+__attribute((objc_root_class)) @interface NSObject
+@end
+
+@interface Dummy : NSObject
+@end
+
+template<typename T> struct shared_ptr {
+ constexpr shared_ptr() {}
+};
+
+@implementation Dummy
+- (void)dealloc
+{
+ constexpr shared_ptr<int> dummy;
+} // expected-warning {{method possibly missing a [super dealloc] call}}
+@end
diff --git a/test/SemaOpenCL/storageclass.cl b/test/SemaOpenCL/storageclass.cl
new file mode 100644
index 0000000..c78e7cd
--- /dev/null
+++ b/test/SemaOpenCL/storageclass.cl
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -cl-std=CL1.2
+
+static int A;
+
+// static is not allowed at local scope.
+void kernel foo() {
+ static int X = 5; // expected-error{{variables in function scope cannot be declared static}}
+ auto int Y = 7; // expected-error{{OpenCL does not support the 'auto' storage class specifier}}
+}
+
+static void kernel bar() { // expected-error{{kernel functions cannot be declared static}}
+}
diff --git a/test/SemaOpenCL/warn-missing-prototypes.cl b/test/SemaOpenCL/warn-missing-prototypes.cl
new file mode 100644
index 0000000..487cb28
--- /dev/null
+++ b/test/SemaOpenCL/warn-missing-prototypes.cl
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-prototypes %s
+
+void f() { } // expected-warning {{no previous prototype for function 'f'}}
+
+// Don't warn about kernel functions.
+kernel void g() { }
diff --git a/test/SemaTemplate/alias-templates.cpp b/test/SemaTemplate/alias-templates.cpp
index 75615ee..20ba6e0 100644
--- a/test/SemaTemplate/alias-templates.cpp
+++ b/test/SemaTemplate/alias-templates.cpp
@@ -73,19 +73,38 @@ namespace PR11848 {
template<typename T> using U = int;
template<typename T, typename ...Ts>
- void f(U<T> i, U<Ts> ...is) { // expected-error {{type 'U<Ts>' (aka 'int') of function parameter pack does not contain any unexpanded parameter packs}}
- return i + f<Ts...>(is...); // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+ void f1(U<T> i, U<Ts> ...is) { // expected-note 2{{couldn't infer template argument 'T'}}
+ return i + f1<Ts...>(is...);
+ }
+
+ // FIXME: This note is technically correct, but could be better. We
+ // should really say that we couldn't infer template argument 'Ts'.
+ template<typename ...Ts>
+ void f2(U<Ts> ...is) { } // expected-note {{requires 0 arguments, but 1 was provided}}
+
+ template<typename...> struct type_tuple {};
+ template<typename ...Ts>
+ void f3(type_tuple<Ts...>, U<Ts> ...is) {} // expected-note {{requires 4 arguments, but 3 were provided}}
+
+ void g() {
+ f1(U<void>()); // expected-error {{no match}}
+ f1(1, 2, 3, 4, 5); // expected-error {{no match}}
+ f2(); // ok
+ f2(1); // expected-error {{no match}}
+ f3(type_tuple<>());
+ f3(type_tuple<void, void, void>(), 1, 2); // expected-error {{no match}}
+ f3(type_tuple<void, void, void>(), 1, 2, 3);
}
template<typename ...Ts>
struct S {
- S(U<Ts>...ts); // expected-error {{does not contain any unexpanded parameter packs}}
+ S(U<Ts>...ts);
};
template<typename T>
struct Hidden1 {
template<typename ...Ts>
- Hidden1(typename T::template U<Ts> ...ts); // expected-error{{type 'typename Hide::U<Ts>' (aka 'int') of function parameter pack does not contain any unexpanded parameter packs}}
+ Hidden1(typename T::template U<Ts> ...ts);
};
template<typename T, typename ...Ts>
@@ -97,6 +116,53 @@ namespace PR11848 {
template<typename T> using U = int;
};
- Hidden1<Hide> h1; // expected-note{{in instantiation of template class 'PR11848::Hidden1<PR11848::Hide>' requested here}}
+ Hidden1<Hide> h1;
Hidden2<Hide, double, char> h2(1, 2);
}
+
+namespace Core22036 {
+ struct X {};
+ void h(...);
+ template<typename T> using Y = X;
+ template<typename T, typename ...Ts> struct S {
+ // An expression can contain an unexpanded pack without being type or
+ // value dependent. This is true even if the expression's type is a pack
+ // expansion type.
+ void f1(Y<T> a) { h(g(a)); } // expected-error {{undeclared identifier 'g'}}
+ void f2(Y<Ts>...as) { h(g(as)...); } // expected-error {{undeclared identifier 'g'}}
+ void f3(Y<Ts>...as) { g(as...); } // ok
+ void f4(Ts ...ts) { h(g(sizeof(ts))...); } // expected-error {{undeclared identifier 'g'}}
+ // FIXME: We can reject this, since it has no valid instantiations because
+ // 'g' never has any associated namespaces.
+ void f5(Ts ...ts) { g(sizeof(ts)...); } // ok
+ };
+}
+
+namespace PR13243 {
+ template<typename A> struct X {};
+ template<int I> struct C {};
+ template<int I> using Ci = C<I>;
+
+ template<typename A, int I> void f(X<A>, Ci<I>) {}
+ template void f(X<int>, C<0>);
+}
+
+namespace PR13136 {
+ template <typename T, T... Numbers>
+ struct NumberTuple { };
+
+ template <unsigned int... Numbers>
+ using MyNumberTuple = NumberTuple<unsigned int, Numbers...>;
+
+ template <typename U, unsigned int... Numbers>
+ void foo(U&&, MyNumberTuple<Numbers...>);
+
+ template <typename U, unsigned int... Numbers>
+ void bar(U&&, NumberTuple<unsigned int, Numbers...>);
+
+ int main() {
+ foo(1, NumberTuple<unsigned int, 0, 1>());
+ bar(1, NumberTuple<unsigned int, 0, 1>());
+ return 0;
+ }
+}
diff --git a/test/SemaTemplate/class-template-decl.cpp b/test/SemaTemplate/class-template-decl.cpp
index ec4e016..fe23d92 100644
--- a/test/SemaTemplate/class-template-decl.cpp
+++ b/test/SemaTemplate/class-template-decl.cpp
@@ -95,3 +95,45 @@ namespace rdar9676205 {
};
}
+namespace redecl {
+ int A; // expected-note {{here}}
+ template<typename T> struct A; // expected-error {{different kind of symbol}}
+
+ int B; // expected-note {{here}}
+ template<typename T> struct B { // expected-error {{different kind of symbol}}
+ };
+
+ template<typename T> struct F;
+ template<typename T> struct K;
+
+ int G, H; // expected-note {{here}}
+
+ struct S {
+ int C; // expected-note {{here}}
+ template<typename T> struct C; // expected-error {{different kind of symbol}}
+
+ int D; // expected-note {{here}}
+ template<typename T> struct D { // expected-error {{different kind of symbol}}
+ };
+
+ int E;
+ template<typename T> friend struct E { // expected-error {{cannot define a type in a friend}}
+ };
+
+ int F;
+ template<typename T> friend struct F; // ok, redecl::F
+
+ template<typename T> struct G; // ok
+
+ template<typename T> friend struct H; // expected-error {{different kind of symbol}}
+
+ int I, J, K;
+
+ struct U {
+ template<typename T> struct I; // ok
+ template<typename T> struct J { // ok
+ };
+ template<typename T> friend struct K; // ok, redecl::K
+ };
+ };
+}
diff --git a/test/SemaTemplate/delegating-constructors.cpp b/test/SemaTemplate/delegating-constructors.cpp
index e177b50..852b89d 100644
--- a/test/SemaTemplate/delegating-constructors.cpp
+++ b/test/SemaTemplate/delegating-constructors.cpp
@@ -29,3 +29,21 @@ namespace PR10457 {
Foo f(1, 1);
}
}
+
+namespace PR12890 {
+ class Document
+ {
+ public:
+ Document() = default;
+
+ template <class T>
+ explicit
+ Document(T&& t) : Document()
+ {
+ }
+ };
+ void f()
+ {
+ Document d(1);
+ }
+}
diff --git a/test/SemaTemplate/dependent-base-classes.cpp b/test/SemaTemplate/dependent-base-classes.cpp
index 895eacc..1853511 100644
--- a/test/SemaTemplate/dependent-base-classes.cpp
+++ b/test/SemaTemplate/dependent-base-classes.cpp
@@ -32,8 +32,7 @@ namespace PR6031 {
template <class TT>
struct FI2
{
- C<typename FI2::type> a; // expected-error{{no type named 'type' in 'FI2<TT>'}} \
- // expected-error{{C++ requires a type specifier for all declarations}}
+ C<typename FI2::type> a; // expected-error{{no type named 'type' in 'FI2<TT>'}}
};
template<typename T>
diff --git a/test/SemaTemplate/explicit-instantiation.cpp b/test/SemaTemplate/explicit-instantiation.cpp
index 13d76be..e3e77d0 100644
--- a/test/SemaTemplate/explicit-instantiation.cpp
+++ b/test/SemaTemplate/explicit-instantiation.cpp
@@ -14,7 +14,7 @@ struct X0 {
T f0(T x) {
return x + 1; // expected-error{{invalid operands}}
}
- T* f0(T*, T*) { return T(); }
+ T* f0(T*, T*) { return T(); } // expected-warning{{expression which evaluates to zero treated as a null pointer constant of type 'int *'}}
template<typename U>
T f0(T, U) { return T(); }
@@ -32,7 +32,7 @@ struct NotDefaultConstructible { // expected-note{{candidate constructor (the im
template NotDefaultConstructible X0<NotDefaultConstructible>::value; // expected-note{{instantiation}}
template int X0<int>::f0(int);
-template int* X0<int>::f0(int*, int*);
+template int* X0<int>::f0(int*, int*); // expected-note{{in instantiation of member function 'X0<int>::f0' requested here}}
template int X0<int>::f0(int, float);
template int X0<int>::f0(int) const; // expected-error{{does not refer}}
diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp
index 9c95fa0..9acbfdc 100644
--- a/test/SemaTemplate/friend-template.cpp
+++ b/test/SemaTemplate/friend-template.cpp
@@ -243,3 +243,62 @@ namespace rdar11147355 {
A<double>::B<double> ab;
}
+
+namespace RedeclUnrelated {
+ struct S {
+ int packaged_task;
+ template<typename> class future {
+ template<typename> friend class packaged_task;
+ };
+ future<void> share;
+ };
+}
+
+namespace PR12557 {
+ template <typename>
+ struct Foo;
+
+ template <typename Foo_>
+ struct Bar {
+ typedef Foo_ Foo; // expected-note {{previous}}
+
+ template <typename> friend struct Foo; // expected-error {{redefinition of 'Foo' as different kind of symbol}}
+ };
+
+ Bar<int> b;
+}
+
+namespace PR12585 {
+ struct A { };
+ template<typename> struct B {
+ template<typename> friend class A::does_not_exist; // \
+ // expected-error {{friend declaration of 'does_not_exist' does not match any declaration in 'PR12585::A'}}
+ };
+
+ struct C {
+ template<typename> struct D;
+ };
+ template<typename> class E {
+ int n;
+ template<typename> friend struct C::D;
+ };
+ template<typename T> struct C::D {
+ int f() {
+ return E<int>().n;
+ }
+ };
+ int n = C::D<void*>().f();
+
+ struct F {
+ template<int> struct G;
+ };
+ template<typename T> struct H {
+ // FIXME: As with cases above, the note here is on an unhelpful declaration,
+ // and should point to the declaration of G within F.
+ template<T> friend struct F::G; // \
+ // expected-error {{different type 'char' in template redeclaration}} \
+ // expected-note {{previous}}
+ };
+ H<int> h1; // ok
+ H<char> h2; // expected-note {{instantiation}}
+}
diff --git a/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp b/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp
index 8a6f9ef..c560c6b 100644
--- a/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp
+++ b/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp
@@ -131,3 +131,17 @@ template<typename T> struct Derived : Base {
Derived<Exc1> d1; // ok
Derived<Exc2> d2; // expected-note {{in instantiation of}}
+
+// If the vtable for a derived class is used, the exception specification of
+// any member function which ends up in that vtable is needed, even if it was
+// declared in a base class.
+namespace PR12763 {
+ template<bool *B> struct T {
+ virtual void f() noexcept (*B); // expected-error {{constant expression}} expected-note {{read of non-const}}
+ };
+ bool b; // expected-note {{here}}
+ struct X : public T<&b> {
+ virtual void g();
+ };
+ void X::g() {} // expected-note {{in instantiation of}}
+}
diff --git a/test/SemaTemplate/instantiate-expr-1.cpp b/test/SemaTemplate/instantiate-expr-1.cpp
index 9395117..e777243 100644
--- a/test/SemaTemplate/instantiate-expr-1.cpp
+++ b/test/SemaTemplate/instantiate-expr-1.cpp
@@ -127,7 +127,7 @@ void test_call_operator(CallOperator call_op, int i, double d) {
template<typename T>
void test_asm(T t) {
- asm ("nop" : "=a"(*t) : "r"(*t)); // expected-error {{indirection requires pointer operand ('int' invalid)}}
+ asm ("nop" : "=r"(*t) : "r"(*t)); // expected-error {{indirection requires pointer operand ('int' invalid)}}
}
void test_asm() {
diff --git a/test/SemaTemplate/instantiate-init.cpp b/test/SemaTemplate/instantiate-init.cpp
index f0ca9a5..612a0b7 100644
--- a/test/SemaTemplate/instantiate-init.cpp
+++ b/test/SemaTemplate/instantiate-init.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
-struct X0 { // expected-note 4{{candidate}}
+struct X0 { // expected-note 8{{candidate}}
X0(int*, float*); // expected-note 4{{candidate}}
};
@@ -107,3 +107,14 @@ namespace PR7985 {
array_lengthof(Description<float*>::data); // expected-error{{no matching function for call to 'array_lengthof'}}
}
}
+
+namespace PR13064 {
+ // Ensure that in-class direct-initialization is instantiated as
+ // direct-initialization and likewise copy-initialization is instantiated as
+ // copy-initialization.
+ struct A { explicit A(int); }; // expected-note{{here}}
+ template<typename T> struct B { T a { 0 }; };
+ B<A> b;
+ template<typename T> struct C { T a = { 0 }; }; // expected-error{{explicit}}
+ C<A> c; // expected-note{{here}}
+}
diff --git a/test/SemaTemplate/instantiate-member-class.cpp b/test/SemaTemplate/instantiate-member-class.cpp
index bb64276..7b42a27 100644
--- a/test/SemaTemplate/instantiate-member-class.cpp
+++ b/test/SemaTemplate/instantiate-member-class.cpp
@@ -124,19 +124,20 @@ namespace rdar10397846 {
{
struct B
{
- struct C { C() { int *ptr = I; } }; // expected-error{{cannot initialize a variable of type 'int *' with an rvalue of type 'int'}}
+ struct C { C() { int *ptr = I; } }; // expected-error{{cannot initialize a variable of type 'int *' with an rvalue of type 'int'}} \
+ expected-warning{{expression which evaluates to zero treated as a null pointer constant of type 'int *'}}
};
};
template<int N> void foo()
{
- class A<N>::B::C X; // expected-note{{in instantiation of member function}}
+ class A<N>::B::C X; // expected-note 2 {{in instantiation of member function}}
int A<N+1>::B::C::*member = 0;
}
void bar()
{
- foo<0>();
+ foo<0>(); // expected-note{{in instantiation of function template}}
foo<1>(); // expected-note{{in instantiation of function template}}
}
}
diff --git a/test/SemaTemplate/instantiate-method.cpp b/test/SemaTemplate/instantiate-method.cpp
index 363115d..5e9da3f 100644
--- a/test/SemaTemplate/instantiate-method.cpp
+++ b/test/SemaTemplate/instantiate-method.cpp
@@ -173,5 +173,12 @@ namespace PR7022 {
typedef X2<> X2_type;
X2_type c;
}
+}
+namespace SameSignatureAfterInstantiation {
+ template<typename T> struct S {
+ void f(T *); // expected-note {{previous}}
+ void f(const T*); // expected-error {{multiple overloads of 'f' instantiate to the same signature 'void (const int *)'}}
+ };
+ S<const int> s; // expected-note {{instantiation}}
}
diff --git a/test/SemaTemplate/instantiate-objc-1.mm b/test/SemaTemplate/instantiate-objc-1.mm
index 2780f8e..f8b97eb 100644
--- a/test/SemaTemplate/instantiate-objc-1.mm
+++ b/test/SemaTemplate/instantiate-objc-1.mm
@@ -46,3 +46,24 @@ template <typename T> struct EncodeTest {
template struct EncodeTest<int>;
template struct EncodeTest<double>;
template struct EncodeTest<wchar_t>;
+
+// @() boxing expressions.
+template <typename T> struct BoxingTest {
+ static id box(T value) {
+ return @(value); // expected-error {{illegal type 'int *' used in a boxed expression}} \
+ // expected-error {{illegal type 'long double' used in a boxed expression}}
+ }
+};
+
+@interface NSNumber
++ (NSNumber *)numberWithInt:(int)value;
+@end
+
+@interface NSString
++ (id)stringWithUTF8String:(const char *)str;
+@end
+
+template struct BoxingTest<int>;
+template struct BoxingTest<const char *>;
+template struct BoxingTest<int *>; // expected-note {{in instantiation of member function 'BoxingTest<int *>::box' requested here}}
+template struct BoxingTest<long double>; // expected-note {{in instantiation of member function 'BoxingTest<long double>::box' requested here}}
diff --git a/test/SemaTemplate/instantiation-backtrace.cpp b/test/SemaTemplate/instantiation-backtrace.cpp
index 21456e9..f640836 100644
--- a/test/SemaTemplate/instantiation-backtrace.cpp
+++ b/test/SemaTemplate/instantiation-backtrace.cpp
@@ -30,3 +30,22 @@ struct G : A<T>, // expected-error{{implicit instantiation of undefined template
void h() {
(void)sizeof(G<int>); // expected-note{{in instantiation of template class 'G<int>' requested here}}
}
+
+namespace PR13365 {
+ template <class T> class ResultTy { // expected-warning {{does not declare any constructor}}
+ T t; // expected-note {{reference member 't' will never be initialized}}
+ };
+
+ template <class T1, class T2>
+ typename ResultTy<T2>::error Deduce( void (T1::*member)(T2) ) {} // \
+ // expected-note {{instantiation of template class 'PR13365::ResultTy<int &>'}} \
+ // expected-note {{substituting deduced template arguments into function template 'Deduce' [with T1 = PR13365::Cls, T2 = int &]}} \
+ // expected-note {{substitution failure [with T1 = PR13365::Cls, T2 = int &]}}
+
+ struct Cls {
+ void method(int&);
+ };
+ void test() {
+ Deduce(&Cls::method); // expected-error {{no matching function}}
+ }
+}
diff --git a/test/SemaTemplate/instantiation-depth-defarg.cpp b/test/SemaTemplate/instantiation-depth-defarg.cpp
new file mode 100644
index 0000000..6550987
--- /dev/null
+++ b/test/SemaTemplate/instantiation-depth-defarg.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -ftemplate-depth 128 -ftemplate-backtrace-limit 4 %s
+
+template<int N> struct S {
+ typedef typename S<N-1>::type type;
+ static int f(int n = S<N-1>::f()); // \
+// expected-error{{recursive template instantiation exceeded maximum depth of 128}} \
+// expected-note 3 {{instantiation of default function argument}} \
+// expected-note {{skipping 125 contexts in backtrace}} \
+// expected-note {{use -ftemplate-depth=N to increase recursive template instantiation depth}}
+
+};
+template<> struct S<0> {
+ typedef int type;
+};
+
+// Incrementally instantiate up to S<2048>.
+template struct S<128>;
+template struct S<256>;
+template struct S<384>;
+template struct S<512>;
+template struct S<640>;
+template struct S<768>;
+template struct S<896>;
+template struct S<1024>;
+template struct S<1152>;
+template struct S<1280>;
+template struct S<1408>;
+template struct S<1536>;
+template struct S<1664>;
+template struct S<1792>;
+template struct S<1920>;
+template struct S<2048>;
+
+// Check that we actually bail out when we hit the instantiation depth limit for
+// the default arguments.
+void g() { S<2048>::f(); } // expected-note {{required here}}
diff --git a/test/SemaTemplate/instantiation-depth-subst-2.cpp b/test/SemaTemplate/instantiation-depth-subst-2.cpp
new file mode 100644
index 0000000..a29d6b5
--- /dev/null
+++ b/test/SemaTemplate/instantiation-depth-subst-2.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -verify %s -ftemplate-depth 2
+
+template<int N> struct S { };
+// FIXME: We produce the same 'instantiation depth' error here many times
+// (2^(depth+1) in total), due to additional lookups performed as part of
+// error recovery in DiagnoseTwoPhaseOperatorLookup.
+template<typename T> S<T() + T()> operator+(T, T); // expected-error 8{{}} expected-note 10{{}}
+S<0> s;
+int k = s + s; // expected-error {{invalid operands to binary expression}}
diff --git a/test/SemaTemplate/instantiation-depth-subst.cpp b/test/SemaTemplate/instantiation-depth-subst.cpp
new file mode 100644
index 0000000..58e6374
--- /dev/null
+++ b/test/SemaTemplate/instantiation-depth-subst.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -std=c++11 -verify %s -ftemplate-depth 2
+
+// PR9793
+template<typename T> auto f(T t) -> decltype(f(t)); // \
+// expected-error {{recursive template instantiation exceeded maximum depth of 2}} \
+// expected-note 3 {{while substituting}} \
+// expected-note {{candidate}}
+
+int k = f(0); // expected-error {{no matching function for call to 'f'}}
diff --git a/test/SemaTemplate/instantiation-depth.cpp b/test/SemaTemplate/instantiation-depth.cpp
index 8e1b803..c0b8bb2 100644
--- a/test/SemaTemplate/instantiation-depth.cpp
+++ b/test/SemaTemplate/instantiation-depth.cpp
@@ -2,12 +2,30 @@
// RUN: %clang -fsyntax-only -Xclang -verify -ftemplate-depth-5 -ftemplate-backtrace-limit=4 %s
// RUN: %clang -fsyntax-only -Xclang -verify -ftemplate-depth=5 -ftemplate-backtrace-limit=4 %s
+#ifndef NOEXCEPT
+
template<typename T> struct X : X<T*> { }; \
// expected-error{{recursive template instantiation exceeded maximum depth of 5}} \
// expected-note 3 {{instantiation of template class}} \
// expected-note {{skipping 2 contexts in backtrace}} \
// expected-note {{use -ftemplate-depth=N to increase recursive template instantiation depth}}
-void test() {
+void test() {
(void)sizeof(X<int>); // expected-note {{instantiation of template class}}
}
+
+#else
+
+// RUN: %clang_cc1 -fsyntax-only -verify -ftemplate-depth 5 -ftemplate-backtrace-limit 4 -std=c++11 -DNOEXCEPT %s
+
+template<typename T> struct S {
+ S() noexcept(noexcept(T()));
+};
+struct T : S<T> {}; \
+// expected-error{{recursive template instantiation exceeded maximum depth of 5}} \
+// expected-note 4 {{in instantiation of exception spec}} \
+// expected-note {{skipping 2 contexts in backtrace}} \
+// expected-note {{use -ftemplate-depth=N to increase recursive template instantiation depth}}
+T t; // expected-note {{implicit default constructor for 'T' first required here}}
+
+#endif
diff --git a/test/SemaTemplate/member-template-access-expr.cpp b/test/SemaTemplate/member-template-access-expr.cpp
index c95b57d..f105ba8 100644
--- a/test/SemaTemplate/member-template-access-expr.cpp
+++ b/test/SemaTemplate/member-template-access-expr.cpp
@@ -60,7 +60,7 @@ struct X1 {
void test_X1(X1 x1) {
float *fp1 = x1.f1<>(17);
- float *fp2 = x1.f1<int>(3.14); // expected-warning {{implicit conversion turns literal floating-point number into integer}}
+ float *fp2 = x1.f1<int>(3.14); // expected-warning {{implicit conversion from 'double' to 'int' changes value from 3.14 to 3}}
int *ip1 = x1.f1(17);
float *ip2 = x1.f1(3.14);
diff --git a/test/SemaTemplate/ms-function-specialization-class-scope.cpp b/test/SemaTemplate/ms-function-specialization-class-scope.cpp
index bda87f9..131922b 100644
--- a/test/SemaTemplate/ms-function-specialization-class-scope.cpp
+++ b/test/SemaTemplate/ms-function-specialization-class-scope.cpp
@@ -69,3 +69,24 @@ void test2()
b.f(100);
}
+
+namespace PR12709 {
+
+template<class T>
+class TemplateClass {
+ void member_function() {
+ specialized_member_template<false>();
+ }
+
+ template<bool b>
+ void specialized_member_template() {}
+
+ template<>
+ void specialized_member_template<false>() {} // expected-warning{{explicit specialization of 'specialized_member_template' within class scope is a Microsoft extension}}
+};
+
+void f() {
+ TemplateClass<int> t;
+}
+
+}
diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp
index 2c422dc..8f80cb5 100644
--- a/test/SemaTemplate/ms-lookup-template-base-classes.cpp
+++ b/test/SemaTemplate/ms-lookup-template-base-classes.cpp
@@ -143,3 +143,32 @@ public:
template class A<C>;
}
+
+namespace PR12701 {
+
+class A {};
+class B {};
+
+template <class T>
+class Base {
+ public:
+ bool base_fun(void* p) { return false; } // expected-note {{must qualify identifier to find this declaration in dependent base clas}}
+ operator T*() const { return 0; }
+};
+
+template <class T>
+class Container : public Base<T> {
+ public:
+ template <typename S>
+ bool operator=(const Container<S>& rhs) {
+ return base_fun(rhs); // expected-warning {{use of identifier 'base_fun' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ }
+};
+
+void f() {
+ Container<A> text_provider;
+ Container<B> text_provider2;
+ text_provider2 = text_provider; // expected-note {{in instantiation of function template specialization}}
+}
+
+} // namespace PR12701
diff --git a/test/SemaTemplate/overload-candidates.cpp b/test/SemaTemplate/overload-candidates.cpp
index 8762cc8..dc6d2a5 100644
--- a/test/SemaTemplate/overload-candidates.cpp
+++ b/test/SemaTemplate/overload-candidates.cpp
@@ -24,7 +24,9 @@ void test_get(void *ptr) {
}
template<typename T>
- typename T::type get_type(const T&); // expected-note{{candidate template ignored: substitution failure [with T = int *]}}
+ typename T::type get_type(const T&); // expected-note{{candidate template ignored: substitution failure [with T = int *]: type 'int *' cannot be used prior to '::'}}
+template<typename T>
+ void get_type(T *, int[(int)sizeof(T) - 9] = 0); // expected-note{{candidate template ignored: substitution failure [with T = int]: array size is negative}}
void test_get_type(int *ptr) {
(void)get_type(ptr); // expected-error{{no matching function for call to 'get_type'}}
@@ -38,3 +40,25 @@ struct X {
void test_X_min(X x) {
(void)x.min(1, 2l); // expected-error{{no matching member function for call to 'min'}}
}
+
+namespace boost {
+ template<bool, typename = void> struct enable_if {};
+ template<typename T> struct enable_if<true, T> { typedef T type; };
+}
+template<typename T> typename boost::enable_if<sizeof(T) == 4, int>::type if_size_4(); // expected-note{{candidate template ignored: disabled by 'enable_if' [with T = char]}}
+int k = if_size_4<char>(); // expected-error{{no matching function}}
+
+namespace llvm {
+ template<typename Cond, typename T = void> struct enable_if : boost::enable_if<Cond::value, T> {};
+}
+template<typename T> struct is_int { enum { value = false }; };
+template<> struct is_int<int> { enum { value = true }; };
+template<typename T> typename llvm::enable_if<is_int<T> >::type if_int(); // expected-note{{candidate template ignored: disabled by 'enable_if' [with T = char]}}
+void test_if_int() {
+ if_int<char>(); // expected-error{{no matching function}}
+}
+
+template<typename T> struct NonTemplateFunction {
+ typename boost::enable_if<sizeof(T) == 4, int>::type f(); // expected-error{{no type named 'type' in 'boost::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration}}
+};
+NonTemplateFunction<char> NTFC; // expected-note{{here}}
diff --git a/test/SemaTemplate/recovery-crash.cpp b/test/SemaTemplate/recovery-crash.cpp
index 0ed3258..61e880b 100644
--- a/test/SemaTemplate/recovery-crash.cpp
+++ b/test/SemaTemplate/recovery-crash.cpp
@@ -1,7 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
-// We don't expect a fix-it to be applied in this case. Clang used to crash
-// trying to recover while adding 'this->' before Work(x);
+// Clang used to crash trying to recover while adding 'this->' before Work(x);
template <typename> struct A {
static void Work(int); // expected-note{{must qualify identifier}}
diff --git a/test/SemaTemplate/typename-specifier.cpp b/test/SemaTemplate/typename-specifier.cpp
index 9eb4f33..733dc7f 100644
--- a/test/SemaTemplate/typename-specifier.cpp
+++ b/test/SemaTemplate/typename-specifier.cpp
@@ -22,8 +22,8 @@ typename N::C::type *ip3 = &i; // expected-error{{typename specifier refers to n
// expected-warning{{'typename' occurs outside of a template}}
void test(double d) {
- typename N::A::type f(typename N::A::type(a)); // expected-warning{{parentheses were disambiguated as a function declarator}} \
- // expected-warning 2{{'typename' occurs outside of a template}}
+ typename N::A::type f(typename N::A::type(a)); // expected-warning{{disambiguated as a function declaration}} \
+ // expected-note{{add a pair of parentheses}} expected-warning 2{{'typename' occurs outside of a template}}
int five = f(5);
using namespace N;
@@ -115,3 +115,40 @@ namespace PR10925 {
using typename BasicGeometry<mydim, int>::operator[]; // expected-error {{typename is allowed for identifiers only}}
};
}
+
+
+namespace missing_typename {
+template <class T1, class T2> struct pair {}; // expected-note 7 {{template parameter is declared here}}
+
+template <class T1, class T2>
+struct map {
+ typedef T1* iterator;
+};
+
+template <class T>
+class ExampleClass1 {
+ struct ExampleItem;
+
+
+ struct ExampleItemSet {
+ typedef ExampleItem* iterator;
+ ExampleItem* operator[](unsigned);
+ };
+
+ void foo() {
+ pair<ExampleItemSet::iterator, int> i; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+ pair<this->ExampleItemSet::iterator, int> i; // expected-error-re {{template argument for template type parameter must be a type$}}
+ pair<ExampleItemSet::operator[], int> i; // expected-error-re {{template argument for template type parameter must be a type$}}
+ }
+ pair<ExampleItemSet::iterator, int> elt; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+
+
+ typedef map<int, ExampleItem*> ExampleItemMap;
+
+ static void bar() {
+ pair<ExampleItemMap::iterator, int> i; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+ }
+ pair<ExampleItemMap::iterator, int> entry; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+ pair<bar, int> foobar; // expected-error {{template argument for template type parameter must be a type}}
+};
+} // namespace missing_typename
diff --git a/test/Tooling/Inputs/lit.local.cfg b/test/Tooling/Inputs/lit.local.cfg
new file mode 100644
index 0000000..e6f55ee
--- /dev/null
+++ b/test/Tooling/Inputs/lit.local.cfg
@@ -0,0 +1 @@
+config.suffixes = []
diff --git a/test/Tooling/Inputs/pch-fail.h b/test/Tooling/Inputs/pch-fail.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Tooling/Inputs/pch-fail.h
diff --git a/test/Tooling/Inputs/pch.cpp b/test/Tooling/Inputs/pch.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Tooling/Inputs/pch.cpp
diff --git a/test/Tooling/Inputs/pch.h b/test/Tooling/Inputs/pch.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Tooling/Inputs/pch.h
diff --git a/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp b/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp
new file mode 100644
index 0000000..9692edc
--- /dev/null
+++ b/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/abc/def/ijk/qwe
+// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/abc/def/ijk/qwe/test.cpp\",\"file\":\"%t/abc/def/ijk/qwe/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/abc/def/ijk/qwe/test.cpp"
+// RUN: PWD="%t/abc/def" clang-check "ijk/qwe/test.cpp" 2>&1 | FileCheck %s
+
+// CHECK: C++ requires
+invalid;
+
+// REQUIRES: shell
diff --git a/test/Tooling/auto-detect-from-source-parent.cpp b/test/Tooling/auto-detect-from-source-parent.cpp
new file mode 100644
index 0000000..ea7eb15
--- /dev/null
+++ b/test/Tooling/auto-detect-from-source-parent.cpp
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/abc/def/ijk/qwe
+// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/abc/def/ijk/qwe/test.cpp\",\"file\":\"%t/abc/def/ijk/qwe/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/abc/def/ijk/qwe/test.cpp"
+// RUN: clang-check "%t/abc/def/ijk/qwe/test.cpp" 2>&1 | FileCheck %s
+
+// CHECK: C++ requires
+invalid;
+
+// REQUIRES: shell
diff --git a/test/Tooling/auto-detect-from-source.cpp b/test/Tooling/auto-detect-from-source.cpp
new file mode 100644
index 0000000..d8e82e7
--- /dev/null
+++ b/test/Tooling/auto-detect-from-source.cpp
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-check "%t/test.cpp" 2>&1 | FileCheck %s
+
+// CHECK: C++ requires
+invalid;
+
+// REQUIRES: shell
diff --git a/test/Tooling/clang-check-args.cpp b/test/Tooling/clang-check-args.cpp
index a14fc7b..9ba5d45 100644
--- a/test/Tooling/clang-check-args.cpp
+++ b/test/Tooling/clang-check-args.cpp
@@ -1,8 +1,7 @@
-// RUN: clang-check . "%s" -- -c 2>&1 | FileCheck %s
+// RUN: clang-check "%s" -- -c 2>&1 | FileCheck %s
// CHECK: C++ requires
invalid;
-// FIXME: JSON doesn't like path separator '\', on Win32 hosts.
-// FIXME: clang-check doesn't like gcc driver on cygming.
-// XFAIL: cygwin,mingw32,win32
+// FIXME: This is incompatible to -fms-compatibility.
+// XFAIL: win32
diff --git a/test/Tooling/clang-check-ast-dump.cpp b/test/Tooling/clang-check-ast-dump.cpp
new file mode 100644
index 0000000..86533af
--- /dev/null
+++ b/test/Tooling/clang-check-ast-dump.cpp
@@ -0,0 +1,35 @@
+// RUN: clang-check -ast-dump "%s" -- 2>&1 | FileCheck %s
+// CHECK: namespace test_namespace
+// CHECK-NEXT: class TheClass
+// CHECK: int theMethod(int x) (CompoundStmt
+// CHECK-NEXT: (ReturnStmt
+// CHECK-NEXT: (BinaryOperator
+//
+// RUN: clang-check -ast-dump -ast-dump-filter test_namespace::TheClass::theMethod "%s" -- 2>&1 | FileCheck -check-prefix CHECK-FILTER %s
+// CHECK-FILTER-NOT: namespace test_namespace
+// CHECK-FILTER-NOT: class TheClass
+// CHECK-FILTER: int theMethod(int x) (CompoundStmt
+// CHECK-FILTER-NEXT: (ReturnStmt
+// CHECK-FILTER-NEXT: (BinaryOperator
+//
+// RUN: clang-check -ast-print "%s" -- 2>&1 | FileCheck -check-prefix CHECK-PRINT %s
+// CHECK-PRINT: namespace test_namespace
+// CHECK-PRINT: class TheClass
+// CHECK-PRINT: int theMethod(int x)
+//
+// RUN: clang-check -ast-list "%s" -- 2>&1 | FileCheck -check-prefix CHECK-LIST %s
+// CHECK-LIST: test_namespace
+// CHECK-LIST-NEXT: test_namespace::TheClass
+// CHECK-LIST-NEXT: test_namespace::TheClass::theMethod
+// CHECK-LIST-NEXT: x
+
+namespace test_namespace {
+
+class TheClass {
+public:
+ int theMethod(int x) {
+ return x + x;
+ }
+};
+
+}
diff --git a/test/Tooling/clang-check-autodetect-dir.cpp b/test/Tooling/clang-check-autodetect-dir.cpp
new file mode 100644
index 0000000..2c39504
--- /dev/null
+++ b/test/Tooling/clang-check-autodetect-dir.cpp
@@ -0,0 +1,11 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/abc/def
+// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-check -p "%t/abc/def" "%t/test.cpp" 2>&1|FileCheck %s
+// FIXME: Make the above easier.
+
+// CHECK: C++ requires
+invalid;
+
+// REQUIRES: shell
diff --git a/test/Tooling/clang-check-builtin-headers.cpp b/test/Tooling/clang-check-builtin-headers.cpp
new file mode 100644
index 0000000..504d197
--- /dev/null
+++ b/test/Tooling/clang-check-builtin-headers.cpp
@@ -0,0 +1,15 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// Add a path that doesn't exist as argv[0] for the compile command line:
+// RUN: echo '[{"directory":".","command":"/random/tool -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-check -p "%t" "%t/test.cpp" 2>&1|FileCheck %s
+// FIXME: Make the above easier.
+
+#include <stddef.h>
+
+// CHECK: C++ requires
+invalid;
+
+// FIXME: This is incompatible to -fms-compatibility.
+// XFAIL: win32
diff --git a/test/Tooling/clang-check-chdir.cpp b/test/Tooling/clang-check-chdir.cpp
new file mode 100644
index 0000000..29b5abb
--- /dev/null
+++ b/test/Tooling/clang-check-chdir.cpp
@@ -0,0 +1,17 @@
+// Verifies that paths are resolved relatively to the directory specified in the
+// compilation database.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "[{\"directory\":\"%t\",\"command\":\"clang -c test.cpp -I.\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\//g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: touch "%t/clang-check-test.h"
+// RUN: clang-check -p "%t" "%t/test.cpp" 2>&1|FileCheck %s
+// FIXME: Make the above easier.
+
+#include "clang-check-test.h"
+
+// CHECK: C++ requires
+invalid;
+
+// FIXME: This is incompatible to -fms-compatibility.
+// XFAIL: win32
diff --git a/test/Tooling/clang-check-pwd.cpp b/test/Tooling/clang-check-pwd.cpp
index cb94344..374c579 100644
--- a/test/Tooling/clang-check-pwd.cpp
+++ b/test/Tooling/clang-check-pwd.cpp
@@ -1,13 +1,11 @@
// RUN: rm -rf %t
// RUN: mkdir %t
-// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" > %t/compile_commands.json
+// RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json
// RUN: cp "%s" "%t/test.cpp"
-// RUN: PWD="%t" clang-check "%t" "test.cpp" 2>&1|FileCheck %s
+// RUN: PWD="%t" clang-check -p "%t" "test.cpp" 2>&1|FileCheck %s
// FIXME: Make the above easier.
// CHECK: C++ requires
invalid;
-// FIXME: JSON doesn't like path separator '\', on Win32 hosts.
-// FIXME: clang-check doesn't like gcc driver on cygming.
-// XFAIL: cygwin,mingw32,win32
+// REQUIRES: shell
diff --git a/test/Tooling/clang-check.cpp b/test/Tooling/clang-check.cpp
index de10e4a..91ab01b 100644
--- a/test/Tooling/clang-check.cpp
+++ b/test/Tooling/clang-check.cpp
@@ -1,13 +1,12 @@
// RUN: rm -rf %t
// RUN: mkdir %t
-// RUN: echo '[{"directory":".","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' > %t/compile_commands.json
+// RUN: echo '[{"directory":".","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json
// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-check "%t" "%t/test.cpp" 2>&1|FileCheck %s
+// RUN: clang-check -p "%t" "%t/test.cpp" 2>&1|FileCheck %s
// FIXME: Make the above easier.
// CHECK: C++ requires
invalid;
-// FIXME: JSON doesn't like path separator '\', on Win32 hosts.
-// FIXME: clang-check doesn't like gcc driver on cygming.
-// XFAIL: cygwin,mingw32,win32
+// FIXME: This is incompatible to -fms-compatibility.
+// XFAIL: win32
diff --git a/test/Tooling/multi-jobs.cpp b/test/Tooling/multi-jobs.cpp
new file mode 100644
index 0000000..a3eb703
--- /dev/null
+++ b/test/Tooling/multi-jobs.cpp
@@ -0,0 +1,7 @@
+// RUN: clang-check "%s" -- -no-integrated-as -c 2>&1 | FileCheck %s
+
+// CHECK: C++ requires
+invalid;
+
+// FIXME: This is incompatible to -fms-compatibility.
+// XFAIL: win32
diff --git a/test/Tooling/pch.cpp b/test/Tooling/pch.cpp
new file mode 100644
index 0000000..715c95d
--- /dev/null
+++ b/test/Tooling/pch.cpp
@@ -0,0 +1,17 @@
+// This is a regression test for handling of stat caches within the tooling
+// infrastructure. This test reproduces the problem under valgrind:
+
+// First, create a pch that we can later load. Loading the pch will insert
+// a stat cache into the FileManager:
+// RUN: %clang -x c++-header %S/Inputs/pch.h -o %t1
+
+// Use the generated pch and enforce a subsequent stat miss by using
+// the test file with an unrelated include as second translation unit:
+// Do not directly pipe into FileCheck, as that would hide errors from
+// valgrind due to pipefail not being set in lit.
+// RUN: clang-check "%S/Inputs/pch.cpp" "%s" -- -include-pch %t1 -I "%S" -c >%t2 2>&1
+// RUN: FileCheck %s < %t2
+
+#include "Inputs/pch-fail.h"
+
+// CHECK: Processing
diff --git a/test/lit.cfg b/test/lit.cfg
index 738dc38..7bc9620 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -222,6 +222,10 @@ if platform.system() not in ['FreeBSD']:
if platform.system() not in ['Windows'] or lit.getBashPath() != '':
config.available_features.add('shell')
+# ANSI escape sequences in non-dump terminal
+if platform.system() not in ['Windows']:
+ config.available_features.add('ansi-escape-sequences')
+
# Registered Targets
def get_llc_props(tool):
set_of_targets = set()
@@ -255,3 +259,7 @@ else:
if llc_props['enable_assertions']:
config.available_features.add('asserts')
+
+if lit.util.which('xmllint'):
+ config.available_features.add('xmllint')
+
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index ab4748d..3a6fef5 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -5,3 +5,12 @@ add_subdirectory(c-arcmt-test)
add_subdirectory(diagtool)
add_subdirectory(driver)
add_subdirectory(clang-check)
+
+# We support checking out the clang-tools-extra repository into the 'extra'
+# subdirectory. It contains tools developed as part of the Clang/LLVM project
+# on top of the Clang tooling platform. We keep them in a separate repository
+# to keep the primary Clang repository small and focused.
+if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/extra AND
+ EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/extra/CMakeLists.txt)
+ add_subdirectory(extra)
+endif()
diff --git a/tools/Makefile b/tools/Makefile
index 5059ade..e7aa2fa 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -8,9 +8,13 @@
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ..
+
+include $(CLANG_LEVEL)/../../Makefile.config
+
DIRS := driver libclang c-index-test arcmt-test c-arcmt-test diagtool \
clang-check
-include $(CLANG_LEVEL)/../../Makefile.config
+# Recurse into the extra repository of tools if present.
+OPTIONAL_DIRS := extra
include $(CLANG_LEVEL)/Makefile
diff --git a/tools/arcmt-test/CMakeLists.txt b/tools/arcmt-test/CMakeLists.txt
index a0029b4..f36b14a 100644
--- a/tools/arcmt-test/CMakeLists.txt
+++ b/tools/arcmt-test/CMakeLists.txt
@@ -1,10 +1,6 @@
-set(LLVM_USED_LIBS
- clangARCMigrate
- clangEdit
- clangRewrite
- )
-
-set( LLVM_LINK_COMPONENTS
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
support
mc
)
@@ -12,3 +8,9 @@ set( LLVM_LINK_COMPONENTS
add_clang_executable(arcmt-test
arcmt-test.cpp
)
+
+target_link_libraries(arcmt-test
+ clangARCMigrate
+ clangEdit
+ clangRewrite
+ )
diff --git a/tools/arcmt-test/Makefile b/tools/arcmt-test/Makefile
index 57cd574..719da75 100644
--- a/tools/arcmt-test/Makefile
+++ b/tools/arcmt-test/Makefile
@@ -16,9 +16,11 @@ TOOL_NO_EXPORTS = 1
# Don't install this. It is used for tests.
NO_INSTALL = 1
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangARCMigrate.a clangRewrite.a \
clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \
- clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a
+ clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a \
+ clangBasic.a
include $(CLANG_LEVEL)/Makefile
diff --git a/tools/c-arcmt-test/CMakeLists.txt b/tools/c-arcmt-test/CMakeLists.txt
index 351f4ad..1e72261 100644
--- a/tools/c-arcmt-test/CMakeLists.txt
+++ b/tools/c-arcmt-test/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(LLVM_USED_LIBS libclang)
-
set( LLVM_LINK_COMPONENTS
support
mc
@@ -9,6 +7,10 @@ add_clang_executable(c-arcmt-test
c-arcmt-test.c
)
+target_link_libraries(c-arcmt-test
+ libclang
+ )
+
set_target_properties(c-arcmt-test
PROPERTIES
LINKER_LANGUAGE CXX)
diff --git a/tools/c-arcmt-test/Makefile b/tools/c-arcmt-test/Makefile
index 818f648..b59afda 100644
--- a/tools/c-arcmt-test/Makefile
+++ b/tools/c-arcmt-test/Makefile
@@ -16,7 +16,12 @@ TOOL_NO_EXPORTS = 1
# Don't install this. It is used for tests.
NO_INSTALL = 1
-LINK_COMPONENTS := support mc
+# Include this here so we can get the configuration of the targets that have
+# been configured for construction. We have to do this early so we can set up
+# LINK_COMPONENTS before including Makefile.rules
+include $(CLANG_LEVEL)/../../Makefile.config
+
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clang.a clangARCMigrate.a clangRewrite.a \
clangFrontend.a clangDriver.a \
clangSerialization.a clangParse.a clangSema.a \
diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt
index c44b34b..6379194 100644
--- a/tools/c-index-test/CMakeLists.txt
+++ b/tools/c-index-test/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(LLVM_USED_LIBS libclang)
-
set( LLVM_LINK_COMPONENTS
support
mc
@@ -9,8 +7,17 @@ add_clang_executable(c-index-test
c-index-test.c
)
+target_link_libraries(c-index-test
+ libclang
+ )
+
set_target_properties(c-index-test
PROPERTIES
LINKER_LANGUAGE CXX)
-install(TARGETS c-index-test RUNTIME DESTINATION bin)
+# If libxml2 is available, make it available for c-index-test.
+if (LIBXML2_FOUND)
+ add_definitions(${LIBXML2_DEFINITIONS} "-DCLANG_HAVE_LIBXML")
+ include_directories(${LIBXML2_INCLUDE_DIR})
+ target_link_libraries(c-index-test ${LIBXML2_LIBRARIES})
+endif()
diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile
index 03519b3..09eff0f 100644
--- a/tools/c-index-test/Makefile
+++ b/tools/c-index-test/Makefile
@@ -17,9 +17,22 @@ INTERNAL_TOOL = 1
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
-LINK_COMPONENTS := support mc
+# Don't install this. It is used for tests.
+NO_INSTALL = 1
+
+# Include this here so we can get the configuration of the targets that have
+# been configured for construction. We have to do this early so we can set up
+# LINK_COMPONENTS before including Makefile.rules
+include $(CLANG_LEVEL)/../../Makefile.config
+
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clang.a clangFrontend.a clangDriver.a \
+ clangTooling.a \
clangSerialization.a clangParse.a clangSema.a \
- clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a
+ clangAnalysis.a clangEdit.a clangAST.a clangLex.a \
+ clangBasic.a
include $(CLANG_LEVEL)/Makefile
+
+LIBS += $(LIBXML2_LIBS)
+CPPFLAGS += $(LIBXML2_INC)
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index eb2a406..5df21ec 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1,12 +1,20 @@
/* c-index-test.c */
#include "clang-c/Index.h"
+#include "clang-c/CXCompilationDatabase.h"
+#include "llvm/Config/config.h"
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
+#ifdef CLANG_HAVE_LIBXML
+#include <libxml/parser.h>
+#include <libxml/relaxng.h>
+#include <libxml/xmlerror.h>
+#endif
+
/******************************************************************************/
/* Utility functions. */
/******************************************************************************/
@@ -25,8 +33,25 @@ char *basename(const char* path)
return((char*)path);
}
+char *dirname(char* path)
+{
+ char* base1 = (char*)strrchr(path, '/');
+ char* base2 = (char*)strrchr(path, '\\');
+ if (base1 && base2)
+ if (base1 > base2)
+ *base1 = 0;
+ else
+ *base2 = 0;
+ else if (base1)
+ *base1 = 0;
+ else if (base2)
+ *base2 = 0;
+
+ return path;
+}
#else
extern char *basename(const char *);
+extern char *dirname(char *);
#endif
/** \brief Return the default parsing options. */
@@ -41,6 +66,8 @@ static unsigned getDefaultParsingOptions() {
options &= ~CXTranslationUnit_CacheCompletionResults;
if (getenv("CINDEXTEST_SKIP_FUNCTION_BODIES"))
options |= CXTranslationUnit_SkipFunctionBodies;
+ if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS"))
+ options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
return options;
}
@@ -137,6 +164,7 @@ int parse_remapped_files(int argc, const char **argv, int start_arg,
(feof(to_file) ? "EOF" : "error"), semi + 1);
fclose(to_file);
free_remapped_files(*unsaved_files, i);
+ free(contents);
*unsaved_files = 0;
*num_unsaved_files = 0;
return -1;
@@ -158,10 +186,62 @@ int parse_remapped_files(int argc, const char **argv, int start_arg,
return 0;
}
+static const char *parse_comments_schema(int argc, const char **argv) {
+ const char *CommentsSchemaArg = "-comments-xml-schema=";
+ const char *CommentSchemaFile = NULL;
+
+ if (argc == 0)
+ return CommentSchemaFile;
+
+ if (!strncmp(argv[0], CommentsSchemaArg, strlen(CommentsSchemaArg)))
+ CommentSchemaFile = argv[0] + strlen(CommentsSchemaArg);
+
+ return CommentSchemaFile;
+}
+
/******************************************************************************/
/* Pretty-printing. */
/******************************************************************************/
+static const char *FileCheckPrefix = "CHECK";
+
+static void PrintCString(const char *CStr) {
+ if (CStr != NULL && CStr[0] != '\0') {
+ for ( ; *CStr; ++CStr) {
+ const char C = *CStr;
+ switch (C) {
+ case '\n': printf("\\n"); break;
+ case '\r': printf("\\r"); break;
+ case '\t': printf("\\t"); break;
+ case '\v': printf("\\v"); break;
+ case '\f': printf("\\f"); break;
+ default: putchar(C); break;
+ }
+ }
+ }
+}
+
+static void PrintCStringWithPrefix(const char *Prefix, const char *CStr) {
+ printf(" %s=[", Prefix);
+ PrintCString(CStr);
+ printf("]");
+}
+
+static void PrintCXStringAndDispose(CXString Str) {
+ PrintCString(clang_getCString(Str));
+ clang_disposeString(Str);
+}
+
+static void PrintCXStringWithPrefix(const char *Prefix, CXString Str) {
+ PrintCStringWithPrefix(Prefix, clang_getCString(Str));
+}
+
+static void PrintCXStringWithPrefixAndDispose(const char *Prefix,
+ CXString Str) {
+ PrintCStringWithPrefix(Prefix, clang_getCString(Str));
+ clang_disposeString(Str);
+}
+
static void PrintRange(CXSourceRange R, const char *str) {
CXFile begin_file, end_file;
unsigned begin_line, begin_column, end_line, end_column;
@@ -180,7 +260,302 @@ static void PrintRange(CXSourceRange R, const char *str) {
int want_display_name = 0;
-static void PrintCursor(CXCursor Cursor) {
+static void printVersion(const char *Prefix, CXVersion Version) {
+ if (Version.Major < 0)
+ return;
+ printf("%s%d", Prefix, Version.Major);
+
+ if (Version.Minor < 0)
+ return;
+ printf(".%d", Version.Minor);
+
+ if (Version.Subminor < 0)
+ return;
+ printf(".%d", Version.Subminor);
+}
+
+struct CommentASTDumpingContext {
+ int IndentLevel;
+};
+
+static void DumpCXCommentInternal(struct CommentASTDumpingContext *Ctx,
+ CXComment Comment) {
+ unsigned i;
+ unsigned e;
+ enum CXCommentKind Kind = clang_Comment_getKind(Comment);
+
+ Ctx->IndentLevel++;
+ for (i = 0, e = Ctx->IndentLevel; i != e; ++i)
+ printf(" ");
+
+ printf("(");
+ switch (Kind) {
+ case CXComment_Null:
+ printf("CXComment_Null");
+ break;
+ case CXComment_Text:
+ printf("CXComment_Text");
+ PrintCXStringWithPrefixAndDispose("Text",
+ clang_TextComment_getText(Comment));
+ if (clang_Comment_isWhitespace(Comment))
+ printf(" IsWhitespace");
+ if (clang_InlineContentComment_hasTrailingNewline(Comment))
+ printf(" HasTrailingNewline");
+ break;
+ case CXComment_InlineCommand:
+ printf("CXComment_InlineCommand");
+ PrintCXStringWithPrefixAndDispose(
+ "CommandName",
+ clang_InlineCommandComment_getCommandName(Comment));
+ switch (clang_InlineCommandComment_getRenderKind(Comment)) {
+ case CXCommentInlineCommandRenderKind_Normal:
+ printf(" RenderNormal");
+ break;
+ case CXCommentInlineCommandRenderKind_Bold:
+ printf(" RenderBold");
+ break;
+ case CXCommentInlineCommandRenderKind_Monospaced:
+ printf(" RenderMonospaced");
+ break;
+ case CXCommentInlineCommandRenderKind_Emphasized:
+ printf(" RenderEmphasized");
+ break;
+ }
+ for (i = 0, e = clang_InlineCommandComment_getNumArgs(Comment);
+ i != e; ++i) {
+ printf(" Arg[%u]=", i);
+ PrintCXStringAndDispose(
+ clang_InlineCommandComment_getArgText(Comment, i));
+ }
+ if (clang_InlineContentComment_hasTrailingNewline(Comment))
+ printf(" HasTrailingNewline");
+ break;
+ case CXComment_HTMLStartTag: {
+ unsigned NumAttrs;
+ printf("CXComment_HTMLStartTag");
+ PrintCXStringWithPrefixAndDispose(
+ "Name",
+ clang_HTMLTagComment_getTagName(Comment));
+ NumAttrs = clang_HTMLStartTag_getNumAttrs(Comment);
+ if (NumAttrs != 0) {
+ printf(" Attrs:");
+ for (i = 0; i != NumAttrs; ++i) {
+ printf(" ");
+ PrintCXStringAndDispose(clang_HTMLStartTag_getAttrName(Comment, i));
+ printf("=");
+ PrintCXStringAndDispose(clang_HTMLStartTag_getAttrValue(Comment, i));
+ }
+ }
+ if (clang_HTMLStartTagComment_isSelfClosing(Comment))
+ printf(" SelfClosing");
+ if (clang_InlineContentComment_hasTrailingNewline(Comment))
+ printf(" HasTrailingNewline");
+ break;
+ }
+ case CXComment_HTMLEndTag:
+ printf("CXComment_HTMLEndTag");
+ PrintCXStringWithPrefixAndDispose(
+ "Name",
+ clang_HTMLTagComment_getTagName(Comment));
+ if (clang_InlineContentComment_hasTrailingNewline(Comment))
+ printf(" HasTrailingNewline");
+ break;
+ case CXComment_Paragraph:
+ printf("CXComment_Paragraph");
+ if (clang_Comment_isWhitespace(Comment))
+ printf(" IsWhitespace");
+ break;
+ case CXComment_BlockCommand:
+ printf("CXComment_BlockCommand");
+ PrintCXStringWithPrefixAndDispose(
+ "CommandName",
+ clang_BlockCommandComment_getCommandName(Comment));
+ for (i = 0, e = clang_BlockCommandComment_getNumArgs(Comment);
+ i != e; ++i) {
+ printf(" Arg[%u]=", i);
+ PrintCXStringAndDispose(
+ clang_BlockCommandComment_getArgText(Comment, i));
+ }
+ break;
+ case CXComment_ParamCommand:
+ printf("CXComment_ParamCommand");
+ switch (clang_ParamCommandComment_getDirection(Comment)) {
+ case CXCommentParamPassDirection_In:
+ printf(" in");
+ break;
+ case CXCommentParamPassDirection_Out:
+ printf(" out");
+ break;
+ case CXCommentParamPassDirection_InOut:
+ printf(" in,out");
+ break;
+ }
+ if (clang_ParamCommandComment_isDirectionExplicit(Comment))
+ printf(" explicitly");
+ else
+ printf(" implicitly");
+ PrintCXStringWithPrefixAndDispose(
+ "ParamName",
+ clang_ParamCommandComment_getParamName(Comment));
+ if (clang_ParamCommandComment_isParamIndexValid(Comment))
+ printf(" ParamIndex=%u", clang_ParamCommandComment_getParamIndex(Comment));
+ else
+ printf(" ParamIndex=Invalid");
+ break;
+ case CXComment_TParamCommand:
+ printf("CXComment_TParamCommand");
+ PrintCXStringWithPrefixAndDispose(
+ "ParamName",
+ clang_TParamCommandComment_getParamName(Comment));
+ if (clang_TParamCommandComment_isParamPositionValid(Comment)) {
+ printf(" ParamPosition={");
+ for (i = 0, e = clang_TParamCommandComment_getDepth(Comment);
+ i != e; ++i) {
+ printf("%u", clang_TParamCommandComment_getIndex(Comment, i));
+ if (i != e - 1)
+ printf(", ");
+ }
+ printf("}");
+ } else
+ printf(" ParamPosition=Invalid");
+ break;
+ case CXComment_VerbatimBlockCommand:
+ printf("CXComment_VerbatimBlockCommand");
+ PrintCXStringWithPrefixAndDispose(
+ "CommandName",
+ clang_BlockCommandComment_getCommandName(Comment));
+ break;
+ case CXComment_VerbatimBlockLine:
+ printf("CXComment_VerbatimBlockLine");
+ PrintCXStringWithPrefixAndDispose(
+ "Text",
+ clang_VerbatimBlockLineComment_getText(Comment));
+ break;
+ case CXComment_VerbatimLine:
+ printf("CXComment_VerbatimLine");
+ PrintCXStringWithPrefixAndDispose(
+ "Text",
+ clang_VerbatimLineComment_getText(Comment));
+ break;
+ case CXComment_FullComment:
+ printf("CXComment_FullComment");
+ break;
+ }
+ if (Kind != CXComment_Null) {
+ const unsigned NumChildren = clang_Comment_getNumChildren(Comment);
+ unsigned i;
+ for (i = 0; i != NumChildren; ++i) {
+ printf("\n// %s: ", FileCheckPrefix);
+ DumpCXCommentInternal(Ctx, clang_Comment_getChild(Comment, i));
+ }
+ }
+ printf(")");
+ Ctx->IndentLevel--;
+}
+
+static void DumpCXComment(CXComment Comment) {
+ struct CommentASTDumpingContext Ctx;
+ Ctx.IndentLevel = 1;
+ printf("\n// %s: CommentAST=[\n// %s:", FileCheckPrefix, FileCheckPrefix);
+ DumpCXCommentInternal(&Ctx, Comment);
+ printf("]");
+}
+
+typedef struct {
+ const char *CommentSchemaFile;
+#ifdef CLANG_HAVE_LIBXML
+ xmlRelaxNGParserCtxtPtr RNGParser;
+ xmlRelaxNGPtr Schema;
+#endif
+} CommentXMLValidationData;
+
+static void ValidateCommentXML(const char *Str,
+ CommentXMLValidationData *ValidationData) {
+#ifdef CLANG_HAVE_LIBXML
+ xmlDocPtr Doc;
+ xmlRelaxNGValidCtxtPtr ValidationCtxt;
+ int status;
+
+ if (!ValidationData || !ValidationData->CommentSchemaFile)
+ return;
+
+ if (!ValidationData->RNGParser) {
+ ValidationData->RNGParser =
+ xmlRelaxNGNewParserCtxt(ValidationData->CommentSchemaFile);
+ ValidationData->Schema = xmlRelaxNGParse(ValidationData->RNGParser);
+ }
+ if (!ValidationData->RNGParser) {
+ printf(" libXMLError");
+ return;
+ }
+
+ Doc = xmlParseDoc((const xmlChar *) Str);
+
+ if (!Doc) {
+ xmlErrorPtr Error = xmlGetLastError();
+ printf(" CommentXMLInvalid [not well-formed XML: %s]", Error->message);
+ return;
+ }
+
+ ValidationCtxt = xmlRelaxNGNewValidCtxt(ValidationData->Schema);
+ status = xmlRelaxNGValidateDoc(ValidationCtxt, Doc);
+ if (!status)
+ printf(" CommentXMLValid");
+ else if (status > 0) {
+ xmlErrorPtr Error = xmlGetLastError();
+ printf(" CommentXMLInvalid [not vaild XML: %s]", Error->message);
+ } else
+ printf(" libXMLError");
+
+ xmlRelaxNGFreeValidCtxt(ValidationCtxt);
+ xmlFreeDoc(Doc);
+#endif
+}
+
+static void PrintCursorComments(CXTranslationUnit TU,
+ CXCursor Cursor,
+ CommentXMLValidationData *ValidationData) {
+ {
+ CXString RawComment;
+ const char *RawCommentCString;
+ CXString BriefComment;
+ const char *BriefCommentCString;
+
+ RawComment = clang_Cursor_getRawCommentText(Cursor);
+ RawCommentCString = clang_getCString(RawComment);
+ if (RawCommentCString != NULL && RawCommentCString[0] != '\0') {
+ PrintCStringWithPrefix("RawComment", RawCommentCString);
+ PrintRange(clang_Cursor_getCommentRange(Cursor), "RawCommentRange");
+
+ BriefComment = clang_Cursor_getBriefCommentText(Cursor);
+ BriefCommentCString = clang_getCString(BriefComment);
+ if (BriefCommentCString != NULL && BriefCommentCString[0] != '\0')
+ PrintCStringWithPrefix("BriefComment", BriefCommentCString);
+ clang_disposeString(BriefComment);
+ }
+ clang_disposeString(RawComment);
+ }
+
+ {
+ CXComment Comment = clang_Cursor_getParsedComment(Cursor);
+ if (clang_Comment_getKind(Comment) != CXComment_Null) {
+ PrintCXStringWithPrefixAndDispose("FullCommentAsHTML",
+ clang_FullComment_getAsHTML(Comment));
+ {
+ CXString XML;
+ XML = clang_FullComment_getAsXML(TU, Comment);
+ PrintCXStringWithPrefix("FullCommentAsXML", XML);
+ ValidateCommentXML(clang_getCString(XML), ValidationData);
+ clang_disposeString(XML);
+ }
+
+ DumpCXComment(Comment);
+ }
+ }
+}
+
+static void PrintCursor(CXCursor Cursor,
+ CommentXMLValidationData *ValidationData) {
CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor);
if (clang_isInvalid(Cursor.kind)) {
CXString ks = clang_getCursorKindSpelling(Cursor.kind);
@@ -197,6 +572,13 @@ static void PrintCursor(CXCursor Cursor) {
unsigned RefNameRangeNr;
CXSourceRange CursorExtent;
CXSourceRange RefNameRange;
+ int AlwaysUnavailable;
+ int AlwaysDeprecated;
+ CXString UnavailableMessage;
+ CXString DeprecatedMessage;
+ CXPlatformAvailability PlatformAvailability[2];
+ int NumPlatformAvailability;
+ int I;
ks = clang_getCursorKindSpelling(Cursor.kind);
string = want_display_name? clang_getCursorDisplayName(Cursor)
@@ -249,6 +631,47 @@ static void PrintCursor(CXCursor Cursor) {
break;
}
+ NumPlatformAvailability
+ = clang_getCursorPlatformAvailability(Cursor,
+ &AlwaysDeprecated,
+ &DeprecatedMessage,
+ &AlwaysUnavailable,
+ &UnavailableMessage,
+ PlatformAvailability, 2);
+ if (AlwaysUnavailable) {
+ printf(" (always unavailable: \"%s\")",
+ clang_getCString(UnavailableMessage));
+ } else if (AlwaysDeprecated) {
+ printf(" (always deprecated: \"%s\")",
+ clang_getCString(DeprecatedMessage));
+ } else {
+ for (I = 0; I != NumPlatformAvailability; ++I) {
+ if (I >= 2)
+ break;
+
+ printf(" (%s", clang_getCString(PlatformAvailability[I].Platform));
+ if (PlatformAvailability[I].Unavailable)
+ printf(", unavailable");
+ else {
+ printVersion(", introduced=", PlatformAvailability[I].Introduced);
+ printVersion(", deprecated=", PlatformAvailability[I].Deprecated);
+ printVersion(", obsoleted=", PlatformAvailability[I].Obsoleted);
+ }
+ if (clang_getCString(PlatformAvailability[I].Message)[0])
+ printf(", message=\"%s\"",
+ clang_getCString(PlatformAvailability[I].Message));
+ printf(")");
+ }
+ }
+ for (I = 0; I != NumPlatformAvailability; ++I) {
+ if (I >= 2)
+ break;
+ clang_disposeCXPlatformAvailability(PlatformAvailability + I);
+ }
+
+ clang_disposeString(DeprecatedMessage);
+ clang_disposeString(UnavailableMessage);
+
if (clang_CXXMethod_isStatic(Cursor))
printf(" (static)");
if (clang_CXXMethod_isVirtual(Cursor))
@@ -336,6 +759,8 @@ static void PrintCursor(CXCursor Cursor) {
if (!clang_equalRanges(CursorExtent, RefNameRange))
PrintRange(RefNameRange, "RefName");
}
+
+ PrintCursorComments(TU, Cursor, ValidationData);
}
}
@@ -458,17 +883,16 @@ void PrintMemoryUsage(CXTranslationUnit TU) {
/* Logic for testing traversal. */
/******************************************************************************/
-static const char *FileCheckPrefix = "CHECK";
-
static void PrintCursorExtent(CXCursor C) {
CXSourceRange extent = clang_getCursorExtent(C);
PrintRange(extent, "Extent");
}
-/* Data used by all of the visitors. */
-typedef struct {
+/* Data used by the visitors. */
+typedef struct {
CXTranslationUnit TU;
enum CXCursorKind *Filter;
+ CommentXMLValidationData ValidationData;
} VisitorData;
@@ -482,7 +906,7 @@ enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor,
clang_getSpellingLocation(Loc, 0, &line, &column, 0);
printf("// %s: %s:%d:%d: ", FileCheckPrefix,
GetCursorSource(Cursor), line, column);
- PrintCursor(Cursor);
+ PrintCursor(Cursor, &Data->ValidationData);
PrintCursorExtent(Cursor);
printf("\n");
return CXChildVisit_Recurse;
@@ -535,7 +959,7 @@ static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor,
} else if (Ref.kind != CXCursor_FunctionDecl) {
printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref),
curLine, curColumn);
- PrintCursor(Ref);
+ PrintCursor(Ref, &Data->ValidationData);
printf("\n");
}
}
@@ -622,7 +1046,7 @@ static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p,
}
if (linkage) {
- PrintCursor(cursor);
+ PrintCursor(cursor, NULL);
printf("linkage=%s\n", linkage);
}
@@ -638,7 +1062,7 @@ static enum CXChildVisitResult PrintTypeKind(CXCursor cursor, CXCursor p,
if (!clang_isInvalid(clang_getCursorKind(cursor))) {
CXType T = clang_getCursorType(cursor);
CXString S = clang_getTypeKindSpelling(T.kind);
- PrintCursor(cursor);
+ PrintCursor(cursor, NULL);
printf(" typekind=%s", clang_getCString(S));
if (clang_isConstQualifiedType(T))
printf(" const");
@@ -698,7 +1122,8 @@ static enum CXChildVisitResult PrintTypeKind(CXCursor cursor, CXCursor p,
static int perform_test_load(CXIndex Idx, CXTranslationUnit TU,
const char *filter, const char *prefix,
CXCursorVisitor Visitor,
- PostVisitTU PV) {
+ PostVisitTU PV,
+ const char *CommentSchemaFile) {
if (prefix)
FileCheckPrefix = prefix;
@@ -729,6 +1154,11 @@ static int perform_test_load(CXIndex Idx, CXTranslationUnit TU,
Data.TU = TU;
Data.Filter = ck;
+ Data.ValidationData.CommentSchemaFile = CommentSchemaFile;
+#ifdef CLANG_HAVE_LIBXML
+ Data.ValidationData.RNGParser = NULL;
+ Data.ValidationData.Schema = NULL;
+#endif
clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data);
}
@@ -760,7 +1190,7 @@ int perform_test_load_tu(const char *file, const char *filter,
return 1;
}
- result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV);
+ result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV, NULL);
clang_disposeIndex(Idx);
return result;
}
@@ -770,6 +1200,7 @@ int perform_test_load_source(int argc, const char **argv,
PostVisitTU PV) {
CXIndex Idx;
CXTranslationUnit TU;
+ const char *CommentSchemaFile;
struct CXUnsavedFile *unsaved_files = 0;
int num_unsaved_files = 0;
int result;
@@ -779,6 +1210,11 @@ int perform_test_load_source(int argc, const char **argv,
!strcmp(filter, "local-display"))? 1 : 0,
/* displayDiagnosics=*/0);
+ if ((CommentSchemaFile = parse_comments_schema(argc, argv))) {
+ argc--;
+ argv++;
+ }
+
if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
clang_disposeIndex(Idx);
return -1;
@@ -796,7 +1232,8 @@ int perform_test_load_source(int argc, const char **argv,
return 1;
}
- result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV);
+ result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV,
+ CommentSchemaFile);
free_remapped_files(unsaved_files, num_unsaved_files);
clang_disposeIndex(Idx);
return result;
@@ -860,7 +1297,7 @@ int perform_test_reparse_source(int argc, const char **argv, int trials,
return -1;
}
- result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV);
+ result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV, NULL);
free_remapped_files(unsaved_files, num_unsaved_files);
clang_disposeIndex(Idx);
@@ -880,7 +1317,7 @@ static void print_cursor_file_scan(CXTranslationUnit TU, CXCursor cursor,
printf("-%s", prefix);
PrintExtent(stdout, start_line, start_col, end_line, end_col);
printf(" ");
- PrintCursor(cursor);
+ PrintCursor(cursor, NULL);
printf("\n");
}
@@ -1104,6 +1541,8 @@ void print_completion_result(CXCompletionResult *completion_result,
unsigned annotationCount;
enum CXCursorKind ParentKind;
CXString ParentName;
+ CXString BriefComment;
+ const char *BriefCommentCString;
fprintf(file, "%s:", clang_getCString(ks));
clang_disposeString(ks);
@@ -1155,6 +1594,14 @@ void print_completion_result(CXCompletionResult *completion_result,
}
clang_disposeString(ParentName);
}
+
+ BriefComment = clang_getCompletionBriefComment(
+ completion_result->CompletionString);
+ BriefCommentCString = clang_getCString(BriefComment);
+ if (BriefCommentCString && *BriefCommentCString != '\0') {
+ fprintf(file, "(brief comment: %s)", BriefCommentCString);
+ }
+ clang_disposeString(BriefComment);
fprintf(file, "\n");
}
@@ -1267,6 +1714,8 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
if (getenv("CINDEXTEST_CODE_COMPLETE_PATTERNS"))
completionOptions |= CXCodeComplete_IncludeCodePatterns;
+ if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS"))
+ completionOptions |= CXCodeComplete_IncludeBriefComments;
if (timing_only)
input += strlen("-code-completion-timing=");
@@ -1465,7 +1914,7 @@ static int inspect_cursor_at(int argc, const char **argv) {
unsigned line, column;
clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0);
printf("%d:%d ", line, column);
- PrintCursor(Cursor);
+ PrintCursor(Cursor, NULL);
PrintCursorExtent(Cursor);
Spelling = clang_getCursorSpelling(Cursor);
cspell = clang_getCString(Spelling);
@@ -1484,6 +1933,9 @@ static int inspect_cursor_at(int argc, const char **argv) {
clang_disposeString(Spelling);
if (clang_Cursor_getObjCSelectorIndex(Cursor) != -1)
printf(" Selector index=%d",clang_Cursor_getObjCSelectorIndex(Cursor));
+ if (clang_Cursor_isDynamicCall(Cursor))
+ printf(" Dynamic-call");
+
if (completionString != NULL) {
printf("\nCompletion string: ");
print_completion_string(completionString, stdout);
@@ -1507,7 +1959,7 @@ static enum CXVisitorResult findFileRefsVisit(void *context,
if (clang_Range_isNull(range))
return CXVisit_Continue;
- PrintCursor(cursor);
+ PrintCursor(cursor, NULL);
PrintRange(range, "");
printf("\n");
return CXVisit_Continue;
@@ -1591,7 +2043,7 @@ static int find_file_refs_at(int argc, const char **argv) {
if (I + 1 == Repeats) {
CXCursorAndRangeVisitor visitor = { 0, findFileRefsVisit };
- PrintCursor(Cursor);
+ PrintCursor(Cursor, NULL);
printf("\n");
clang_findReferencesInFile(Cursor, file, visitor);
free(Locations[Loc].filename);
@@ -1789,7 +2241,7 @@ static void printEntityInfo(const char *cb,
for (i = 0; i != info->numAttributes; ++i) {
const CXIdxAttrInfo *Attr = info->attributes[i];
printf(" <attribute>: ");
- PrintCursor(Attr->cursor);
+ PrintCursor(Attr->cursor, NULL);
}
}
@@ -1797,7 +2249,7 @@ static void printBaseClassInfo(CXClientData client_data,
const CXIdxBaseClassInfo *info) {
printEntityInfo(" <base>", client_data, info->base);
printf(" | cursor: ");
- PrintCursor(info->cursor);
+ PrintCursor(info->cursor, NULL);
printf(" | loc: ");
printCXIndexLoc(info->loc, client_data);
}
@@ -1809,7 +2261,7 @@ static void printProtocolList(const CXIdxObjCProtocolRefListInfo *ProtoInfo,
printEntityInfo(" <protocol>", client_data,
ProtoInfo->protocols[i]->protocol);
printf(" | cursor: ");
- PrintCursor(ProtoInfo->protocols[i]->cursor);
+ PrintCursor(ProtoInfo->protocols[i]->cursor, NULL);
printf(" | loc: ");
printCXIndexLoc(ProtoInfo->protocols[i]->loc, client_data);
printf("\n");
@@ -1899,7 +2351,7 @@ static void index_indexDeclaration(CXClientData client_data,
printEntityInfo("[indexDeclaration]", client_data, info->entityInfo);
printf(" | cursor: ");
- PrintCursor(info->cursor);
+ PrintCursor(info->cursor, NULL);
printf(" | loc: ");
printCXIndexLoc(info->loc, client_data);
printf(" | semantic-container: ");
@@ -1914,7 +2366,7 @@ static void index_indexDeclaration(CXClientData client_data,
for (i = 0; i != info->numAttributes; ++i) {
const CXIdxAttrInfo *Attr = info->attributes[i];
printf(" <attribute>: ");
- PrintCursor(Attr->cursor);
+ PrintCursor(Attr->cursor, NULL);
printf("\n");
}
@@ -1937,7 +2389,7 @@ static void index_indexDeclaration(CXClientData client_data,
printEntityInfo(" <ObjCCategoryInfo>: class", client_data,
CatInfo->objcClass);
printf(" | cursor: ");
- PrintCursor(CatInfo->classCursor);
+ PrintCursor(CatInfo->classCursor, NULL);
printf(" | loc: ");
printCXIndexLoc(CatInfo->classLoc, client_data);
printf("\n");
@@ -1981,7 +2433,7 @@ static void index_indexEntityReference(CXClientData client_data,
const CXIdxEntityRefInfo *info) {
printEntityInfo("[indexEntityReference]", client_data, info->referencedEntity);
printf(" | cursor: ");
- PrintCursor(info->cursor);
+ PrintCursor(info->cursor, NULL);
printf(" | loc: ");
printCXIndexLoc(info->loc, client_data);
printEntityInfo(" | <parent>:", client_data, info->parentEntity);
@@ -2051,7 +2503,6 @@ static int index_file(int argc, const char **argv) {
return 1;
}
idxAction = 0;
- result = 1;
index_data.check_prefix = check_prefix;
index_data.first_check_printed = 0;
@@ -2148,8 +2599,10 @@ int perform_token_annotation(int argc, const char **argv) {
&second_line, &second_column)))
return errorCode;
- if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
+ if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) {
+ free(filename);
return -1;
+ }
CIdx = clang_createIndex(0, 1);
TU = clang_parseTranslationUnit(CIdx, argv[argc - 1],
@@ -2167,8 +2620,10 @@ int perform_token_annotation(int argc, const char **argv) {
}
errorCode = 0;
- if (checkForErrors(TU) != 0)
- return -1;
+ if (checkForErrors(TU) != 0) {
+ errorCode = -1;
+ goto teardown;
+ }
if (getenv("CINDEXTEST_EDITING")) {
for (i = 0; i < 5; ++i) {
@@ -2247,7 +2702,7 @@ int perform_token_annotation(int argc, const char **argv) {
PrintExtent(stdout, start_line, start_column, end_line, end_column);
if (!clang_isInvalid(cursors[i].kind)) {
printf(" ");
- PrintCursor(cursors[i]);
+ PrintCursor(cursors[i], NULL);
}
printf("\n");
}
@@ -2263,6 +2718,89 @@ int perform_token_annotation(int argc, const char **argv) {
return errorCode;
}
+static int
+perform_test_compilation_db(const char *database, int argc, const char **argv) {
+ CXCompilationDatabase db;
+ CXCompileCommands CCmds;
+ CXCompileCommand CCmd;
+ CXCompilationDatabase_Error ec;
+ CXString wd;
+ CXString arg;
+ int errorCode = 0;
+ char *tmp;
+ unsigned len;
+ char *buildDir;
+ int i, j, a, numCmds, numArgs;
+
+ len = strlen(database);
+ tmp = (char *) malloc(len+1);
+ memcpy(tmp, database, len+1);
+ buildDir = dirname(tmp);
+
+ db = clang_CompilationDatabase_fromDirectory(buildDir, &ec);
+
+ if (db) {
+
+ if (ec!=CXCompilationDatabase_NoError) {
+ printf("unexpected error %d code while loading compilation database\n", ec);
+ errorCode = -1;
+ goto cdb_end;
+ }
+
+ for (i=0; i<argc && errorCode==0; ) {
+ if (strcmp(argv[i],"lookup")==0){
+ CCmds = clang_CompilationDatabase_getCompileCommands(db, argv[i+1]);
+
+ if (!CCmds) {
+ printf("file %s not found in compilation db\n", argv[i+1]);
+ errorCode = -1;
+ break;
+ }
+
+ numCmds = clang_CompileCommands_getSize(CCmds);
+
+ if (numCmds==0) {
+ fprintf(stderr, "should not get an empty compileCommand set for file"
+ " '%s'\n", argv[i+1]);
+ errorCode = -1;
+ break;
+ }
+
+ for (j=0; j<numCmds; ++j) {
+ CCmd = clang_CompileCommands_getCommand(CCmds, j);
+
+ wd = clang_CompileCommand_getDirectory(CCmd);
+ printf("workdir:'%s'", clang_getCString(wd));
+ clang_disposeString(wd);
+
+ printf(" cmdline:'");
+ numArgs = clang_CompileCommand_getNumArgs(CCmd);
+ for (a=0; a<numArgs; ++a) {
+ if (a) printf(" ");
+ arg = clang_CompileCommand_getArg(CCmd, a);
+ printf("%s", clang_getCString(arg));
+ clang_disposeString(arg);
+ }
+ printf("'\n");
+ }
+
+ clang_CompileCommands_dispose(CCmds);
+
+ i += 2;
+ }
+ }
+ clang_CompilationDatabase_dispose(db);
+ } else {
+ printf("database loading failed with error code %d.\n", ec);
+ errorCode = -1;
+ }
+
+cdb_end:
+ free(tmp);
+
+ return errorCode;
+}
+
/******************************************************************************/
/* USR printing. */
/******************************************************************************/
@@ -2703,6 +3241,8 @@ static void print_usage(void) {
" c-index-test -print-usr-file <file>\n"
" c-index-test -write-pch <file> <compiler arguments>\n");
fprintf(stderr,
+ " c-index-test -compilation-db [lookup <filename>] database\n");
+ fprintf(stderr,
" c-index-test -read-diagnostics <file>\n\n");
fprintf(stderr,
" <symbol filter> values:\n%s",
@@ -2788,7 +3328,9 @@ int cindextest_main(int argc, const char **argv) {
return print_usrs_file(argv[2]);
else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0)
return write_pch_file(argv[2], argc - 3, argv + 3);
-
+ else if (argc > 2 && strcmp(argv[1], "-compilation-db") == 0)
+ return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2);
+
print_usage();
return 1;
}
@@ -2815,6 +3357,10 @@ void thread_runner(void *client_data_v) {
int main(int argc, const char **argv) {
thread_info client_data;
+#ifdef CLANG_HAVE_LIBXML
+ LIBXML_TEST_VERSION
+#endif
+
if (getenv("CINDEXTEST_NOTHREADS"))
return cindextest_main(argc, argv);
diff --git a/tools/clang-check/CMakeLists.txt b/tools/clang-check/CMakeLists.txt
index 851d6cd..85e229f 100644
--- a/tools/clang-check/CMakeLists.txt
+++ b/tools/clang-check/CMakeLists.txt
@@ -1,5 +1,18 @@
-set(LLVM_USED_LIBS clangTooling clangBasic)
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
add_clang_executable(clang-check
ClangCheck.cpp
)
+
+target_link_libraries(clang-check
+ clangTooling
+ clangBasic
+ )
+
+install(TARGETS clang-check
+ RUNTIME DESTINATION bin)
diff --git a/tools/clang-check/ClangCheck.cpp b/tools/clang-check/ClangCheck.cpp
index d68e282..9e58077 100644
--- a/tools/clang-check/ClangCheck.cpp
+++ b/tools/clang-check/ClangCheck.cpp
@@ -1,4 +1,4 @@
-//===- examples/Tooling/ClangCheck.cpp - Clang check tool -----------------===//
+//===- tools/clang-check/ClangCheck.cpp - Clang check tool ----------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -10,57 +10,79 @@
// This file implements a clang-check tool that runs the
// clang::SyntaxOnlyAction over a number of translation units.
//
-// Usage:
-// clang-check <cmake-output-dir> <file1> <file2> ...
-//
-// Where <cmake-output-dir> is a CMake build directory in which a file named
-// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
-// CMake to get this output).
-//
-// <file1> ... specify the paths of files in the CMake source tree. This path
-// is looked up in the compile command database. If the path of a file is
-// absolute, it needs to point into CMake's source tree. If the path is
-// relative, the current working directory needs to be in the CMake source
-// tree and the file must be in a subdirectory of the current working
-// directory. "./" prefixes in the relative files will be automatically
-// removed, but the rest of a relative path must be a suffix of a path in
-// the compile command line database.
-//
-// For example, to use clang-check on all files in a subtree of the source
-// tree, use:
-//
-// /path/in/subtree $ find . -name '*.cpp'| xargs clang-check /path/to/source
+// This tool uses the Clang Tooling infrastructure, see
+// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+// for details on setting it up with LLVM source tree.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/CommandLine.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Driver/OptTable.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/FrontendActions.h"
-#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/CommandLineClangTool.h"
#include "clang/Tooling/Tooling.h"
+using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
-cl::opt<std::string> BuildPath(
- cl::Positional,
- cl::desc("<build-path>"));
+static const char *MoreHelpText =
+ "\tFor example, to run clang-check on all files in a subtree of the\n"
+ "\tsource tree, use:\n"
+ "\n"
+ "\t find path/in/subtree -name '*.cpp'|xargs clang-check\n"
+ "\n"
+ "\tor using a specific build path:\n"
+ "\n"
+ "\t find path/in/subtree -name '*.cpp'|xargs clang-check -p build/path\n"
+ "\n"
+ "\tNote, that path/in/subtree and current directory should follow the\n"
+ "\trules described above.\n"
+ "\n";
-cl::list<std::string> SourcePaths(
- cl::Positional,
- cl::desc("<source0> [... <sourceN>]"),
- cl::OneOrMore);
+namespace {
+class ActionFactory {
+public:
+ ActionFactory()
+ : Options(createDriverOptTable()),
+ ASTDump(
+ "ast-dump",
+ cl::desc(Options->getOptionHelpText(options::OPT_ast_dump))),
+ ASTList(
+ "ast-list",
+ cl::desc(Options->getOptionHelpText(options::OPT_ast_list))),
+ ASTPrint(
+ "ast-print",
+ cl::desc(Options->getOptionHelpText(options::OPT_ast_print))),
+ ASTDumpFilter(
+ "ast-dump-filter",
+ cl::desc(Options->getOptionHelpText(options::OPT_ast_dump_filter))) {}
-int main(int argc, const char **argv) {
- llvm::OwningPtr<CompilationDatabase> Compilations(
- FixedCompilationDatabase::loadFromCommandLine(argc, argv));
- cl::ParseCommandLineOptions(argc, argv);
- if (!Compilations) {
- std::string ErrorMessage;
- Compilations.reset(CompilationDatabase::loadFromDirectory(BuildPath,
- ErrorMessage));
- if (!Compilations)
- llvm::report_fatal_error(ErrorMessage);
+ clang::ASTConsumer *newASTConsumer() {
+ if (ASTList)
+ return clang::CreateASTDeclNodeLister();
+ if (ASTDump)
+ return clang::CreateASTDumper(ASTDumpFilter);
+ if (ASTPrint)
+ return clang::CreateASTPrinter(&llvm::outs(), ASTDumpFilter);
+ return new clang::ASTConsumer();
}
- ClangTool Tool(*Compilations, SourcePaths);
- return Tool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>());
+private:
+ OwningPtr<OptTable> Options;
+ cl::opt<bool> ASTDump;
+ cl::opt<bool> ASTList;
+ cl::opt<bool> ASTPrint;
+ cl::opt<std::string> ASTDumpFilter;
+};
+}
+
+int main(int argc, const char **argv) {
+ ActionFactory Factory;
+ CommandLineClangTool Tool;
+ cl::extrahelp MoreHelp(MoreHelpText);
+ Tool.initialize(argc, argv);
+ return Tool.run(newFrontendActionFactory(&Factory));
}
diff --git a/tools/clang-check/Makefile b/tools/clang-check/Makefile
index 49b1ada..6775c65 100644
--- a/tools/clang-check/Makefile
+++ b/tools/clang-check/Makefile
@@ -10,12 +10,12 @@
CLANG_LEVEL := ../..
TOOLNAME = clang-check
-NO_INSTALL = 1
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \
clangTooling.a clangParse.a clangSema.a clangAnalysis.a \
clangEdit.a clangAST.a clangLex.a clangBasic.a
diff --git a/tools/diag-build/diag-build.sh b/tools/diag-build/diag-build.sh
new file mode 100755
index 0000000..4fef8fb
--- /dev/null
+++ b/tools/diag-build/diag-build.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+
+print_usage () {
+ echo 'Usage: diag-build.sh [-v] xcodebuild [flags]'
+ echo ' diag-build.sh [-v] make [flags]'
+ echo ' diag-build.sh [-v] <other build command>'
+ echo
+ echo 'diagtool must be in your PATH'
+ echo 'If using an alternate build command, you must ensure that'
+ echo 'the compiler used matches the CC environment variable.'
+}
+
+# Mac OS X's BSD sed uses -E for extended regular expressions,
+# but GNU sed uses -r. Find out which one this system accepts.
+EXTENDED_SED_FLAG='-E'
+echo -n | sed $EXTENDED_SED_FLAG 's/a/b/' 2>/dev/null || EXTENDED_SED_FLAG='-r'
+
+if [[ "$1" == "-v" ]]; then
+ verbose=$1
+ shift
+fi
+
+guessing_cc=0
+
+if [[ -z "$CC" ]]; then
+ guessing_cc=1
+ if [[ -x $(dirname $0)/clang ]]; then
+ CC=$(dirname $0)/clang
+ elif [[ ! -z $(which clang) ]]; then
+ CC=$(which clang)
+ else
+ echo -n 'Error: could not find an appropriate compiler'
+ echo ' to generate build commands.' 1>&2
+ echo 'Use the CC environment variable to set one explicitly.' 1>&2
+ exit 1
+ fi
+fi
+
+if [[ -z "$CXX" ]]; then
+ if [[ -x $(dirname $0)/clang++ ]]; then
+ CXX=$(dirname $0)/clang++
+ elif [[ ! -z $(which clang++) ]]; then
+ CXX=$(which clang++)
+ else
+ CXX=$CC
+ fi
+fi
+
+diagtool=$(which diagtool)
+if [[ -z "$diagtool" ]]; then
+ if [[ -x $(dirname $0)/diagtool ]]; then
+ diagtool=$(dirname $0)/diagtool
+ else
+ echo 'Error: could not find diagtool.' 1>&2
+ exit 1
+ fi
+fi
+
+
+tool=$1
+shift
+
+if [[ -z "$tool" ]]; then
+ print_usage
+ exit 1
+elif [[ "$tool" == "xcodebuild" ]]; then
+ dry_run='-dry-run'
+ set_compiler="CC='$CC' CXX='$CXX'"
+elif [[ "$tool" == "make" ]]; then
+ dry_run='-n'
+ set_compiler="CC='$CC' CXX='$CXX'"
+else
+ echo "Warning: unknown build system '$tool'" 1>&2
+ if [[ $guessing_cc -eq 1 ]]; then
+ # FIXME: We really only need $CC /or/ $CXX
+ echo 'Error: $CC must be set for other build systems' 1>&2
+ exit 1
+ fi
+fi
+
+escape () {
+ echo $@ | sed 's:[]:\\|/.+*?^$(){}[]:\\&:g'
+}
+
+escCC=$(escape $CC)
+escCXX=$(escape $CXX)
+command=$(
+ eval $tool $dry_run $set_compiler $@ 2>/dev/null |
+ # Remove "if" early on so we can find the right command line.
+ sed $EXTENDED_SED_FLAG "s:^[[:blank:]]*if[[:blank:]]{1,}::g" |
+ # Combine lines with trailing backslashes
+ sed -e :a -e '/\\$/N; s/\\\n//; ta' |
+ grep -E "^[[:blank:]]*($escCC|$escCXX)" |
+ head -n1 |
+ sed $EXTENDED_SED_FLAG "s:($escCC|$escCXX):${diagtool//:/\\:} show-enabled:g"
+)
+
+if [[ -z "$command" ]]; then
+ echo 'Error: could not find any build commands.' 1>&2
+ if [[ "$tool" != "xcodebuild" ]]; then
+ # xcodebuild always echoes the compile commands on their own line,
+ # but other tools give no such guarantees.
+ echo -n 'This may occur if your build system embeds the call to ' 2>&1
+ echo -n 'the compiler in a larger expression. ' 2>&1
+ fi
+ exit 2
+fi
+
+# Chop off trailing '&&', '||', and ';'
+command=${command%%&&*}
+command=${command%%||*}
+command=${command%%;*}
+
+[[ -n "$verbose" ]] && echo $command
+eval $command
diff --git a/tools/diagtool/CMakeLists.txt b/tools/diagtool/CMakeLists.txt
index f1fd9de..a107cbd 100644
--- a/tools/diagtool/CMakeLists.txt
+++ b/tools/diagtool/CMakeLists.txt
@@ -1,24 +1,32 @@
-set( LLVM_LINK_COMPONENTS
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
support
- )
-
-set( LLVM_USED_LIBS
- clangBasic
- clangLex
- clangSema
+ mc
)
add_clang_executable(diagtool
diagtool_main.cpp
DiagTool.cpp
+ DiagnosticNames.cpp
ListWarnings.cpp
+ ShowEnabledWarnings.cpp
+ TreeView.cpp
)
+add_dependencies(diagtool
+ ClangDiagnosticIndexName
+ )
+
+target_link_libraries(diagtool
+ clangBasic
+ clangLex
+ clangSema
+ clangFrontend
+ )
+
if(UNIX)
set(CLANGXX_LINK_OR_COPY create_symlink)
else()
set(CLANGXX_LINK_OR_COPY copy)
endif()
-
-install(TARGETS diagtool
- RUNTIME DESTINATION bin)
diff --git a/tools/diagtool/DiagnosticNames.cpp b/tools/diagtool/DiagnosticNames.cpp
new file mode 100644
index 0000000..31f3524
--- /dev/null
+++ b/tools/diagtool/DiagnosticNames.cpp
@@ -0,0 +1,78 @@
+//===- DiagnosticNames.cpp - Defines a table of all builtin diagnostics ----==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagnosticNames.h"
+#include "clang/Basic/AllDiagnostics.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+using namespace diagtool;
+
+static const DiagnosticRecord BuiltinDiagnosticsByName[] = {
+#define DIAG_NAME_INDEX(ENUM) { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) },
+#include "clang/Basic/DiagnosticIndexName.inc"
+#undef DIAG_NAME_INDEX
+};
+
+llvm::ArrayRef<DiagnosticRecord> diagtool::getBuiltinDiagnosticsByName() {
+ return llvm::makeArrayRef(BuiltinDiagnosticsByName);
+}
+
+
+// FIXME: Is it worth having two tables, especially when this one can get
+// out of sync easily?
+static const DiagnosticRecord BuiltinDiagnosticsByID[] = {
+#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP, \
+ SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER, \
+ CATEGORY) \
+ { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) },
+#include "clang/Basic/DiagnosticCommonKinds.inc"
+#include "clang/Basic/DiagnosticDriverKinds.inc"
+#include "clang/Basic/DiagnosticFrontendKinds.inc"
+#include "clang/Basic/DiagnosticSerializationKinds.inc"
+#include "clang/Basic/DiagnosticLexKinds.inc"
+#include "clang/Basic/DiagnosticParseKinds.inc"
+#include "clang/Basic/DiagnosticASTKinds.inc"
+#include "clang/Basic/DiagnosticCommentKinds.inc"
+#include "clang/Basic/DiagnosticSemaKinds.inc"
+#include "clang/Basic/DiagnosticAnalysisKinds.inc"
+#undef DIAG
+};
+
+static bool orderByID(const DiagnosticRecord &Left,
+ const DiagnosticRecord &Right) {
+ return Left.DiagID < Right.DiagID;
+}
+
+const DiagnosticRecord &diagtool::getDiagnosticForID(short DiagID) {
+ DiagnosticRecord Key = {0, DiagID, 0};
+
+ const DiagnosticRecord *Result =
+ std::lower_bound(BuiltinDiagnosticsByID,
+ llvm::array_endof(BuiltinDiagnosticsByID),
+ Key, orderByID);
+ assert(Result && "diagnostic not found; table may be out of date");
+ return *Result;
+}
+
+
+#define GET_DIAG_ARRAYS
+#include "clang/Basic/DiagnosticGroups.inc"
+#undef GET_DIAG_ARRAYS
+
+// Second the table of options, sorted by name for fast binary lookup.
+static const GroupRecord OptionTable[] = {
+#define GET_DIAG_TABLE
+#include "clang/Basic/DiagnosticGroups.inc"
+#undef GET_DIAG_TABLE
+};
+
+llvm::ArrayRef<GroupRecord> diagtool::getDiagnosticGroups() {
+ return llvm::makeArrayRef(OptionTable);
+}
diff --git a/tools/diagtool/DiagnosticNames.h b/tools/diagtool/DiagnosticNames.h
new file mode 100644
index 0000000..9a73158
--- /dev/null
+++ b/tools/diagtool/DiagnosticNames.h
@@ -0,0 +1,128 @@
+//===- DiagnosticNames.h - Defines a table of all builtin diagnostics ------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DataTypes.h"
+
+namespace diagtool {
+
+ struct DiagnosticRecord {
+ const char *NameStr;
+ short DiagID;
+ uint8_t NameLen;
+
+ llvm::StringRef getName() const {
+ return llvm::StringRef(NameStr, NameLen);
+ }
+
+ bool operator<(const DiagnosticRecord &Other) const {
+ return getName() < Other.getName();
+ }
+ };
+
+ /// \brief Get every diagnostic in the system, sorted by name.
+ llvm::ArrayRef<DiagnosticRecord> getBuiltinDiagnosticsByName();
+
+ /// \brief Get a diagnostic by its ID.
+ const DiagnosticRecord &getDiagnosticForID(short DiagID);
+
+
+ struct GroupRecord {
+ // Be safe with the size of 'NameLen' because we don't statically check if
+ // the size will fit in the field; the struct size won't decrease with a
+ // shorter type anyway.
+ size_t NameLen;
+ const char *NameStr;
+ const short *Members;
+ const short *SubGroups;
+
+ llvm::StringRef getName() const {
+ return llvm::StringRef(NameStr, NameLen);
+ }
+
+ template<typename RecordType>
+ class group_iterator {
+ const short *CurrentID;
+
+ friend struct GroupRecord;
+ group_iterator(const short *Start) : CurrentID(Start) {
+ if (CurrentID && *CurrentID == -1)
+ CurrentID = 0;
+ }
+
+ public:
+ typedef RecordType value_type;
+ typedef const value_type & reference;
+ typedef const value_type * pointer;
+ typedef std::forward_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+
+ inline reference operator*() const;
+ inline pointer operator->() const {
+ return &operator*();
+ }
+
+ inline short getID() const {
+ return *CurrentID;
+ }
+
+ group_iterator &operator++() {
+ ++CurrentID;
+ if (*CurrentID == -1)
+ CurrentID = 0;
+ return *this;
+ }
+
+ bool operator==(group_iterator &Other) const {
+ return CurrentID == Other.CurrentID;
+ }
+
+ bool operator!=(group_iterator &Other) const {
+ return CurrentID != Other.CurrentID;
+ }
+ };
+
+ typedef group_iterator<GroupRecord> subgroup_iterator;
+ subgroup_iterator subgroup_begin() const {
+ return SubGroups;
+ }
+ subgroup_iterator subgroup_end() const {
+ return 0;
+ }
+
+ typedef group_iterator<DiagnosticRecord> diagnostics_iterator;
+ diagnostics_iterator diagnostics_begin() const {
+ return Members;
+ }
+ diagnostics_iterator diagnostics_end() const {
+ return 0;
+ }
+
+ bool operator<(const GroupRecord &Other) const {
+ return getName() < Other.getName();
+ }
+ };
+
+ /// \brief Get every diagnostic group in the system, sorted by name.
+ llvm::ArrayRef<GroupRecord> getDiagnosticGroups();
+
+ template<>
+ inline GroupRecord::subgroup_iterator::reference
+ GroupRecord::subgroup_iterator::operator*() const {
+ return getDiagnosticGroups()[*CurrentID];
+ }
+
+ template<>
+ inline GroupRecord::diagnostics_iterator::reference
+ GroupRecord::diagnostics_iterator::operator*() const {
+ return getDiagnosticForID(*CurrentID);
+ }
+} // end namespace diagtool
+
diff --git a/tools/diagtool/ListWarnings.cpp b/tools/diagtool/ListWarnings.cpp
index 2bbeca8..d554a2e 100644
--- a/tools/diagtool/ListWarnings.cpp
+++ b/tools/diagtool/ListWarnings.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "DiagTool.h"
+#include "DiagnosticNames.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/Support/Format.h"
#include "llvm/ADT/StringMap.h"
@@ -24,28 +25,7 @@ DEF_DIAGTOOL("list-warnings",
ListWarnings)
using namespace clang;
-
-namespace {
-struct StaticDiagNameIndexRec {
- const char *NameStr;
- unsigned short DiagID;
- uint8_t NameLen;
-
- StringRef getName() const {
- return StringRef(NameStr, NameLen);
- }
-};
-}
-
-static const StaticDiagNameIndexRec StaticDiagNameIndex[] = {
-#define DIAG_NAME_INDEX(ENUM) { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) },
-#include "clang/Basic/DiagnosticIndexName.inc"
-#undef DIAG_NAME_INDEX
- { 0, 0, 0 }
-};
-
-static const unsigned StaticDiagNameIndexSize =
- sizeof(StaticDiagNameIndex)/sizeof(StaticDiagNameIndex[0])-1;
+using namespace diagtool;
namespace {
struct Entry {
@@ -73,9 +53,11 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
std::vector<Entry> Flagged, Unflagged;
llvm::StringMap<std::vector<unsigned> > flagHistogram;
- for (const StaticDiagNameIndexRec *di = StaticDiagNameIndex, *de = StaticDiagNameIndex + StaticDiagNameIndexSize;
+ ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
+
+ for (ArrayRef<DiagnosticRecord>::iterator di = AllDiagnostics.begin(),
+ de = AllDiagnostics.end();
di != de; ++di) {
-
unsigned diagID = di->DiagID;
if (DiagnosticIDs::isBuiltinNote(diagID))
@@ -95,9 +77,6 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
}
}
- std::sort(Flagged.begin(), Flagged.end());
- std::sort(Unflagged.begin(), Unflagged.end());
-
out << "Warnings with flags (" << Flagged.size() << "):\n";
printEntries(Flagged, out);
@@ -119,6 +98,10 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
out << " Average number of diagnostics per flag: "
<< llvm::format("%.4g", avgDiagsPerFlag) << '\n';
+ out << " Number in -Wpedantic (not covered by other -W flags): "
+ << flagHistogram.GetOrCreateValue("pedantic").getValue().size()
+ << '\n';
+
out << '\n';
return 0;
diff --git a/tools/diagtool/Makefile b/tools/diagtool/Makefile
index 6e3bcfc..b629712 100644
--- a/tools/diagtool/Makefile
+++ b/tools/diagtool/Makefile
@@ -1,4 +1,4 @@
-##===- tools/driver/Makefile -------------------------------*- Makefile -*-===##
+##===- tools/diagtool/Makefile -----------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
@@ -16,9 +16,11 @@ TOOL_NO_EXPORTS := 1
# Don't install this.
NO_INSTALL = 1
-LINK_COMPONENTS := support
-
-USEDLIBS = clangBasic.a
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
+USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \
+ clangSema.a clangAnalysis.a clangEdit.a clangAST.a clangLex.a \
+ clangBasic.a
include $(CLANG_LEVEL)/Makefile
diff --git a/tools/diagtool/ShowEnabledWarnings.cpp b/tools/diagtool/ShowEnabledWarnings.cpp
new file mode 100644
index 0000000..7162451
--- /dev/null
+++ b/tools/diagtool/ShowEnabledWarnings.cpp
@@ -0,0 +1,148 @@
+//===- ShowEnabledWarnings - diagtool tool for printing enabled flags -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagTool.h"
+#include "DiagnosticNames.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Frontend/Utils.h"
+#include "llvm/Support/TargetSelect.h"
+
+DEF_DIAGTOOL("show-enabled",
+ "Show which warnings are enabled for a given command line",
+ ShowEnabledWarnings)
+
+using namespace clang;
+using namespace diagtool;
+
+namespace {
+ struct PrettyDiag {
+ StringRef Name;
+ StringRef Flag;
+ DiagnosticsEngine::Level Level;
+
+ PrettyDiag(StringRef name, StringRef flag, DiagnosticsEngine::Level level)
+ : Name(name), Flag(flag), Level(level) {}
+
+ bool operator<(const PrettyDiag &x) const { return Name < x.Name; }
+ };
+}
+
+static void printUsage() {
+ llvm::errs() << "Usage: diagtool show-enabled [<flags>] <single-input.c>\n";
+}
+
+static char getCharForLevel(DiagnosticsEngine::Level Level) {
+ switch (Level) {
+ case DiagnosticsEngine::Ignored: return ' ';
+ case DiagnosticsEngine::Note: return '-';
+ case DiagnosticsEngine::Warning: return 'W';
+ case DiagnosticsEngine::Error: return 'E';
+ case DiagnosticsEngine::Fatal: return 'F';
+ }
+
+ llvm_unreachable("Unknown diagnostic level");
+}
+
+static IntrusiveRefCntPtr<DiagnosticsEngine>
+createDiagnostics(unsigned int argc, char **argv) {
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs());
+
+ // Buffer diagnostics from argument parsing so that we can output them using a
+ // well formed diagnostic object.
+ TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
+ IntrusiveRefCntPtr<DiagnosticsEngine> InterimDiags(
+ new DiagnosticsEngine(DiagIDs, DiagsBuffer));
+
+ // Try to build a CompilerInvocation.
+ OwningPtr<CompilerInvocation> Invocation(
+ createInvocationFromCommandLine(ArrayRef<const char *>(argv, argc),
+ InterimDiags));
+ if (!Invocation)
+ return NULL;
+
+ // Build the diagnostics parser
+ IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags =
+ CompilerInstance::createDiagnostics(Invocation->getDiagnosticOpts(),
+ argc, argv);
+ if (!FinalDiags)
+ return NULL;
+
+ // Flush any errors created when initializing everything. This could happen
+ // for invalid command lines, which will probably give non-sensical results.
+ DiagsBuffer->FlushDiagnostics(*FinalDiags);
+
+ return FinalDiags;
+}
+
+int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
+ // First check our one flag (--levels).
+ bool ShouldShowLevels = true;
+ if (argc > 0) {
+ StringRef FirstArg(*argv);
+ if (FirstArg.equals("--no-levels")) {
+ ShouldShowLevels = false;
+ --argc;
+ ++argv;
+ } else if (FirstArg.equals("--levels")) {
+ ShouldShowLevels = true;
+ --argc;
+ ++argv;
+ }
+ }
+
+ // Create the diagnostic engine.
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv);
+ if (!Diags) {
+ printUsage();
+ return EXIT_FAILURE;
+ }
+
+ // Now we have our diagnostics. Iterate through EVERY diagnostic and see
+ // which ones are turned on.
+ // FIXME: It would be very nice to print which flags are turning on which
+ // diagnostics, but this can be done with a diff.
+ ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
+ std::vector<PrettyDiag> Active;
+
+ for (ArrayRef<DiagnosticRecord>::iterator I = AllDiagnostics.begin(),
+ E = AllDiagnostics.end();
+ I != E; ++I) {
+ unsigned DiagID = I->DiagID;
+
+ if (DiagnosticIDs::isBuiltinNote(DiagID))
+ continue;
+
+ if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID))
+ continue;
+
+ DiagnosticsEngine::Level DiagLevel =
+ Diags->getDiagnosticLevel(DiagID, SourceLocation());
+ if (DiagLevel == DiagnosticsEngine::Ignored)
+ continue;
+
+ StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID);
+ Active.push_back(PrettyDiag(I->getName(), WarningOpt, DiagLevel));
+ }
+
+ // Print them all out.
+ for (std::vector<PrettyDiag>::const_iterator I = Active.begin(),
+ E = Active.end(); I != E; ++I) {
+ if (ShouldShowLevels)
+ Out << getCharForLevel(I->Level) << " ";
+ Out << I->Name;
+ if (!I->Flag.empty())
+ Out << " [-W" << I->Flag << "]";
+ Out << '\n';
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/diagtool/TreeView.cpp b/tools/diagtool/TreeView.cpp
new file mode 100644
index 0000000..9db2c26
--- /dev/null
+++ b/tools/diagtool/TreeView.cpp
@@ -0,0 +1,135 @@
+//===- TreeView.cpp - diagtool tool for printing warning flags ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This diagnostic tool
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagTool.h"
+#include "DiagnosticNames.h"
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/Support/Format.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/Basic/AllDiagnostics.h"
+
+DEF_DIAGTOOL("tree",
+ "Show warning flags in a tree view",
+ TreeView)
+
+using namespace clang;
+using namespace diagtool;
+
+static void printUsage() {
+ llvm::errs() << "Usage: diagtool tree [--flags-only] [<diagnostic-group>]\n";
+}
+
+static void printGroup(llvm::raw_ostream &out, const GroupRecord &Group,
+ bool FlagsOnly, unsigned Indent = 0) {
+ out.indent(Indent * 2);
+ out << "-W" << Group.getName() << "\n";
+
+ ++Indent;
+ for (GroupRecord::subgroup_iterator I = Group.subgroup_begin(),
+ E = Group.subgroup_end();
+ I != E; ++I) {
+ printGroup(out, *I, FlagsOnly, Indent);
+ }
+
+ if (!FlagsOnly) {
+ for (GroupRecord::diagnostics_iterator I = Group.diagnostics_begin(),
+ E = Group.diagnostics_end();
+ I != E; ++I) {
+ out.indent(Indent * 2);
+ out << I->getName() << "\n";
+ }
+ }
+}
+
+static int showGroup(llvm::raw_ostream &out, StringRef RootGroup,
+ bool FlagsOnly) {
+ ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups();
+
+ GroupRecord Key = { RootGroup.size(), RootGroup.data(), 0, 0 };
+ const GroupRecord *Found =
+ std::lower_bound(AllGroups.begin(), AllGroups.end(), Key);
+
+ if (Found == AllGroups.end() || Found->getName() != RootGroup) {
+ llvm::errs() << "No such diagnostic group exists\n";
+ return 1;
+ }
+
+ printGroup(out, *Found, FlagsOnly);
+
+ return 0;
+}
+
+static int showAll(llvm::raw_ostream &out, bool FlagsOnly) {
+ ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups();
+ llvm::DenseSet<unsigned> NonRootGroupIDs;
+
+ for (ArrayRef<GroupRecord>::iterator I = AllGroups.begin(),
+ E = AllGroups.end();
+ I != E; ++I) {
+ for (GroupRecord::subgroup_iterator SI = I->subgroup_begin(),
+ SE = I->subgroup_end();
+ SI != SE; ++SI) {
+ NonRootGroupIDs.insert((unsigned)SI.getID());
+ }
+ }
+
+ assert(NonRootGroupIDs.size() < AllGroups.size());
+
+ for (unsigned i = 0, e = AllGroups.size(); i != e; ++i) {
+ if (!NonRootGroupIDs.count(i))
+ printGroup(out, AllGroups[i], FlagsOnly);
+ }
+
+ return 0;
+}
+
+int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
+ // First check our one flag (--flags-only).
+ bool FlagsOnly = false;
+ if (argc > 0) {
+ StringRef FirstArg(*argv);
+ if (FirstArg.equals("--flags-only")) {
+ FlagsOnly = true;
+ --argc;
+ ++argv;
+ }
+ }
+
+ bool ShowAll = false;
+ StringRef RootGroup;
+
+ switch (argc) {
+ case 0:
+ ShowAll = true;
+ break;
+ case 1:
+ RootGroup = argv[0];
+ if (RootGroup.startswith("-W"))
+ RootGroup = RootGroup.substr(2);
+ if (RootGroup == "everything")
+ ShowAll = true;
+ // FIXME: Handle other special warning flags, like -pedantic.
+ break;
+ default:
+ printUsage();
+ return -1;
+ }
+
+ if (ShowAll)
+ return showAll(out, FlagsOnly);
+
+ return showGroup(out, RootGroup, FlagsOnly);
+}
+
diff --git a/tools/diagtool/diagtool_main.cpp b/tools/diagtool/diagtool_main.cpp
index e34f0dc..4eef54d 100644
--- a/tools/diagtool/diagtool_main.cpp
+++ b/tools/diagtool/diagtool_main.cpp
@@ -18,7 +18,7 @@ using namespace diagtool;
int main(int argc, char *argv[]) {
if (argc > 1)
if (DiagTool *tool = diagTools->getTool(argv[1]))
- return tool->run(argc - 1, &argv[2], llvm::errs());
+ return tool->run(argc - 2, &argv[2], llvm::outs());
llvm::errs() << "usage: diagtool <command> [<args>]\n\n";
diagTools->printCommands(llvm::errs());
diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt
index ae49ac1..71d7766 100644
--- a/tools/driver/CMakeLists.txt
+++ b/tools/driver/CMakeLists.txt
@@ -1,4 +1,22 @@
-set( LLVM_USED_LIBS
+set( LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ bitreader
+ bitwriter
+ codegen
+ instrumentation
+ ipo
+ linker
+ selectiondag
+ )
+
+add_clang_executable(clang
+ driver.cpp
+ cc1_main.cpp
+ cc1as_main.cpp
+ )
+
+target_link_libraries(clang
clangFrontendTool
clangAST
clangAnalysis
@@ -19,26 +37,10 @@ set( LLVM_USED_LIBS
clangStaticAnalyzerCore
)
-set( LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
- asmparser
- bitreader
- bitwriter
- codegen
- instrumentation
- ipo
- linker
- selectiondag
- )
-
-add_clang_executable(clang
- driver.cpp
- cc1_main.cpp
- cc1as_main.cpp
- )
-
set_target_properties(clang PROPERTIES VERSION ${CLANG_EXECUTABLE_VERSION})
+add_dependencies(clang clang-headers)
+
if(UNIX)
set(CLANGXX_LINK_OR_COPY create_symlink)
# Create a relative symlink
diff --git a/tools/driver/cc1_main.cpp b/tools/driver/cc1_main.cpp
index a211090..f8e8a6b 100644
--- a/tools/driver/cc1_main.cpp
+++ b/tools/driver/cc1_main.cpp
@@ -15,7 +15,7 @@
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
-#include "clang/Driver/CC1Options.h"
+#include "clang/Driver/Options.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/OptTable.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -58,7 +58,7 @@ static int cc1_test(DiagnosticsEngine &Diags,
llvm::errs() << "\n";
// Parse the arguments.
- OptTable *Opts = createCC1OptTable();
+ OptTable *Opts = createDriverOptTable();
unsigned MissingArgIndex, MissingArgCount;
InputArgList *Args = Opts->ParseArgs(ArgBegin, ArgEnd,
MissingArgIndex, MissingArgCount);
diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp
index 508d6da..7dd54bd 100644
--- a/tools/driver/cc1as_main.cpp
+++ b/tools/driver/cc1as_main.cpp
@@ -328,7 +328,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
MCCodeEmitter *CE = 0;
MCAsmBackend *MAB = 0;
if (Opts.ShowEncoding) {
- CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx);
+ CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx);
MAB = TheTarget->createMCAsmBackend(Opts.Triple);
}
Str.reset(TheTarget->createAsmStreamer(Ctx, *Out, /*asmverbose*/true,
@@ -342,7 +342,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
} else {
assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
"Invalid file type!");
- MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx);
+ MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx);
MCAsmBackend *MAB = TheTarget->createMCAsmBackend(Opts.Triple);
Str.reset(TheTarget->createMCObjectStreamer(Opts.Triple, Ctx, *MAB, *Out,
CE, Opts.RelaxAll,
diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp
index 8c05fff..12a9329 100644
--- a/tools/driver/driver.cpp
+++ b/tools/driver/driver.cpp
@@ -13,7 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Driver/ArgList.h"
-#include "clang/Driver/CC1Options.h"
+#include "clang/Driver/Options.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Option.h"
@@ -378,7 +378,7 @@ int main(int argc_, const char **argv_) {
DiagnosticOptions DiagOpts;
{
// Note that ParseDiagnosticArgs() uses the cc1 option table.
- OwningPtr<OptTable> CC1Opts(createCC1OptTable());
+ OwningPtr<OptTable> CC1Opts(createDriverOptTable());
unsigned MissingArgIndex, MissingArgCount;
OwningPtr<InputArgList> Args(CC1Opts->ParseArgs(argv.begin()+1, argv.end(),
MissingArgIndex, MissingArgCount));
@@ -475,6 +475,10 @@ int main(int argc_, const char **argv_) {
if (C.get())
Res = TheDriver.ExecuteCompilation(*C, FailingCommand);
+ // Force a crash to test the diagnostics.
+ if(::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"))
+ Res = -1;
+
// If result status is < 0, then the driver command signalled an error.
// In this case, generate additional diagnostic information if possible.
if (Res < 0)
@@ -486,5 +490,13 @@ int main(int argc_, const char **argv_) {
llvm::llvm_shutdown();
+#ifdef _WIN32
+ // Exit status should not be negative on Win32, unless abnormal termination.
+ // Once abnormal termiation was caught, negative status should not be
+ // propagated.
+ if (Res < 0)
+ Res = 1;
+#endif
+
return Res;
}
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 605cc8b..bd27b4c 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "CIndexer.h"
+#include "CXComment.h"
#include "CXCursor.h"
#include "CXTranslationUnit.h"
#include "CXString.h"
@@ -61,6 +62,7 @@ CXTranslationUnit cxtu::MakeCXTranslationUnit(CIndexer *CIdx, ASTUnit *TU) {
D->TUData = TU;
D->StringPool = createCXStringPool();
D->Diagnostics = 0;
+ D->OverridenCursorsPool = createOverridenCXCursorsPool();
return D;
}
@@ -1067,7 +1069,8 @@ bool CursorVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
bool CursorVisitor::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *PD) {
if (ObjCIvarDecl *Ivar = PD->getPropertyIvarDecl())
- return Visit(MakeCursorMemberRef(Ivar, PD->getPropertyIvarDeclLoc(), TU));
+ if (PD->isIvarNameSpecified())
+ return Visit(MakeCursorMemberRef(Ivar, PD->getPropertyIvarDeclLoc(), TU));
return false;
}
@@ -2462,7 +2465,8 @@ CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx,
CXXIdx->getOnlyLocalDecls(),
0, 0,
/*CaptureDiagnostics=*/true,
- /*AllowPCHWithCompilerErrors=*/true);
+ /*AllowPCHWithCompilerErrors=*/true,
+ /*UserFilesAreVolatile=*/true);
return MakeCXTranslationUnit(CXXIdx, TU);
}
@@ -2521,6 +2525,8 @@ static void clang_parseTranslationUnit_Impl(void *UserData) {
= (options & CXTranslationUnit_Incomplete)? TU_Prefix : TU_Complete;
bool CacheCodeCompetionResults
= options & CXTranslationUnit_CacheCompletionResults;
+ bool IncludeBriefCommentsInCodeCompletion
+ = options & CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
bool SkipFunctionBodies = options & CXTranslationUnit_SkipFunctionBodies;
// Configure the diagnostics.
@@ -2605,8 +2611,10 @@ static void clang_parseTranslationUnit_Impl(void *UserData) {
PrecompilePreamble,
TUKind,
CacheCodeCompetionResults,
+ IncludeBriefCommentsInCodeCompletion,
/*AllowPCHWithCompilerErrors=*/true,
SkipFunctionBodies,
+ /*UserFilesAreVolatile=*/true,
&ErrUnit));
if (NumErrors != Diags->getClient()->getNumErrors()) {
@@ -2691,6 +2699,8 @@ int clang_saveTranslationUnit(CXTranslationUnit TU, const char *FileName,
ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData);
ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+ if (!CXXUnit->hasSema())
+ return CXSaveError_InvalidTU;
SaveTranslationUnitInfo STUI = { TU, FileName, options, CXSaveError_None };
@@ -2734,6 +2744,7 @@ void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) {
delete static_cast<ASTUnit *>(CTUnit->TUData);
disposeCXStringPool(CTUnit->StringPool);
delete static_cast<CXDiagnosticSetImpl *>(CTUnit->Diagnostics);
+ disposeOverridenCXCursorsPool(CTUnit->OverridenCursorsPool);
delete CTUnit;
}
}
@@ -2829,8 +2840,8 @@ CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) {
}
CXCursor clang_getTranslationUnitCursor(CXTranslationUnit TU) {
- CXCursor Result = { CXCursor_TranslationUnit, 0, { 0, 0, TU } };
- return Result;
+ ASTUnit *CXXUnit = static_cast<ASTUnit*>(TU->TUData);
+ return MakeCXCursor(CXXUnit->getASTContext().getTranslationUnitDecl(), TU);
}
} // end: extern "C"
@@ -3501,6 +3512,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
return createCXString("ReturnStmt");
case CXCursor_AsmStmt:
return createCXString("AsmStmt");
+ case CXCursor_MSAsmStmt:
+ return createCXString("MSAsmStmt");
case CXCursor_ObjCAtTryStmt:
return createCXString("ObjCAtTryStmt");
case CXCursor_ObjCAtCatchStmt:
@@ -3609,12 +3622,15 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
struct GetCursorData {
SourceLocation TokenBeginLoc;
bool PointsAtMacroArgExpansion;
+ bool VisitedObjCPropertyImplDecl;
+ SourceLocation VisitedDeclaratorDeclStartLoc;
CXCursor &BestCursor;
GetCursorData(SourceManager &SM,
SourceLocation tokenBegin, CXCursor &outputCursor)
: TokenBeginLoc(tokenBegin), BestCursor(outputCursor) {
PointsAtMacroArgExpansion = SM.isMacroArgExpansion(tokenBegin);
+ VisitedObjCPropertyImplDecl = false;
}
};
@@ -3654,6 +3670,32 @@ static enum CXChildVisitResult GetCursorVisitor(CXCursor cursor,
!ID->isThisDeclarationADefinition())
return CXChildVisit_Break;
}
+
+ } else if (DeclaratorDecl *DD
+ = dyn_cast_or_null<DeclaratorDecl>(getCursorDecl(cursor))) {
+ SourceLocation StartLoc = DD->getSourceRange().getBegin();
+ // Check that when we have multiple declarators in the same line,
+ // that later ones do not override the previous ones.
+ // If we have:
+ // int Foo, Bar;
+ // source ranges for both start at 'int', so 'Bar' will end up overriding
+ // 'Foo' even though the cursor location was at 'Foo'.
+ if (Data->VisitedDeclaratorDeclStartLoc == StartLoc)
+ return CXChildVisit_Break;
+ Data->VisitedDeclaratorDeclStartLoc = StartLoc;
+
+ } else if (ObjCPropertyImplDecl *PropImp
+ = dyn_cast_or_null<ObjCPropertyImplDecl>(getCursorDecl(cursor))) {
+ (void)PropImp;
+ // Check that when we have multiple @synthesize in the same line,
+ // that later ones do not override the previous ones.
+ // If we have:
+ // @synthesize Foo, Bar;
+ // source ranges for both start at '@', so 'Bar' will end up overriding
+ // 'Foo' even though the cursor location was at 'Foo'.
+ if (Data->VisitedObjCPropertyImplDecl)
+ return CXChildVisit_Break;
+ Data->VisitedObjCPropertyImplDecl = true;
}
}
@@ -5487,6 +5529,90 @@ enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) {
return CXAvailability_Available;
}
+static CXVersion convertVersion(VersionTuple In) {
+ CXVersion Out = { -1, -1, -1 };
+ if (In.empty())
+ return Out;
+
+ Out.Major = In.getMajor();
+
+ if (llvm::Optional<unsigned> Minor = In.getMinor())
+ Out.Minor = *Minor;
+ else
+ return Out;
+
+ if (llvm::Optional<unsigned> Subminor = In.getSubminor())
+ Out.Subminor = *Subminor;
+
+ return Out;
+}
+
+int clang_getCursorPlatformAvailability(CXCursor cursor,
+ int *always_deprecated,
+ CXString *deprecated_message,
+ int *always_unavailable,
+ CXString *unavailable_message,
+ CXPlatformAvailability *availability,
+ int availability_size) {
+ if (always_deprecated)
+ *always_deprecated = 0;
+ if (deprecated_message)
+ *deprecated_message = cxstring::createCXString("", /*DupString=*/false);
+ if (always_unavailable)
+ *always_unavailable = 0;
+ if (unavailable_message)
+ *unavailable_message = cxstring::createCXString("", /*DupString=*/false);
+
+ if (!clang_isDeclaration(cursor.kind))
+ return 0;
+
+ Decl *D = cxcursor::getCursorDecl(cursor);
+ if (!D)
+ return 0;
+
+ int N = 0;
+ for (Decl::attr_iterator A = D->attr_begin(), AEnd = D->attr_end(); A != AEnd;
+ ++A) {
+ if (DeprecatedAttr *Deprecated = dyn_cast<DeprecatedAttr>(*A)) {
+ if (always_deprecated)
+ *always_deprecated = 1;
+ if (deprecated_message)
+ *deprecated_message = cxstring::createCXString(Deprecated->getMessage());
+ continue;
+ }
+
+ if (UnavailableAttr *Unavailable = dyn_cast<UnavailableAttr>(*A)) {
+ if (always_unavailable)
+ *always_unavailable = 1;
+ if (unavailable_message) {
+ *unavailable_message
+ = cxstring::createCXString(Unavailable->getMessage());
+ }
+ continue;
+ }
+
+ if (AvailabilityAttr *Avail = dyn_cast<AvailabilityAttr>(*A)) {
+ if (N < availability_size) {
+ availability[N].Platform
+ = cxstring::createCXString(Avail->getPlatform()->getName());
+ availability[N].Introduced = convertVersion(Avail->getIntroduced());
+ availability[N].Deprecated = convertVersion(Avail->getDeprecated());
+ availability[N].Obsoleted = convertVersion(Avail->getObsoleted());
+ availability[N].Unavailable = Avail->getUnavailable();
+ availability[N].Message = cxstring::createCXString(Avail->getMessage());
+ }
+ ++N;
+ }
+ }
+
+ return N;
+}
+
+void clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability) {
+ clang_disposeString(availability->Platform);
+ clang_disposeString(availability->Message);
+}
+
CXLanguageKind clang_getCursorLanguage(CXCursor cursor) {
if (clang_isDeclaration(cursor.kind))
return getDeclLanguage(cxcursor::getCursorDecl(cursor));
@@ -5549,44 +5675,73 @@ CXCursor clang_getCursorLexicalParent(CXCursor cursor) {
return clang_getNullCursor();
}
-void clang_getOverriddenCursors(CXCursor cursor,
- CXCursor **overridden,
- unsigned *num_overridden) {
- if (overridden)
- *overridden = 0;
- if (num_overridden)
- *num_overridden = 0;
- if (!overridden || !num_overridden)
- return;
- if (!clang_isDeclaration(cursor.kind))
- return;
+CXFile clang_getIncludedFile(CXCursor cursor) {
+ if (cursor.kind != CXCursor_InclusionDirective)
+ return 0;
+
+ InclusionDirective *ID = getCursorInclusionDirective(cursor);
+ return (void *)ID->getFile();
+}
- SmallVector<CXCursor, 8> Overridden;
- cxcursor::getOverriddenCursors(cursor, Overridden);
+CXSourceRange clang_Cursor_getCommentRange(CXCursor C) {
+ if (!clang_isDeclaration(C.kind))
+ return clang_getNullRange();
- // Don't allocate memory if we have no overriden cursors.
- if (Overridden.size() == 0)
- return;
+ const Decl *D = getCursorDecl(C);
+ ASTContext &Context = getCursorContext(C);
+ const RawComment *RC = Context.getRawCommentForAnyRedecl(D);
+ if (!RC)
+ return clang_getNullRange();
- *num_overridden = Overridden.size();
- *overridden = new CXCursor [Overridden.size()];
- std::copy(Overridden.begin(), Overridden.end(), *overridden);
+ return cxloc::translateSourceRange(Context, RC->getSourceRange());
}
-void clang_disposeOverriddenCursors(CXCursor *overridden) {
- delete [] overridden;
+CXString clang_Cursor_getRawCommentText(CXCursor C) {
+ if (!clang_isDeclaration(C.kind))
+ return createCXString((const char *) NULL);
+
+ const Decl *D = getCursorDecl(C);
+ ASTContext &Context = getCursorContext(C);
+ const RawComment *RC = Context.getRawCommentForAnyRedecl(D);
+ StringRef RawText = RC ? RC->getRawText(Context.getSourceManager()) :
+ StringRef();
+
+ // Don't duplicate the string because RawText points directly into source
+ // code.
+ return createCXString(RawText, false);
}
-CXFile clang_getIncludedFile(CXCursor cursor) {
- if (cursor.kind != CXCursor_InclusionDirective)
- return 0;
-
- InclusionDirective *ID = getCursorInclusionDirective(cursor);
- return (void *)ID->getFile();
+CXString clang_Cursor_getBriefCommentText(CXCursor C) {
+ if (!clang_isDeclaration(C.kind))
+ return createCXString((const char *) NULL);
+
+ const Decl *D = getCursorDecl(C);
+ const ASTContext &Context = getCursorContext(C);
+ const RawComment *RC = Context.getRawCommentForAnyRedecl(D);
+
+ if (RC) {
+ StringRef BriefText = RC->getBriefText(Context);
+
+ // Don't duplicate the string because RawComment ensures that this memory
+ // will not go away.
+ return createCXString(BriefText, false);
+ }
+
+ return createCXString((const char *) NULL);
}
-
-} // end: extern "C"
+CXComment clang_Cursor_getParsedComment(CXCursor C) {
+ if (!clang_isDeclaration(C.kind))
+ return cxcomment::createCXComment(NULL);
+
+ const Decl *D = getCursorDecl(C);
+ const ASTContext &Context = getCursorContext(C);
+ const comments::FullComment *FC = Context.getCommentForDecl(D);
+
+ return cxcomment::createCXComment(FC);
+}
+
+} // end: extern "C"
//===----------------------------------------------------------------------===//
// C++ AST instrospection.
diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp
index 303fb1f..0073b50 100644
--- a/tools/libclang/CIndexCodeCompletion.cpp
+++ b/tools/libclang/CIndexCodeCompletion.cpp
@@ -227,6 +227,17 @@ clang_getCompletionParent(CXCompletionString completion_string,
*kind = CCStr->getParentContextKind();
return createCXString(CCStr->getParentContextName(), /*DupString=*/false);
}
+
+CXString
+clang_getCompletionBriefComment(CXCompletionString completion_string) {
+ CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+
+ if (!CCStr)
+ return createCXString((const char *) NULL);
+
+ return createCXString(CCStr->getBriefComment(), /*DupString=*/false);
+}
+
/// \brief The CXCodeCompleteResults structure we allocate internally;
/// the client only sees the initial CXCodeCompleteResults structure.
@@ -509,9 +520,10 @@ namespace {
SmallVector<CXCompletionResult, 16> StoredResults;
CXTranslationUnit *TU;
public:
- CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results,
+ CaptureCompletionResults(const CodeCompleteOptions &Opts,
+ AllocatedCXCodeCompleteResults &Results,
CXTranslationUnit *TranslationUnit)
- : CodeCompleteConsumer(true, false, true, false),
+ : CodeCompleteConsumer(Opts, false),
AllocatedResults(Results), CCTUInfo(Results.CodeCompletionAllocator),
TU(TranslationUnit) { }
~CaptureCompletionResults() { Finish(); }
@@ -524,7 +536,8 @@ namespace {
for (unsigned I = 0; I != NumResults; ++I) {
CodeCompletionString *StoredCompletion
= Results[I].CreateCodeCompletionString(S, getAllocator(),
- getCodeCompletionTUInfo());
+ getCodeCompletionTUInfo(),
+ includeBriefComments());
CXCompletionResult R;
R.CursorKind = Results[I].CursorKind;
@@ -658,6 +671,7 @@ void clang_codeCompleteAt_Impl(void *UserData) {
struct CXUnsavedFile *unsaved_files = CCAI->unsaved_files;
unsigned num_unsaved_files = CCAI->num_unsaved_files;
unsigned options = CCAI->options;
+ bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments;
CCAI->result = 0;
#ifdef UDP_CODE_COMPLETION_LOGGER
@@ -699,13 +713,16 @@ void clang_codeCompleteAt_Impl(void *UserData) {
Results->NumResults = 0;
// Create a code-completion consumer to capture the results.
- CaptureCompletionResults Capture(*Results, &TU);
+ CodeCompleteOptions Opts;
+ Opts.IncludeBriefComments = IncludeBriefComments;
+ CaptureCompletionResults Capture(Opts, *Results, &TU);
// Perform completion.
AST->CodeComplete(complete_filename, complete_line, complete_column,
RemappedFiles.data(), RemappedFiles.size(),
(options & CXCodeComplete_IncludeMacros),
(options & CXCodeComplete_IncludeCodePatterns),
+ IncludeBriefComments,
Capture,
*Results->Diag, Results->LangOpts, *Results->SourceMgr,
*Results->FileMgr, Results->Diagnostics,
diff --git a/tools/libclang/CIndexDiagnostic.cpp b/tools/libclang/CIndexDiagnostic.cpp
index 8fbe3d8..14722e0 100644
--- a/tools/libclang/CIndexDiagnostic.cpp
+++ b/tools/libclang/CIndexDiagnostic.cpp
@@ -86,11 +86,10 @@ public:
class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
public:
- CXDiagnosticRenderer(const SourceManager &SM,
- const LangOptions &LangOpts,
+ CXDiagnosticRenderer(const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts,
CXDiagnosticSetImpl *mainSet)
- : DiagnosticNoteRenderer(SM, LangOpts, DiagOpts),
+ : DiagnosticNoteRenderer(LangOpts, DiagOpts),
CurrentSet(mainSet), MainSet(mainSet) {}
virtual ~CXDiagnosticRenderer() {}
@@ -116,26 +115,38 @@ public:
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag D) {
if (!D.isNull())
return;
- CXSourceLocation L = translateSourceLocation(SM, LangOpts, Loc);
+ CXSourceLocation L;
+ if (SM)
+ L = translateSourceLocation(*SM, LangOpts, Loc);
+ else
+ L = clang_getNullLocation();
CXDiagnosticImpl *CD = new CXDiagnosticCustomNoteImpl(Message, L);
CurrentSet->appendDiagnostic(CD);
}
virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges) {}
+ ArrayRef<CharSourceRange> Ranges,
+ const SourceManager &SM) {}
virtual void emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints) {}
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {}
- virtual void emitNote(SourceLocation Loc, StringRef Message) {
- CXSourceLocation L = translateSourceLocation(SM, LangOpts, Loc);
+ virtual void emitNote(SourceLocation Loc, StringRef Message,
+ const SourceManager *SM) {
+ CXSourceLocation L;
+ if (SM)
+ L = translateSourceLocation(*SM, LangOpts, Loc);
+ else
+ L = clang_getNullLocation();
CurrentSet->appendDiagnostic(new CXDiagnosticCustomNoteImpl(Message,
L));
}
@@ -181,8 +192,7 @@ CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl();
TU->Diagnostics = Set;
DiagnosticOptions DOpts;
- CXDiagnosticRenderer Renderer(AU->getSourceManager(),
- AU->getASTContext().getLangOpts(),
+ CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(),
DOpts, Set);
for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(),
diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp
index 7c79b69..c885dd5 100644
--- a/tools/libclang/CIndexUSRs.cpp
+++ b/tools/libclang/CIndexUSRs.cpp
@@ -754,7 +754,7 @@ void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) {
case TemplateArgument::Integral:
Out << 'V';
VisitType(Arg.getIntegralType());
- Out << *Arg.getAsIntegral();
+ Out << Arg.getAsIntegral();
break;
}
}
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index fb0b91f..283276f 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -1,16 +1,6 @@
-set(LLVM_USED_LIBS
- clangARCMigrate
- clangRewrite
- clangFrontend
- clangDriver
- clangSerialization
- clangSema
- clangEdit
- clangAST
- clangLex
- clangBasic)
-
-set( LLVM_LINK_COMPONENTS
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
support
mc
)
@@ -27,8 +17,10 @@ set(SOURCES
CIndexUSRs.cpp
CIndexer.cpp
CIndexer.h
+ CXComment.cpp
CXCursor.cpp
CXCursor.h
+ CXCompilationDatabase.cpp
CXLoadedDiagnostic.cpp
CXLoadedDiagnostic.h
CXSourceLocation.cpp
@@ -49,32 +41,60 @@ set(SOURCES
../../include/clang-c/Index.h
)
+set(LIBRARIES
+ clangARCMigrate
+ clangRewrite
+ clangFrontend
+ clangDriver
+ clangSerialization
+ clangSema
+ clangEdit
+ clangAST
+ clangLex
+ clangTooling
+ clangBasic
+ )
+
+set(GENERATED_HEADERS
+ ClangAttrClasses
+ ClangAttrList
+ ClangAttrParsedAttrList
+ ClangCommentNodes
+ ClangDiagnosticCommon
+ ClangDiagnosticFrontend
+ ClangDeclNodes
+ ClangStmtNodes
+ )
+
if( LLVM_ENABLE_PIC )
set(SHARED_LIBRARY TRUE)
add_clang_library(libclang ${SOURCES})
+ target_link_libraries(libclang ${LIBRARIES})
+ add_dependencies(libclang ${GENERATED_HEADERS})
- set_target_properties(libclang
- PROPERTIES
- OUTPUT_NAME "libclang"
- VERSION ${LIBCLANG_LIBRARY_VERSION}
- DEFINE_SYMBOL _CINDEX_LIB_)
+ if(WIN32)
+ set_target_properties(libclang
+ PROPERTIES
+ OUTPUT_NAME "libclang"
+ VERSION ${LIBCLANG_LIBRARY_VERSION}
+ DEFINE_SYMBOL _CINDEX_LIB_)
+ else()
+ set_target_properties(libclang
+ PROPERTIES
+ OUTPUT_NAME "clang"
+ VERSION ${LIBCLANG_LIBRARY_VERSION}
+ DEFINE_SYMBOL _CINDEX_LIB_)
+ endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LIBCLANG_LINK_FLAGS
- "-Wl,-compatibility_version -Wl,1 -Wl,-dead_strip -Wl,-seg1addr -Wl,0xE0000000")
+ "-Wl,-compatibility_version -Wl,1 -Wl,-dead_strip")
set_target_properties(libclang
PROPERTIES
LINK_FLAGS "${LIBCLANG_LINK_FLAGS}"
INSTALL_NAME_DIR "@executable_path/../lib")
endif()
- if(MSVC)
- # windows.h doesn't compile with /Za
- get_target_property(NON_ANSI_COMPILE_FLAGS libclang COMPILE_FLAGS)
- string(REPLACE "/Za" "" NON_ANSI_COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS})
- set_target_properties(libclang PROPERTIES
- COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS})
- endif()
set(LIBCLANG_STATIC_TARGET_NAME libclang_static)
else()
@@ -83,8 +103,10 @@ endif()
if( NOT BUILD_SHARED_LIBS AND NOT WIN32 )
add_clang_library(${LIBCLANG_STATIC_TARGET_NAME} STATIC ${SOURCES})
+ target_link_libraries(${LIBCLANG_STATIC_TARGET_NAME} ${LIBRARIES})
+ add_dependencies(${LIBCLANG_STATIC_TARGET_NAME} ${GENERATED_HEADERS})
set_target_properties(${LIBCLANG_STATIC_TARGET_NAME}
PROPERTIES
- OUTPUT_NAME "libclang")
+ OUTPUT_NAME "clang")
endif()
diff --git a/tools/libclang/CXComment.cpp b/tools/libclang/CXComment.cpp
new file mode 100644
index 0000000..c5c9ca8
--- /dev/null
+++ b/tools/libclang/CXComment.cpp
@@ -0,0 +1,1230 @@
+//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines all libclang APIs related to walking comment AST.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-c/Index.h"
+#include "CXString.h"
+#include "CXComment.h"
+#include "CXCursor.h"
+#include "CXTranslationUnit.h"
+
+#include "clang/AST/CommentVisitor.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/Decl.h"
+#include "clang/Frontend/ASTUnit.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <climits>
+
+using namespace clang;
+using namespace clang::cxstring;
+using namespace clang::comments;
+using namespace clang::cxcomment;
+
+extern "C" {
+
+enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
+ const Comment *C = getASTNode(CXC);
+ if (!C)
+ return CXComment_Null;
+
+ switch (C->getCommentKind()) {
+ case Comment::NoCommentKind:
+ return CXComment_Null;
+
+ case Comment::TextCommentKind:
+ return CXComment_Text;
+
+ case Comment::InlineCommandCommentKind:
+ return CXComment_InlineCommand;
+
+ case Comment::HTMLStartTagCommentKind:
+ return CXComment_HTMLStartTag;
+
+ case Comment::HTMLEndTagCommentKind:
+ return CXComment_HTMLEndTag;
+
+ case Comment::ParagraphCommentKind:
+ return CXComment_Paragraph;
+
+ case Comment::BlockCommandCommentKind:
+ return CXComment_BlockCommand;
+
+ case Comment::ParamCommandCommentKind:
+ return CXComment_ParamCommand;
+
+ case Comment::TParamCommandCommentKind:
+ return CXComment_TParamCommand;
+
+ case Comment::VerbatimBlockCommentKind:
+ return CXComment_VerbatimBlockCommand;
+
+ case Comment::VerbatimBlockLineCommentKind:
+ return CXComment_VerbatimBlockLine;
+
+ case Comment::VerbatimLineCommentKind:
+ return CXComment_VerbatimLine;
+
+ case Comment::FullCommentKind:
+ return CXComment_FullComment;
+ }
+ llvm_unreachable("unknown CommentKind");
+}
+
+unsigned clang_Comment_getNumChildren(CXComment CXC) {
+ const Comment *C = getASTNode(CXC);
+ if (!C)
+ return 0;
+
+ return C->child_count();
+}
+
+CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
+ const Comment *C = getASTNode(CXC);
+ if (!C || ChildIdx >= C->child_count())
+ return createCXComment(NULL);
+
+ return createCXComment(*(C->child_begin() + ChildIdx));
+}
+
+unsigned clang_Comment_isWhitespace(CXComment CXC) {
+ const Comment *C = getASTNode(CXC);
+ if (!C)
+ return false;
+
+ if (const TextComment *TC = dyn_cast<TextComment>(C))
+ return TC->isWhitespace();
+
+ if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
+ return PC->isWhitespace();
+
+ return false;
+}
+
+unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
+ const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
+ if (!ICC)
+ return false;
+
+ return ICC->hasTrailingNewline();
+}
+
+CXString clang_TextComment_getText(CXComment CXC) {
+ const TextComment *TC = getASTNodeAs<TextComment>(CXC);
+ if (!TC)
+ return createCXString((const char *) 0);
+
+ return createCXString(TC->getText(), /*DupString=*/ false);
+}
+
+CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
+ const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
+ if (!ICC)
+ return createCXString((const char *) 0);
+
+ return createCXString(ICC->getCommandName(), /*DupString=*/ false);
+}
+
+enum CXCommentInlineCommandRenderKind
+clang_InlineCommandComment_getRenderKind(CXComment CXC) {
+ const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
+ if (!ICC)
+ return CXCommentInlineCommandRenderKind_Normal;
+
+ switch (ICC->getRenderKind()) {
+ case InlineCommandComment::RenderNormal:
+ return CXCommentInlineCommandRenderKind_Normal;
+
+ case InlineCommandComment::RenderBold:
+ return CXCommentInlineCommandRenderKind_Bold;
+
+ case InlineCommandComment::RenderMonospaced:
+ return CXCommentInlineCommandRenderKind_Monospaced;
+
+ case InlineCommandComment::RenderEmphasized:
+ return CXCommentInlineCommandRenderKind_Emphasized;
+ }
+ llvm_unreachable("unknown InlineCommandComment::RenderKind");
+}
+
+unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
+ const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
+ if (!ICC)
+ return 0;
+
+ return ICC->getNumArgs();
+}
+
+CXString clang_InlineCommandComment_getArgText(CXComment CXC,
+ unsigned ArgIdx) {
+ const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
+ if (!ICC || ArgIdx >= ICC->getNumArgs())
+ return createCXString((const char *) 0);
+
+ return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
+}
+
+CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
+ const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
+ if (!HTC)
+ return createCXString((const char *) 0);
+
+ return createCXString(HTC->getTagName(), /*DupString=*/ false);
+}
+
+unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
+ const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
+ if (!HST)
+ return false;
+
+ return HST->isSelfClosing();
+}
+
+unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
+ const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
+ if (!HST)
+ return 0;
+
+ return HST->getNumAttrs();
+}
+
+CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
+ const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
+ if (!HST || AttrIdx >= HST->getNumAttrs())
+ return createCXString((const char *) 0);
+
+ return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
+}
+
+CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
+ const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
+ if (!HST || AttrIdx >= HST->getNumAttrs())
+ return createCXString((const char *) 0);
+
+ return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
+}
+
+CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
+ const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
+ if (!BCC)
+ return createCXString((const char *) 0);
+
+ return createCXString(BCC->getCommandName(), /*DupString=*/ false);
+}
+
+unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
+ const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
+ if (!BCC)
+ return 0;
+
+ return BCC->getNumArgs();
+}
+
+CXString clang_BlockCommandComment_getArgText(CXComment CXC,
+ unsigned ArgIdx) {
+ const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
+ if (!BCC || ArgIdx >= BCC->getNumArgs())
+ return createCXString((const char *) 0);
+
+ return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
+}
+
+CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
+ const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
+ if (!BCC)
+ return createCXComment(NULL);
+
+ return createCXComment(BCC->getParagraph());
+}
+
+CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
+ const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
+ if (!PCC || !PCC->hasParamName())
+ return createCXString((const char *) 0);
+
+ return createCXString(PCC->getParamName(), /*DupString=*/ false);
+}
+
+unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
+ const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
+ if (!PCC)
+ return false;
+
+ return PCC->isParamIndexValid();
+}
+
+unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
+ const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
+ if (!PCC || !PCC->isParamIndexValid())
+ return ParamCommandComment::InvalidParamIndex;
+
+ return PCC->getParamIndex();
+}
+
+unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
+ const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
+ if (!PCC)
+ return false;
+
+ return PCC->isDirectionExplicit();
+}
+
+enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
+ CXComment CXC) {
+ const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
+ if (!PCC)
+ return CXCommentParamPassDirection_In;
+
+ switch (PCC->getDirection()) {
+ case ParamCommandComment::In:
+ return CXCommentParamPassDirection_In;
+
+ case ParamCommandComment::Out:
+ return CXCommentParamPassDirection_Out;
+
+ case ParamCommandComment::InOut:
+ return CXCommentParamPassDirection_InOut;
+ }
+ llvm_unreachable("unknown ParamCommandComment::PassDirection");
+}
+
+CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
+ const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+ if (!TPCC || !TPCC->hasParamName())
+ return createCXString((const char *) 0);
+
+ return createCXString(TPCC->getParamName(), /*DupString=*/ false);
+}
+
+unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
+ const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+ if (!TPCC)
+ return false;
+
+ return TPCC->isPositionValid();
+}
+
+unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
+ const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+ if (!TPCC || !TPCC->isPositionValid())
+ return 0;
+
+ return TPCC->getDepth();
+}
+
+unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
+ const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+ if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
+ return 0;
+
+ return TPCC->getIndex(Depth);
+}
+
+CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
+ const VerbatimBlockLineComment *VBL =
+ getASTNodeAs<VerbatimBlockLineComment>(CXC);
+ if (!VBL)
+ return createCXString((const char *) 0);
+
+ return createCXString(VBL->getText(), /*DupString=*/ false);
+}
+
+CXString clang_VerbatimLineComment_getText(CXComment CXC) {
+ const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
+ if (!VLC)
+ return createCXString((const char *) 0);
+
+ return createCXString(VLC->getText(), /*DupString=*/ false);
+}
+
+} // end extern "C"
+
+//===----------------------------------------------------------------------===//
+// Helpers for converting comment AST to HTML.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+/// This comparison will sort parameters with valid index by index and
+/// invalid (unresolved) parameters last.
+class ParamCommandCommentCompareIndex {
+public:
+ bool operator()(const ParamCommandComment *LHS,
+ const ParamCommandComment *RHS) const {
+ unsigned LHSIndex = UINT_MAX;
+ unsigned RHSIndex = UINT_MAX;
+ if (LHS->isParamIndexValid())
+ LHSIndex = LHS->getParamIndex();
+ if (RHS->isParamIndexValid())
+ RHSIndex = RHS->getParamIndex();
+
+ return LHSIndex < RHSIndex;
+ }
+};
+
+/// This comparison will sort template parameters in the following order:
+/// \li real template parameters (depth = 1) in index order;
+/// \li all other names (depth > 1);
+/// \li unresolved names.
+class TParamCommandCommentComparePosition {
+public:
+ bool operator()(const TParamCommandComment *LHS,
+ const TParamCommandComment *RHS) const {
+ // Sort unresolved names last.
+ if (!LHS->isPositionValid())
+ return false;
+ if (!RHS->isPositionValid())
+ return true;
+
+ if (LHS->getDepth() > 1)
+ return false;
+ if (RHS->getDepth() > 1)
+ return true;
+
+ // Sort template parameters in index order.
+ if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
+ return LHS->getIndex(0) < RHS->getIndex(0);
+
+ // Leave all other names in source order.
+ return true;
+ }
+};
+
+/// Separate parts of a FullComment.
+struct FullCommentParts {
+ /// Take a full comment apart and initialize members accordingly.
+ FullCommentParts(const FullComment *C);
+
+ const BlockContentComment *Brief;
+ const ParagraphComment *FirstParagraph;
+ const BlockCommandComment *Returns;
+ SmallVector<const ParamCommandComment *, 8> Params;
+ SmallVector<const TParamCommandComment *, 4> TParams;
+ SmallVector<const BlockContentComment *, 8> MiscBlocks;
+};
+
+FullCommentParts::FullCommentParts(const FullComment *C) :
+ Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
+ const CommandTraits Traits;
+ for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
+ I != E; ++I) {
+ const Comment *Child = *I;
+ if (!Child)
+ continue;
+ switch (Child->getCommentKind()) {
+ case Comment::NoCommentKind:
+ continue;
+
+ case Comment::ParagraphCommentKind: {
+ const ParagraphComment *PC = cast<ParagraphComment>(Child);
+ if (PC->isWhitespace())
+ break;
+ if (!FirstParagraph)
+ FirstParagraph = PC;
+
+ MiscBlocks.push_back(PC);
+ break;
+ }
+
+ case Comment::BlockCommandCommentKind: {
+ const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
+ StringRef CommandName = BCC->getCommandName();
+ if (!Brief && Traits.isBriefCommand(CommandName)) {
+ Brief = BCC;
+ break;
+ }
+ if (!Returns && Traits.isReturnsCommand(CommandName)) {
+ Returns = BCC;
+ break;
+ }
+ MiscBlocks.push_back(BCC);
+ break;
+ }
+
+ case Comment::ParamCommandCommentKind: {
+ const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
+ if (!PCC->hasParamName())
+ break;
+
+ if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
+ break;
+
+ Params.push_back(PCC);
+ break;
+ }
+
+ case Comment::TParamCommandCommentKind: {
+ const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
+ if (!TPCC->hasParamName())
+ break;
+
+ if (!TPCC->hasNonWhitespaceParagraph())
+ break;
+
+ TParams.push_back(TPCC);
+ break;
+ }
+
+ case Comment::VerbatimBlockCommentKind:
+ MiscBlocks.push_back(cast<BlockCommandComment>(Child));
+ break;
+
+ case Comment::VerbatimLineCommentKind: {
+ const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
+ if (!Traits.isDeclarationCommand(VLC->getCommandName()))
+ MiscBlocks.push_back(VLC);
+ break;
+ }
+
+ case Comment::TextCommentKind:
+ case Comment::InlineCommandCommentKind:
+ case Comment::HTMLStartTagCommentKind:
+ case Comment::HTMLEndTagCommentKind:
+ case Comment::VerbatimBlockLineCommentKind:
+ case Comment::FullCommentKind:
+ llvm_unreachable("AST node of this kind can't be a child of "
+ "a FullComment");
+ }
+ }
+
+ // Sort params in order they are declared in the function prototype.
+ // Unresolved parameters are put at the end of the list in the same order
+ // they were seen in the comment.
+ std::stable_sort(Params.begin(), Params.end(),
+ ParamCommandCommentCompareIndex());
+
+ std::stable_sort(TParams.begin(), TParams.end(),
+ TParamCommandCommentComparePosition());
+}
+
+void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
+ llvm::raw_svector_ostream &Result) {
+ Result << "<" << C->getTagName();
+
+ if (C->getNumAttrs() != 0) {
+ for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
+ Result << " ";
+ const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
+ Result << Attr.Name;
+ if (!Attr.Value.empty())
+ Result << "=\"" << Attr.Value << "\"";
+ }
+ }
+
+ if (!C->isSelfClosing())
+ Result << ">";
+ else
+ Result << "/>";
+}
+
+class CommentASTToHTMLConverter :
+ public ConstCommentVisitor<CommentASTToHTMLConverter> {
+public:
+ /// \param Str accumulator for HTML.
+ CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
+
+ // Inline content.
+ void visitTextComment(const TextComment *C);
+ void visitInlineCommandComment(const InlineCommandComment *C);
+ void visitHTMLStartTagComment(const HTMLStartTagComment *C);
+ void visitHTMLEndTagComment(const HTMLEndTagComment *C);
+
+ // Block content.
+ void visitParagraphComment(const ParagraphComment *C);
+ void visitBlockCommandComment(const BlockCommandComment *C);
+ void visitParamCommandComment(const ParamCommandComment *C);
+ void visitTParamCommandComment(const TParamCommandComment *C);
+ void visitVerbatimBlockComment(const VerbatimBlockComment *C);
+ void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
+ void visitVerbatimLineComment(const VerbatimLineComment *C);
+
+ void visitFullComment(const FullComment *C);
+
+ // Helpers.
+
+ /// Convert a paragraph that is not a block by itself (an argument to some
+ /// command).
+ void visitNonStandaloneParagraphComment(const ParagraphComment *C);
+
+ void appendToResultWithHTMLEscaping(StringRef S);
+
+private:
+ const CommandTraits Traits;
+
+ /// Output stream for HTML.
+ llvm::raw_svector_ostream Result;
+};
+} // end unnamed namespace
+
+void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
+ appendToResultWithHTMLEscaping(C->getText());
+}
+
+void CommentASTToHTMLConverter::visitInlineCommandComment(
+ const InlineCommandComment *C) {
+ // Nothing to render if no arguments supplied.
+ if (C->getNumArgs() == 0)
+ return;
+
+ // Nothing to render if argument is empty.
+ StringRef Arg0 = C->getArgText(0);
+ if (Arg0.empty())
+ return;
+
+ switch (C->getRenderKind()) {
+ case InlineCommandComment::RenderNormal:
+ for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
+ appendToResultWithHTMLEscaping(C->getArgText(i));
+ Result << " ";
+ }
+ return;
+
+ case InlineCommandComment::RenderBold:
+ assert(C->getNumArgs() == 1);
+ Result << "<b>";
+ appendToResultWithHTMLEscaping(Arg0);
+ Result << "</b>";
+ return;
+ case InlineCommandComment::RenderMonospaced:
+ assert(C->getNumArgs() == 1);
+ Result << "<tt>";
+ appendToResultWithHTMLEscaping(Arg0);
+ Result<< "</tt>";
+ return;
+ case InlineCommandComment::RenderEmphasized:
+ assert(C->getNumArgs() == 1);
+ Result << "<em>";
+ appendToResultWithHTMLEscaping(Arg0);
+ Result << "</em>";
+ return;
+ }
+}
+
+void CommentASTToHTMLConverter::visitHTMLStartTagComment(
+ const HTMLStartTagComment *C) {
+ PrintHTMLStartTagComment(C, Result);
+}
+
+void CommentASTToHTMLConverter::visitHTMLEndTagComment(
+ const HTMLEndTagComment *C) {
+ Result << "</" << C->getTagName() << ">";
+}
+
+void CommentASTToHTMLConverter::visitParagraphComment(
+ const ParagraphComment *C) {
+ if (C->isWhitespace())
+ return;
+
+ Result << "<p>";
+ for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
+ I != E; ++I) {
+ visit(*I);
+ }
+ Result << "</p>";
+}
+
+void CommentASTToHTMLConverter::visitBlockCommandComment(
+ const BlockCommandComment *C) {
+ StringRef CommandName = C->getCommandName();
+ if (Traits.isBriefCommand(CommandName)) {
+ Result << "<p class=\"para-brief\">";
+ visitNonStandaloneParagraphComment(C->getParagraph());
+ Result << "</p>";
+ return;
+ }
+ if (Traits.isReturnsCommand(CommandName)) {
+ Result << "<p class=\"para-returns\">"
+ "<span class=\"word-returns\">Returns</span> ";
+ visitNonStandaloneParagraphComment(C->getParagraph());
+ Result << "</p>";
+ return;
+ }
+ // We don't know anything about this command. Just render the paragraph.
+ visit(C->getParagraph());
+}
+
+void CommentASTToHTMLConverter::visitParamCommandComment(
+ const ParamCommandComment *C) {
+ if (C->isParamIndexValid()) {
+ Result << "<dt class=\"param-name-index-"
+ << C->getParamIndex()
+ << "\">";
+ } else
+ Result << "<dt class=\"param-name-index-invalid\">";
+
+ appendToResultWithHTMLEscaping(C->getParamName());
+ Result << "</dt>";
+
+ if (C->isParamIndexValid()) {
+ Result << "<dd class=\"param-descr-index-"
+ << C->getParamIndex()
+ << "\">";
+ } else
+ Result << "<dd class=\"param-descr-index-invalid\">";
+
+ visitNonStandaloneParagraphComment(C->getParagraph());
+ Result << "</dd>";
+}
+
+void CommentASTToHTMLConverter::visitTParamCommandComment(
+ const TParamCommandComment *C) {
+ if (C->isPositionValid()) {
+ if (C->getDepth() == 1)
+ Result << "<dt class=\"tparam-name-index-"
+ << C->getIndex(0)
+ << "\">";
+ else
+ Result << "<dt class=\"tparam-name-index-other\">";
+ } else
+ Result << "<dt class=\"tparam-name-index-invalid\">";
+
+ appendToResultWithHTMLEscaping(C->getParamName());
+ Result << "</dt>";
+
+ if (C->isPositionValid()) {
+ if (C->getDepth() == 1)
+ Result << "<dd class=\"tparam-descr-index-"
+ << C->getIndex(0)
+ << "\">";
+ else
+ Result << "<dd class=\"tparam-descr-index-other\">";
+ } else
+ Result << "<dd class=\"tparam-descr-index-invalid\">";
+
+ visitNonStandaloneParagraphComment(C->getParagraph());
+ Result << "</dd>";
+}
+
+void CommentASTToHTMLConverter::visitVerbatimBlockComment(
+ const VerbatimBlockComment *C) {
+ unsigned NumLines = C->getNumLines();
+ if (NumLines == 0)
+ return;
+
+ Result << "<pre>";
+ for (unsigned i = 0; i != NumLines; ++i) {
+ appendToResultWithHTMLEscaping(C->getText(i));
+ if (i + 1 != NumLines)
+ Result << '\n';
+ }
+ Result << "</pre>";
+}
+
+void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
+ const VerbatimBlockLineComment *C) {
+ llvm_unreachable("should not see this AST node");
+}
+
+void CommentASTToHTMLConverter::visitVerbatimLineComment(
+ const VerbatimLineComment *C) {
+ Result << "<pre>";
+ appendToResultWithHTMLEscaping(C->getText());
+ Result << "</pre>";
+}
+
+void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
+ FullCommentParts Parts(C);
+
+ bool FirstParagraphIsBrief = false;
+ if (Parts.Brief)
+ visit(Parts.Brief);
+ else if (Parts.FirstParagraph) {
+ Result << "<p class=\"para-brief\">";
+ visitNonStandaloneParagraphComment(Parts.FirstParagraph);
+ Result << "</p>";
+ FirstParagraphIsBrief = true;
+ }
+
+ for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
+ const Comment *C = Parts.MiscBlocks[i];
+ if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
+ continue;
+ visit(C);
+ }
+
+ if (Parts.TParams.size() != 0) {
+ Result << "<dl>";
+ for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
+ visit(Parts.TParams[i]);
+ Result << "</dl>";
+ }
+
+ if (Parts.Params.size() != 0) {
+ Result << "<dl>";
+ for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
+ visit(Parts.Params[i]);
+ Result << "</dl>";
+ }
+
+ if (Parts.Returns)
+ visit(Parts.Returns);
+
+ Result.flush();
+}
+
+void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
+ const ParagraphComment *C) {
+ if (!C)
+ return;
+
+ for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
+ I != E; ++I) {
+ visit(*I);
+ }
+}
+
+void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
+ for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
+ const char C = *I;
+ switch (C) {
+ case '&':
+ Result << "&amp;";
+ break;
+ case '<':
+ Result << "&lt;";
+ break;
+ case '>':
+ Result << "&gt;";
+ break;
+ case '"':
+ Result << "&quot;";
+ break;
+ case '\'':
+ Result << "&#39;";
+ break;
+ case '/':
+ Result << "&#47;";
+ break;
+ default:
+ Result << C;
+ break;
+ }
+ }
+}
+
+extern "C" {
+
+CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
+ const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
+ if (!HTC)
+ return createCXString((const char *) 0);
+
+ SmallString<128> HTML;
+ CommentASTToHTMLConverter Converter(HTML);
+ Converter.visit(HTC);
+ return createCXString(HTML.str(), /* DupString = */ true);
+}
+
+CXString clang_FullComment_getAsHTML(CXComment CXC) {
+ const FullComment *FC = getASTNodeAs<FullComment>(CXC);
+ if (!FC)
+ return createCXString((const char *) 0);
+
+ SmallString<1024> HTML;
+ CommentASTToHTMLConverter Converter(HTML);
+ Converter.visit(FC);
+ return createCXString(HTML.str(), /* DupString = */ true);
+}
+
+} // end extern "C"
+
+namespace {
+class CommentASTToXMLConverter :
+ public ConstCommentVisitor<CommentASTToXMLConverter> {
+public:
+ /// \param Str accumulator for XML.
+ CommentASTToXMLConverter(const SourceManager &SM,
+ SmallVectorImpl<char> &Str) :
+ SM(SM), Result(Str) { }
+
+ // Inline content.
+ void visitTextComment(const TextComment *C);
+ void visitInlineCommandComment(const InlineCommandComment *C);
+ void visitHTMLStartTagComment(const HTMLStartTagComment *C);
+ void visitHTMLEndTagComment(const HTMLEndTagComment *C);
+
+ // Block content.
+ void visitParagraphComment(const ParagraphComment *C);
+ void visitBlockCommandComment(const BlockCommandComment *C);
+ void visitParamCommandComment(const ParamCommandComment *C);
+ void visitTParamCommandComment(const TParamCommandComment *C);
+ void visitVerbatimBlockComment(const VerbatimBlockComment *C);
+ void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
+ void visitVerbatimLineComment(const VerbatimLineComment *C);
+
+ void visitFullComment(const FullComment *C);
+
+ // Helpers.
+ void appendToResultWithXMLEscaping(StringRef S);
+
+private:
+ const SourceManager &SM;
+
+ /// Output stream for XML.
+ llvm::raw_svector_ostream Result;
+};
+} // end unnamed namespace
+
+void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
+ appendToResultWithXMLEscaping(C->getText());
+}
+
+void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
+ // Nothing to render if no arguments supplied.
+ if (C->getNumArgs() == 0)
+ return;
+
+ // Nothing to render if argument is empty.
+ StringRef Arg0 = C->getArgText(0);
+ if (Arg0.empty())
+ return;
+
+ switch (C->getRenderKind()) {
+ case InlineCommandComment::RenderNormal:
+ for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
+ appendToResultWithXMLEscaping(C->getArgText(i));
+ Result << " ";
+ }
+ return;
+ case InlineCommandComment::RenderBold:
+ assert(C->getNumArgs() == 1);
+ Result << "<bold>";
+ appendToResultWithXMLEscaping(Arg0);
+ Result << "</bold>";
+ return;
+ case InlineCommandComment::RenderMonospaced:
+ assert(C->getNumArgs() == 1);
+ Result << "<monospaced>";
+ appendToResultWithXMLEscaping(Arg0);
+ Result << "</monospaced>";
+ return;
+ case InlineCommandComment::RenderEmphasized:
+ assert(C->getNumArgs() == 1);
+ Result << "<emphasized>";
+ appendToResultWithXMLEscaping(Arg0);
+ Result << "</emphasized>";
+ return;
+ }
+}
+
+void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
+ Result << "<rawHTML><![CDATA[";
+ PrintHTMLStartTagComment(C, Result);
+ Result << "]]></rawHTML>";
+}
+
+void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
+ Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
+}
+
+void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
+ if (C->isWhitespace())
+ return;
+
+ Result << "<Para>";
+ for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
+ I != E; ++I) {
+ visit(*I);
+ }
+ Result << "</Para>";
+}
+
+void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
+ visit(C->getParagraph());
+}
+
+void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
+ Result << "<Parameter><Name>";
+ appendToResultWithXMLEscaping(C->getParamName());
+ Result << "</Name>";
+
+ if (C->isParamIndexValid())
+ Result << "<Index>" << C->getParamIndex() << "</Index>";
+
+ Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
+ switch (C->getDirection()) {
+ case ParamCommandComment::In:
+ Result << "in";
+ break;
+ case ParamCommandComment::Out:
+ Result << "out";
+ break;
+ case ParamCommandComment::InOut:
+ Result << "in,out";
+ break;
+ }
+ Result << "</Direction><Discussion>";
+ visit(C->getParagraph());
+ Result << "</Discussion></Parameter>";
+}
+
+void CommentASTToXMLConverter::visitTParamCommandComment(
+ const TParamCommandComment *C) {
+ Result << "<Parameter><Name>";
+ appendToResultWithXMLEscaping(C->getParamName());
+ Result << "</Name>";
+
+ if (C->isPositionValid() && C->getDepth() == 1) {
+ Result << "<Index>" << C->getIndex(0) << "</Index>";
+ }
+
+ Result << "<Discussion>";
+ visit(C->getParagraph());
+ Result << "</Discussion></Parameter>";
+}
+
+void CommentASTToXMLConverter::visitVerbatimBlockComment(
+ const VerbatimBlockComment *C) {
+ unsigned NumLines = C->getNumLines();
+ if (NumLines == 0)
+ return;
+
+ Result << llvm::StringSwitch<const char *>(C->getCommandName())
+ .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
+ .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
+ for (unsigned i = 0; i != NumLines; ++i) {
+ appendToResultWithXMLEscaping(C->getText(i));
+ if (i + 1 != NumLines)
+ Result << '\n';
+ }
+ Result << "</Verbatim>";
+}
+
+void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
+ const VerbatimBlockLineComment *C) {
+ llvm_unreachable("should not see this AST node");
+}
+
+void CommentASTToXMLConverter::visitVerbatimLineComment(
+ const VerbatimLineComment *C) {
+ Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
+ appendToResultWithXMLEscaping(C->getText());
+ Result << "</Verbatim>";
+}
+
+void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
+ FullCommentParts Parts(C);
+
+ const DeclInfo *DI = C->getDeclInfo();
+ StringRef RootEndTag;
+ if (DI) {
+ switch (DI->getKind()) {
+ case DeclInfo::OtherKind:
+ RootEndTag = "</Other>";
+ Result << "<Other";
+ break;
+ case DeclInfo::FunctionKind:
+ RootEndTag = "</Function>";
+ Result << "<Function";
+ switch (DI->TemplateKind) {
+ case DeclInfo::NotTemplate:
+ break;
+ case DeclInfo::Template:
+ Result << " templateKind=\"template\"";
+ break;
+ case DeclInfo::TemplateSpecialization:
+ Result << " templateKind=\"specialization\"";
+ break;
+ case DeclInfo::TemplatePartialSpecialization:
+ llvm_unreachable("partial specializations of functions "
+ "are not allowed in C++");
+ }
+ if (DI->IsInstanceMethod)
+ Result << " isInstanceMethod=\"1\"";
+ if (DI->IsClassMethod)
+ Result << " isClassMethod=\"1\"";
+ break;
+ case DeclInfo::ClassKind:
+ RootEndTag = "</Class>";
+ Result << "<Class";
+ switch (DI->TemplateKind) {
+ case DeclInfo::NotTemplate:
+ break;
+ case DeclInfo::Template:
+ Result << " templateKind=\"template\"";
+ break;
+ case DeclInfo::TemplateSpecialization:
+ Result << " templateKind=\"specialization\"";
+ break;
+ case DeclInfo::TemplatePartialSpecialization:
+ Result << " templateKind=\"partialSpecialization\"";
+ break;
+ }
+ break;
+ case DeclInfo::VariableKind:
+ RootEndTag = "</Variable>";
+ Result << "<Variable";
+ break;
+ case DeclInfo::NamespaceKind:
+ RootEndTag = "</Namespace>";
+ Result << "<Namespace";
+ break;
+ case DeclInfo::TypedefKind:
+ RootEndTag = "</Typedef>";
+ Result << "<Typedef";
+ break;
+ case DeclInfo::EnumKind:
+ RootEndTag = "</Enum>";
+ Result << "<Enum";
+ break;
+ }
+
+ {
+ // Print line and column number.
+ SourceLocation Loc = DI->ThisDecl->getLocation();
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+ FileID FID = LocInfo.first;
+ unsigned FileOffset = LocInfo.second;
+
+ if (!FID.isInvalid()) {
+ if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
+ Result << " file=\"";
+ appendToResultWithXMLEscaping(FE->getName());
+ Result << "\"";
+ }
+ Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
+ << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
+ << "\"";
+ }
+ }
+
+ // Finish the root tag.
+ Result << ">";
+
+ bool FoundName = false;
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
+ if (DeclarationName DeclName = ND->getDeclName()) {
+ Result << "<Name>";
+ std::string Name = DeclName.getAsString();
+ appendToResultWithXMLEscaping(Name);
+ FoundName = true;
+ Result << "</Name>";
+ }
+ }
+ if (!FoundName)
+ Result << "<Name>&lt;anonymous&gt;</Name>";
+
+ {
+ // Print USR.
+ SmallString<128> USR;
+ cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
+ if (!USR.empty()) {
+ Result << "<USR>";
+ appendToResultWithXMLEscaping(USR);
+ Result << "</USR>";
+ }
+ }
+ } else {
+ // No DeclInfo -- just emit some root tag and name tag.
+ RootEndTag = "</Other>";
+ Result << "<Other><Name>unknown</Name>";
+ }
+
+ bool FirstParagraphIsBrief = false;
+ if (Parts.Brief) {
+ Result << "<Abstract>";
+ visit(Parts.Brief);
+ Result << "</Abstract>";
+ } else if (Parts.FirstParagraph) {
+ Result << "<Abstract>";
+ visit(Parts.FirstParagraph);
+ Result << "</Abstract>";
+ FirstParagraphIsBrief = true;
+ }
+
+ if (Parts.TParams.size() != 0) {
+ Result << "<TemplateParameters>";
+ for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
+ visit(Parts.TParams[i]);
+ Result << "</TemplateParameters>";
+ }
+
+ if (Parts.Params.size() != 0) {
+ Result << "<Parameters>";
+ for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
+ visit(Parts.Params[i]);
+ Result << "</Parameters>";
+ }
+
+ if (Parts.Returns) {
+ Result << "<ResultDiscussion>";
+ visit(Parts.Returns);
+ Result << "</ResultDiscussion>";
+ }
+
+ {
+ bool StartTagEmitted = false;
+ for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
+ const Comment *C = Parts.MiscBlocks[i];
+ if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
+ continue;
+ if (!StartTagEmitted) {
+ Result << "<Discussion>";
+ StartTagEmitted = true;
+ }
+ visit(C);
+ }
+ if (StartTagEmitted)
+ Result << "</Discussion>";
+ }
+
+ Result << RootEndTag;
+
+ Result.flush();
+}
+
+void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
+ for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
+ const char C = *I;
+ switch (C) {
+ case '&':
+ Result << "&amp;";
+ break;
+ case '<':
+ Result << "&lt;";
+ break;
+ case '>':
+ Result << "&gt;";
+ break;
+ case '"':
+ Result << "&quot;";
+ break;
+ case '\'':
+ Result << "&apos;";
+ break;
+ default:
+ Result << C;
+ break;
+ }
+ }
+}
+
+extern "C" {
+
+CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
+ const FullComment *FC = getASTNodeAs<FullComment>(CXC);
+ if (!FC)
+ return createCXString((const char *) 0);
+
+ SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
+
+ SmallString<1024> XML;
+ CommentASTToXMLConverter Converter(SM, XML);
+ Converter.visit(FC);
+ return createCXString(XML.str(), /* DupString = */ true);
+}
+
+} // end extern "C"
+
diff --git a/tools/libclang/CXComment.h b/tools/libclang/CXComment.h
new file mode 100644
index 0000000..753877e
--- /dev/null
+++ b/tools/libclang/CXComment.h
@@ -0,0 +1,47 @@
+//===- CXComment.h - Routines for manipulating CXComments -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines routines for manipulating CXComments.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CXCOMMENT_H
+#define LLVM_CLANG_CXCOMMENT_H
+
+#include "clang-c/Index.h"
+
+#include "clang/AST/Comment.h"
+
+namespace clang {
+namespace cxcomment {
+
+inline CXComment createCXComment(const comments::Comment *C) {
+ CXComment Result;
+ Result.Data = C;
+ return Result;
+}
+
+inline const comments::Comment *getASTNode(CXComment CXC) {
+ return static_cast<const comments::Comment *>(CXC.Data);
+}
+
+template<typename T>
+inline const T *getASTNodeAs(CXComment CXC) {
+ const comments::Comment *C = getASTNode(CXC);
+ if (!C)
+ return NULL;
+
+ return dyn_cast<T>(C);
+}
+
+} // end namespace cxcomment
+} // end namespace clang
+
+#endif
+
diff --git a/tools/libclang/CXCompilationDatabase.cpp b/tools/libclang/CXCompilationDatabase.cpp
new file mode 100644
index 0000000..7bd319a
--- /dev/null
+++ b/tools/libclang/CXCompilationDatabase.cpp
@@ -0,0 +1,129 @@
+#include "clang-c/CXCompilationDatabase.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "CXString.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::cxstring;
+
+extern "C" {
+
+// FIXME: do something more usefull with the error message
+CXCompilationDatabase
+clang_CompilationDatabase_fromDirectory(const char *BuildDir,
+ CXCompilationDatabase_Error *ErrorCode)
+{
+ std::string ErrorMsg;
+ CXCompilationDatabase_Error Err = CXCompilationDatabase_NoError;
+
+ CompilationDatabase *db = CompilationDatabase::loadFromDirectory(BuildDir,
+ ErrorMsg);
+
+ if (!db) {
+ fprintf(stderr, "LIBCLANG TOOLING ERROR: %s\n", ErrorMsg.c_str());
+ Err = CXCompilationDatabase_CanNotLoadDatabase;
+ }
+
+ if (ErrorCode)
+ *ErrorCode = Err;
+
+ return db;
+}
+
+void
+clang_CompilationDatabase_dispose(CXCompilationDatabase CDb)
+{
+ delete static_cast<CompilationDatabase *>(CDb);
+}
+
+struct AllocatedCXCompileCommands
+{
+ std::vector<CompileCommand> CCmd;
+
+ AllocatedCXCompileCommands(const std::vector<CompileCommand>& Cmd)
+ : CCmd(Cmd)
+ { }
+};
+
+CXCompileCommands
+clang_CompilationDatabase_getCompileCommands(CXCompilationDatabase CDb,
+ const char *CompleteFileName)
+{
+ if (CompilationDatabase *db = static_cast<CompilationDatabase *>(CDb)) {
+ const std::vector<CompileCommand>
+ CCmd(db->getCompileCommands(CompleteFileName));
+ if (!CCmd.empty())
+ return new AllocatedCXCompileCommands( CCmd );
+ }
+
+ return 0;
+}
+
+void
+clang_CompileCommands_dispose(CXCompileCommands Cmds)
+{
+ delete static_cast<AllocatedCXCompileCommands *>(Cmds);
+}
+
+unsigned
+clang_CompileCommands_getSize(CXCompileCommands Cmds)
+{
+ if (!Cmds)
+ return 0;
+
+ AllocatedCXCompileCommands *ACC =
+ static_cast<AllocatedCXCompileCommands *>(Cmds);
+
+ return ACC->CCmd.size();
+}
+
+CXCompileCommand
+clang_CompileCommands_getCommand(CXCompileCommands Cmds, unsigned I)
+{
+ if (!Cmds)
+ return 0;
+
+ AllocatedCXCompileCommands *ACC =
+ static_cast<AllocatedCXCompileCommands *>(Cmds);
+
+ if (I >= ACC->CCmd.size())
+ return 0;
+
+ return &ACC->CCmd[I];
+}
+
+CXString
+clang_CompileCommand_getDirectory(CXCompileCommand CCmd)
+{
+ if (!CCmd)
+ return createCXString((const char*)NULL);
+
+ CompileCommand *cmd = static_cast<CompileCommand *>(CCmd);
+ return createCXString(cmd->Directory);
+}
+
+unsigned
+clang_CompileCommand_getNumArgs(CXCompileCommand CCmd)
+{
+ if (!CCmd)
+ return 0;
+
+ return static_cast<CompileCommand *>(CCmd)->CommandLine.size();
+}
+
+CXString
+clang_CompileCommand_getArg(CXCompileCommand CCmd, unsigned Arg)
+{
+ if (!CCmd)
+ return createCXString((const char*)NULL);
+
+ CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd);
+
+ if (Arg >= Cmd->CommandLine.size())
+ return createCXString((const char*)NULL);
+
+ return createCXString(Cmd->CommandLine[Arg]);
+}
+
+
+} // end: extern "C"
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index a298759..2757af4 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -30,9 +30,9 @@
using namespace clang;
using namespace cxcursor;
-CXCursor cxcursor::MakeCXCursorInvalid(CXCursorKind K) {
+CXCursor cxcursor::MakeCXCursorInvalid(CXCursorKind K, CXTranslationUnit TU) {
assert(K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid);
- CXCursor C = { K, 0, { 0, 0, 0 } };
+ CXCursor C = { K, 0, { 0, 0, TU } };
return C;
}
@@ -148,6 +148,10 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
case Stmt::AsmStmtClass:
K = CXCursor_AsmStmt;
break;
+
+ case Stmt::MSAsmStmtClass:
+ K = CXCursor_MSAsmStmt;
+ break;
case Stmt::ObjCAtTryStmtClass:
K = CXCursor_ObjCAtTryStmt;
@@ -229,7 +233,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
case Stmt::VAArgExprClass:
case Stmt::ObjCArrayLiteralClass:
case Stmt::ObjCDictionaryLiteralClass:
- case Stmt::ObjCNumericLiteralClass:
+ case Stmt::ObjCBoxedExprClass:
case Stmt::ObjCSubscriptRefExprClass:
K = CXCursor_UnexposedExpr;
break;
@@ -864,27 +868,10 @@ static inline void CollectOverriddenMethods(CXTranslationUnit TU,
/*MovedToSuper=*/false);
}
-void cxcursor::getOverriddenCursors(CXCursor cursor,
- SmallVectorImpl<CXCursor> &overridden) {
- assert(clang_isDeclaration(cursor.kind));
- Decl *D = getCursorDecl(cursor);
- if (!D)
- return;
-
- // Handle C++ member functions.
- CXTranslationUnit TU = getCursorTU(cursor);
- if (CXXMethodDecl *CXXMethod = dyn_cast<CXXMethodDecl>(D)) {
- for (CXXMethodDecl::method_iterator
- M = CXXMethod->begin_overridden_methods(),
- MEnd = CXXMethod->end_overridden_methods();
- M != MEnd; ++M)
- overridden.push_back(MakeCXCursor(const_cast<CXXMethodDecl*>(*M), TU));
- return;
- }
-
- ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(D);
- if (!Method)
- return;
+static void collectOverriddenMethodsSlow(CXTranslationUnit TU,
+ ObjCMethodDecl *Method,
+ SmallVectorImpl<CXCursor> &overridden) {
+ assert(Method->isOverriding());
if (ObjCProtocolDecl *
ProtD = dyn_cast<ObjCProtocolDecl>(Method->getDeclContext())) {
@@ -921,6 +908,83 @@ void cxcursor::getOverriddenCursors(CXCursor cursor,
}
}
+static void collectOnCategoriesAfterLocation(SourceLocation Loc,
+ ObjCInterfaceDecl *Class,
+ CXTranslationUnit TU,
+ ObjCMethodDecl *Method,
+ SmallVectorImpl<CXCursor> &Methods) {
+ if (!Class)
+ return;
+
+ SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
+ for (ObjCCategoryDecl *Category = Class->getCategoryList();
+ Category; Category = Category->getNextClassCategory())
+ if (SM.isBeforeInTranslationUnit(Loc, Category->getLocation()))
+ CollectOverriddenMethodsRecurse(TU, Category, Method, Methods, true);
+
+ collectOnCategoriesAfterLocation(Loc, Class->getSuperClass(), TU,
+ Method, Methods);
+}
+
+/// \brief Faster collection that is enabled when ObjCMethodDecl::isOverriding()
+/// returns false.
+/// You'd think that in that case there are no overrides but categories can
+/// "introduce" new overridden methods that are missed by Sema because the
+/// overrides lookup that it does for methods, inside implementations, will
+/// stop at the interface level (if there is a method there) and not look
+/// further in super classes.
+static void collectOverriddenMethodsFast(CXTranslationUnit TU,
+ ObjCMethodDecl *Method,
+ SmallVectorImpl<CXCursor> &Methods) {
+ assert(!Method->isOverriding());
+
+ ObjCContainerDecl *ContD = cast<ObjCContainerDecl>(Method->getDeclContext());
+ if (isa<ObjCInterfaceDecl>(ContD) || isa<ObjCProtocolDecl>(ContD))
+ return;
+ ObjCInterfaceDecl *Class = Method->getClassInterface();
+ if (!Class)
+ return;
+
+ collectOnCategoriesAfterLocation(Class->getLocation(), Class->getSuperClass(),
+ TU, Method, Methods);
+}
+
+void cxcursor::getOverriddenCursors(CXCursor cursor,
+ SmallVectorImpl<CXCursor> &overridden) {
+ assert(clang_isDeclaration(cursor.kind));
+ Decl *D = getCursorDecl(cursor);
+ if (!D)
+ return;
+
+ // Handle C++ member functions.
+ CXTranslationUnit TU = getCursorTU(cursor);
+ if (CXXMethodDecl *CXXMethod = dyn_cast<CXXMethodDecl>(D)) {
+ for (CXXMethodDecl::method_iterator
+ M = CXXMethod->begin_overridden_methods(),
+ MEnd = CXXMethod->end_overridden_methods();
+ M != MEnd; ++M)
+ overridden.push_back(MakeCXCursor(const_cast<CXXMethodDecl*>(*M), TU));
+ return;
+ }
+
+ ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(D);
+ if (!Method)
+ return;
+
+ if (Method->isRedeclaration()) {
+ Method = cast<ObjCContainerDecl>(Method->getDeclContext())->
+ getMethod(Method->getSelector(), Method->isInstanceMethod());
+ }
+
+ if (!Method->isOverriding()) {
+ collectOverriddenMethodsFast(TU, Method, overridden);
+ } else {
+ collectOverriddenMethodsSlow(TU, Method, overridden);
+ assert(!overridden.empty() &&
+ "ObjCMethodDecl's overriding bit is not as expected");
+ }
+}
+
std::pair<int, SourceLocation>
cxcursor::getSelectorIdentifierIndexAndLoc(CXCursor cursor) {
if (cursor.kind == CXCursor_ObjCMessageExpr) {
@@ -1134,7 +1198,8 @@ CXCompletionString clang_getCursorCompletionString(CXCursor cursor) {
= Result.CreateCodeCompletionString(unit->getASTContext(),
unit->getPreprocessor(),
unit->getCodeCompletionTUInfo().getAllocator(),
- unit->getCodeCompletionTUInfo());
+ unit->getCodeCompletionTUInfo(),
+ true);
return String;
}
}
@@ -1147,10 +1212,137 @@ CXCompletionString clang_getCursorCompletionString(CXCursor cursor) {
= Result.CreateCodeCompletionString(unit->getASTContext(),
unit->getPreprocessor(),
unit->getCodeCompletionTUInfo().getAllocator(),
- unit->getCodeCompletionTUInfo());
+ unit->getCodeCompletionTUInfo(),
+ false);
return String;
}
return NULL;
}
+} // end: extern C.
+
+namespace {
+ struct OverridenCursorsPool {
+ typedef llvm::SmallVector<CXCursor, 2> CursorVec;
+ std::vector<CursorVec*> AllCursors;
+ std::vector<CursorVec*> AvailableCursors;
+
+ ~OverridenCursorsPool() {
+ for (std::vector<CursorVec*>::iterator I = AllCursors.begin(),
+ E = AllCursors.end(); I != E; ++I) {
+ delete *I;
+ }
+ }
+ };
+}
+
+void *cxcursor::createOverridenCXCursorsPool() {
+ return new OverridenCursorsPool();
+}
+
+void cxcursor::disposeOverridenCXCursorsPool(void *pool) {
+ delete static_cast<OverridenCursorsPool*>(pool);
+}
+
+extern "C" {
+void clang_getOverriddenCursors(CXCursor cursor,
+ CXCursor **overridden,
+ unsigned *num_overridden) {
+ if (overridden)
+ *overridden = 0;
+ if (num_overridden)
+ *num_overridden = 0;
+
+ CXTranslationUnit TU = cxcursor::getCursorTU(cursor);
+
+ if (!overridden || !num_overridden || !TU)
+ return;
+
+ if (!clang_isDeclaration(cursor.kind))
+ return;
+
+ OverridenCursorsPool &pool =
+ *static_cast<OverridenCursorsPool*>(TU->OverridenCursorsPool);
+
+ OverridenCursorsPool::CursorVec *Vec = 0;
+ if (!pool.AvailableCursors.empty()) {
+ Vec = pool.AvailableCursors.back();
+ pool.AvailableCursors.pop_back();
+ }
+ else {
+ Vec = new OverridenCursorsPool::CursorVec();
+ pool.AllCursors.push_back(Vec);
+ }
+
+ // Clear out the vector, but don't free the memory contents. This
+ // reduces malloc() traffic.
+ Vec->clear();
+
+ // Use the first entry to contain a back reference to the vector.
+ // This is a complete hack.
+ CXCursor backRefCursor = MakeCXCursorInvalid(CXCursor_InvalidFile, TU);
+ backRefCursor.data[0] = Vec;
+ assert(cxcursor::getCursorTU(backRefCursor) == TU);
+ Vec->push_back(backRefCursor);
+
+ // Get the overriden cursors.
+ cxcursor::getOverriddenCursors(cursor, *Vec);
+
+ // Did we get any overriden cursors? If not, return Vec to the pool
+ // of available cursor vectors.
+ if (Vec->size() == 1) {
+ pool.AvailableCursors.push_back(Vec);
+ return;
+ }
+
+ // Now tell the caller about the overriden cursors.
+ assert(Vec->size() > 1);
+ *overridden = &((*Vec)[1]);
+ *num_overridden = Vec->size() - 1;
+}
+
+void clang_disposeOverriddenCursors(CXCursor *overridden) {
+ if (!overridden)
+ return;
+
+ // Use pointer arithmetic to get back the first faux entry
+ // which has a back-reference to the TU and the vector.
+ --overridden;
+ OverridenCursorsPool::CursorVec *Vec =
+ static_cast<OverridenCursorsPool::CursorVec*>(overridden->data[0]);
+ CXTranslationUnit TU = getCursorTU(*overridden);
+
+ assert(Vec && TU);
+
+ OverridenCursorsPool &pool =
+ *static_cast<OverridenCursorsPool*>(TU->OverridenCursorsPool);
+
+ pool.AvailableCursors.push_back(Vec);
+}
+
+int clang_Cursor_isDynamicCall(CXCursor C) {
+ const Expr *E = 0;
+ if (clang_isExpression(C.kind))
+ E = getCursorExpr(C);
+ if (!E)
+ return 0;
+
+ if (const ObjCMessageExpr *MsgE = dyn_cast<ObjCMessageExpr>(E))
+ return MsgE->getReceiverKind() == ObjCMessageExpr::Instance;
+
+ const MemberExpr *ME = 0;
+ if (isa<MemberExpr>(E))
+ ME = cast<MemberExpr>(E);
+ else if (const CallExpr *CE = dyn_cast<CallExpr>(E))
+ ME = dyn_cast_or_null<MemberExpr>(CE->getCallee());
+
+ if (ME) {
+ if (const CXXMethodDecl *
+ MD = dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl()))
+ return MD->isVirtual() && !ME->hasQualifier();
+ }
+
+ return 0;
+}
+
} // end: extern "C"
diff --git a/tools/libclang/CXCursor.h b/tools/libclang/CXCursor.h
index 947b0a3..120b881 100644
--- a/tools/libclang/CXCursor.h
+++ b/tools/libclang/CXCursor.h
@@ -55,7 +55,7 @@ CXCursor MakeCXCursor(clang::Decl *D, CXTranslationUnit TU,
CXCursor MakeCXCursor(clang::Stmt *S, clang::Decl *Parent,
CXTranslationUnit TU,
SourceRange RegionOfInterest = SourceRange());
-CXCursor MakeCXCursorInvalid(CXCursorKind K);
+CXCursor MakeCXCursorInvalid(CXCursorKind K, CXTranslationUnit TU = 0);
/// \brief Create an Objective-C superclass reference at the given location.
CXCursor MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super,
@@ -206,8 +206,15 @@ ASTUnit *getCursorASTUnit(CXCursor Cursor);
CXTranslationUnit getCursorTU(CXCursor Cursor);
void getOverriddenCursors(CXCursor cursor,
- SmallVectorImpl<CXCursor> &overridden);
+ SmallVectorImpl<CXCursor> &overridden);
+
+/// \brief Create an opaque pool used for fast generation of overriden
+/// CXCursor arrays.
+void *createOverridenCXCursorsPool();
+/// \brief Dispose of the overriden CXCursors pool.
+void disposeOverridenCXCursorsPool(void *pool);
+
/// \brief Returns a index/location pair for a selector identifier if the cursor
/// points to one.
std::pair<int, SourceLocation> getSelectorIdentifierIndexAndLoc(CXCursor);
diff --git a/tools/libclang/CXStoredDiagnostic.cpp b/tools/libclang/CXStoredDiagnostic.cpp
index 8284dc9..fa331de 100644
--- a/tools/libclang/CXStoredDiagnostic.cpp
+++ b/tools/libclang/CXStoredDiagnostic.cpp
@@ -66,13 +66,8 @@ CXString CXStoredDiagnostic::getDiagnosticOption(CXString *Disable) const {
*Disable = createCXString("-ferror-limit=0");
return createCXString("-ferror-limit=");
}
-
- bool EnabledByDefault;
- if (DiagnosticIDs::isBuiltinExtensionDiag(ID, EnabledByDefault) &&
- !EnabledByDefault)
- return createCXString("-pedantic");
- return createCXString("");
+ return createCXString("");
}
unsigned CXStoredDiagnostic::getCategory() const {
diff --git a/tools/libclang/CXTranslationUnit.h b/tools/libclang/CXTranslationUnit.h
index 3ad867c..37789aa 100644
--- a/tools/libclang/CXTranslationUnit.h
+++ b/tools/libclang/CXTranslationUnit.h
@@ -20,6 +20,7 @@ struct CXTranslationUnitImpl {
void *TUData;
void *StringPool;
void *Diagnostics;
+ void *OverridenCursorsPool;
};
}
diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp
index 850fac1..15f818a 100644
--- a/tools/libclang/CXType.cpp
+++ b/tools/libclang/CXType.cpp
@@ -94,7 +94,23 @@ static CXTypeKind GetTypeKind(QualType T) {
CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) {
- CXTypeKind TK = GetTypeKind(T);
+ CXTypeKind TK = CXType_Invalid;
+
+ if (TU && !T.isNull()) {
+ ASTContext &Ctx = static_cast<ASTUnit *>(TU->TUData)->getASTContext();
+ if (Ctx.getLangOpts().ObjC1) {
+ QualType UnqualT = T.getUnqualifiedType();
+ if (Ctx.isObjCIdType(UnqualT))
+ TK = CXType_ObjCId;
+ else if (Ctx.isObjCClassType(UnqualT))
+ TK = CXType_ObjCClass;
+ else if (Ctx.isObjCSelType(UnqualT))
+ TK = CXType_ObjCSel;
+ }
+ }
+ if (TK == CXType_Invalid)
+ TK = GetTypeKind(T);
+
CXType CT = { TK, { TK == CXType_Invalid ? 0 : T.getAsOpaquePtr(), TU }};
return CT;
}
diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp
index 74a8d37..acf8838 100644
--- a/tools/libclang/IndexBody.cpp
+++ b/tools/libclang/IndexBody.cpp
@@ -9,14 +9,14 @@
#include "IndexingContext.h"
-#include "clang/AST/RecursiveASTVisitor.h"
+#include "RecursiveASTVisitor.h"
using namespace clang;
using namespace cxindex;
namespace {
-class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
+class BodyIndexer : public cxindex::RecursiveASTVisitor<BodyIndexer> {
IndexingContext &IndexCtx;
const NamedDecl *Parent;
const DeclContext *ParentDC;
@@ -90,13 +90,19 @@ public:
return true;
}
- bool VisitObjCNumericLiteral(ObjCNumericLiteral *E) {
- if (ObjCMethodDecl *MD = E->getObjCNumericLiteralMethod())
+ bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
+ IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(),
+ Parent, ParentDC, E, CXIdxEntityRef_Direct);
+ return true;
+ }
+
+ bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) {
+ if (ObjCMethodDecl *MD = E->getBoxingMethod())
IndexCtx.handleReference(MD, E->getLocStart(),
Parent, ParentDC, E, CXIdxEntityRef_Implicit);
return true;
}
-
+
bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) {
if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod())
IndexCtx.handleReference(MD, E->getLocStart(),
diff --git a/tools/libclang/IndexDecl.cpp b/tools/libclang/IndexDecl.cpp
index c257c34..7560398 100644
--- a/tools/libclang/IndexDecl.cpp
+++ b/tools/libclang/IndexDecl.cpp
@@ -154,7 +154,20 @@ public:
IndexCtx.handleObjCImplementation(D);
IndexCtx.indexTUDeclsInObjCContainer();
- IndexCtx.indexDeclContext(D);
+
+ // Index the ivars first to make sure the synthesized ivars are indexed
+ // before indexing the methods that can reference them.
+ for (ObjCImplementationDecl::ivar_iterator
+ IvarI = D->ivar_begin(),
+ IvarE = D->ivar_end(); IvarI != IvarE; ++IvarI) {
+ IndexCtx.indexDecl(*IvarI);
+ }
+ for (DeclContext::decl_iterator
+ I = D->decls_begin(), E = D->decls_end(); I != E; ++I) {
+ if (!isa<ObjCIvarDecl>(*I))
+ IndexCtx.indexDecl(*I);
+ }
+
return true;
}
diff --git a/tools/libclang/IndexTypeSourceInfo.cpp b/tools/libclang/IndexTypeSourceInfo.cpp
index b62d521..67a06f2 100644
--- a/tools/libclang/IndexTypeSourceInfo.cpp
+++ b/tools/libclang/IndexTypeSourceInfo.cpp
@@ -9,14 +9,14 @@
#include "IndexingContext.h"
-#include "clang/AST/RecursiveASTVisitor.h"
+#include "RecursiveASTVisitor.h"
using namespace clang;
using namespace cxindex;
namespace {
-class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> {
+class TypeIndexer : public cxindex::RecursiveASTVisitor<TypeIndexer> {
IndexingContext &IndexCtx;
const NamedDecl *Parent;
const DeclContext *ParentDC;
diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp
index e660c4d..8fe9c36 100644
--- a/tools/libclang/Indexing.cpp
+++ b/tools/libclang/Indexing.cpp
@@ -353,7 +353,8 @@ static void clang_indexSourceFile_Impl(void *UserData) {
CInvok->getDiagnosticOpts().IgnoreWarnings = true;
ASTUnit *Unit = ASTUnit::create(CInvok.getPtr(), Diags,
- /*CaptureDiagnostics=*/true);
+ /*CaptureDiagnostics=*/true,
+ /*UserFilesAreVolatile=*/true);
OwningPtr<CXTUOwner> CXTU(new CXTUOwner(MakeCXTranslationUnit(CXXIdx, Unit)));
// Recover resources if we crash before exiting this method.
@@ -369,7 +370,6 @@ static void clang_indexSourceFile_Impl(void *UserData) {
IndexActionCleanup(IndexAction.get());
bool Persistent = requestedToGetTU;
- StringRef ResourceFilesPath = CXXIdx->getClangResourcesPath();
bool OnlyLocalDecls = false;
bool PrecompilePreamble = false;
bool CacheCodeCompletionResults = false;
@@ -393,11 +393,13 @@ static void clang_indexSourceFile_Impl(void *UserData) {
IndexAction.get(),
Unit,
Persistent,
- ResourceFilesPath,
+ CXXIdx->getClangResourcesPath(),
OnlyLocalDecls,
/*CaptureDiagnostics=*/true,
PrecompilePreamble,
- CacheCodeCompletionResults);
+ CacheCodeCompletionResults,
+ /*IncludeBriefCommentsInCodeCompletion=*/false,
+ /*UserFilesAreVolatile=*/true);
if (DiagTrap.hasErrorOccurred() && CXXIdx->getDisplayDiagnostics())
printDiagsToStderr(Unit);
diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h
index 6271660..00e1096 100644
--- a/tools/libclang/IndexingContext.h
+++ b/tools/libclang/IndexingContext.h
@@ -542,10 +542,8 @@ namespace llvm {
}
static unsigned getHashValue(clang::cxindex::RefFileOccurence S) {
- llvm::FoldingSetNodeID ID;
- ID.AddPointer(S.File);
- ID.AddPointer(S.Dcl);
- return ID.ComputeHash();
+ typedef std::pair<const clang::FileEntry *, const clang::Decl *> PairTy;
+ return DenseMapInfo<PairTy>::getHashValue(PairTy(S.File, S.Dcl));
}
static bool isEqual(clang::cxindex::RefFileOccurence LHS,
diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile
index 08bf3c6..975d381 100644
--- a/tools/libclang/Makefile
+++ b/tools/libclang/Makefile
@@ -15,11 +15,12 @@ EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/libclang.exports
LINK_LIBS_IN_SHARED = 1
SHARED_LIBRARY = 1
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangARCMigrate.a clangRewrite.a clangFrontend.a clangDriver.a \
clangSerialization.a \
clangParse.a clangSema.a clangEdit.a clangAnalysis.a \
- clangAST.a clangLex.a clangBasic.a
+ clangAST.a clangLex.a clangTooling.a clangBasic.a
include $(CLANG_LEVEL)/Makefile
@@ -42,7 +43,7 @@ ifeq ($(HOST_OS),Darwin)
endif
# Extra options to override libtool defaults.
- LLVMLibsOptions += -Wl,-dead_strip -Wl,-seg1addr,0xE0000000
+ LLVMLibsOptions += -Wl,-dead_strip
# Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line
DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/')
diff --git a/tools/libclang/RecursiveASTVisitor.h b/tools/libclang/RecursiveASTVisitor.h
new file mode 100644
index 0000000..7131025
--- /dev/null
+++ b/tools/libclang/RecursiveASTVisitor.h
@@ -0,0 +1,2185 @@
+//===--- RecursiveASTVisitor.h - Recursive AST Visitor ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the RecursiveASTVisitor interface, which recursively
+// traverses the entire AST.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_LIBCLANG_RECURSIVEASTVISITOR_H
+#define LLVM_CLANG_LIBCLANG_RECURSIVEASTVISITOR_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
+
+// The following three macros are used for meta programming. The code
+// using them is responsible for defining macro OPERATOR().
+
+// All unary operators.
+#define UNARYOP_LIST() \
+ OPERATOR(PostInc) OPERATOR(PostDec) \
+ OPERATOR(PreInc) OPERATOR(PreDec) \
+ OPERATOR(AddrOf) OPERATOR(Deref) \
+ OPERATOR(Plus) OPERATOR(Minus) \
+ OPERATOR(Not) OPERATOR(LNot) \
+ OPERATOR(Real) OPERATOR(Imag) \
+ OPERATOR(Extension)
+
+// All binary operators (excluding compound assign operators).
+#define BINOP_LIST() \
+ OPERATOR(PtrMemD) OPERATOR(PtrMemI) \
+ OPERATOR(Mul) OPERATOR(Div) OPERATOR(Rem) \
+ OPERATOR(Add) OPERATOR(Sub) OPERATOR(Shl) \
+ OPERATOR(Shr) \
+ \
+ OPERATOR(LT) OPERATOR(GT) OPERATOR(LE) \
+ OPERATOR(GE) OPERATOR(EQ) OPERATOR(NE) \
+ OPERATOR(And) OPERATOR(Xor) OPERATOR(Or) \
+ OPERATOR(LAnd) OPERATOR(LOr) \
+ \
+ OPERATOR(Assign) \
+ OPERATOR(Comma)
+
+// All compound assign operators.
+#define CAO_LIST() \
+ OPERATOR(Mul) OPERATOR(Div) OPERATOR(Rem) OPERATOR(Add) OPERATOR(Sub) \
+ OPERATOR(Shl) OPERATOR(Shr) OPERATOR(And) OPERATOR(Or) OPERATOR(Xor)
+
+namespace clang {
+namespace cxindex {
+
+// A helper macro to implement short-circuiting when recursing. It
+// invokes CALL_EXPR, which must be a method call, on the derived
+// object (s.t. a user of RecursiveASTVisitor can override the method
+// in CALL_EXPR).
+#define TRY_TO(CALL_EXPR) \
+ do { if (!getDerived().CALL_EXPR) return false; } while (0)
+
+/// \brief A class that does preorder depth-first traversal on the
+/// entire Clang AST and visits each node.
+///
+/// This class performs three distinct tasks:
+/// 1. traverse the AST (i.e. go to each node);
+/// 2. at a given node, walk up the class hierarchy, starting from
+/// the node's dynamic type, until the top-most class (e.g. Stmt,
+/// Decl, or Type) is reached.
+/// 3. given a (node, class) combination, where 'class' is some base
+/// class of the dynamic type of 'node', call a user-overridable
+/// function to actually visit the node.
+///
+/// These tasks are done by three groups of methods, respectively:
+/// 1. TraverseDecl(Decl *x) does task #1. It is the entry point
+/// for traversing an AST rooted at x. This method simply
+/// dispatches (i.e. forwards) to TraverseFoo(Foo *x) where Foo
+/// is the dynamic type of *x, which calls WalkUpFromFoo(x) and
+/// then recursively visits the child nodes of x.
+/// TraverseStmt(Stmt *x) and TraverseType(QualType x) work
+/// similarly.
+/// 2. WalkUpFromFoo(Foo *x) does task #2. It does not try to visit
+/// any child node of x. Instead, it first calls WalkUpFromBar(x)
+/// where Bar is the direct parent class of Foo (unless Foo has
+/// no parent), and then calls VisitFoo(x) (see the next list item).
+/// 3. VisitFoo(Foo *x) does task #3.
+///
+/// These three method groups are tiered (Traverse* > WalkUpFrom* >
+/// Visit*). A method (e.g. Traverse*) may call methods from the same
+/// tier (e.g. other Traverse*) or one tier lower (e.g. WalkUpFrom*).
+/// It may not call methods from a higher tier.
+///
+/// Note that since WalkUpFromFoo() calls WalkUpFromBar() (where Bar
+/// is Foo's super class) before calling VisitFoo(), the result is
+/// that the Visit*() methods for a given node are called in the
+/// top-down order (e.g. for a node of type NamedDecl, the order will
+/// be VisitDecl(), VisitNamedDecl(), and then VisitNamespaceDecl()).
+///
+/// This scheme guarantees that all Visit*() calls for the same AST
+/// node are grouped together. In other words, Visit*() methods for
+/// different nodes are never interleaved.
+///
+/// Stmts are traversed internally using a data queue to avoid a stack overflow
+/// with hugely nested ASTs.
+///
+/// Clients of this visitor should subclass the visitor (providing
+/// themselves as the template argument, using the curiously recurring
+/// template pattern) and override any of the Traverse*, WalkUpFrom*,
+/// and Visit* methods for declarations, types, statements,
+/// expressions, or other AST nodes where the visitor should customize
+/// behavior. Most users only need to override Visit*. Advanced
+/// users may override Traverse* and WalkUpFrom* to implement custom
+/// traversal strategies. Returning false from one of these overridden
+/// functions will abort the entire traversal.
+///
+/// By default, this visitor tries to visit every part of the explicit
+/// source code exactly once. The default policy towards templates
+/// is to descend into the 'pattern' class or function body, not any
+/// explicit or implicit instantiations. Explicit specializations
+/// are still visited, and the patterns of partial specializations
+/// are visited separately. This behavior can be changed by
+/// overriding shouldVisitTemplateInstantiations() in the derived class
+/// to return true, in which case all known implicit and explicit
+/// instantiations will be visited at the same time as the pattern
+/// from which they were produced.
+template<typename Derived>
+class RecursiveASTVisitor {
+public:
+ /// \brief Return a reference to the derived class.
+ Derived &getDerived() { return *static_cast<Derived*>(this); }
+
+ /// \brief Return whether this visitor should recurse into
+ /// template instantiations.
+ bool shouldVisitTemplateInstantiations() const { return false; }
+
+ /// \brief Return whether this visitor should recurse into the types of
+ /// TypeLocs.
+ bool shouldWalkTypesOfTypeLocs() const { return true; }
+
+ /// \brief Recursively visit a statement or expression, by
+ /// dispatching to Traverse*() based on the argument's dynamic type.
+ ///
+ /// \returns false if the visitation was terminated early, true
+ /// otherwise (including when the argument is NULL).
+ bool TraverseStmt(Stmt *S);
+
+ /// \brief Recursively visit a type, by dispatching to
+ /// Traverse*Type() based on the argument's getTypeClass() property.
+ ///
+ /// \returns false if the visitation was terminated early, true
+ /// otherwise (including when the argument is a Null type).
+ bool TraverseType(QualType T);
+
+ /// \brief Recursively visit a type with location, by dispatching to
+ /// Traverse*TypeLoc() based on the argument type's getTypeClass() property.
+ ///
+ /// \returns false if the visitation was terminated early, true
+ /// otherwise (including when the argument is a Null type location).
+ bool TraverseTypeLoc(TypeLoc TL);
+
+ /// \brief Recursively visit a declaration, by dispatching to
+ /// Traverse*Decl() based on the argument's dynamic type.
+ ///
+ /// \returns false if the visitation was terminated early, true
+ /// otherwise (including when the argument is NULL).
+ bool TraverseDecl(Decl *D);
+
+ /// \brief Recursively visit a C++ nested-name-specifier.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS);
+
+ /// \brief Recursively visit a C++ nested-name-specifier with location
+ /// information.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS);
+
+ /// \brief Recursively visit a name with its location information.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo);
+
+ /// \brief Recursively visit a template name and dispatch to the
+ /// appropriate method.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseTemplateName(TemplateName Template);
+
+ /// \brief Recursively visit a template argument and dispatch to the
+ /// appropriate method for the argument type.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ // FIXME: migrate callers to TemplateArgumentLoc instead.
+ bool TraverseTemplateArgument(const TemplateArgument &Arg);
+
+ /// \brief Recursively visit a template argument location and dispatch to the
+ /// appropriate method for the argument type.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc);
+
+ /// \brief Recursively visit a set of template arguments.
+ /// This can be overridden by a subclass, but it's not expected that
+ /// will be needed -- this visitor always dispatches to another.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ // FIXME: take a TemplateArgumentLoc* (or TemplateArgumentListInfo) instead.
+ bool TraverseTemplateArguments(const TemplateArgument *Args,
+ unsigned NumArgs);
+
+ /// \brief Recursively visit a constructor initializer. This
+ /// automatically dispatches to another visitor for the initializer
+ /// expression, but not for the name of the initializer, so may
+ /// be overridden for clients that need access to the name.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseConstructorInitializer(CXXCtorInitializer *Init);
+
+ /// \brief Recursively visit a lambda capture.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseLambdaCapture(LambdaExpr::Capture C);
+
+ // ---- Methods on Stmts ----
+
+ // Declare Traverse*() for all concrete Stmt classes.
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, PARENT) \
+ bool Traverse##CLASS(CLASS *S);
+#include "clang/AST/StmtNodes.inc"
+ // The above header #undefs ABSTRACT_STMT and STMT upon exit.
+
+ // Define WalkUpFrom*() and empty Visit*() for all Stmt classes.
+ bool WalkUpFromStmt(Stmt *S) { return getDerived().VisitStmt(S); }
+ bool VisitStmt(Stmt *S) { return true; }
+#define STMT(CLASS, PARENT) \
+ bool WalkUpFrom##CLASS(CLASS *S) { \
+ TRY_TO(WalkUpFrom##PARENT(S)); \
+ TRY_TO(Visit##CLASS(S)); \
+ return true; \
+ } \
+ bool Visit##CLASS(CLASS *S) { return true; }
+#include "clang/AST/StmtNodes.inc"
+
+ // Define Traverse*(), WalkUpFrom*(), and Visit*() for unary
+ // operator methods. Unary operators are not classes in themselves
+ // (they're all opcodes in UnaryOperator) but do have visitors.
+#define OPERATOR(NAME) \
+ bool TraverseUnary##NAME(UnaryOperator *S) { \
+ TRY_TO(WalkUpFromUnary##NAME(S)); \
+ StmtQueueAction StmtQueue(*this); \
+ StmtQueue.queue(S->getSubExpr()); \
+ return true; \
+ } \
+ bool WalkUpFromUnary##NAME(UnaryOperator *S) { \
+ TRY_TO(WalkUpFromUnaryOperator(S)); \
+ TRY_TO(VisitUnary##NAME(S)); \
+ return true; \
+ } \
+ bool VisitUnary##NAME(UnaryOperator *S) { return true; }
+
+ UNARYOP_LIST()
+#undef OPERATOR
+
+ // Define Traverse*(), WalkUpFrom*(), and Visit*() for binary
+ // operator methods. Binary operators are not classes in themselves
+ // (they're all opcodes in BinaryOperator) but do have visitors.
+#define GENERAL_BINOP_FALLBACK(NAME, BINOP_TYPE) \
+ bool TraverseBin##NAME(BINOP_TYPE *S) { \
+ TRY_TO(WalkUpFromBin##NAME(S)); \
+ StmtQueueAction StmtQueue(*this); \
+ StmtQueue.queue(S->getLHS()); \
+ StmtQueue.queue(S->getRHS()); \
+ return true; \
+ } \
+ bool WalkUpFromBin##NAME(BINOP_TYPE *S) { \
+ TRY_TO(WalkUpFrom##BINOP_TYPE(S)); \
+ TRY_TO(VisitBin##NAME(S)); \
+ return true; \
+ } \
+ bool VisitBin##NAME(BINOP_TYPE *S) { return true; }
+
+#define OPERATOR(NAME) GENERAL_BINOP_FALLBACK(NAME, BinaryOperator)
+ BINOP_LIST()
+#undef OPERATOR
+
+ // Define Traverse*(), WalkUpFrom*(), and Visit*() for compound
+ // assignment methods. Compound assignment operators are not
+ // classes in themselves (they're all opcodes in
+ // CompoundAssignOperator) but do have visitors.
+#define OPERATOR(NAME) \
+ GENERAL_BINOP_FALLBACK(NAME##Assign, CompoundAssignOperator)
+
+ CAO_LIST()
+#undef OPERATOR
+#undef GENERAL_BINOP_FALLBACK
+
+ // ---- Methods on Types ----
+ // FIXME: revamp to take TypeLoc's rather than Types.
+
+ // Declare Traverse*() for all concrete Type classes.
+#define ABSTRACT_TYPE(CLASS, BASE)
+#define TYPE(CLASS, BASE) \
+ bool Traverse##CLASS##Type(CLASS##Type *T);
+#include "clang/AST/TypeNodes.def"
+ // The above header #undefs ABSTRACT_TYPE and TYPE upon exit.
+
+ // Define WalkUpFrom*() and empty Visit*() for all Type classes.
+ bool WalkUpFromType(Type *T) { return getDerived().VisitType(T); }
+ bool VisitType(Type *T) { return true; }
+#define TYPE(CLASS, BASE) \
+ bool WalkUpFrom##CLASS##Type(CLASS##Type *T) { \
+ TRY_TO(WalkUpFrom##BASE(T)); \
+ TRY_TO(Visit##CLASS##Type(T)); \
+ return true; \
+ } \
+ bool Visit##CLASS##Type(CLASS##Type *T) { return true; }
+#include "clang/AST/TypeNodes.def"
+
+ // ---- Methods on TypeLocs ----
+ // FIXME: this currently just calls the matching Type methods
+
+ // Declare Traverse*() for all concrete Type classes.
+#define ABSTRACT_TYPELOC(CLASS, BASE)
+#define TYPELOC(CLASS, BASE) \
+ bool Traverse##CLASS##TypeLoc(CLASS##TypeLoc TL);
+#include "clang/AST/TypeLocNodes.def"
+ // The above header #undefs ABSTRACT_TYPELOC and TYPELOC upon exit.
+
+ // Define WalkUpFrom*() and empty Visit*() for all TypeLoc classes.
+ bool WalkUpFromTypeLoc(TypeLoc TL) { return getDerived().VisitTypeLoc(TL); }
+ bool VisitTypeLoc(TypeLoc TL) { return true; }
+
+ // QualifiedTypeLoc and UnqualTypeLoc are not declared in
+ // TypeNodes.def and thus need to be handled specially.
+ bool WalkUpFromQualifiedTypeLoc(QualifiedTypeLoc TL) {
+ return getDerived().VisitUnqualTypeLoc(TL.getUnqualifiedLoc());
+ }
+ bool VisitQualifiedTypeLoc(QualifiedTypeLoc TL) { return true; }
+ bool WalkUpFromUnqualTypeLoc(UnqualTypeLoc TL) {
+ return getDerived().VisitUnqualTypeLoc(TL.getUnqualifiedLoc());
+ }
+ bool VisitUnqualTypeLoc(UnqualTypeLoc TL) { return true; }
+
+ // Note that BASE includes trailing 'Type' which CLASS doesn't.
+#define TYPE(CLASS, BASE) \
+ bool WalkUpFrom##CLASS##TypeLoc(CLASS##TypeLoc TL) { \
+ TRY_TO(WalkUpFrom##BASE##Loc(TL)); \
+ TRY_TO(Visit##CLASS##TypeLoc(TL)); \
+ return true; \
+ } \
+ bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; }
+#include "clang/AST/TypeNodes.def"
+
+ // ---- Methods on Decls ----
+
+ // Declare Traverse*() for all concrete Decl classes.
+#define ABSTRACT_DECL(DECL)
+#define DECL(CLASS, BASE) \
+ bool Traverse##CLASS##Decl(CLASS##Decl *D);
+#include "clang/AST/DeclNodes.inc"
+ // The above header #undefs ABSTRACT_DECL and DECL upon exit.
+
+ // Define WalkUpFrom*() and empty Visit*() for all Decl classes.
+ bool WalkUpFromDecl(Decl *D) { return getDerived().VisitDecl(D); }
+ bool VisitDecl(Decl *D) { return true; }
+#define DECL(CLASS, BASE) \
+ bool WalkUpFrom##CLASS##Decl(CLASS##Decl *D) { \
+ TRY_TO(WalkUpFrom##BASE(D)); \
+ TRY_TO(Visit##CLASS##Decl(D)); \
+ return true; \
+ } \
+ bool Visit##CLASS##Decl(CLASS##Decl *D) { return true; }
+#include "clang/AST/DeclNodes.inc"
+
+private:
+ // These are helper methods used by more than one Traverse* method.
+ bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
+ bool TraverseClassInstantiations(ClassTemplateDecl *D);
+ bool TraverseFunctionInstantiations(FunctionTemplateDecl *D) ;
+ bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
+ unsigned Count);
+ bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
+ bool TraverseRecordHelper(RecordDecl *D);
+ bool TraverseCXXRecordHelper(CXXRecordDecl *D);
+ bool TraverseDeclaratorHelper(DeclaratorDecl *D);
+ bool TraverseDeclContextHelper(DeclContext *DC);
+ bool TraverseFunctionHelper(FunctionDecl *D);
+ bool TraverseVarHelper(VarDecl *D);
+
+ typedef SmallVector<Stmt *, 16> StmtsTy;
+ typedef SmallVector<StmtsTy *, 4> QueuesTy;
+
+ QueuesTy Queues;
+
+ class NewQueueRAII {
+ RecursiveASTVisitor &RAV;
+ public:
+ NewQueueRAII(StmtsTy &queue, RecursiveASTVisitor &RAV) : RAV(RAV) {
+ RAV.Queues.push_back(&queue);
+ }
+ ~NewQueueRAII() {
+ RAV.Queues.pop_back();
+ }
+ };
+
+ StmtsTy &getCurrentQueue() {
+ assert(!Queues.empty() && "base TraverseStmt was never called?");
+ return *Queues.back();
+ }
+
+public:
+ class StmtQueueAction {
+ StmtsTy &CurrQueue;
+ public:
+ explicit StmtQueueAction(RecursiveASTVisitor &RAV)
+ : CurrQueue(RAV.getCurrentQueue()) { }
+
+ void queue(Stmt *S) {
+ CurrQueue.push_back(S);
+ }
+ };
+};
+
+#define DISPATCH(NAME, CLASS, VAR) \
+ return getDerived().Traverse##NAME(static_cast<CLASS*>(VAR))
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseStmt(Stmt *S) {
+ if (!S)
+ return true;
+
+ StmtsTy Queue, StmtsToEnqueu;
+ Queue.push_back(S);
+ NewQueueRAII NQ(StmtsToEnqueu, *this);
+
+ while (!Queue.empty()) {
+ S = Queue.pop_back_val();
+ if (!S)
+ continue;
+
+ StmtsToEnqueu.clear();
+
+#define DISPATCH_STMT(NAME, CLASS, VAR) \
+ TRY_TO(Traverse##NAME(static_cast<CLASS*>(VAR))); break
+
+ // If we have a binary expr, dispatch to the subcode of the binop. A smart
+ // optimizer (e.g. LLVM) will fold this comparison into the switch stmt
+ // below.
+ if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
+ switch (BinOp->getOpcode()) {
+#define OPERATOR(NAME) \
+ case BO_##NAME: DISPATCH_STMT(Bin##NAME, BinaryOperator, S);
+
+ BINOP_LIST()
+#undef OPERATOR
+#undef BINOP_LIST
+
+#define OPERATOR(NAME) \
+ case BO_##NAME##Assign: \
+ DISPATCH_STMT(Bin##NAME##Assign, CompoundAssignOperator, S);
+
+ CAO_LIST()
+#undef OPERATOR
+#undef CAO_LIST
+ }
+ } else if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(S)) {
+ switch (UnOp->getOpcode()) {
+#define OPERATOR(NAME) \
+ case UO_##NAME: DISPATCH_STMT(Unary##NAME, UnaryOperator, S);
+
+ UNARYOP_LIST()
+#undef OPERATOR
+#undef UNARYOP_LIST
+ }
+ } else {
+
+ // Top switch stmt: dispatch to TraverseFooStmt for each concrete FooStmt.
+ switch (S->getStmtClass()) {
+ case Stmt::NoStmtClass: break;
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, PARENT) \
+ case Stmt::CLASS##Class: DISPATCH_STMT(CLASS, CLASS, S);
+#include "clang/AST/StmtNodes.inc"
+ }
+ }
+
+ for (SmallVector<Stmt *, 8>::reverse_iterator
+ RI = StmtsToEnqueu.rbegin(),
+ RE = StmtsToEnqueu.rend(); RI != RE; ++RI)
+ Queue.push_back(*RI);
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseType(QualType T) {
+ if (T.isNull())
+ return true;
+
+ switch (T->getTypeClass()) {
+#define ABSTRACT_TYPE(CLASS, BASE)
+#define TYPE(CLASS, BASE) \
+ case Type::CLASS: DISPATCH(CLASS##Type, CLASS##Type, \
+ const_cast<Type*>(T.getTypePtr()));
+#include "clang/AST/TypeNodes.def"
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTypeLoc(TypeLoc TL) {
+ if (TL.isNull())
+ return true;
+
+ switch (TL.getTypeLocClass()) {
+#define ABSTRACT_TYPELOC(CLASS, BASE)
+#define TYPELOC(CLASS, BASE) \
+ case TypeLoc::CLASS: \
+ return getDerived().Traverse##CLASS##TypeLoc(*cast<CLASS##TypeLoc>(&TL));
+#include "clang/AST/TypeLocNodes.def"
+ }
+
+ return true;
+}
+
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
+ if (!D)
+ return true;
+
+ // As a syntax visitor, we want to ignore declarations for
+ // implicitly-defined declarations (ones not typed explicitly by the
+ // user).
+ if (D->isImplicit())
+ return true;
+
+ switch (D->getKind()) {
+#define ABSTRACT_DECL(DECL)
+#define DECL(CLASS, BASE) \
+ case Decl::CLASS: DISPATCH(CLASS##Decl, CLASS##Decl, D);
+#include "clang/AST/DeclNodes.inc"
+ }
+
+ return true;
+}
+
+#undef DISPATCH
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifier(
+ NestedNameSpecifier *NNS) {
+ if (!NNS)
+ return true;
+
+ if (NNS->getPrefix())
+ TRY_TO(TraverseNestedNameSpecifier(NNS->getPrefix()));
+
+ switch (NNS->getKind()) {
+ case NestedNameSpecifier::Identifier:
+ case NestedNameSpecifier::Namespace:
+ case NestedNameSpecifier::NamespaceAlias:
+ case NestedNameSpecifier::Global:
+ return true;
+
+ case NestedNameSpecifier::TypeSpec:
+ case NestedNameSpecifier::TypeSpecWithTemplate:
+ TRY_TO(TraverseType(QualType(NNS->getAsType(), 0)));
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifierLoc(
+ NestedNameSpecifierLoc NNS) {
+ if (!NNS)
+ return true;
+
+ if (NestedNameSpecifierLoc Prefix = NNS.getPrefix())
+ TRY_TO(TraverseNestedNameSpecifierLoc(Prefix));
+
+ switch (NNS.getNestedNameSpecifier()->getKind()) {
+ case NestedNameSpecifier::Identifier:
+ case NestedNameSpecifier::Namespace:
+ case NestedNameSpecifier::NamespaceAlias:
+ case NestedNameSpecifier::Global:
+ return true;
+
+ case NestedNameSpecifier::TypeSpec:
+ case NestedNameSpecifier::TypeSpecWithTemplate:
+ TRY_TO(TraverseTypeLoc(NNS.getTypeLoc()));
+ break;
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseDeclarationNameInfo(
+ DeclarationNameInfo NameInfo) {
+ switch (NameInfo.getName().getNameKind()) {
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ if (TypeSourceInfo *TSInfo = NameInfo.getNamedTypeInfo())
+ TRY_TO(TraverseTypeLoc(TSInfo->getTypeLoc()));
+
+ break;
+
+ case DeclarationName::Identifier:
+ case DeclarationName::ObjCZeroArgSelector:
+ case DeclarationName::ObjCOneArgSelector:
+ case DeclarationName::ObjCMultiArgSelector:
+ case DeclarationName::CXXOperatorName:
+ case DeclarationName::CXXLiteralOperatorName:
+ case DeclarationName::CXXUsingDirective:
+ break;
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateName(TemplateName Template) {
+ if (DependentTemplateName *DTN = Template.getAsDependentTemplateName())
+ TRY_TO(TraverseNestedNameSpecifier(DTN->getQualifier()));
+ else if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName())
+ TRY_TO(TraverseNestedNameSpecifier(QTN->getQualifier()));
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateArgument(
+ const TemplateArgument &Arg) {
+ switch (Arg.getKind()) {
+ case TemplateArgument::Null:
+ case TemplateArgument::Declaration:
+ case TemplateArgument::Integral:
+ return true;
+
+ case TemplateArgument::Type:
+ return getDerived().TraverseType(Arg.getAsType());
+
+ case TemplateArgument::Template:
+ case TemplateArgument::TemplateExpansion:
+ return getDerived().TraverseTemplateName(
+ Arg.getAsTemplateOrTemplatePattern());
+
+ case TemplateArgument::Expression:
+ return getDerived().TraverseStmt(Arg.getAsExpr());
+
+ case TemplateArgument::Pack:
+ return getDerived().TraverseTemplateArguments(Arg.pack_begin(),
+ Arg.pack_size());
+ }
+
+ return true;
+}
+
+// FIXME: no template name location?
+// FIXME: no source locations for a template argument pack?
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLoc(
+ const TemplateArgumentLoc &ArgLoc) {
+ const TemplateArgument &Arg = ArgLoc.getArgument();
+
+ switch (Arg.getKind()) {
+ case TemplateArgument::Null:
+ case TemplateArgument::Declaration:
+ case TemplateArgument::Integral:
+ return true;
+
+ case TemplateArgument::Type: {
+ // FIXME: how can TSI ever be NULL?
+ if (TypeSourceInfo *TSI = ArgLoc.getTypeSourceInfo())
+ return getDerived().TraverseTypeLoc(TSI->getTypeLoc());
+ else
+ return getDerived().TraverseType(Arg.getAsType());
+ }
+
+ case TemplateArgument::Template:
+ case TemplateArgument::TemplateExpansion:
+ if (ArgLoc.getTemplateQualifierLoc())
+ TRY_TO(getDerived().TraverseNestedNameSpecifierLoc(
+ ArgLoc.getTemplateQualifierLoc()));
+ return getDerived().TraverseTemplateName(
+ Arg.getAsTemplateOrTemplatePattern());
+
+ case TemplateArgument::Expression:
+ return getDerived().TraverseStmt(ArgLoc.getSourceExpression());
+
+ case TemplateArgument::Pack:
+ return getDerived().TraverseTemplateArguments(Arg.pack_begin(),
+ Arg.pack_size());
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateArguments(
+ const TemplateArgument *Args,
+ unsigned NumArgs) {
+ for (unsigned I = 0; I != NumArgs; ++I) {
+ TRY_TO(TraverseTemplateArgument(Args[I]));
+ }
+
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConstructorInitializer(
+ CXXCtorInitializer *Init) {
+ if (TypeSourceInfo *TInfo = Init->getTypeSourceInfo())
+ TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+
+ if (Init->isWritten())
+ TRY_TO(TraverseStmt(Init->getInit()));
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseLambdaCapture(LambdaExpr::Capture C){
+ return true;
+}
+
+// ----------------- Type traversal -----------------
+
+// This macro makes available a variable T, the passed-in type.
+#define DEF_TRAVERSE_TYPE(TYPE, CODE) \
+ template<typename Derived> \
+ bool RecursiveASTVisitor<Derived>::Traverse##TYPE (TYPE *T) { \
+ TRY_TO(WalkUpFrom##TYPE (T)); \
+ { CODE; } \
+ return true; \
+ }
+
+DEF_TRAVERSE_TYPE(BuiltinType, { })
+
+DEF_TRAVERSE_TYPE(ComplexType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPE(PointerType, {
+ TRY_TO(TraverseType(T->getPointeeType()));
+ })
+
+DEF_TRAVERSE_TYPE(BlockPointerType, {
+ TRY_TO(TraverseType(T->getPointeeType()));
+ })
+
+DEF_TRAVERSE_TYPE(LValueReferenceType, {
+ TRY_TO(TraverseType(T->getPointeeType()));
+ })
+
+DEF_TRAVERSE_TYPE(RValueReferenceType, {
+ TRY_TO(TraverseType(T->getPointeeType()));
+ })
+
+DEF_TRAVERSE_TYPE(MemberPointerType, {
+ TRY_TO(TraverseType(QualType(T->getClass(), 0)));
+ TRY_TO(TraverseType(T->getPointeeType()));
+ })
+
+DEF_TRAVERSE_TYPE(ConstantArrayType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPE(IncompleteArrayType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPE(VariableArrayType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ TRY_TO(TraverseStmt(T->getSizeExpr()));
+ })
+
+DEF_TRAVERSE_TYPE(DependentSizedArrayType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ if (T->getSizeExpr())
+ TRY_TO(TraverseStmt(T->getSizeExpr()));
+ })
+
+DEF_TRAVERSE_TYPE(DependentSizedExtVectorType, {
+ if (T->getSizeExpr())
+ TRY_TO(TraverseStmt(T->getSizeExpr()));
+ TRY_TO(TraverseType(T->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPE(VectorType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPE(ExtVectorType, {
+ TRY_TO(TraverseType(T->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPE(FunctionNoProtoType, {
+ TRY_TO(TraverseType(T->getResultType()));
+ })
+
+DEF_TRAVERSE_TYPE(FunctionProtoType, {
+ TRY_TO(TraverseType(T->getResultType()));
+
+ for (FunctionProtoType::arg_type_iterator A = T->arg_type_begin(),
+ AEnd = T->arg_type_end();
+ A != AEnd; ++A) {
+ TRY_TO(TraverseType(*A));
+ }
+
+ for (FunctionProtoType::exception_iterator E = T->exception_begin(),
+ EEnd = T->exception_end();
+ E != EEnd; ++E) {
+ TRY_TO(TraverseType(*E));
+ }
+ })
+
+DEF_TRAVERSE_TYPE(UnresolvedUsingType, { })
+DEF_TRAVERSE_TYPE(TypedefType, { })
+
+DEF_TRAVERSE_TYPE(TypeOfExprType, {
+ TRY_TO(TraverseStmt(T->getUnderlyingExpr()));
+ })
+
+DEF_TRAVERSE_TYPE(TypeOfType, {
+ TRY_TO(TraverseType(T->getUnderlyingType()));
+ })
+
+DEF_TRAVERSE_TYPE(DecltypeType, {
+ TRY_TO(TraverseStmt(T->getUnderlyingExpr()));
+ })
+
+DEF_TRAVERSE_TYPE(UnaryTransformType, {
+ TRY_TO(TraverseType(T->getBaseType()));
+ TRY_TO(TraverseType(T->getUnderlyingType()));
+ })
+
+DEF_TRAVERSE_TYPE(AutoType, {
+ TRY_TO(TraverseType(T->getDeducedType()));
+ })
+
+DEF_TRAVERSE_TYPE(RecordType, { })
+DEF_TRAVERSE_TYPE(EnumType, { })
+DEF_TRAVERSE_TYPE(TemplateTypeParmType, { })
+DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, { })
+DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, { })
+
+DEF_TRAVERSE_TYPE(TemplateSpecializationType, {
+ TRY_TO(TraverseTemplateName(T->getTemplateName()));
+ TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs()));
+ })
+
+DEF_TRAVERSE_TYPE(InjectedClassNameType, { })
+
+DEF_TRAVERSE_TYPE(AttributedType, {
+ TRY_TO(TraverseType(T->getModifiedType()));
+ })
+
+DEF_TRAVERSE_TYPE(ParenType, {
+ TRY_TO(TraverseType(T->getInnerType()));
+ })
+
+DEF_TRAVERSE_TYPE(ElaboratedType, {
+ if (T->getQualifier()) {
+ TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+ }
+ TRY_TO(TraverseType(T->getNamedType()));
+ })
+
+DEF_TRAVERSE_TYPE(DependentNameType, {
+ TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+ })
+
+DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, {
+ TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+ TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs()));
+ })
+
+DEF_TRAVERSE_TYPE(PackExpansionType, {
+ TRY_TO(TraverseType(T->getPattern()));
+ })
+
+DEF_TRAVERSE_TYPE(ObjCInterfaceType, { })
+
+DEF_TRAVERSE_TYPE(ObjCObjectType, {
+ // We have to watch out here because an ObjCInterfaceType's base
+ // type is itself.
+ if (T->getBaseType().getTypePtr() != T)
+ TRY_TO(TraverseType(T->getBaseType()));
+ })
+
+DEF_TRAVERSE_TYPE(ObjCObjectPointerType, {
+ TRY_TO(TraverseType(T->getPointeeType()));
+ })
+
+DEF_TRAVERSE_TYPE(AtomicType, {
+ TRY_TO(TraverseType(T->getValueType()));
+ })
+
+#undef DEF_TRAVERSE_TYPE
+
+// ----------------- TypeLoc traversal -----------------
+
+// This macro makes available a variable TL, the passed-in TypeLoc.
+// If requested, it calls WalkUpFrom* for the Type in the given TypeLoc,
+// in addition to WalkUpFrom* for the TypeLoc itself, such that existing
+// clients that override the WalkUpFrom*Type() and/or Visit*Type() methods
+// continue to work.
+#define DEF_TRAVERSE_TYPELOC(TYPE, CODE) \
+ template<typename Derived> \
+ bool RecursiveASTVisitor<Derived>::Traverse##TYPE##Loc(TYPE##Loc TL) { \
+ if (getDerived().shouldWalkTypesOfTypeLocs()) \
+ TRY_TO(WalkUpFrom##TYPE(const_cast<TYPE*>(TL.getTypePtr()))); \
+ TRY_TO(WalkUpFrom##TYPE##Loc(TL)); \
+ { CODE; } \
+ return true; \
+ }
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseQualifiedTypeLoc(
+ QualifiedTypeLoc TL) {
+ // Move this over to the 'main' typeloc tree. Note that this is a
+ // move -- we pretend that we were really looking at the unqualified
+ // typeloc all along -- rather than a recursion, so we don't follow
+ // the normal CRTP plan of going through
+ // getDerived().TraverseTypeLoc. If we did, we'd be traversing
+ // twice for the same type (once as a QualifiedTypeLoc version of
+ // the type, once as an UnqualifiedTypeLoc version of the type),
+ // which in effect means we'd call VisitTypeLoc twice with the
+ // 'same' type. This solves that problem, at the cost of never
+ // seeing the qualified version of the type (unless the client
+ // subclasses TraverseQualifiedTypeLoc themselves). It's not a
+ // perfect solution. A perfect solution probably requires making
+ // QualifiedTypeLoc a wrapper around TypeLoc -- like QualType is a
+ // wrapper around Type* -- rather than being its own class in the
+ // type hierarchy.
+ return TraverseTypeLoc(TL.getUnqualifiedLoc());
+}
+
+DEF_TRAVERSE_TYPELOC(BuiltinType, { })
+
+// FIXME: ComplexTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(ComplexType, {
+ TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPELOC(PointerType, {
+ TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(BlockPointerType, {
+ TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(LValueReferenceType, {
+ TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(RValueReferenceType, {
+ TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+ })
+
+// FIXME: location of base class?
+// We traverse this in the type case as well, but how is it not reached through
+// the pointee type?
+DEF_TRAVERSE_TYPELOC(MemberPointerType, {
+ TRY_TO(TraverseType(QualType(TL.getTypePtr()->getClass(), 0)));
+ TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+ })
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseArrayTypeLocHelper(ArrayTypeLoc TL) {
+ // This isn't available for ArrayType, but is for the ArrayTypeLoc.
+ TRY_TO(TraverseStmt(TL.getSizeExpr()));
+ return true;
+}
+
+DEF_TRAVERSE_TYPELOC(ConstantArrayType, {
+ TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+ return TraverseArrayTypeLocHelper(TL);
+ })
+
+DEF_TRAVERSE_TYPELOC(IncompleteArrayType, {
+ TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+ return TraverseArrayTypeLocHelper(TL);
+ })
+
+DEF_TRAVERSE_TYPELOC(VariableArrayType, {
+ TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+ return TraverseArrayTypeLocHelper(TL);
+ })
+
+DEF_TRAVERSE_TYPELOC(DependentSizedArrayType, {
+ TRY_TO(TraverseTypeLoc(TL.getElementLoc()));
+ return TraverseArrayTypeLocHelper(TL);
+ })
+
+// FIXME: order? why not size expr first?
+// FIXME: base VectorTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(DependentSizedExtVectorType, {
+ if (TL.getTypePtr()->getSizeExpr())
+ TRY_TO(TraverseStmt(TL.getTypePtr()->getSizeExpr()));
+ TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+ })
+
+// FIXME: VectorTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(VectorType, {
+ TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+ })
+
+// FIXME: size and attributes
+// FIXME: base VectorTypeLoc is unfinished
+DEF_TRAVERSE_TYPELOC(ExtVectorType, {
+ TRY_TO(TraverseType(TL.getTypePtr()->getElementType()));
+ })
+
+DEF_TRAVERSE_TYPELOC(FunctionNoProtoType, {
+ TRY_TO(TraverseTypeLoc(TL.getResultLoc()));
+ })
+
+// FIXME: location of exception specifications (attributes?)
+DEF_TRAVERSE_TYPELOC(FunctionProtoType, {
+ TRY_TO(TraverseTypeLoc(TL.getResultLoc()));
+
+ const FunctionProtoType *T = TL.getTypePtr();
+
+ for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
+ if (TL.getArg(I)) {
+ TRY_TO(TraverseDecl(TL.getArg(I)));
+ } else if (I < T->getNumArgs()) {
+ TRY_TO(TraverseType(T->getArgType(I)));
+ }
+ }
+
+ for (FunctionProtoType::exception_iterator E = T->exception_begin(),
+ EEnd = T->exception_end();
+ E != EEnd; ++E) {
+ TRY_TO(TraverseType(*E));
+ }
+ })
+
+DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, { })
+DEF_TRAVERSE_TYPELOC(TypedefType, { })
+
+DEF_TRAVERSE_TYPELOC(TypeOfExprType, {
+ TRY_TO(TraverseStmt(TL.getUnderlyingExpr()));
+ })
+
+DEF_TRAVERSE_TYPELOC(TypeOfType, {
+ TRY_TO(TraverseTypeLoc(TL.getUnderlyingTInfo()->getTypeLoc()));
+ })
+
+// FIXME: location of underlying expr
+DEF_TRAVERSE_TYPELOC(DecltypeType, {
+ TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr()));
+ })
+
+DEF_TRAVERSE_TYPELOC(UnaryTransformType, {
+ TRY_TO(TraverseTypeLoc(TL.getUnderlyingTInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(AutoType, {
+ TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType()));
+ })
+
+DEF_TRAVERSE_TYPELOC(RecordType, { })
+DEF_TRAVERSE_TYPELOC(EnumType, { })
+DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, { })
+DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, { })
+DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, { })
+
+// FIXME: use the loc for the template name?
+DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, {
+ TRY_TO(TraverseTemplateName(TL.getTypePtr()->getTemplateName()));
+ for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
+ TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
+ }
+ })
+
+DEF_TRAVERSE_TYPELOC(InjectedClassNameType, { })
+
+DEF_TRAVERSE_TYPELOC(ParenType, {
+ TRY_TO(TraverseTypeLoc(TL.getInnerLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(AttributedType, {
+ TRY_TO(TraverseTypeLoc(TL.getModifiedLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(ElaboratedType, {
+ if (TL.getQualifierLoc()) {
+ TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
+ }
+ TRY_TO(TraverseTypeLoc(TL.getNamedTypeLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(DependentNameType, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(DependentTemplateSpecializationType, {
+ if (TL.getQualifierLoc()) {
+ TRY_TO(TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()));
+ }
+
+ for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) {
+ TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
+ }
+ })
+
+DEF_TRAVERSE_TYPELOC(PackExpansionType, {
+ TRY_TO(TraverseTypeLoc(TL.getPatternLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(ObjCInterfaceType, { })
+
+DEF_TRAVERSE_TYPELOC(ObjCObjectType, {
+ // We have to watch out here because an ObjCInterfaceType's base
+ // type is itself.
+ if (TL.getTypePtr()->getBaseType().getTypePtr() != TL.getTypePtr())
+ TRY_TO(TraverseTypeLoc(TL.getBaseLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(ObjCObjectPointerType, {
+ TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
+ })
+
+DEF_TRAVERSE_TYPELOC(AtomicType, {
+ TRY_TO(TraverseTypeLoc(TL.getValueLoc()));
+ })
+
+#undef DEF_TRAVERSE_TYPELOC
+
+// ----------------- Decl traversal -----------------
+//
+// For a Decl, we automate (in the DEF_TRAVERSE_DECL macro) traversing
+// the children that come from the DeclContext associated with it.
+// Therefore each Traverse* only needs to worry about children other
+// than those.
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseDeclContextHelper(DeclContext *DC) {
+ if (!DC)
+ return true;
+
+ for (DeclContext::decl_iterator Child = DC->decls_begin(),
+ ChildEnd = DC->decls_end();
+ Child != ChildEnd; ++Child) {
+ // BlockDecls are traversed through BlockExprs.
+ if (!isa<BlockDecl>(*Child))
+ TRY_TO(TraverseDecl(*Child));
+ }
+
+ return true;
+}
+
+// This macro makes available a variable D, the passed-in decl.
+#define DEF_TRAVERSE_DECL(DECL, CODE) \
+template<typename Derived> \
+bool RecursiveASTVisitor<Derived>::Traverse##DECL (DECL *D) { \
+ TRY_TO(WalkUpFrom##DECL (D)); \
+ { CODE; } \
+ TRY_TO(TraverseDeclContextHelper(dyn_cast<DeclContext>(D))); \
+ return true; \
+}
+
+DEF_TRAVERSE_DECL(AccessSpecDecl, { })
+
+DEF_TRAVERSE_DECL(BlockDecl, {
+ if (TypeSourceInfo *TInfo = D->getSignatureAsWritten())
+ TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+ TRY_TO(TraverseStmt(D->getBody()));
+ // This return statement makes sure the traversal of nodes in
+ // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
+ // is skipped - don't remove it.
+ return true;
+ })
+
+DEF_TRAVERSE_DECL(FileScopeAsmDecl, {
+ TRY_TO(TraverseStmt(D->getAsmString()));
+ })
+
+DEF_TRAVERSE_DECL(ImportDecl, { })
+
+DEF_TRAVERSE_DECL(FriendDecl, {
+ // Friend is either decl or a type.
+ if (D->getFriendType())
+ TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+ else
+ TRY_TO(TraverseDecl(D->getFriendDecl()));
+ })
+
+DEF_TRAVERSE_DECL(FriendTemplateDecl, {
+ if (D->getFriendType())
+ TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+ else
+ TRY_TO(TraverseDecl(D->getFriendDecl()));
+ for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
+ TemplateParameterList *TPL = D->getTemplateParameterList(I);
+ for (TemplateParameterList::iterator ITPL = TPL->begin(),
+ ETPL = TPL->end();
+ ITPL != ETPL; ++ITPL) {
+ TRY_TO(TraverseDecl(*ITPL));
+ }
+ }
+ })
+
+DEF_TRAVERSE_DECL(ClassScopeFunctionSpecializationDecl, {
+ TRY_TO(TraverseDecl(D->getSpecialization()));
+ })
+
+DEF_TRAVERSE_DECL(LinkageSpecDecl, { })
+
+DEF_TRAVERSE_DECL(ObjCPropertyImplDecl, {
+ // FIXME: implement this
+ })
+
+DEF_TRAVERSE_DECL(StaticAssertDecl, {
+ TRY_TO(TraverseStmt(D->getAssertExpr()));
+ TRY_TO(TraverseStmt(D->getMessage()));
+ })
+
+DEF_TRAVERSE_DECL(TranslationUnitDecl, {
+ // Code in an unnamed namespace shows up automatically in
+ // decls_begin()/decls_end(). Thus we don't need to recurse on
+ // D->getAnonymousNamespace().
+ })
+
+DEF_TRAVERSE_DECL(NamespaceAliasDecl, {
+ // We shouldn't traverse an aliased namespace, since it will be
+ // defined (and, therefore, traversed) somewhere else.
+ //
+ // This return statement makes sure the traversal of nodes in
+ // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
+ // is skipped - don't remove it.
+ return true;
+ })
+
+DEF_TRAVERSE_DECL(LabelDecl, {
+ // There is no code in a LabelDecl.
+})
+
+
+DEF_TRAVERSE_DECL(NamespaceDecl, {
+ // Code in an unnamed namespace shows up automatically in
+ // decls_begin()/decls_end(). Thus we don't need to recurse on
+ // D->getAnonymousNamespace().
+ })
+
+DEF_TRAVERSE_DECL(ObjCCompatibleAliasDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(ObjCCategoryDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(ObjCCategoryImplDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(ObjCImplementationDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(ObjCInterfaceDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(ObjCProtocolDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(ObjCMethodDecl, {
+ if (D->getResultTypeSourceInfo()) {
+ TRY_TO(TraverseTypeLoc(D->getResultTypeSourceInfo()->getTypeLoc()));
+ }
+ for (ObjCMethodDecl::param_iterator
+ I = D->param_begin(), E = D->param_end(); I != E; ++I) {
+ TRY_TO(TraverseDecl(*I));
+ }
+ if (D->isThisDeclarationADefinition()) {
+ TRY_TO(TraverseStmt(D->getBody()));
+ }
+ return true;
+ })
+
+DEF_TRAVERSE_DECL(ObjCPropertyDecl, {
+ // FIXME: implement
+ })
+
+DEF_TRAVERSE_DECL(UsingDecl, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
+ })
+
+DEF_TRAVERSE_DECL(UsingDirectiveDecl, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ })
+
+DEF_TRAVERSE_DECL(UsingShadowDecl, { })
+
+// A helper method for TemplateDecl's children.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateParameterListHelper(
+ TemplateParameterList *TPL) {
+ if (TPL) {
+ for (TemplateParameterList::iterator I = TPL->begin(), E = TPL->end();
+ I != E; ++I) {
+ TRY_TO(TraverseDecl(*I));
+ }
+ }
+ return true;
+}
+
+// A helper method for traversing the implicit instantiations of a
+// class template.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseClassInstantiations(
+ ClassTemplateDecl *D) {
+ ClassTemplateDecl::spec_iterator end = D->spec_end();
+ for (ClassTemplateDecl::spec_iterator it = D->spec_begin(); it != end; ++it) {
+ ClassTemplateSpecializationDecl* SD = *it;
+
+ switch (SD->getSpecializationKind()) {
+ // Visit the implicit instantiations with the requested pattern.
+ case TSK_Undeclared:
+ case TSK_ImplicitInstantiation:
+ TRY_TO(TraverseDecl(SD));
+ break;
+
+ // We don't need to do anything on an explicit instantiation
+ // or explicit specialization because there will be an explicit
+ // node for it elsewhere.
+ case TSK_ExplicitInstantiationDeclaration:
+ case TSK_ExplicitInstantiationDefinition:
+ case TSK_ExplicitSpecialization:
+ break;
+ }
+ }
+
+ return true;
+}
+
+DEF_TRAVERSE_DECL(ClassTemplateDecl, {
+ CXXRecordDecl* TempDecl = D->getTemplatedDecl();
+ TRY_TO(TraverseDecl(TempDecl));
+ TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+
+ // By default, we do not traverse the instantiations of
+ // class templates since they do not appear in the user code. The
+ // following code optionally traverses them.
+ //
+ // We only traverse the class instantiations when we see the canonical
+ // declaration of the template, to ensure we only visit them once.
+ if (getDerived().shouldVisitTemplateInstantiations() &&
+ D == D->getCanonicalDecl())
+ TRY_TO(TraverseClassInstantiations(D));
+
+ // Note that getInstantiatedFromMemberTemplate() is just a link
+ // from a template instantiation back to the template from which
+ // it was instantiated, and thus should not be traversed.
+ })
+
+// A helper method for traversing the instantiations of a
+// function while skipping its specializations.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseFunctionInstantiations(
+ FunctionTemplateDecl *D) {
+ FunctionTemplateDecl::spec_iterator end = D->spec_end();
+ for (FunctionTemplateDecl::spec_iterator it = D->spec_begin(); it != end;
+ ++it) {
+ FunctionDecl* FD = *it;
+ switch (FD->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ImplicitInstantiation:
+ // We don't know what kind of FunctionDecl this is.
+ TRY_TO(TraverseDecl(FD));
+ break;
+
+ // No need to visit explicit instantiations, we'll find the node
+ // eventually.
+ case TSK_ExplicitInstantiationDeclaration:
+ case TSK_ExplicitInstantiationDefinition:
+ break;
+
+ case TSK_ExplicitSpecialization:
+ break;
+ }
+ }
+
+ return true;
+}
+
+DEF_TRAVERSE_DECL(FunctionTemplateDecl, {
+ TRY_TO(TraverseDecl(D->getTemplatedDecl()));
+ TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+
+ // By default, we do not traverse the instantiations of
+ // function templates since they do not appear in the user code. The
+ // following code optionally traverses them.
+ //
+ // We only traverse the function instantiations when we see the canonical
+ // declaration of the template, to ensure we only visit them once.
+ if (getDerived().shouldVisitTemplateInstantiations() &&
+ D == D->getCanonicalDecl())
+ TRY_TO(TraverseFunctionInstantiations(D));
+ })
+
+DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, {
+ // D is the "T" in something like
+ // template <template <typename> class T> class container { };
+ TRY_TO(TraverseDecl(D->getTemplatedDecl()));
+ if (D->hasDefaultArgument()) {
+ TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument()));
+ }
+ TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+ })
+
+DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
+ // D is the "T" in something like "template<typename T> class vector;"
+ if (D->getTypeForDecl())
+ TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+ if (D->hasDefaultArgument())
+ TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_DECL(TypedefDecl, {
+ TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+ // We shouldn't traverse D->getTypeForDecl(); it's a result of
+ // declaring the typedef, not something that was written in the
+ // source.
+ })
+
+DEF_TRAVERSE_DECL(TypeAliasDecl, {
+ TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+ // We shouldn't traverse D->getTypeForDecl(); it's a result of
+ // declaring the type alias, not something that was written in the
+ // source.
+ })
+
+DEF_TRAVERSE_DECL(TypeAliasTemplateDecl, {
+ TRY_TO(TraverseDecl(D->getTemplatedDecl()));
+ TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+ })
+
+DEF_TRAVERSE_DECL(UnresolvedUsingTypenameDecl, {
+ // A dependent using declaration which was marked with 'typename'.
+ // template<class T> class A : public B<T> { using typename B<T>::foo; };
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ // We shouldn't traverse D->getTypeForDecl(); it's a result of
+ // declaring the type, not something that was written in the
+ // source.
+ })
+
+DEF_TRAVERSE_DECL(EnumDecl, {
+ if (D->getTypeForDecl())
+ TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ // The enumerators are already traversed by
+ // decls_begin()/decls_end().
+ })
+
+
+// Helper methods for RecordDecl and its children.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseRecordHelper(
+ RecordDecl *D) {
+ // We shouldn't traverse D->getTypeForDecl(); it's a result of
+ // declaring the type, not something that was written in the source.
+
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ return true;
+}
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseCXXRecordHelper(
+ CXXRecordDecl *D) {
+ if (!TraverseRecordHelper(D))
+ return false;
+ if (D->isCompleteDefinition()) {
+ for (CXXRecordDecl::base_class_iterator I = D->bases_begin(),
+ E = D->bases_end();
+ I != E; ++I) {
+ TRY_TO(TraverseTypeLoc(I->getTypeSourceInfo()->getTypeLoc()));
+ }
+ // We don't traverse the friends or the conversions, as they are
+ // already in decls_begin()/decls_end().
+ }
+ return true;
+}
+
+DEF_TRAVERSE_DECL(RecordDecl, {
+ TRY_TO(TraverseRecordHelper(D));
+ })
+
+DEF_TRAVERSE_DECL(CXXRecordDecl, {
+ TRY_TO(TraverseCXXRecordHelper(D));
+ })
+
+DEF_TRAVERSE_DECL(ClassTemplateSpecializationDecl, {
+ // For implicit instantiations ("set<int> x;"), we don't want to
+ // recurse at all, since the instatiated class isn't written in
+ // the source code anywhere. (Note the instatiated *type* --
+ // set<int> -- is written, and will still get a callback of
+ // TemplateSpecializationType). For explicit instantiations
+ // ("template set<int>;"), we do need a callback, since this
+ // is the only callback that's made for this instantiation.
+ // We use getTypeAsWritten() to distinguish.
+ if (TypeSourceInfo *TSI = D->getTypeAsWritten())
+ TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+
+ if (!getDerived().shouldVisitTemplateInstantiations() &&
+ D->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+ // Returning from here skips traversing the
+ // declaration context of the ClassTemplateSpecializationDecl
+ // (embedded in the DEF_TRAVERSE_DECL() macro)
+ // which contains the instantiated members of the class.
+ return true;
+ })
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLocsHelper(
+ const TemplateArgumentLoc *TAL, unsigned Count) {
+ for (unsigned I = 0; I < Count; ++I) {
+ TRY_TO(TraverseTemplateArgumentLoc(TAL[I]));
+ }
+ return true;
+}
+
+DEF_TRAVERSE_DECL(ClassTemplatePartialSpecializationDecl, {
+ // The partial specialization.
+ if (TemplateParameterList *TPL = D->getTemplateParameters()) {
+ for (TemplateParameterList::iterator I = TPL->begin(), E = TPL->end();
+ I != E; ++I) {
+ TRY_TO(TraverseDecl(*I));
+ }
+ }
+ // The args that remains unspecialized.
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ D->getTemplateArgsAsWritten(), D->getNumTemplateArgsAsWritten()));
+
+ // Don't need the ClassTemplatePartialSpecializationHelper, even
+ // though that's our parent class -- we already visit all the
+ // template args here.
+ TRY_TO(TraverseCXXRecordHelper(D));
+
+ // Instantiations will have been visited with the primary template.
+ })
+
+DEF_TRAVERSE_DECL(EnumConstantDecl, {
+ TRY_TO(TraverseStmt(D->getInitExpr()));
+ })
+
+DEF_TRAVERSE_DECL(UnresolvedUsingValueDecl, {
+ // Like UnresolvedUsingTypenameDecl, but without the 'typename':
+ // template <class T> Class A : public Base<T> { using Base<T>::foo; };
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
+ })
+
+DEF_TRAVERSE_DECL(IndirectFieldDecl, {})
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseDeclaratorHelper(DeclaratorDecl *D) {
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ if (D->getTypeSourceInfo())
+ TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+ else
+ TRY_TO(TraverseType(D->getType()));
+ return true;
+}
+
+DEF_TRAVERSE_DECL(FieldDecl, {
+ TRY_TO(TraverseDeclaratorHelper(D));
+ if (D->isBitField())
+ TRY_TO(TraverseStmt(D->getBitWidth()));
+ else if (D->hasInClassInitializer())
+ TRY_TO(TraverseStmt(D->getInClassInitializer()));
+ })
+
+DEF_TRAVERSE_DECL(ObjCAtDefsFieldDecl, {
+ TRY_TO(TraverseDeclaratorHelper(D));
+ if (D->isBitField())
+ TRY_TO(TraverseStmt(D->getBitWidth()));
+ // FIXME: implement the rest.
+ })
+
+DEF_TRAVERSE_DECL(ObjCIvarDecl, {
+ TRY_TO(TraverseDeclaratorHelper(D));
+ if (D->isBitField())
+ TRY_TO(TraverseStmt(D->getBitWidth()));
+ // FIXME: implement the rest.
+ })
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
+ TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
+
+ // If we're an explicit template specialization, iterate over the
+ // template args that were explicitly specified. If we were doing
+ // this in typing order, we'd do it between the return type and
+ // the function args, but both are handled by the FunctionTypeLoc
+ // above, so we have to choose one side. I've decided to do before.
+ if (const FunctionTemplateSpecializationInfo *FTSI =
+ D->getTemplateSpecializationInfo()) {
+ if (FTSI->getTemplateSpecializationKind() != TSK_Undeclared &&
+ FTSI->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) {
+ // A specialization might not have explicit template arguments if it has
+ // a templated return type and concrete arguments.
+ if (const ASTTemplateArgumentListInfo *TALI =
+ FTSI->TemplateArgumentsAsWritten) {
+ TRY_TO(TraverseTemplateArgumentLocsHelper(TALI->getTemplateArgs(),
+ TALI->NumTemplateArgs));
+ }
+ }
+ }
+
+ // Visit the function type itself, which can be either
+ // FunctionNoProtoType or FunctionProtoType, or a typedef. This
+ // also covers the return type and the function parameters,
+ // including exception specifications.
+ TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc()));
+
+ if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
+ // Constructor initializers.
+ for (CXXConstructorDecl::init_iterator I = Ctor->init_begin(),
+ E = Ctor->init_end();
+ I != E; ++I) {
+ TRY_TO(TraverseConstructorInitializer(*I));
+ }
+ }
+
+ if (D->isThisDeclarationADefinition()) {
+ TRY_TO(TraverseStmt(D->getBody())); // Function body.
+ }
+ return true;
+}
+
+DEF_TRAVERSE_DECL(FunctionDecl, {
+ // We skip decls_begin/decls_end, which are already covered by
+ // TraverseFunctionHelper().
+ return TraverseFunctionHelper(D);
+ })
+
+DEF_TRAVERSE_DECL(CXXMethodDecl, {
+ // We skip decls_begin/decls_end, which are already covered by
+ // TraverseFunctionHelper().
+ return TraverseFunctionHelper(D);
+ })
+
+DEF_TRAVERSE_DECL(CXXConstructorDecl, {
+ // We skip decls_begin/decls_end, which are already covered by
+ // TraverseFunctionHelper().
+ return TraverseFunctionHelper(D);
+ })
+
+// CXXConversionDecl is the declaration of a type conversion operator.
+// It's not a cast expression.
+DEF_TRAVERSE_DECL(CXXConversionDecl, {
+ // We skip decls_begin/decls_end, which are already covered by
+ // TraverseFunctionHelper().
+ return TraverseFunctionHelper(D);
+ })
+
+DEF_TRAVERSE_DECL(CXXDestructorDecl, {
+ // We skip decls_begin/decls_end, which are already covered by
+ // TraverseFunctionHelper().
+ return TraverseFunctionHelper(D);
+ })
+
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseVarHelper(VarDecl *D) {
+ TRY_TO(TraverseDeclaratorHelper(D));
+ // Default params are taken care of when we traverse the ParmVarDecl.
+ if (!isa<ParmVarDecl>(D))
+ TRY_TO(TraverseStmt(D->getInit()));
+ return true;
+}
+
+DEF_TRAVERSE_DECL(VarDecl, {
+ TRY_TO(TraverseVarHelper(D));
+ })
+
+DEF_TRAVERSE_DECL(ImplicitParamDecl, {
+ TRY_TO(TraverseVarHelper(D));
+ })
+
+DEF_TRAVERSE_DECL(NonTypeTemplateParmDecl, {
+ // A non-type template parameter, e.g. "S" in template<int S> class Foo ...
+ TRY_TO(TraverseDeclaratorHelper(D));
+ TRY_TO(TraverseStmt(D->getDefaultArgument()));
+ })
+
+DEF_TRAVERSE_DECL(ParmVarDecl, {
+ TRY_TO(TraverseVarHelper(D));
+
+ if (D->hasDefaultArg() &&
+ D->hasUninstantiatedDefaultArg() &&
+ !D->hasUnparsedDefaultArg())
+ TRY_TO(TraverseStmt(D->getUninstantiatedDefaultArg()));
+
+ if (D->hasDefaultArg() &&
+ !D->hasUninstantiatedDefaultArg() &&
+ !D->hasUnparsedDefaultArg())
+ TRY_TO(TraverseStmt(D->getDefaultArg()));
+ })
+
+#undef DEF_TRAVERSE_DECL
+
+// ----------------- Stmt traversal -----------------
+//
+// For stmts, we automate (in the DEF_TRAVERSE_STMT macro) iterating
+// over the children defined in children() (every stmt defines these,
+// though sometimes the range is empty). Each individual Traverse*
+// method only needs to worry about children other than those. To see
+// what children() does for a given class, see, e.g.,
+// http://clang.llvm.org/doxygen/Stmt_8cpp_source.html
+
+// This macro makes available a variable S, the passed-in stmt.
+#define DEF_TRAVERSE_STMT(STMT, CODE) \
+template<typename Derived> \
+bool RecursiveASTVisitor<Derived>::Traverse##STMT (STMT *S) { \
+ TRY_TO(WalkUpFrom##STMT(S)); \
+ StmtQueueAction StmtQueue(*this); \
+ { CODE; } \
+ for (Stmt::child_range range = S->children(); range; ++range) { \
+ StmtQueue.queue(*range); \
+ } \
+ return true; \
+}
+
+DEF_TRAVERSE_STMT(AsmStmt, {
+ StmtQueue.queue(S->getAsmString());
+ for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) {
+ StmtQueue.queue(S->getInputConstraintLiteral(I));
+ }
+ for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) {
+ StmtQueue.queue(S->getOutputConstraintLiteral(I));
+ }
+ for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) {
+ StmtQueue.queue(S->getClobber(I));
+ }
+ // children() iterates over inputExpr and outputExpr.
+ })
+
+DEF_TRAVERSE_STMT(MSAsmStmt, {
+ // FIXME: MS Asm doesn't currently parse Constraints, Clobbers, etc. Once
+ // added this needs to be implemented.
+ })
+
+DEF_TRAVERSE_STMT(CXXCatchStmt, {
+ TRY_TO(TraverseDecl(S->getExceptionDecl()));
+ // children() iterates over the handler block.
+ })
+
+DEF_TRAVERSE_STMT(DeclStmt, {
+ for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();
+ I != E; ++I) {
+ TRY_TO(TraverseDecl(*I));
+ }
+ // Suppress the default iteration over children() by
+ // returning. Here's why: A DeclStmt looks like 'type var [=
+ // initializer]'. The decls above already traverse over the
+ // initializers, so we don't have to do it again (which
+ // children() would do).
+ return true;
+ })
+
+
+// These non-expr stmts (most of them), do not need any action except
+// iterating over the children.
+DEF_TRAVERSE_STMT(BreakStmt, { })
+DEF_TRAVERSE_STMT(CXXTryStmt, { })
+DEF_TRAVERSE_STMT(CaseStmt, { })
+DEF_TRAVERSE_STMT(CompoundStmt, { })
+DEF_TRAVERSE_STMT(ContinueStmt, { })
+DEF_TRAVERSE_STMT(DefaultStmt, { })
+DEF_TRAVERSE_STMT(DoStmt, { })
+DEF_TRAVERSE_STMT(ForStmt, { })
+DEF_TRAVERSE_STMT(GotoStmt, { })
+DEF_TRAVERSE_STMT(IfStmt, { })
+DEF_TRAVERSE_STMT(IndirectGotoStmt, { })
+DEF_TRAVERSE_STMT(LabelStmt, { })
+DEF_TRAVERSE_STMT(AttributedStmt, { })
+DEF_TRAVERSE_STMT(NullStmt, { })
+DEF_TRAVERSE_STMT(ObjCAtCatchStmt, { })
+DEF_TRAVERSE_STMT(ObjCAtFinallyStmt, { })
+DEF_TRAVERSE_STMT(ObjCAtSynchronizedStmt, { })
+DEF_TRAVERSE_STMT(ObjCAtThrowStmt, { })
+DEF_TRAVERSE_STMT(ObjCAtTryStmt, { })
+DEF_TRAVERSE_STMT(ObjCForCollectionStmt, { })
+DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, { })
+DEF_TRAVERSE_STMT(CXXForRangeStmt, { })
+DEF_TRAVERSE_STMT(MSDependentExistsStmt, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+})
+DEF_TRAVERSE_STMT(ReturnStmt, { })
+DEF_TRAVERSE_STMT(SwitchStmt, { })
+DEF_TRAVERSE_STMT(WhileStmt, { })
+
+
+DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(S->getMemberNameInfo()));
+ if (S->hasExplicitTemplateArgs()) {
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ S->getTemplateArgs(), S->getNumTemplateArgs()));
+ }
+ })
+
+DEF_TRAVERSE_STMT(DeclRefExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ S->getTemplateArgs(), S->getNumTemplateArgs()));
+ })
+
+DEF_TRAVERSE_STMT(DependentScopeDeclRefExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+ if (S->hasExplicitTemplateArgs()) {
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ S->getExplicitTemplateArgs().getTemplateArgs(),
+ S->getNumTemplateArgs()));
+ }
+ })
+
+DEF_TRAVERSE_STMT(MemberExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(S->getMemberNameInfo()));
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ S->getTemplateArgs(), S->getNumTemplateArgs()));
+ })
+
+DEF_TRAVERSE_STMT(ImplicitCastExpr, {
+ // We don't traverse the cast type, as it's not written in the
+ // source code.
+ })
+
+DEF_TRAVERSE_STMT(CStyleCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXFunctionalCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXConstCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXDynamicCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXReinterpretCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXStaticCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+ })
+
+// InitListExpr is a tricky one, because we want to do all our work on
+// the syntactic form of the listexpr, but this method takes the
+// semantic form by default. We can't use the macro helper because it
+// calls WalkUp*() on the semantic form, before our code can convert
+// to the syntactic form.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseInitListExpr(InitListExpr *S) {
+ if (InitListExpr *Syn = S->getSyntacticForm())
+ S = Syn;
+ TRY_TO(WalkUpFromInitListExpr(S));
+ StmtQueueAction StmtQueue(*this);
+ // All we need are the default actions. FIXME: use a helper function.
+ for (Stmt::child_range range = S->children(); range; ++range) {
+ StmtQueue.queue(*range);
+ }
+ return true;
+}
+
+// GenericSelectionExpr is a special case because the types and expressions
+// are interleaved. We also need to watch out for null types (default
+// generic associations).
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::
+TraverseGenericSelectionExpr(GenericSelectionExpr *S) {
+ TRY_TO(WalkUpFromGenericSelectionExpr(S));
+ StmtQueueAction StmtQueue(*this);
+ StmtQueue.queue(S->getControllingExpr());
+ for (unsigned i = 0; i != S->getNumAssocs(); ++i) {
+ if (TypeSourceInfo *TS = S->getAssocTypeSourceInfo(i))
+ TRY_TO(TraverseTypeLoc(TS->getTypeLoc()));
+ StmtQueue.queue(S->getAssocExpr(i));
+ }
+ return true;
+}
+
+// PseudoObjectExpr is a special case because of the wierdness with
+// syntactic expressions and opaque values.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::
+TraversePseudoObjectExpr(PseudoObjectExpr *S) {
+ TRY_TO(WalkUpFromPseudoObjectExpr(S));
+ StmtQueueAction StmtQueue(*this);
+ StmtQueue.queue(S->getSyntacticForm());
+ for (PseudoObjectExpr::semantics_iterator
+ i = S->semantics_begin(), e = S->semantics_end(); i != e; ++i) {
+ Expr *sub = *i;
+ if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
+ sub = OVE->getSourceExpr();
+ StmtQueue.queue(sub);
+ }
+ return true;
+}
+
+DEF_TRAVERSE_STMT(CXXScalarValueInitExpr, {
+ // This is called for code like 'return T()' where T is a built-in
+ // (i.e. non-class) type.
+ TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXNewExpr, {
+ // The child-iterator will pick up the other arguments.
+ TRY_TO(TraverseTypeLoc(S->getAllocatedTypeSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(OffsetOfExpr, {
+ // The child-iterator will pick up the expression representing
+ // the field.
+ // FIMXE: for code like offsetof(Foo, a.b.c), should we get
+ // making a MemberExpr callbacks for Foo.a, Foo.a.b, and Foo.a.b.c?
+ TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(UnaryExprOrTypeTraitExpr, {
+ // The child-iterator will pick up the arg if it's an expression,
+ // but not if it's a type.
+ if (S->isArgumentType())
+ TRY_TO(TraverseTypeLoc(S->getArgumentTypeInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXTypeidExpr, {
+ // The child-iterator will pick up the arg if it's an expression,
+ // but not if it's a type.
+ if (S->isTypeOperand())
+ TRY_TO(TraverseTypeLoc(S->getTypeOperandSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXUuidofExpr, {
+ // The child-iterator will pick up the arg if it's an expression,
+ // but not if it's a type.
+ if (S->isTypeOperand())
+ TRY_TO(TraverseTypeLoc(S->getTypeOperandSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(UnaryTypeTraitExpr, {
+ TRY_TO(TraverseTypeLoc(S->getQueriedTypeSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(BinaryTypeTraitExpr, {
+ TRY_TO(TraverseTypeLoc(S->getLhsTypeSourceInfo()->getTypeLoc()));
+ TRY_TO(TraverseTypeLoc(S->getRhsTypeSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(TypeTraitExpr, {
+ for (unsigned I = 0, N = S->getNumArgs(); I != N; ++I)
+ TRY_TO(TraverseTypeLoc(S->getArg(I)->getTypeLoc()));
+})
+
+DEF_TRAVERSE_STMT(ArrayTypeTraitExpr, {
+ TRY_TO(TraverseTypeLoc(S->getQueriedTypeSourceInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(ExpressionTraitExpr, {
+ StmtQueue.queue(S->getQueriedExpression());
+ })
+
+DEF_TRAVERSE_STMT(VAArgExpr, {
+ // The child-iterator will pick up the expression argument.
+ TRY_TO(TraverseTypeLoc(S->getWrittenTypeInfo()->getTypeLoc()));
+ })
+
+DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, {
+ // This is called for code like 'return T()' where T is a class type.
+ TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+ })
+
+// Walk only the visible parts of lambda expressions.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseLambdaExpr(LambdaExpr *S) {
+ for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(),
+ CEnd = S->explicit_capture_end();
+ C != CEnd; ++C) {
+ TRY_TO(TraverseLambdaCapture(*C));
+ }
+
+ if (S->hasExplicitParameters() || S->hasExplicitResultType()) {
+ TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
+ if (S->hasExplicitParameters() && S->hasExplicitResultType()) {
+ // Visit the whole type.
+ TRY_TO(TraverseTypeLoc(TL));
+ } else if (isa<FunctionProtoTypeLoc>(TL)) {
+ FunctionProtoTypeLoc Proto = cast<FunctionProtoTypeLoc>(TL);
+ if (S->hasExplicitParameters()) {
+ // Visit parameters.
+ for (unsigned I = 0, N = Proto.getNumArgs(); I != N; ++I) {
+ TRY_TO(TraverseDecl(Proto.getArg(I)));
+ }
+ } else {
+ TRY_TO(TraverseTypeLoc(Proto.getResultLoc()));
+ }
+ }
+ }
+
+ StmtQueueAction StmtQueue(*this);
+ StmtQueue.queue(S->getBody());
+ return true;
+}
+
+DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, {
+ // This is called for code like 'T()', where T is a template argument.
+ TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+ })
+
+// These expressions all might take explicit template arguments.
+// We traverse those if so. FIXME: implement these.
+DEF_TRAVERSE_STMT(CXXConstructExpr, { })
+DEF_TRAVERSE_STMT(CallExpr, { })
+DEF_TRAVERSE_STMT(CXXMemberCallExpr, { })
+
+// These exprs (most of them), do not need any action except iterating
+// over the children.
+DEF_TRAVERSE_STMT(AddrLabelExpr, { })
+DEF_TRAVERSE_STMT(ArraySubscriptExpr, { })
+DEF_TRAVERSE_STMT(BlockExpr, {
+ TRY_TO(TraverseDecl(S->getBlockDecl()));
+ return true; // no child statements to loop through.
+})
+DEF_TRAVERSE_STMT(ChooseExpr, { })
+DEF_TRAVERSE_STMT(CompoundLiteralExpr, { })
+DEF_TRAVERSE_STMT(CXXBindTemporaryExpr, { })
+DEF_TRAVERSE_STMT(CXXBoolLiteralExpr, { })
+DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { })
+DEF_TRAVERSE_STMT(CXXDeleteExpr, { })
+DEF_TRAVERSE_STMT(ExprWithCleanups, { })
+DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { })
+DEF_TRAVERSE_STMT(CXXPseudoDestructorExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ if (TypeSourceInfo *ScopeInfo = S->getScopeTypeInfo())
+ TRY_TO(TraverseTypeLoc(ScopeInfo->getTypeLoc()));
+ if (TypeSourceInfo *DestroyedTypeInfo = S->getDestroyedTypeInfo())
+ TRY_TO(TraverseTypeLoc(DestroyedTypeInfo->getTypeLoc()));
+})
+DEF_TRAVERSE_STMT(CXXThisExpr, { })
+DEF_TRAVERSE_STMT(CXXThrowExpr, { })
+DEF_TRAVERSE_STMT(UserDefinedLiteral, { })
+DEF_TRAVERSE_STMT(DesignatedInitExpr, { })
+DEF_TRAVERSE_STMT(ExtVectorElementExpr, { })
+DEF_TRAVERSE_STMT(GNUNullExpr, { })
+DEF_TRAVERSE_STMT(ImplicitValueInitExpr, { })
+DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, { })
+DEF_TRAVERSE_STMT(ObjCEncodeExpr, {
+ if (TypeSourceInfo *TInfo = S->getEncodedTypeSourceInfo())
+ TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+})
+DEF_TRAVERSE_STMT(ObjCIsaExpr, { })
+DEF_TRAVERSE_STMT(ObjCIvarRefExpr, { })
+DEF_TRAVERSE_STMT(ObjCMessageExpr, { })
+DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, { })
+DEF_TRAVERSE_STMT(ObjCSubscriptRefExpr, { })
+DEF_TRAVERSE_STMT(ObjCProtocolExpr, { })
+DEF_TRAVERSE_STMT(ObjCSelectorExpr, { })
+DEF_TRAVERSE_STMT(ObjCIndirectCopyRestoreExpr, { })
+DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, {
+ TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
+})
+DEF_TRAVERSE_STMT(ParenExpr, { })
+DEF_TRAVERSE_STMT(ParenListExpr, { })
+DEF_TRAVERSE_STMT(PredefinedExpr, { })
+DEF_TRAVERSE_STMT(ShuffleVectorExpr, { })
+DEF_TRAVERSE_STMT(StmtExpr, { })
+DEF_TRAVERSE_STMT(UnresolvedLookupExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ if (S->hasExplicitTemplateArgs()) {
+ TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+ S->getNumTemplateArgs()));
+ }
+})
+
+DEF_TRAVERSE_STMT(UnresolvedMemberExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ if (S->hasExplicitTemplateArgs()) {
+ TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(),
+ S->getNumTemplateArgs()));
+ }
+})
+
+DEF_TRAVERSE_STMT(SEHTryStmt, {})
+DEF_TRAVERSE_STMT(SEHExceptStmt, {})
+DEF_TRAVERSE_STMT(SEHFinallyStmt,{})
+
+DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { })
+DEF_TRAVERSE_STMT(OpaqueValueExpr, { })
+DEF_TRAVERSE_STMT(CUDAKernelCallExpr, { })
+
+// These operators (all of them) do not need any action except
+// iterating over the children.
+DEF_TRAVERSE_STMT(BinaryConditionalOperator, { })
+DEF_TRAVERSE_STMT(ConditionalOperator, { })
+DEF_TRAVERSE_STMT(UnaryOperator, { })
+DEF_TRAVERSE_STMT(BinaryOperator, { })
+DEF_TRAVERSE_STMT(CompoundAssignOperator, { })
+DEF_TRAVERSE_STMT(CXXNoexceptExpr, { })
+DEF_TRAVERSE_STMT(PackExpansionExpr, { })
+DEF_TRAVERSE_STMT(SizeOfPackExpr, { })
+DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, { })
+DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, { })
+DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { })
+DEF_TRAVERSE_STMT(AtomicExpr, { })
+
+// These literals (all of them) do not need any action.
+DEF_TRAVERSE_STMT(IntegerLiteral, { })
+DEF_TRAVERSE_STMT(CharacterLiteral, { })
+DEF_TRAVERSE_STMT(FloatingLiteral, { })
+DEF_TRAVERSE_STMT(ImaginaryLiteral, { })
+DEF_TRAVERSE_STMT(StringLiteral, { })
+DEF_TRAVERSE_STMT(ObjCStringLiteral, { })
+DEF_TRAVERSE_STMT(ObjCBoxedExpr, { })
+DEF_TRAVERSE_STMT(ObjCArrayLiteral, { })
+DEF_TRAVERSE_STMT(ObjCDictionaryLiteral, { })
+
+// Traverse OpenCL: AsType, Convert.
+DEF_TRAVERSE_STMT(AsTypeExpr, { })
+
+// FIXME: look at the following tricky-seeming exprs to see if we
+// need to recurse on anything. These are ones that have methods
+// returning decls or qualtypes or nestednamespecifier -- though I'm
+// not sure if they own them -- or just seemed very complicated, or
+// had lots of sub-types to explore.
+//
+// VisitOverloadExpr and its children: recurse on template args? etc?
+
+// FIXME: go through all the stmts and exprs again, and see which of them
+// create new types, and recurse on the types (TypeLocs?) of those.
+// Candidates:
+//
+// http://clang.llvm.org/doxygen/classclang_1_1CXXTypeidExpr.html
+// http://clang.llvm.org/doxygen/classclang_1_1UnaryExprOrTypeTraitExpr.html
+// http://clang.llvm.org/doxygen/classclang_1_1TypesCompatibleExpr.html
+// Every class that has getQualifier.
+
+#undef DEF_TRAVERSE_STMT
+
+#undef TRY_TO
+
+} // end namespace cxindex
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIBCLANG_RECURSIVEASTVISITOR_H
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index d3b64db..610bd91 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -5,14 +5,52 @@ clang_CXIndex_setGlobalOptions
clang_CXXMethod_isStatic
clang_CXXMethod_isVirtual
clang_Cursor_getArgument
+clang_Cursor_getBriefCommentText
+clang_Cursor_getCommentRange
+clang_Cursor_getParsedComment
+clang_Cursor_getRawCommentText
clang_Cursor_getNumArguments
clang_Cursor_getObjCSelectorIndex
clang_Cursor_getSpellingNameRange
clang_Cursor_getTranslationUnit
+clang_Cursor_isDynamicCall
clang_Cursor_isNull
clang_IndexAction_create
clang_IndexAction_dispose
clang_Range_isNull
+clang_Comment_getKind
+clang_Comment_getNumChildren
+clang_Comment_getChild
+clang_Comment_isWhitespace
+clang_InlineContentComment_hasTrailingNewline
+clang_TextComment_getText
+clang_InlineCommandComment_getCommandName
+clang_InlineCommandComment_getRenderKind
+clang_InlineCommandComment_getNumArgs
+clang_InlineCommandComment_getArgText
+clang_HTMLTagComment_getTagName
+clang_HTMLStartTagComment_isSelfClosing
+clang_HTMLStartTag_getNumAttrs
+clang_HTMLStartTag_getAttrName
+clang_HTMLStartTag_getAttrValue
+clang_BlockCommandComment_getCommandName
+clang_BlockCommandComment_getNumArgs
+clang_BlockCommandComment_getArgText
+clang_BlockCommandComment_getParagraph
+clang_ParamCommandComment_getParamName
+clang_ParamCommandComment_isParamIndexValid
+clang_ParamCommandComment_getParamIndex
+clang_ParamCommandComment_isDirectionExplicit
+clang_ParamCommandComment_getDirection
+clang_TParamCommandComment_getParamName
+clang_TParamCommandComment_isParamPositionValid
+clang_TParamCommandComment_getDepth
+clang_TParamCommandComment_getIndex
+clang_VerbatimBlockLineComment_getText
+clang_VerbatimLineComment_getText
+clang_HTMLTagComment_getAsString
+clang_FullComment_getAsHTML
+clang_FullComment_getAsXML
clang_annotateTokens
clang_codeCompleteAt
clang_codeCompleteGetContainerKind
@@ -43,6 +81,7 @@ clang_disposeDiagnostic
clang_disposeDiagnosticSet
clang_disposeIndex
clang_disposeOverriddenCursors
+clang_disposeCXPlatformAvailability
clang_disposeString
clang_disposeTokens
clang_disposeTranslationUnit
@@ -67,6 +106,7 @@ clang_getChildDiagnostics
clang_getClangVersion
clang_getCompletionAnnotation
clang_getCompletionAvailability
+clang_getCompletionBriefComment
clang_getCompletionChunkCompletionString
clang_getCompletionChunkKind
clang_getCompletionChunkText
@@ -85,6 +125,7 @@ clang_getCursorLanguage
clang_getCursorLexicalParent
clang_getCursorLinkage
clang_getCursorLocation
+clang_getCursorPlatformAvailability
clang_getCursorReferenceNameRange
clang_getCursorReferenced
clang_getCursorResultType
@@ -199,5 +240,14 @@ clang_saveTranslationUnit
clang_sortCodeCompletionResults
clang_toggleCrashRecovery
clang_tokenize
+clang_CompilationDatabase_fromDirectory
+clang_CompilationDatabase_dispose
+clang_CompilationDatabase_getCompileCommands
+clang_CompileCommands_dispose
+clang_CompileCommands_getSize
+clang_CompileCommands_getCommand
+clang_CompileCommand_getDirectory
+clang_CompileCommand_getNumArgs
+clang_CompileCommand_getArg
clang_visitChildren
clang_visitChildrenWithBlock
diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer
index d01bd0f..c7636f9 100755
--- a/tools/scan-build/ccc-analyzer
+++ b/tools/scan-build/ccc-analyzer
@@ -182,11 +182,6 @@ sub Analyze {
}
else {
$Cmd = $Clang;
- if ($Lang eq "objective-c" || $Lang eq "objective-c++") {
- push @Args,'-DIBOutlet=__attribute__((iboutlet))';
- push @Args,'-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection)))';
- push @Args,'-DIBAction=void)__attribute__((ibaction)';
- }
# Create arguments for doing regular parsing.
my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args);
@@ -352,6 +347,8 @@ my %LinkerOptionMap = (
my %CompilerLinkerOptionMap = (
'-fobjc-arc' => 0,
'-fobjc-abi-version' => 0, # This is really a 1 argument, but always has '='
+ '-fobjc-legacy-dispatch' => 0,
+ '-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '='
'-isysroot' => 1,
'-arch' => 1,
'-m32' => 0,
@@ -434,12 +431,18 @@ if ($Status) { exit($Status >> 8); }
# Get the analysis options.
my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
+# Get the plugins to load.
+my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'};
+
# Get the store model.
my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
# Get the constraints engine.
my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
+#Get the internal stats setting.
+my $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'};
+
# Get the output format.
my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
if (!defined $OutputFormat) { $OutputFormat = "html"; }
@@ -644,11 +647,19 @@ if ($Action eq 'compile' or $Action eq 'link') {
if (defined $ConstraintsModel) {
push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
}
+
+ if (defined $InternalStats) {
+ push @AnalyzeArgs, "-analyzer-stats";
+ }
if (defined $Analyses) {
push @AnalyzeArgs, split '\s+', $Analyses;
}
+ if (defined $Plugins) {
+ push @AnalyzeArgs, split '\s+', $Plugins;
+ }
+
if (defined $OutputFormat) {
push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
if ($OutputFormat =~ /plist/) {
diff --git a/tools/scan-build/scan-build b/tools/scan-build/scan-build
index 59b0baf..65c4893 100755
--- a/tools/scan-build/scan-build
+++ b/tools/scan-build/scan-build
@@ -28,7 +28,7 @@ my $BuildName;
my $BuildDate;
my $TERM = $ENV{'TERM'};
-my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT
+my $UseColor = (defined $TERM and $TERM =~ 'xterm-.*color' and -t STDOUT
and defined $ENV{'SCAN_BUILD_COLOR'});
my $UserName = HtmlEscape(getpwuid($<) || 'unknown');
@@ -36,6 +36,7 @@ my $HostName = HtmlEscape(hostname() || 'unknown');
my $CurrentDir = HtmlEscape(getcwd());
my $CurrentDirSuffix = basename($CurrentDir);
+my @PluginsToLoad;
my $CmdArgs;
my $HtmlTitle;
@@ -77,6 +78,21 @@ sub DieDiag {
}
##----------------------------------------------------------------------------##
+# Print default checker names
+##----------------------------------------------------------------------------##
+
+if (grep /^--help-checkers$/, @ARGV) {
+ my @options = qx($0 -h);
+ foreach (@options) {
+ next unless /^ \+/;
+ s/^\s*//;
+ my ($sign, $name, @text) = split ' ', $_;
+ print $name, $/ if $sign eq '+';
+ }
+ exit 1;
+}
+
+##----------------------------------------------------------------------------##
# Some initial preprocessing of Clang options.
##----------------------------------------------------------------------------##
@@ -91,13 +107,16 @@ if (!defined $ClangSB || ! -x $ClangSB) {
$Clang = `which clang`;
chomp $Clang;
if ($Clang eq "") {
- DieDiag("No 'clang' executable found in path.");
+ DieDiag("No 'clang' executable found in path.\n");
}
}
else {
$Clang = $ClangSB;
}
-my $ClangCXX = $Clang . "++";
+my $ClangCXX = $Clang;
+$ClangCXX =~ s/\-\d+\.\d+$//;
+$ClangCXX .= "++";
+my $ClangVersion = HtmlEscape(`$Clang --version`);
##----------------------------------------------------------------------------##
# GetHTMLRunDir - Construct an HTML directory name for the current sub-run.
@@ -590,6 +609,7 @@ function ToggleDisplay(CheckButton, ClassName) {
<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr>
<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr>
<tr><th>Command Line:</th><td>${CmdArgs}</td></tr>
+<tr><th>Clang Version:</th><td>${ClangVersion}</td></tr>
<tr><th>Date:</th><td>${Date}</td></tr>
ENDTEXT
@@ -848,19 +868,87 @@ sub AddIfNotPresent {
}
}
-sub RunBuildCommand {
+sub SetEnv {
+ my $Options = shift @_;
+ foreach my $opt ('CC', 'CXX', 'CLANG', 'CLANG_CXX',
+ 'CCC_ANALYZER_ANALYSIS', 'CCC_ANALYZER_PLUGINS') {
+ die "$opt is undefined\n" if (!defined $opt);
+ $ENV{$opt} = $Options->{$opt};
+ }
+ foreach my $opt ('CCC_ANALYZER_STORE_MODEL',
+ 'CCC_ANALYZER_PLUGINS',
+ 'CCC_ANALYZER_INTERNAL_STATS',
+ 'CCC_ANALYZER_OUTPUT_FORMAT') {
+ my $x = $Options->{$opt};
+ if (defined $x) { $ENV{$opt} = $x }
+ }
+ my $Verbose = $Options->{'VERBOSE'};
+ if ($Verbose >= 2) {
+ $ENV{'CCC_ANALYZER_VERBOSE'} = 1;
+ }
+ if ($Verbose >= 3) {
+ $ENV{'CCC_ANALYZER_LOG'} = 1;
+ }
+}
+
+sub RunXcodebuild {
+ my $Args = shift;
+ my $IgnoreErrors = shift;
+ my $CCAnalyzer = shift;
+ my $CXXAnalyzer = shift;
+ my $Options = shift;
+
+ if ($IgnoreErrors) {
+ AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES");
+ }
+
+ # Default to old behavior where we insert a bogus compiler.
+ SetEnv($Options);
+
+ # Check if using iPhone SDK 3.0 (simulator). If so the compiler being
+ # used should be gcc-4.2.
+ if (!defined $ENV{"CCC_CC"}) {
+ for (my $i = 0 ; $i < scalar(@$Args); ++$i) {
+ if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) {
+ if (@$Args[$i+1] =~ /^iphonesimulator3/) {
+ $ENV{"CCC_CC"} = "gcc-4.2";
+ $ENV{"CCC_CXX"} = "g++-4.2";
+ }
+ }
+ }
+ }
+
+ # Disable PCH files until clang supports them.
+ AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO");
+ # When 'CC' is set, xcodebuild uses it to do all linking, even if we are
+ # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++'
+ # (via c++-analyzer) when linking such files.
+ $ENV{"LDPLUSPLUS"} = $CXXAnalyzer;
+
+ return (system(@$Args) >> 8);
+}
+
+sub RunBuildCommand {
my $Args = shift;
my $IgnoreErrors = shift;
my $Cmd = $Args->[0];
my $CCAnalyzer = shift;
my $CXXAnalyzer = shift;
+ my $Options = shift;
# Get only the part of the command after the last '/'.
if ($Cmd =~ /\/([^\/]+)$/) {
$Cmd = $1;
}
+ if ($Cmd eq "xcodebuild") {
+ return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $Options);
+ }
+
+ # Setup the environment.
+ SetEnv($Options);
+
if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or
$Cmd =~ /(.*\/?cc[^\/]*$)/ or
$Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or
@@ -892,34 +980,8 @@ sub RunBuildCommand {
AddIfNotPresent($Args,"-k");
AddIfNotPresent($Args,"-i");
}
- elsif ($Cmd eq "xcodebuild") {
- AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES");
- }
}
-
- if ($Cmd eq "xcodebuild") {
- # Check if using iPhone SDK 3.0 (simulator). If so the compiler being
- # used should be gcc-4.2.
- if (!defined $ENV{"CCC_CC"}) {
- for (my $i = 0 ; $i < scalar(@$Args); ++$i) {
- if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) {
- if (@$Args[$i+1] =~ /^iphonesimulator3/) {
- $ENV{"CCC_CC"} = "gcc-4.2";
- $ENV{"CCC_CXX"} = "g++-4.2";
- }
- }
- }
- }
- # Disable PCH files until clang supports them.
- AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO");
-
- # When 'CC' is set, xcodebuild uses it to do all linking, even if we are
- # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++'
- # (via c++-analyzer) when linking such files.
- $ENV{"LDPLUSPLUS"} = $CXXAnalyzer;
- }
-
return (system(@$Args) >> 8);
}
@@ -1007,7 +1069,8 @@ ADVANCED OPTIONS:
-maxloop N - specifiy the number of times a block can be visited before giving
up. Default is 4. Increase for more comprehensive coverage at a
cost of speed.
-
+ -internal-stats - Generate internal analyzer statistics.
+
CONTROLLING CHECKERS:
A default group of checkers are always run unless explicitly disabled.
@@ -1015,9 +1078,23 @@ CONTROLLING CHECKERS:
-enable-checker [checker name]
-disable-checker [checker name]
+
+LOADING CHECKERS:
+
+ Loading external checkers using the clang plugin interface:
+
+ -load-plugin [plugin library]
ENDTEXT
# Query clang for list of checkers that are enabled.
+
+# create a list to load the plugins via the 'Xclang' command line
+# argument
+my @PluginLoadCommandline_xclang;
+foreach my $param ( @PluginsToLoad ) {
+ push ( @PluginLoadCommandline_xclang, "-Xclang" );
+ push ( @PluginLoadCommandline_xclang, $param );
+}
my %EnabledCheckers;
foreach my $lang ("c", "objective-c", "objective-c++", "c++") {
pipe(FROM_CHILD, TO_PARENT);
@@ -1026,7 +1103,7 @@ foreach my $lang ("c", "objective-c", "objective-c++", "c++") {
close FROM_CHILD;
open(STDOUT,">&", \*TO_PARENT);
open(STDERR,">&", \*TO_PARENT);
- exec $Clang, ('--analyze', '-x', $lang, '-', '-###');
+ exec $Clang, ( @PluginLoadCommandline_xclang, '--analyze', '-x', $lang, '-', '-###');
}
close(TO_PARENT);
while(<FROM_CHILD>) {
@@ -1048,7 +1125,7 @@ if ($pid == 0) {
close FROM_CHILD;
open(STDOUT,">&", \*TO_PARENT);
open(STDERR,">&", \*TO_PARENT);
- exec $Clang, ('-cc1', '-analyzer-checker-help');
+ exec $Clang, ('-cc1', @PluginsToLoad , '-analyzer-checker-help');
}
close(TO_PARENT);
my $foundCheckers = 0;
@@ -1084,7 +1161,9 @@ else {
if ($EnabledCheckers{$aggregate}) {
$enabled =1;
last;
- }
+ }
+ # append a dot, if an additional domain is added in the next iteration
+ $aggregate .= ".";
}
if ($enabled) {
@@ -1160,6 +1239,7 @@ my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found
my @AnalysesToRun;
my $StoreModel;
my $ConstraintsModel;
+my $InternalStats;
my $OutputFormat = "html";
my $AnalyzerStats = 0;
my $MaxLoop = 0;
@@ -1291,6 +1371,12 @@ while (@ARGV) {
$ConstraintsModel = shift @ARGV;
next;
}
+
+ if ($arg eq "-internal-stats") {
+ shift @ARGV;
+ $InternalStats = 1;
+ next;
+ }
if ($arg eq "-plist") {
shift @ARGV;
@@ -1327,7 +1413,12 @@ while (@ARGV) {
push @AnalysesToRun, "-analyzer-disable-checker", shift @ARGV;
next;
}
-
+ if ($arg eq "-load-plugin") {
+ shift @ARGV;
+ push @PluginsToLoad, "-load", shift @ARGV;
+ next;
+ }
+
DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);
last;
@@ -1371,42 +1462,42 @@ if (!defined $ClangSB || ! -x $ClangSB) {
Diag("Using 'clang' from path: $Clang\n");
}
-# Set the appropriate environment variables.
SetHtmlEnv(\@ARGV, $HtmlDir);
-$ENV{'CC'} = $Cmd;
-$ENV{'CXX'} = $CmdCXX;
-$ENV{'CLANG'} = $Clang;
-$ENV{'CLANG_CXX'} = $ClangCXX;
-if ($Verbose >= 2) {
- $ENV{'CCC_ANALYZER_VERBOSE'} = 1;
-}
-if ($Verbose >= 3) {
- $ENV{'CCC_ANALYZER_LOG'} = 1;
-}
-if ($AnalyzeHeaders) {
- push @AnalysesToRun,"-analyzer-opt-analyze-headers";
-}
-if ($AnalyzerStats) {
- push @AnalysesToRun, '-analyzer-checker', 'debug.Stats';
-}
-if ($MaxLoop > 0) {
- push @AnalysesToRun, '-analyzer-max-loop ' . $MaxLoop;
-}
-
-$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun;
+if ($AnalyzeHeaders) { push @AnalysesToRun,"-analyzer-opt-analyze-headers"; }
+if ($AnalyzerStats) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; }
+if ($MaxLoop > 0) { push @AnalysesToRun, '-analyzer-max-loop=$MaxLoop'; }
+
+# Delay setting up other environment variables in case we can do true
+# interposition.
+my $CCC_ANALYZER_ANALYSIS = join ' ',@AnalysesToRun;
+my $CCC_ANALYZER_PLUGINS = join ' ',@PluginsToLoad;
+my %Options = (
+ 'CC' => $Cmd,
+ 'CXX' => $CmdCXX,
+ 'CLANG' => $Clang,
+ 'CLANG_CXX' => $ClangCXX,
+ 'VERBOSE' => $Verbose,
+ 'CCC_ANALYZER_ANALYSIS' => $CCC_ANALYZER_ANALYSIS,
+ 'CCC_ANALYZER_PLUGINS' => $CCC_ANALYZER_PLUGINS,
+ 'OUTPUT_DIR' => $HtmlDir
+);
if (defined $StoreModel) {
- $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel;
+ $Options{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel;
}
if (defined $ConstraintsModel) {
- $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel;
+ $Options{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel;
+}
+if (defined $InternalStats) {
+ $Options{'CCC_ANALYZER_INTERNAL_STATS'} = 1;
}
if (defined $OutputFormat) {
- $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat;
+ $Options{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat;
}
# Run the build.
-my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd, $CmdCXX);
+my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd, $CmdCXX,
+ \%Options);
if (defined $OutputFormat) {
if ($OutputFormat =~ /plist/) {
diff --git a/tools/scan-build/scan-build.1 b/tools/scan-build/scan-build.1
new file mode 100644
index 0000000..0f43196
--- /dev/null
+++ b/tools/scan-build/scan-build.1
@@ -0,0 +1,348 @@
+.\" This file is distributed under the University of Illinois Open Source
+.\" License. See LICENSE.TXT for details.
+.\" $Id: scan-build.1 157412 2012-05-24 20:16:00Z kremenek $
+.Dd May 25, 2012
+.Os "clang" "3.1"
+.Dt SCAN-BUILD \&1 CLANG
+.Sh NAME
+.Nm scan-build
+.Nd Clang static analyzer
+.Sh SYNOPSIS
+.Nm
+.Op Fl ohkvV
+.Op Fl analyze-headers
+.Op Fl enable-checker Op Ar checker_name
+.Op Fl disable-checker Op Ar checker_name
+.Op Fl Fl help
+.Op Fl Fl help-checkers
+.Op Fl Fl html-title Op Ar =title
+.Op Fl Fl keep-going
+.Op Fl plist
+.Op Fl plist-html
+.Op Fl Fl status-bugs
+.Op Fl Fl use-c++ Op Ar =compiler_path
+.Op Fl Fl use-cc Op Ar =compiler_path
+.Op Fl Fl view
+.Op Fl constraints Op Ar model
+.Op Fl maxloop Ar N
+.Op Fl no-failure-reports
+.Op Fl stats
+.Op Fl store Op Ar model
+.Ar build_command
+.Op build_options
+.\"
+.\" Sh DESCRIPTION
+.Sh DESCRIPTION
+.Nm
+is a Perl script that invokes the Clang static analyzer. Options used by
+.Nm
+or by the analyzer appear first, followed by the
+.Ar build_command
+and any
+.Ar build_options
+normally used to build the target system.
+.Pp
+The static analyzer employs a long list of checking algorithms, see
+.Sx CHECKERS .
+Output can be written in standard
+.Li .plist
+and/or HTML format.
+.Pp
+The following options are supported:
+.Bl -tag -width indent
+.It Fl analyze-headers
+Also analyze functions in #included files.
+.It Fl enable-checker Ar checker_name , Fl disable-checker Ar checker_name
+Enable/disable
+.Ar checker_name .
+See
+.Sx CHECKERS .
+.It Fl h , Fl Fl help
+Display this message.
+.It Fl Fl help-checkers
+List default checkers, see
+.Sx CHECKERS .
+.It Fl Fl html-title Ns Op = Ns Ar title
+Specify the title used on generated HTML pages.
+A default title is generated if
+.Ar title
+is not specified.
+.It Fl k , Fl Fl keep-going
+Add a
+.Dq keep on going
+option to
+.Ar build_command .
+Currently supports make and xcodebuild. This is a convenience option;
+one can specify this behavior directly using build options.
+.It Fl o
+Target directory for HTML report files. Subdirectories will be
+created as needed to represent separate invocations
+of the analyzer. If this option is not specified, a directory is
+created in /tmp (TMPDIR on Mac OS X) to store the reports.
+.It Fl plist
+Output the results as a set of
+.Li .plist
+files. (By default the output of
+.Nm
+is a set of HTML files.)
+.It Fl plist-html
+Output the results as a set of HTML and .plist files
+.It Fl Fl status-bugs
+Set exit status to 1 if it found potential bugs and 0 otherwise. By
+default the exit status of
+.Nm
+is that returned by
+.Ar build_command .
+.It Fl Fl use-c++ Ns Op = Ns Ar compiler_path
+Guess the default compiler for your C++ and Objective-C++ code. Use this
+option to specify an alternate compiler.
+.It Fl Fl use-cc Ns Op = Ns Ar compiler_path
+Guess the default compiler for your C and Objective-C code. Use this
+option to specify an alternate compiler.
+.It Fl v
+Verbose output from
+.Nm
+and the analyzer. A second and
+third
+.Ar v
+increases verbosity.
+.It Fl V , Fl Fl view
+View analysis results in a web browser when the build completes.
+.It Fl constraints Op Ar model
+Specify the contraint engine used by the analyzer. By default the
+.Ql range
+model is used. Specifying
+.Ql basic
+uses a simpler, less powerful constraint model used by checker-0.160
+and earlier.
+.It Fl maxloop Ar N
+Specifiy the number of times a block can be visited before giving
+up. Default is 4. Increase for more comprehensive coverage at a
+cost of speed.
+.It Fl no-failure-reports
+Do not create a
+.Ql failures
+subdirectory that includes analyzer crash reports and preprocessed
+source files.
+.It Fl stats
+Generates visitation statistics for the project being analyzed.
+.It Fl store Op Ar model
+Specify the store model used by the analyzer. By default, the
+.Ql region
+store model is used.
+.Ql region
+specifies a field-
+sensitive store model. Users can also specify
+.Ql basic
+which is far less precise but can more quickly analyze code.
+.Ql basic
+was the default store model for checker-0.221 and earlier.
+.\"
+.El
+.Sh RETURN VALUES
+.Nm
+returns the value returned by
+.Ar build_command
+unless
+.Fl Fl status-bugs
+or
+.Fl Fl keep-going
+is used.
+.\"
+.\" Other sections not yet used ...
+.\" .Sh ENVIRONMENT
+.\" .Sh FILES
+.\" .Sh DIAGNOSTICS
+.\" .Sh COMPATIBILITY
+.\" .Sh HISTORY
+.\" .Sh BUGS
+.\"
+.Sh CHECKERS
+The checkers listed below may be enabled/disabled using the
+.Fl enable-checker
+and
+.Fl disable-checker
+options.
+A default group of checkers is run unless explicitly disabled.
+Exactly which checkers constitute the default group is a function
+of the operating system in use; they are listed with
+.Fl Fl help-checkers .
+.Bl -tag -width indent.
+.It core.AdjustedReturnValue
+Check to see if the return value of a function call is different than
+the caller expects (e.g., from calls through function pointers).
+.It core.AttributeNonNull
+Check for null pointers passed as arguments to a function whose arguments are marked with the
+.Ql nonnull
+attribute.
+.It core.CallAndMessage
+Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers).
+.It core.DivideZero
+Check for division by zero.
+.It core.NullDereference
+Check for dereferences of null pointers.
+.It core.StackAddressEscape
+Check that addresses to stack memory do not escape the function.
+.It core.UndefinedBinaryOperatorResult
+Check for undefined results of binary operators.
+.It core.VLASize
+Check for declarations of VLA of undefined or zero size.
+.It core.builtin.BuiltinFunctions
+Evaluate compiler builtin functions, e.g.
+.Fn alloca .
+.It core.builtin.NoReturnFunctions
+Evaluate
+.Ql panic
+functions that are known to not return to the caller.
+.It core.uninitialized.ArraySubscript
+Check for uninitialized values used as array subscripts.
+.It core.uninitialized.Assign
+Check for assigning uninitialized values.
+.It core.uninitialized.Branch
+Check for uninitialized values used as branch conditions.
+.It core.uninitialized.CapturedBlockVariable
+Check for blocks that capture uninitialized values.
+.It core.uninitialized.UndefReturn
+Check for uninitialized values being returned to the caller.
+.It deadcode.DeadStores
+Check for values stored to variables that are never read afterwards.
+.It debug.DumpCFG
+Display Control-Flow Graphs.
+.It debug.DumpCallGraph
+Display Call Graph.
+.It debug.DumpDominators
+Print the dominance tree for a given Control-Flow Graph.
+.It debug.DumpLiveVars
+Print results of live variable analysis.
+.It debug.Stats
+Emit warnings with analyzer statistics.
+.It debug.TaintTest
+Mark tainted symbols as such.
+.It debug.ViewCFG
+View Control-Flow Graphs using
+.Ic GraphViz .
+.It debug.ViewCallGraph
+View Call Graph using
+.Ic GraphViz .
+.It llvm.Conventions
+Check code for LLVM codebase conventions.
+.It osx.API
+Check for proper uses of various Mac OS X APIs.
+.It osx.AtomicCAS
+Evaluate calls to
+.Vt OSAtomic
+functions.
+.It osx.SecKeychainAPI
+Check for proper uses of Secure Keychain APIs.
+.It osx.cocoa.AtSync
+Check for null pointers used as mutexes for @synchronized.
+.It osx.cocoa.ClassRelease
+Check for sending
+.Ql retain ,
+.Ql release,
+or
+.Ql autorelease
+directly to a Class.
+.It osx.cocoa.IncompatibleMethodTypes
+Warn about Objective-C method signatures with type incompatibilities.
+.It osx.cocoa.NSAutoreleasePool
+Warn for suboptimal uses of
+.Vt NSAutoreleasePool
+in Objective-C GC mode.
+.It osx.cocoa.NSError
+Check usage of NSError** parameters.
+.It osx.cocoa.NilArg
+Check for prohibited nil arguments to Objective-C method calls.
+.It osx.cocoa.RetainCount
+Check for leaks and improper reference count management.
+.It osx.cocoa.SelfInit
+Check that
+.Ql self
+is properly initialized inside an initializer method.
+.It osx.cocoa.UnusedIvars
+Warn about private ivars that are never used.
+.It osx.cocoa.VariadicMethodTypes
+Check for passing non-Objective-C types to variadic methods that expect only Objective-C types.
+.It osx.coreFoundation.CFError
+Check usage of CFErrorRef* parameters.
+.It osx.coreFoundation.CFNumber
+Check for proper uses of
+.Fn CFNumberCreate .
+.It osx.coreFoundation.CFRetainRelease
+Check for null arguments to
+.Fn CFRetain
+and
+.Fn CFRelease .
+.It osx.coreFoundation.containers.OutOfBounds
+Checks for index out-of-bounds when using the
+.Vt CFArray
+API.
+.It osx.coreFoundation.containers.PointerSizedValues
+Warns if
+.Vt CFArray ,
+.Vt CFDictionary ,
+or
+.Vt CFSet
+are created with non-pointer-size values.
+.It security.FloatLoopCounter
+Warn on using a floating point value as a loop counter (CERT: FLP30-C, FLP30-CPP).
+.It security.insecureAPI.UncheckedReturn
+Warn on uses of functions whose return values must be always checked.
+.It security.insecureAPI.getpw
+Warn on uses of
+.Fn getpw .
+.It security.insecureAPI.gets
+Warn on uses of
+.Fn gets .
+.It security.insecureAPI.mkstemp
+Warn when
+.Fn mkstemp
+is passed fewer than 6 X's in the format string.
+.It security.insecureAPI.mktemp
+Warn on uses of
+.Fn mktemp .
+.It security.insecureAPI.rand
+Warn on uses of
+.Fn rand ,
+.Fn random ,
+and related functions.
+.It security.insecureAPI.strcpy
+Warn on uses of
+.Fn strcpy
+and
+.Fn strcat .
+.It security.insecureAPI.vfork
+Warn on uses of
+.Fn vfork .
+.It unix.API
+Check calls to various UNIX/Posix functions.
+.It unix.Malloc
+Check for memory leaks, double free, and use-after-free.
+.It unix.cstring.BadSizeArg
+Check the size argument passed into C string functions for common
+erroneous patterns.
+.It unix.cstring.NullArg
+Check for null pointers being passed as arguments to C string functions.
+.El
+.\"
+.Sh EXAMPLE
+.Ic scan-build -o /tmp/myhtmldir make -j4
+.Pp
+The above example causes analysis reports to be deposited into
+a subdirectory of
+.Pa /tmp/myhtmldir
+and to run
+.Ic make
+with the
+.Fl j4
+option.
+A different subdirectory is created each time
+.Nm
+analyzes a project.
+The analyzer should support most parallel builds, but not distributed builds.
+.Sh AUTHORS
+.Nm
+was written by
+.An "Ted Kremenek" .
+Documentation contributed by
+.An "James K. Lowden" Aq jklowden@schemamania.org .
diff --git a/tools/scan-build/set-xcode-analyzer b/tools/scan-build/set-xcode-analyzer
index 06e1d85..c280bb4 100755
--- a/tools/scan-build/set-xcode-analyzer
+++ b/tools/scan-build/set-xcode-analyzer
@@ -75,7 +75,11 @@ def main():
print "(+) Using the Clang bundled with Xcode"
path = options.default
- xcode_path = subprocess.check_output(["xcode-select", "-print-path"])
+ try:
+ xcode_path = subprocess.check_output(["xcode-select", "-print-path"])
+ except AttributeError:
+ # Fall back to the default install location when using Python < 2.7.0
+ xcode_path = "/Developer"
if (re.search("Xcode.app", xcode_path)):
# Cut off the 'Developer' dir, as the xcspec lies in another part
# of the Xcode.app subtree.
diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt
new file mode 100644
index 0000000..63418a2
--- /dev/null
+++ b/unittests/AST/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_clang_unittest(ASTTests
+ CommentLexer.cpp
+ CommentParser.cpp
+ )
+
+target_link_libraries(ASTTests
+ clangAST
+ )
diff --git a/unittests/AST/CommentLexer.cpp b/unittests/AST/CommentLexer.cpp
new file mode 100644
index 0000000..cab0fdd
--- /dev/null
+++ b/unittests/AST/CommentLexer.cpp
@@ -0,0 +1,1636 @@
+//===- unittests/AST/CommentLexer.cpp ------ Comment lexer tests ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/STLExtras.h"
+#include <vector>
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace comments {
+
+namespace {
+class CommentLexerTest : public ::testing::Test {
+protected:
+ CommentLexerTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::BumpPtrAllocator Allocator;
+
+ void lexString(const char *Source, std::vector<Token> &Toks);
+};
+
+void CommentLexerTest::lexString(const char *Source,
+ std::vector<Token> &Toks) {
+ MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID File = SourceMgr.createFileIDForMemBuffer(Buf);
+ SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
+
+ comments::CommandTraits Traits;
+ comments::Lexer L(Allocator, Traits, Begin, CommentOptions(),
+ Source, Source + strlen(Source));
+
+ while (1) {
+ Token Tok;
+ L.lex(Tok);
+ if (Tok.is(tok::eof))
+ break;
+ Toks.push_back(Tok);
+ }
+}
+
+} // unnamed namespace
+
+// Empty source range should be handled.
+TEST_F(CommentLexerTest, Basic1) {
+ const char *Source = "";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(0U, Toks.size());
+}
+
+// Empty comments should be handled.
+TEST_F(CommentLexerTest, Basic2) {
+ const char *Sources[] = {
+ "//", "///", "//!", "///<", "//!<"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(1U, Toks.size());
+
+ ASSERT_EQ(tok::newline, Toks[0].getKind());
+ }
+}
+
+// Empty comments should be handled.
+TEST_F(CommentLexerTest, Basic3) {
+ const char *Sources[] = {
+ "/**/", "/***/", "/*!*/", "/**<*/", "/*!<*/"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::newline, Toks[0].getKind());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// Single comment with plain text.
+TEST_F(CommentLexerTest, Basic4) {
+ const char *Sources[] = {
+ "// Meow", "/// Meow", "//! Meow",
+ "// Meow\n", "// Meow\r\n", "//! Meow\r",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// Single comment with plain text.
+TEST_F(CommentLexerTest, Basic5) {
+ const char *Sources[] = {
+ "/* Meow*/", "/** Meow*/", "/*! Meow*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// Test newline escaping.
+TEST_F(CommentLexerTest, Basic6) {
+ const char *Sources[] = {
+ "// Aaa\\\n" " Bbb\\ \n" " Ccc?" "?/\n",
+ "// Aaa\\\r\n" " Bbb\\ \r\n" " Ccc?" "?/\r\n",
+ "// Aaa\\\r" " Bbb\\ \r" " Ccc?" "?/\r"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[1].getText());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[3].getText());
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[4].getText());
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[5].getText());
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" Ccc?" "?/"), Toks[7].getText());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+ }
+}
+
+// Check that we skip C-style aligned stars correctly.
+TEST_F(CommentLexerTest, Basic7) {
+ const char *Source =
+ "/* Aaa\n"
+ " * Bbb\r\n"
+ "\t* Ccc\n"
+ " ! Ddd\n"
+ " * Eee\n"
+ " ** Fff\n"
+ " */";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(15U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[2].getText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Ccc"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" ! Ddd"), Toks[6].getText());
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+
+ ASSERT_EQ(tok::text, Toks[8].getKind());
+ ASSERT_EQ(StringRef(" Eee"), Toks[8].getText());
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+
+ ASSERT_EQ(tok::text, Toks[10].getKind());
+ ASSERT_EQ(StringRef("* Fff"), Toks[10].getText());
+ ASSERT_EQ(tok::newline, Toks[11].getKind());
+
+ ASSERT_EQ(tok::text, Toks[12].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[12].getText());
+
+ ASSERT_EQ(tok::newline, Toks[13].getKind());
+ ASSERT_EQ(tok::newline, Toks[14].getKind());
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand1) {
+ const char *Sources[] = { "//@", "///@", "//!@" };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand2) {
+ const char *Sources[] = { "/*@*/", "/**@*/", "/*!@*/"};
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand3) {
+ const char *Sources[] = { "/*\\*/", "/**\\*/" };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// Doxygen escape sequences.
+TEST_F(CommentLexerTest, DoxygenCommand4) {
+ const char *Source =
+ "/// \\\\ \\@ \\& \\$ \\# \\< \\> \\% \\\" \\. \\::";
+ const char *Text[] = {
+ " ",
+ "\\", " ", "@", " ", "&", " ", "$", " ", "#", " ",
+ "<", " ", ">", " ", "%", " ", "\"", " ", ".", " ",
+ "::", ""
+ };
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(array_lengthof(Text), Toks.size());
+
+ for (size_t i = 0, e = Toks.size(); i != e; i++) {
+ if(Toks[i].is(tok::text))
+ ASSERT_EQ(StringRef(Text[i]), Toks[i].getText())
+ << "index " << i;
+ }
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand5) {
+ const char *Source = "/// \\brief Aaa.";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("brief"), Toks[1].getCommandName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa."), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand6) {
+ const char *Source = "/// \\aaa\\bbb \\ccc\t\\ddd\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(8U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("aaa"), Toks[1].getCommandName());
+
+ ASSERT_EQ(tok::command, Toks[2].getKind());
+ ASSERT_EQ(StringRef("bbb"), Toks[2].getCommandName());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[3].getText());
+
+ ASSERT_EQ(tok::command, Toks[4].getKind());
+ ASSERT_EQ(StringRef("ccc"), Toks[4].getCommandName());
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef("\t"), Toks[5].getText());
+
+ ASSERT_EQ(tok::command, Toks[6].getKind());
+ ASSERT_EQ(StringRef("ddd"), Toks[6].getCommandName());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand7) {
+ const char *Source = "// \\c\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("c"), Toks[1].getCommandName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+// Empty verbatim block.
+TEST_F(CommentLexerTest, VerbatimBlock1) {
+ const char *Sources[] = {
+ "/// \\verbatim\\endverbatim\n//",
+ "/** \\verbatim\\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[2].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[2].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+// Empty verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock2) {
+ const char *Source = "/// \\verbatim";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+// Empty verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock3) {
+ const char *Source = "/** \\verbatim*/";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+// Single-line verbatim block.
+TEST_F(CommentLexerTest, VerbatimBlock4) {
+ const char *Sources[] = {
+ "/// Meow \\verbatim aaa \\endverbatim\n//",
+ "/** Meow \\verbatim aaa \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[3].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[3].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+// Single-line verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock5) {
+ const char *Sources[] = {
+ "/// Meow \\verbatim aaa \n//",
+ "/** Meow \\verbatim aaa */"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, VerbatimBlock6) {
+ const char *Source =
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "//\n"
+ "// Bbb\n"
+ "// \\endverbatim\n";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[6].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[8].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[8].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+}
+
+TEST_F(CommentLexerTest, VerbatimBlock7) {
+ const char *Source =
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " *\n"
+ " * Bbb\n"
+ " * \\endverbatim\n"
+ " */";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef(""), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[4].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[5].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[5].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[7].getText());
+
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+}
+
+// Complex test for verbatim blocks.
+TEST_F(CommentLexerTest, VerbatimBlock8) {
+ const char *Source =
+ "/* Meow \\verbatim aaa\\$\\@\n"
+ "bbb \\endverbati\r"
+ "ccc\r\n"
+ "ddd \\endverbatim Blah \\verbatim eee\n"
+ "\\endverbatim BlahBlah*/";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(14U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa\\$\\@"), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef("bbb \\endverbati"), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[4].getKind());
+ ASSERT_EQ(StringRef("ccc"), Toks[4].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[5].getKind());
+ ASSERT_EQ(StringRef("ddd "), Toks[5].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[6].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[6].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" Blah "), Toks[7].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[8].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[8].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" eee"), Toks[9].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[10].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[10].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[11].getKind());
+ ASSERT_EQ(StringRef(" BlahBlah"), Toks[11].getText());
+
+ ASSERT_EQ(tok::newline, Toks[12].getKind());
+ ASSERT_EQ(tok::newline, Toks[13].getKind());
+}
+
+// LaTeX verbatim blocks.
+TEST_F(CommentLexerTest, VerbatimBlock9) {
+ const char *Source =
+ "/// \\f$ Aaa \\f$ \\f[ Bbb \\f] \\f{ Ccc \\f}";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(13U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("f$"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[3].getKind());
+ ASSERT_EQ(StringRef("f$"), Toks[3].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[4].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[5].getKind());
+ ASSERT_EQ(StringRef("f["), Toks[5].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Bbb "), Toks[6].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[7].getKind());
+ ASSERT_EQ(StringRef("f]"), Toks[7].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[8].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[8].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[9].getKind());
+ ASSERT_EQ(StringRef("f{"), Toks[9].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[10].getKind());
+ ASSERT_EQ(StringRef(" Ccc "), Toks[10].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[11].getKind());
+ ASSERT_EQ(StringRef("f}"), Toks[11].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[12].getKind());
+}
+
+// Empty verbatim line.
+TEST_F(CommentLexerTest, VerbatimLine1) {
+ const char *Sources[] = {
+ "/// \\fn\n//",
+ "/** \\fn*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+// Verbatim line with Doxygen escape sequences, which should not be expanded.
+TEST_F(CommentLexerTest, VerbatimLine2) {
+ const char *Sources[] = {
+ "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
+ "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName());
+
+ ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"),
+ Toks[2].getVerbatimLineText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+// Verbatim line should not eat anything from next source line.
+TEST_F(CommentLexerTest, VerbatimLine3) {
+ const char *Source =
+ "/** \\fn void *foo(const char *zzz = \"\\$\");\n"
+ " * Meow\n"
+ " */";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(9U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName());
+
+ ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"),
+ Toks[2].getVerbatimLineText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[6].getText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML1) {
+ const char *Source =
+ "// <";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML2) {
+ const char *Source =
+ "// a<2";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" a"), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("2"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML3) {
+ const char *Source =
+ "// < tag";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" tag"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML4) {
+ const char *Sources[] = {
+ "// <tag",
+ "// <tag "
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML5) {
+ const char *Source =
+ "// <tag 42";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("42"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML6) {
+ const char *Source = "// <tag> Meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[3].getText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML7) {
+ const char *Source = "// <tag=";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("="), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML8) {
+ const char *Source = "// <tag attr=> Meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(7U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_greater, Toks[4].getKind());
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[5].getText());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML9) {
+ const char *Sources[] = {
+ "// <tag attr",
+ "// <tag attr "
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML10) {
+ const char *Sources[] = {
+ "// <tag attr=",
+ "// <tag attr ="
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML11) {
+ const char *Sources[] = {
+ "// <tag attr=\"",
+ "// <tag attr = \"",
+ "// <tag attr=\'",
+ "// <tag attr = \'"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef(""), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML12) {
+ const char *Source = "// <tag attr=@";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[4].getText());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML13) {
+ const char *Sources[] = {
+ "// <tag attr=\"val\\\"\\'val",
+ "// <tag attr=\"val\\\"\\'val\"",
+ "// <tag attr=\'val\\\"\\'val",
+ "// <tag attr=\'val\\\"\\'val\'"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef("val\\\"\\'val"), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML14) {
+ const char *Sources[] = {
+ "// <tag attr=\"val\\\"\\'val\">",
+ "// <tag attr=\'val\\\"\\'val\'>"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(7U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef("val\\\"\\'val"), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::html_greater, Toks[5].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML15) {
+ const char *Sources[] = {
+ "// <tag/>",
+ "// <tag />"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_slash_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML16) {
+ const char *Sources[] = {
+ "// <tag/ Aaa",
+ "// <tag / Aaa"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("/"), Toks[2].getText());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[3].getText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML17) {
+ const char *Source = "// </";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef(""), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML18) {
+ const char *Source = "// </@";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef(""), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML19) {
+ const char *Source = "// </tag";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML20) {
+ const char *Sources[] = {
+ "// </tag>",
+ "// </ tag>",
+ "// </ tag >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::html_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences1) {
+ const char *Source = "// &";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences2) {
+ const char *Source = "// &!";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("!"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences3) {
+ const char *Source = "// &amp";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&amp"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences4) {
+ const char *Source = "// &amp!";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&amp"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("!"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences5) {
+ const char *Source = "// &#";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences6) {
+ const char *Source = "// &#a";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("a"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences7) {
+ const char *Source = "// &#42";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#42"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences8) {
+ const char *Source = "// &#42a";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#42"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("a"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences9) {
+ const char *Source = "// &#x";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#x"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences10) {
+ const char *Source = "// &#xz";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#x"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("z"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences11) {
+ const char *Source = "// &#xab";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#xab"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences12) {
+ const char *Source = "// &#xaBz";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#xaB"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("z"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences13) {
+ const char *Source = "// &amp;";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences14) {
+ const char *Source = "// &amp;&lt;";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences15) {
+ const char *Source = "// &amp; meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" meow"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences16) {
+ const char *Sources[] = {
+ "// &#61;",
+ "// &#x3d;",
+ "// &#X3d;"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("="), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, MultipleComments) {
+ const char *Source =
+ "// Aaa\n"
+ "/// Bbb\n"
+ "/* Ccc\n"
+ " * Ddd*/\n"
+ "/** Eee*/";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(12U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[2].getText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Ccc"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Ddd"), Toks[6].getText());
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+
+ ASSERT_EQ(tok::text, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" Eee"), Toks[9].getText());
+
+ ASSERT_EQ(tok::newline, Toks[10].getKind());
+ ASSERT_EQ(tok::newline, Toks[11].getKind());
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp
new file mode 100644
index 0000000..7258a7e
--- /dev/null
+++ b/unittests/AST/CommentParser.cpp
@@ -0,0 +1,1383 @@
+//===- unittests/AST/CommentParser.cpp ------ Comment parser tests --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentParser.h"
+#include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Allocator.h"
+#include <vector>
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace comments {
+
+namespace {
+
+const bool DEBUG = true;
+
+class CommentParserTest : public ::testing::Test {
+protected:
+ CommentParserTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::BumpPtrAllocator Allocator;
+
+ FullComment *parseString(const char *Source);
+};
+
+FullComment *CommentParserTest::parseString(const char *Source) {
+ MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID File = SourceMgr.createFileIDForMemBuffer(Buf);
+ SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
+
+ comments::CommandTraits Traits;
+ comments::Lexer L(Allocator, Traits, Begin, CommentOptions(),
+ Source, Source + strlen(Source));
+
+ comments::Sema S(Allocator, SourceMgr, Diags, Traits);
+ comments::Parser P(L, S, Allocator, SourceMgr, Diags, Traits);
+ comments::FullComment *FC = P.parseFullComment();
+
+ if (DEBUG) {
+ llvm::errs() << "=== Source:\n" << Source << "\n=== AST:\n";
+ FC->dump(SourceMgr);
+ }
+
+ Token Tok;
+ L.lex(Tok);
+ if (Tok.is(tok::eof))
+ return FC;
+ else
+ return NULL;
+}
+
+::testing::AssertionResult HasChildCount(const Comment *C, size_t Count) {
+ if (!C)
+ return ::testing::AssertionFailure() << "Comment is NULL";
+
+ if (Count != C->child_count())
+ return ::testing::AssertionFailure()
+ << "Count = " << Count
+ << ", child_count = " << C->child_count();
+
+ return ::testing::AssertionSuccess();
+}
+
+template <typename T>
+::testing::AssertionResult GetChildAt(const Comment *C,
+ size_t Idx,
+ T *&Child) {
+ if (!C)
+ return ::testing::AssertionFailure() << "Comment is NULL";
+
+ if (Idx >= C->child_count())
+ return ::testing::AssertionFailure()
+ << "Idx out of range. Idx = " << Idx
+ << ", child_count = " << C->child_count();
+
+ Comment::child_iterator I = C->child_begin() + Idx;
+ Comment *CommentChild = *I;
+ if (!CommentChild)
+ return ::testing::AssertionFailure() << "Child is NULL";
+
+ Child = dyn_cast<T>(CommentChild);
+ if (!Child)
+ return ::testing::AssertionFailure()
+ << "Child is not of requested type, but a "
+ << CommentChild->getCommentKindName();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTextAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ TextComment *TC;
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualText = TC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "TextComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ if (TC->hasTrailingNewline())
+ return ::testing::AssertionFailure()
+ << "TextComment has a trailing newline";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTextWithNewlineAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ TextComment *TC;
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualText = TC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "TextComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ if (!TC->hasTrailingNewline())
+ return ::testing::AssertionFailure()
+ << "TextComment has no trailing newline";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasBlockCommandAt(const Comment *C,
+ size_t Idx,
+ BlockCommandComment *&BCC,
+ StringRef Name,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, BCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = BCC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "BlockCommandComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ Paragraph = BCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasParamCommandAt(
+ const Comment *C,
+ size_t Idx,
+ ParamCommandComment *&PCC,
+ StringRef CommandName,
+ ParamCommandComment::PassDirection Direction,
+ bool IsDirectionExplicit,
+ StringRef ParamName,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, PCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualCommandName = PCC->getCommandName();
+ if (ActualCommandName != CommandName)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+ "expected \"" << CommandName.str() << "\"";
+
+ if (PCC->getDirection() != Direction)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has direction " << PCC->getDirection() << ", "
+ "expected " << Direction;
+
+ if (PCC->isDirectionExplicit() != IsDirectionExplicit)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has "
+ << (PCC->isDirectionExplicit() ? "explicit" : "implicit")
+ << " direction, "
+ "expected " << (IsDirectionExplicit ? "explicit" : "implicit");
+
+ if (!ParamName.empty() && !PCC->hasParamName())
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has no parameter name";
+
+ StringRef ActualParamName = PCC->hasParamName() ? PCC->getParamName() : "";
+ if (ActualParamName != ParamName)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has parameter name \"" << ActualParamName.str()
+ << "\", "
+ "expected \"" << ParamName.str() << "\"";
+
+ Paragraph = PCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTParamCommandAt(
+ const Comment *C,
+ size_t Idx,
+ TParamCommandComment *&TPCC,
+ StringRef CommandName,
+ StringRef ParamName,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TPCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualCommandName = TPCC->getCommandName();
+ if (ActualCommandName != CommandName)
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+ "expected \"" << CommandName.str() << "\"";
+
+ if (!ParamName.empty() && !TPCC->hasParamName())
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has no parameter name";
+
+ StringRef ActualParamName = TPCC->hasParamName() ? TPCC->getParamName() : "";
+ if (ActualParamName != ParamName)
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has parameter name \"" << ActualParamName.str()
+ << "\", "
+ "expected \"" << ParamName.str() << "\"";
+
+ Paragraph = TPCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, ICC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = ICC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct NoArgs {};
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name,
+ NoArgs) {
+ ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name);
+ if (!AR)
+ return AR;
+
+ if (ICC->getNumArgs() != 0)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name,
+ StringRef Arg) {
+ ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name);
+ if (!AR)
+ return AR;
+
+ if (ICC->getNumArgs() != 1)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
+ "expected 1";
+
+ StringRef ActualArg = ICC->getArgText(0);
+ if (ActualArg != Arg)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has argument \"" << ActualArg.str() << "\", "
+ "expected \"" << Arg.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, HST);
+ if (!AR)
+ return AR;
+
+ StringRef ActualTagName = HST->getTagName();
+ if (ActualTagName != TagName)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has name \"" << ActualTagName.str() << "\", "
+ "expected \"" << TagName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct SelfClosing {};
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ SelfClosing) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (!HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is not self-closing";
+
+ return ::testing::AssertionSuccess();
+}
+
+
+struct NoAttrs {};
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ NoAttrs) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is self-closing";
+
+ if (HST->getNumAttrs() != 0)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ StringRef AttrName,
+ StringRef AttrValue) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is self-closing";
+
+ if (HST->getNumAttrs() != 1)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
+ "expected 1";
+
+ StringRef ActualName = HST->getAttr(0).Name;
+ if (ActualName != AttrName)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has attr \"" << ActualName.str() << "\", "
+ "expected \"" << AttrName.str() << "\"";
+
+ StringRef ActualValue = HST->getAttr(0).Value;
+ if (ActualValue != AttrValue)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has attr value \"" << ActualValue.str() << "\", "
+ "expected \"" << AttrValue.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLEndTagAt(const Comment *C,
+ size_t Idx,
+ HTMLEndTagComment *&HET,
+ StringRef TagName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, HET);
+ if (!AR)
+ return AR;
+
+ StringRef ActualTagName = HET->getTagName();
+ if (ActualTagName != TagName)
+ return ::testing::AssertionFailure()
+ << "HTMLEndTagComment has name \"" << ActualTagName.str() << "\", "
+ "expected \"" << TagName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasParagraphCommentAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ ParagraphComment *PC;
+
+ {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, PC);
+ if (!AR)
+ return AR;
+ }
+
+ {
+ ::testing::AssertionResult AR = HasChildCount(PC, 1);
+ if (!AR)
+ return AR;
+ }
+
+ {
+ ::testing::AssertionResult AR = HasTextAt(PC, 0, Text);
+ if (!AR)
+ return AR;
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, VBC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = VBC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ StringRef ActualCloseName = VBC->getCloseName();
+ if (ActualCloseName != CloseName)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has closing command name \""
+ << ActualCloseName.str() << "\", "
+ "expected \"" << CloseName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct NoLines {};
+struct Lines {};
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ NoLines) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ Lines,
+ StringRef Line0) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 1)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 1";
+
+ StringRef ActualLine0 = VBC->getText(0);
+ if (ActualLine0 != Line0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
+ "expected \"" << Line0.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ Lines,
+ StringRef Line0,
+ StringRef Line1) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 2)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 2";
+
+ StringRef ActualLine0 = VBC->getText(0);
+ if (ActualLine0 != Line0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
+ "expected \"" << Line0.str() << "\"";
+
+ StringRef ActualLine1 = VBC->getText(1);
+ if (ActualLine1 != Line1)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[1] \"" << ActualLine1.str() << "\", "
+ "expected \"" << Line1.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimLineAt(const Comment *C,
+ size_t Idx,
+ VerbatimLineComment *&VLC,
+ StringRef Name,
+ StringRef Text) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, VLC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = VLC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "VerbatimLineComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ StringRef ActualText = VLC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "VerbatimLineComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+
+TEST_F(CommentParserTest, Basic1) {
+ const char *Source = "//";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 0));
+}
+
+TEST_F(CommentParserTest, Basic2) {
+ const char *Source = "// Meow";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Meow"));
+}
+
+TEST_F(CommentParserTest, Basic3) {
+ const char *Source =
+ "// Aaa\n"
+ "// Bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 1, " Bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph1) {
+ const char *Sources[] = {
+ "// Aaa\n"
+ "//\n"
+ "// Bbb",
+
+ "// Aaa\n"
+ "//\n"
+ "//\n"
+ "// Bbb",
+ };
+
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Aaa"));
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 1, " Bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph2) {
+ const char *Source =
+ "// \\brief Aaa\n"
+ "//\n"
+ "// Bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Aaa"));
+ }
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 2, " Bbb"));
+}
+
+TEST_F(CommentParserTest, Paragraph3) {
+ const char *Source = "// \\brief \\author";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " "));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC));
+
+ ASSERT_TRUE(GetChildAt(BCC, 0, PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph4) {
+ const char *Source =
+ "// \\brief Aaa\n"
+ "// Bbb \\author\n"
+ "// Ccc";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(GetChildAt(BCC, 0, PC));
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 1, " Bbb "));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Ccc"));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand1) {
+ const char *Source = "// \\param aaa";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand2) {
+ const char *Source = "// \\param\\brief";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "brief", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand3) {
+ const char *Sources[] = {
+ "// \\param aaa Bbb\n",
+ "// \\param\n"
+ "// aaa Bbb\n",
+ "// \\param \n"
+ "// aaa Bbb\n",
+ "// \\param aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand4) {
+ const char *Sources[] = {
+ "// \\param [in] aaa Bbb\n",
+ "// \\param[in] aaa Bbb\n",
+ "// \\param\n"
+ "// [in] aaa Bbb\n",
+ "// \\param [in]\n"
+ "// aaa Bbb\n",
+ "// \\param [in] aaa\n"
+ "// Bbb\n",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand5) {
+ const char *Sources[] = {
+ "// \\param [out] aaa Bbb\n",
+ "// \\param[out] aaa Bbb\n",
+ "// \\param\n"
+ "// [out] aaa Bbb\n",
+ "// \\param [out]\n"
+ "// aaa Bbb\n",
+ "// \\param [out] aaa\n"
+ "// Bbb\n",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::Out,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand6) {
+ const char *Sources[] = {
+ "// \\param [in,out] aaa Bbb\n",
+ "// \\param[in,out] aaa Bbb\n",
+ "// \\param [in, out] aaa Bbb\n",
+ "// \\param [in,\n"
+ "// out] aaa Bbb\n",
+ "// \\param [in,out]\n"
+ "// aaa Bbb\n",
+ "// \\param [in,out] aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::InOut,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand7) {
+ const char *Source =
+ "// \\param aaa \\% Bbb \\$ ccc\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+
+ ASSERT_TRUE(HasChildCount(PC, 5));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasTextAt(PC, 1, "%"));
+ ASSERT_TRUE(HasTextAt(PC, 2, " Bbb "));
+ ASSERT_TRUE(HasTextAt(PC, 3, "$"));
+ ASSERT_TRUE(HasTextAt(PC, 4, " ccc"));
+ }
+}
+
+TEST_F(CommentParserTest, TParamCommand1) {
+ const char *Sources[] = {
+ "// \\tparam aaa Bbb\n",
+ "// \\tparam\n"
+ "// aaa Bbb\n",
+ "// \\tparam \n"
+ "// aaa Bbb\n",
+ "// \\tparam aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ TParamCommandComment *TPCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam",
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(TPCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, TParamCommand2) {
+ const char *Source = "// \\tparam\\brief";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ TParamCommandComment *TPCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam", "", PC));
+ ASSERT_TRUE(HasChildCount(TPCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "brief", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+
+TEST_F(CommentParserTest, InlineCommand1) {
+ const char *Source = "// \\c";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs()));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand2) {
+ const char *Source = "// \\c ";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs()));
+ ASSERT_TRUE(HasTextAt(PC, 2, " "));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand3) {
+ const char *Source = "// \\c aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand4) {
+ const char *Source = "// \\c aaa bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 2, " bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand5) {
+ const char *Source = "// \\unknown aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "unknown", NoArgs()));
+ ASSERT_TRUE(HasTextAt(PC, 2, " aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, HTML1) {
+ const char *Sources[] = {
+ "// <a",
+ "// <a>",
+ "// <a >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", NoAttrs()));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML2) {
+ const char *Sources[] = {
+ "// <br/>",
+ "// <br />"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "br", SelfClosing()));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML3) {
+ const char *Sources[] = {
+ "// <a href",
+ "// <a href ",
+ "// <a href>",
+ "// <a href >",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", ""));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML4) {
+ const char *Sources[] = {
+ "// <a href=\"bbb\"",
+ "// <a href=\"bbb\">",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", "bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML5) {
+ const char *Sources[] = {
+ "// </a",
+ "// </a>",
+ "// </a >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLEndTagComment *HET;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLEndTagAt(PC, 1, HET, "a"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML6) {
+ const char *Source =
+ "// <pre>\n"
+ "// Aaa\n"
+ "// Bbb\n"
+ "// </pre>\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ HTMLEndTagComment *HET;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 6));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "pre", NoAttrs()));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 2, " Aaa"));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 3, " Bbb"));
+ ASSERT_TRUE(HasTextAt(PC, 4, " "));
+ ASSERT_TRUE(HasHTMLEndTagAt(PC, 5, HET, "pre"));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock1) {
+ const char *Source = "// \\verbatim\\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VCC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VCC, "verbatim", "endverbatim",
+ NoLines()));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock2) {
+ const char *Source = "// \\verbatim Aaa \\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa "));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock3) {
+ const char *Source = "// \\verbatim Aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "",
+ Lines(), " Aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock4) {
+ const char *Source =
+ "//\\verbatim\n"
+ "//\\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim",
+ NoLines()));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock5) {
+ const char *Sources[] = {
+ "//\\verbatim\n"
+ "// Aaa\n"
+ "//\\endverbatim\n",
+
+ "/*\\verbatim\n"
+ " * Aaa\n"
+ " *\\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock6) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " * \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock7) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "// Bbb\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " * Bbb\n"
+ " * \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa", " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock8) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "//\n"
+ "// Bbb\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " *\n"
+ " * Bbb\n"
+ " * \\endverbatim*/"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim"));
+ ASSERT_EQ(3U, VBC->getNumLines());
+ ASSERT_EQ(" Aaa", VBC->getText(0));
+ ASSERT_EQ("", VBC->getText(1));
+ ASSERT_EQ(" Bbb", VBC->getText(2));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimLine1) {
+ const char *Sources[] = {
+ "// \\fn",
+ "// \\fn\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimLineComment *VLC;
+ ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn", ""));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimLine2) {
+ const char *Sources[] = {
+ "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
+ "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimLineComment *VLC;
+ ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn",
+ " void *foo(const char *zzz = \"\\$\");"));
+ }
+ }
+}
+
+} // unnamed namespace
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/unittests/AST/Makefile b/unittests/AST/Makefile
new file mode 100644
index 0000000..31cd5de
--- /dev/null
+++ b/unittests/AST/Makefile
@@ -0,0 +1,15 @@
+##===- unittests/AST/Makefile ------------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../..
+TESTNAME = AST
+LINK_COMPONENTS := support mc
+USEDLIBS = clangAST.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
new file mode 100644
index 0000000..cf37c7d
--- /dev/null
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -0,0 +1,2312 @@
+//===- unittest/Tooling/ASTMatchersTest.cpp - AST matcher unit tests ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTMatchersTest.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+#if GTEST_HAS_DEATH_TEST
+TEST(HasNameDeathTest, DiesOnEmptyName) {
+ ASSERT_DEBUG_DEATH({
+ DeclarationMatcher HasEmptyName = record(hasName(""));
+ EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
+ }, "");
+}
+
+TEST(HasNameDeathTest, DiesOnEmptyPattern) {
+ ASSERT_DEBUG_DEATH({
+ DeclarationMatcher HasEmptyName = record(matchesName(""));
+ EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
+ }, "");
+}
+
+TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
+ ASSERT_DEBUG_DEATH({
+ DeclarationMatcher IsDerivedFromEmpty = record(isDerivedFrom(""));
+ EXPECT_TRUE(notMatches("class X {};", IsDerivedFromEmpty));
+ }, "");
+}
+#endif
+
+TEST(Decl, MatchesDeclarations) {
+ EXPECT_TRUE(notMatches("", decl(usingDecl())));
+ EXPECT_TRUE(matches("namespace x { class X {}; } using x::X;",
+ decl(usingDecl())));
+}
+
+TEST(NameableDeclaration, MatchesVariousDecls) {
+ DeclarationMatcher NamedX = nameableDeclaration(hasName("X"));
+ EXPECT_TRUE(matches("typedef int X;", NamedX));
+ EXPECT_TRUE(matches("int X;", NamedX));
+ EXPECT_TRUE(matches("class foo { virtual void X(); };", NamedX));
+ EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", NamedX));
+ EXPECT_TRUE(matches("void foo() { int X; }", NamedX));
+ EXPECT_TRUE(matches("namespace X { }", NamedX));
+ EXPECT_TRUE(matches("enum X { A, B, C };", NamedX));
+
+ EXPECT_TRUE(notMatches("#define X 1", NamedX));
+}
+
+TEST(NameableDeclaration, REMatchesVariousDecls) {
+ DeclarationMatcher NamedX = nameableDeclaration(matchesName("::X"));
+ EXPECT_TRUE(matches("typedef int Xa;", NamedX));
+ EXPECT_TRUE(matches("int Xb;", NamedX));
+ EXPECT_TRUE(matches("class foo { virtual void Xc(); };", NamedX));
+ EXPECT_TRUE(matches("void foo() try { } catch(int Xdef) { }", NamedX));
+ EXPECT_TRUE(matches("void foo() { int Xgh; }", NamedX));
+ EXPECT_TRUE(matches("namespace Xij { }", NamedX));
+ EXPECT_TRUE(matches("enum X { A, B, C };", NamedX));
+
+ EXPECT_TRUE(notMatches("#define Xkl 1", NamedX));
+
+ DeclarationMatcher StartsWithNo = nameableDeclaration(matchesName("::no"));
+ EXPECT_TRUE(matches("int no_foo;", StartsWithNo));
+ EXPECT_TRUE(matches("class foo { virtual void nobody(); };", StartsWithNo));
+
+ DeclarationMatcher Abc = nameableDeclaration(matchesName("a.*b.*c"));
+ EXPECT_TRUE(matches("int abc;", Abc));
+ EXPECT_TRUE(matches("int aFOObBARc;", Abc));
+ EXPECT_TRUE(notMatches("int cab;", Abc));
+ EXPECT_TRUE(matches("int cabc;", Abc));
+}
+
+TEST(DeclarationMatcher, MatchClass) {
+ DeclarationMatcher ClassMatcher(record());
+#if !defined(_MSC_VER)
+ EXPECT_FALSE(matches("", ClassMatcher));
+#else
+ // Matches class type_info.
+ EXPECT_TRUE(matches("", ClassMatcher));
+#endif
+
+ DeclarationMatcher ClassX = record(record(hasName("X")));
+ EXPECT_TRUE(matches("class X;", ClassX));
+ EXPECT_TRUE(matches("class X {};", ClassX));
+ EXPECT_TRUE(matches("template<class T> class X {};", ClassX));
+ EXPECT_TRUE(notMatches("", ClassX));
+}
+
+TEST(DeclarationMatcher, ClassIsDerived) {
+ DeclarationMatcher IsDerivedFromX = record(isDerivedFrom("X"));
+
+ EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX));
+ EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX));
+ EXPECT_TRUE(matches("class X {};", IsDerivedFromX));
+ EXPECT_TRUE(matches("class X;", IsDerivedFromX));
+ EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX));
+ EXPECT_TRUE(notMatches("", IsDerivedFromX));
+
+ DeclarationMatcher ZIsDerivedFromX =
+ record(hasName("Z"), isDerivedFrom("X"));
+ EXPECT_TRUE(
+ matches("class X {}; class Y : public X {}; class Z : public Y {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {};"
+ "template<class T> class Y : public X {};"
+ "class Z : public Y<int> {};", ZIsDerivedFromX));
+ EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T> class X {}; "
+ "template<class T> class Z : public X<T> {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T, class U=T> class X {}; "
+ "template<class T> class Z : public X<T> {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class A { class Z : public X {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class X> class A { public: class Z : public X {}; }; "
+ "class X{}; void y() { A<X>::Z z; }", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template <class T> class X {}; "
+ "template<class Y> class A { class Z : public X<Y> {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<template<class T> class X> class A { "
+ " class Z : public X<int> {}; };", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<template<class T> class X> class A { "
+ " public: class Z : public X<int> {}; }; "
+ "template<class T> class X {}; void y() { A<X>::Z z; }",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class A { class Z : public X::D {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class X> class A { public: "
+ " class Z : public X::D {}; }; "
+ "class Y { public: class X {}; typedef X D; }; "
+ "void y() { A<Y>::Z z; }", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X Y; class Z : public Y {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T> class Y { typedef typename T::U X; "
+ " class Z : public X {}; };", ZIsDerivedFromX));
+ EXPECT_TRUE(matches("class X {}; class Z : public ::X {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class T> class X {}; "
+ "template<class T> class A { class Z : public X<T>::D {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T> class X { public: typedef X<T> D; }; "
+ "template<class T> class A { public: "
+ " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class A { class Z : public X::D::E {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X V; typedef V W; class Z : public W {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; class Y : public X {}; "
+ "typedef Y V; typedef V W; class Z : public W {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T, class U> class X {}; "
+ "template<class T> class A { class Z : public X<T, int> {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class D { typedef X A; typedef A B; "
+ " typedef B C; class Z : public C {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X A; typedef A B; "
+ "class Z : public B {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X A; typedef A B; typedef B C; "
+ "class Z : public C {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class U {}; typedef U X; typedef X V; "
+ "class Z : public V {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class Base {}; typedef Base X; "
+ "class Z : public Base {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class Base {}; typedef Base Base2; typedef Base2 X; "
+ "class Z : public Base {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("class Base {}; class Base2 {}; typedef Base2 X; "
+ "class Z : public Base {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class A {}; typedef A X; typedef A Y; "
+ "class Z : public Y {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template <typename T> class Z;"
+ "template <> class Z<void> {};"
+ "template <typename T> class Z : public Z<void> {};",
+ IsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template <typename T> class X;"
+ "template <> class X<void> {};"
+ "template <typename T> class X : public X<void> {};",
+ IsDerivedFromX));
+ EXPECT_TRUE(matches(
+ "class X {};"
+ "template <typename T> class Z;"
+ "template <> class Z<void> {};"
+ "template <typename T> class Z : public Z<void>, public X {};",
+ ZIsDerivedFromX));
+
+ // FIXME: Once we have better matchers for template type matching,
+ // get rid of the Variable(...) matching and match the right template
+ // declarations directly.
+ const char *RecursiveTemplateOneParameter =
+ "class Base1 {}; class Base2 {};"
+ "template <typename T> class Z;"
+ "template <> class Z<void> : public Base1 {};"
+ "template <> class Z<int> : public Base2 {};"
+ "template <> class Z<float> : public Z<void> {};"
+ "template <> class Z<double> : public Z<int> {};"
+ "template <typename T> class Z : public Z<float>, public Z<double> {};"
+ "void f() { Z<float> z_float; Z<double> z_double; Z<char> z_char; }";
+ EXPECT_TRUE(matches(
+ RecursiveTemplateOneParameter,
+ variable(hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1")))))));
+ EXPECT_TRUE(notMatches(
+ RecursiveTemplateOneParameter,
+ variable(
+ hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base2")))))));
+ EXPECT_TRUE(matches(
+ RecursiveTemplateOneParameter,
+ variable(
+ hasName("z_char"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1"),
+ isDerivedFrom("Base2")))))));
+
+ const char *RecursiveTemplateTwoParameters =
+ "class Base1 {}; class Base2 {};"
+ "template <typename T1, typename T2> class Z;"
+ "template <typename T> class Z<void, T> : public Base1 {};"
+ "template <typename T> class Z<int, T> : public Base2 {};"
+ "template <typename T> class Z<float, T> : public Z<void, T> {};"
+ "template <typename T> class Z<double, T> : public Z<int, T> {};"
+ "template <typename T1, typename T2> class Z : "
+ " public Z<float, T2>, public Z<double, T2> {};"
+ "void f() { Z<float, void> z_float; Z<double, void> z_double; "
+ " Z<char, void> z_char; }";
+ EXPECT_TRUE(matches(
+ RecursiveTemplateTwoParameters,
+ variable(
+ hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1")))))));
+ EXPECT_TRUE(notMatches(
+ RecursiveTemplateTwoParameters,
+ variable(
+ hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base2")))))));
+ EXPECT_TRUE(matches(
+ RecursiveTemplateTwoParameters,
+ variable(
+ hasName("z_char"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1"),
+ isDerivedFrom("Base2")))))));
+ EXPECT_TRUE(matches(
+ "namespace ns { class X {}; class Y : public X {}; }",
+ record(isDerivedFrom("::ns::X"))));
+ EXPECT_TRUE(notMatches(
+ "class X {}; class Y : public X {};",
+ record(isDerivedFrom("::ns::X"))));
+
+ EXPECT_TRUE(matches(
+ "class X {}; class Y : public X {};",
+ record(isDerivedFrom(record(hasName("X")).bind("test")))));
+}
+
+TEST(AllOf, AllOverloadsWork) {
+ const char Program[] =
+ "struct T { }; int f(int, T*); void g(int x) { T t; f(x, &t); }";
+ EXPECT_TRUE(matches(Program,
+ call(allOf(callee(function(hasName("f"))),
+ hasArgument(0, declarationReference(to(variable())))))));
+ EXPECT_TRUE(matches(Program,
+ call(allOf(callee(function(hasName("f"))),
+ hasArgument(0, declarationReference(to(variable()))),
+ hasArgument(1, hasType(pointsTo(record(hasName("T")))))))));
+}
+
+TEST(DeclarationMatcher, MatchAnyOf) {
+ DeclarationMatcher YOrZDerivedFromX =
+ record(anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z"))));
+ EXPECT_TRUE(
+ matches("class X {}; class Z : public X {};", YOrZDerivedFromX));
+ EXPECT_TRUE(matches("class Y {};", YOrZDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("class X {}; class W : public X {};", YOrZDerivedFromX));
+ EXPECT_TRUE(notMatches("class Z {};", YOrZDerivedFromX));
+
+ DeclarationMatcher XOrYOrZOrU =
+ record(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U")));
+ EXPECT_TRUE(matches("class X {};", XOrYOrZOrU));
+ EXPECT_TRUE(notMatches("class V {};", XOrYOrZOrU));
+
+ DeclarationMatcher XOrYOrZOrUOrV =
+ record(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"),
+ hasName("V")));
+ EXPECT_TRUE(matches("class X {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class Y {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class Z {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class U {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class V {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(notMatches("class A {};", XOrYOrZOrUOrV));
+}
+
+TEST(DeclarationMatcher, MatchHas) {
+ DeclarationMatcher HasClassX = record(has(record(hasName("X"))));
+
+ EXPECT_TRUE(matches("class Y { class X {}; };", HasClassX));
+ EXPECT_TRUE(matches("class X {};", HasClassX));
+
+ DeclarationMatcher YHasClassX =
+ record(hasName("Y"), has(record(hasName("X"))));
+ EXPECT_TRUE(matches("class Y { class X {}; };", YHasClassX));
+ EXPECT_TRUE(notMatches("class X {};", YHasClassX));
+ EXPECT_TRUE(
+ notMatches("class Y { class Z { class X {}; }; };", YHasClassX));
+}
+
+TEST(DeclarationMatcher, MatchHasRecursiveAllOf) {
+ DeclarationMatcher Recursive =
+ record(
+ has(record(
+ has(record(hasName("X"))),
+ has(record(hasName("Y"))),
+ hasName("Z"))),
+ has(record(
+ has(record(hasName("A"))),
+ has(record(hasName("B"))),
+ hasName("C"))),
+ hasName("F"));
+
+ EXPECT_TRUE(matches(
+ "class F {"
+ " class Z {"
+ " class X {};"
+ " class Y {};"
+ " };"
+ " class C {"
+ " class A {};"
+ " class B {};"
+ " };"
+ "};", Recursive));
+
+ EXPECT_TRUE(matches(
+ "class F {"
+ " class Z {"
+ " class A {};"
+ " class X {};"
+ " class Y {};"
+ " };"
+ " class C {"
+ " class X {};"
+ " class A {};"
+ " class B {};"
+ " };"
+ "};", Recursive));
+
+ EXPECT_TRUE(matches(
+ "class O1 {"
+ " class O2 {"
+ " class F {"
+ " class Z {"
+ " class A {};"
+ " class X {};"
+ " class Y {};"
+ " };"
+ " class C {"
+ " class X {};"
+ " class A {};"
+ " class B {};"
+ " };"
+ " };"
+ " };"
+ "};", Recursive));
+}
+
+TEST(DeclarationMatcher, MatchHasRecursiveAnyOf) {
+ DeclarationMatcher Recursive =
+ record(
+ anyOf(
+ has(record(
+ anyOf(
+ has(record(
+ hasName("X"))),
+ has(record(
+ hasName("Y"))),
+ hasName("Z")))),
+ has(record(
+ anyOf(
+ hasName("C"),
+ has(record(
+ hasName("A"))),
+ has(record(
+ hasName("B")))))),
+ hasName("F")));
+
+ EXPECT_TRUE(matches("class F {};", Recursive));
+ EXPECT_TRUE(matches("class Z {};", Recursive));
+ EXPECT_TRUE(matches("class C {};", Recursive));
+ EXPECT_TRUE(matches("class M { class N { class X {}; }; };", Recursive));
+ EXPECT_TRUE(matches("class M { class N { class B {}; }; };", Recursive));
+ EXPECT_TRUE(
+ matches("class O1 { class O2 {"
+ " class M { class N { class B {}; }; }; "
+ "}; };", Recursive));
+}
+
+TEST(DeclarationMatcher, MatchNot) {
+ DeclarationMatcher NotClassX =
+ record(
+ isDerivedFrom("Y"),
+ unless(hasName("Y")),
+ unless(hasName("X")));
+ EXPECT_TRUE(notMatches("", NotClassX));
+ EXPECT_TRUE(notMatches("class Y {};", NotClassX));
+ EXPECT_TRUE(matches("class Y {}; class Z : public Y {};", NotClassX));
+ EXPECT_TRUE(notMatches("class Y {}; class X : public Y {};", NotClassX));
+ EXPECT_TRUE(
+ notMatches("class Y {}; class Z {}; class X : public Y {};",
+ NotClassX));
+
+ DeclarationMatcher ClassXHasNotClassY =
+ record(
+ hasName("X"),
+ has(record(hasName("Z"))),
+ unless(
+ has(record(hasName("Y")))));
+ EXPECT_TRUE(matches("class X { class Z {}; };", ClassXHasNotClassY));
+ EXPECT_TRUE(notMatches("class X { class Y {}; class Z {}; };",
+ ClassXHasNotClassY));
+}
+
+TEST(DeclarationMatcher, HasDescendant) {
+ DeclarationMatcher ZDescendantClassX =
+ record(
+ hasDescendant(record(hasName("X"))),
+ hasName("Z"));
+ EXPECT_TRUE(matches("class Z { class X {}; };", ZDescendantClassX));
+ EXPECT_TRUE(
+ matches("class Z { class Y { class X {}; }; };", ZDescendantClassX));
+ EXPECT_TRUE(
+ matches("class Z { class A { class Y { class X {}; }; }; };",
+ ZDescendantClassX));
+ EXPECT_TRUE(
+ matches("class Z { class A { class B { class Y { class X {}; }; }; }; };",
+ ZDescendantClassX));
+ EXPECT_TRUE(notMatches("class Z {};", ZDescendantClassX));
+
+ DeclarationMatcher ZDescendantClassXHasClassY =
+ record(
+ hasDescendant(record(has(record(hasName("Y"))),
+ hasName("X"))),
+ hasName("Z"));
+ EXPECT_TRUE(matches("class Z { class X { class Y {}; }; };",
+ ZDescendantClassXHasClassY));
+ EXPECT_TRUE(
+ matches("class Z { class A { class B { class X { class Y {}; }; }; }; };",
+ ZDescendantClassXHasClassY));
+ EXPECT_TRUE(notMatches(
+ "class Z {"
+ " class A {"
+ " class B {"
+ " class X {"
+ " class C {"
+ " class Y {};"
+ " };"
+ " };"
+ " }; "
+ " };"
+ "};", ZDescendantClassXHasClassY));
+
+ DeclarationMatcher ZDescendantClassXDescendantClassY =
+ record(
+ hasDescendant(record(hasDescendant(record(hasName("Y"))),
+ hasName("X"))),
+ hasName("Z"));
+ EXPECT_TRUE(
+ matches("class Z { class A { class X { class B { class Y {}; }; }; }; };",
+ ZDescendantClassXDescendantClassY));
+ EXPECT_TRUE(matches(
+ "class Z {"
+ " class A {"
+ " class X {"
+ " class B {"
+ " class Y {};"
+ " };"
+ " class Y {};"
+ " };"
+ " };"
+ "};", ZDescendantClassXDescendantClassY));
+}
+
+TEST(Enum, DoesNotMatchClasses) {
+ EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X"))));
+}
+
+TEST(Enum, MatchesEnums) {
+ EXPECT_TRUE(matches("enum X {};", enumDecl(hasName("X"))));
+}
+
+TEST(EnumConstant, Matches) {
+ DeclarationMatcher Matcher = enumConstant(hasName("A"));
+ EXPECT_TRUE(matches("enum X{ A };", Matcher));
+ EXPECT_TRUE(notMatches("enum X{ B };", Matcher));
+ EXPECT_TRUE(notMatches("enum X {};", Matcher));
+}
+
+TEST(StatementMatcher, Has) {
+ StatementMatcher HasVariableI =
+ expression(
+ hasType(pointsTo(record(hasName("X")))),
+ has(declarationReference(to(variable(hasName("i"))))));
+
+ EXPECT_TRUE(matches(
+ "class X; X *x(int); void c() { int i; x(i); }", HasVariableI));
+ EXPECT_TRUE(notMatches(
+ "class X; X *x(int); void c() { int i; x(42); }", HasVariableI));
+}
+
+TEST(StatementMatcher, HasDescendant) {
+ StatementMatcher HasDescendantVariableI =
+ expression(
+ hasType(pointsTo(record(hasName("X")))),
+ hasDescendant(declarationReference(to(variable(hasName("i"))))));
+
+ EXPECT_TRUE(matches(
+ "class X; X *x(bool); bool b(int); void c() { int i; x(b(i)); }",
+ HasDescendantVariableI));
+ EXPECT_TRUE(notMatches(
+ "class X; X *x(bool); bool b(int); void c() { int i; x(b(42)); }",
+ HasDescendantVariableI));
+}
+
+TEST(TypeMatcher, MatchesClassType) {
+ TypeMatcher TypeA = hasDeclaration(record(hasName("A")));
+
+ EXPECT_TRUE(matches("class A { public: A *a; };", TypeA));
+ EXPECT_TRUE(notMatches("class A {};", TypeA));
+
+ TypeMatcher TypeDerivedFromA = hasDeclaration(record(isDerivedFrom("A")));
+
+ EXPECT_TRUE(matches("class A {}; class B : public A { public: B *b; };",
+ TypeDerivedFromA));
+ EXPECT_TRUE(notMatches("class A {};", TypeA));
+
+ TypeMatcher TypeAHasClassB = hasDeclaration(
+ record(hasName("A"), has(record(hasName("B")))));
+
+ EXPECT_TRUE(
+ matches("class A { public: A *a; class B {}; };", TypeAHasClassB));
+}
+
+// Returns from Run whether 'bound_nodes' contain a Decl bound to 'Id', which
+// can be dynamically casted to T.
+// Optionally checks that the check succeeded a specific number of times.
+template <typename T>
+class VerifyIdIsBoundToDecl : public BoundNodesCallback {
+public:
+ // Create an object that checks that a node of type 'T' was bound to 'Id'.
+ // Does not check for a certain number of matches.
+ explicit VerifyIdIsBoundToDecl(const std::string& Id)
+ : Id(Id), ExpectedCount(-1), Count(0) {}
+
+ // Create an object that checks that a node of type 'T' was bound to 'Id'.
+ // Checks that there were exactly 'ExpectedCount' matches.
+ explicit VerifyIdIsBoundToDecl(const std::string& Id, int ExpectedCount)
+ : Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
+
+ ~VerifyIdIsBoundToDecl() {
+ if (ExpectedCount != -1) {
+ EXPECT_EQ(ExpectedCount, Count);
+ }
+ }
+
+ virtual bool run(const BoundNodes *Nodes) {
+ if (Nodes->getDeclAs<T>(Id) != NULL) {
+ ++Count;
+ return true;
+ }
+ return false;
+ }
+
+private:
+ const std::string Id;
+ const int ExpectedCount;
+ int Count;
+};
+template <typename T>
+class VerifyIdIsBoundToStmt : public BoundNodesCallback {
+public:
+ explicit VerifyIdIsBoundToStmt(const std::string &Id) : Id(Id) {}
+ virtual bool run(const BoundNodes *Nodes) {
+ const T *Node = Nodes->getStmtAs<T>(Id);
+ return Node != NULL;
+ }
+private:
+ const std::string Id;
+};
+
+TEST(Matcher, BindMatchedNodes) {
+ DeclarationMatcher ClassX = has(record(hasName("::X")).bind("x"));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class X {};",
+ ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class X {};",
+ ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("other-id")));
+
+ TypeMatcher TypeAHasClassB = hasDeclaration(
+ record(hasName("A"), has(record(hasName("B")).bind("b"))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };",
+ TypeAHasClassB,
+ new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ StatementMatcher MethodX = call(callee(method(hasName("x")))).bind("x");
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };",
+ MethodX,
+ new VerifyIdIsBoundToStmt<CXXMemberCallExpr>("x")));
+}
+
+TEST(Matcher, BindTheSameNameInAlternatives) {
+ StatementMatcher matcher = anyOf(
+ binaryOperator(hasOperatorName("+"),
+ hasLHS(expression().bind("x")),
+ hasRHS(integerLiteral(equals(0)))),
+ binaryOperator(hasOperatorName("+"),
+ hasLHS(integerLiteral(equals(0))),
+ hasRHS(expression().bind("x"))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ // The first branch of the matcher binds x to 0 but then fails.
+ // The second branch binds x to f() and succeeds.
+ "int f() { return 0 + f(); }",
+ matcher,
+ new VerifyIdIsBoundToStmt<CallExpr>("x")));
+}
+
+TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) {
+ TypeMatcher ClassX = hasDeclaration(record(hasName("X")));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X *x) { x; }",
+ expression(hasType(ClassX))));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X *x) { x; }",
+ expression(hasType(pointsTo(ClassX)))));
+}
+
+TEST(HasType, TakesQualTypeMatcherAndMatchesValueDecl) {
+ TypeMatcher ClassX = hasDeclaration(record(hasName("X")));
+ EXPECT_TRUE(
+ matches("class X {}; void y() { X x; }", variable(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX))));
+ EXPECT_TRUE(
+ matches("class X {}; void y() { X *x; }",
+ variable(hasType(pointsTo(ClassX)))));
+}
+
+TEST(HasType, TakesDeclMatcherAndMatchesExpr) {
+ DeclarationMatcher ClassX = record(hasName("X"));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X *x) { x; }",
+ expression(hasType(ClassX))));
+}
+
+TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) {
+ DeclarationMatcher ClassX = record(hasName("X"));
+ EXPECT_TRUE(
+ matches("class X {}; void y() { X x; }", variable(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX))));
+}
+
+TEST(Matcher, Call) {
+ // FIXME: Do we want to overload Call() to directly take
+ // Matcher<Decl>, too?
+ StatementMatcher MethodX = call(hasDeclaration(method(hasName("x"))));
+
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };", MethodX));
+ EXPECT_TRUE(notMatches("class Y { void x() {} };", MethodX));
+
+ StatementMatcher MethodOnY = memberCall(on(hasType(record(hasName("Y")))));
+
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y &y) { y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
+ MethodOnY));
+
+ StatementMatcher MethodOnYPointer =
+ memberCall(on(hasType(pointsTo(record(hasName("Y"))))));
+
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z(Y &y) { y.x(); }",
+ MethodOnYPointer));
+}
+
+TEST(HasType, MatchesAsString) {
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }",
+ memberCall(on(hasType(asString("class Y *"))))));
+ EXPECT_TRUE(matches("class X { void x(int x) {} };",
+ method(hasParameter(0, hasType(asString("int"))))));
+ EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };",
+ field(hasType(asString("ns::A")))));
+ EXPECT_TRUE(matches("namespace { struct A {}; } struct B { A a; };",
+ field(hasType(asString("struct <anonymous>::A")))));
+}
+
+TEST(Matcher, OverloadedOperatorCall) {
+ StatementMatcher OpCall = overloadedOperatorCall();
+ // Unary operator
+ EXPECT_TRUE(matches("class Y { }; "
+ "bool operator!(Y x) { return false; }; "
+ "Y y; bool c = !y;", OpCall));
+ // No match -- special operators like "new", "delete"
+ // FIXME: operator new takes size_t, for which we need stddef.h, for which
+ // we need to figure out include paths in the test.
+ // EXPECT_TRUE(NotMatches("#include <stddef.h>\n"
+ // "class Y { }; "
+ // "void *operator new(size_t size) { return 0; } "
+ // "Y *y = new Y;", OpCall));
+ EXPECT_TRUE(notMatches("class Y { }; "
+ "void operator delete(void *p) { } "
+ "void a() {Y *y = new Y; delete y;}", OpCall));
+ // Binary operator
+ EXPECT_TRUE(matches("class Y { }; "
+ "bool operator&&(Y x, Y y) { return true; }; "
+ "Y a; Y b; bool c = a && b;",
+ OpCall));
+ // No match -- normal operator, not an overloaded one.
+ EXPECT_TRUE(notMatches("bool x = true, y = true; bool t = x && y;", OpCall));
+ EXPECT_TRUE(notMatches("int t = 5 << 2;", OpCall));
+}
+
+TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) {
+ StatementMatcher OpCallAndAnd =
+ overloadedOperatorCall(hasOverloadedOperatorName("&&"));
+ EXPECT_TRUE(matches("class Y { }; "
+ "bool operator&&(Y x, Y y) { return true; }; "
+ "Y a; Y b; bool c = a && b;", OpCallAndAnd));
+ StatementMatcher OpCallLessLess =
+ overloadedOperatorCall(hasOverloadedOperatorName("<<"));
+ EXPECT_TRUE(notMatches("class Y { }; "
+ "bool operator&&(Y x, Y y) { return true; }; "
+ "Y a; Y b; bool c = a && b;",
+ OpCallLessLess));
+}
+
+TEST(Matcher, ThisPointerType) {
+ StatementMatcher MethodOnY =
+ memberCall(thisPointerType(record(hasName("Y"))));
+
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y &y) { y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
+ MethodOnY));
+
+ EXPECT_TRUE(matches(
+ "class Y {"
+ " public: virtual void x();"
+ "};"
+ "class X : public Y {"
+ " public: virtual void x();"
+ "};"
+ "void z() { X *x; x->Y::x(); }", MethodOnY));
+}
+
+TEST(Matcher, VariableUsage) {
+ StatementMatcher Reference =
+ declarationReference(to(
+ variable(hasInitializer(
+ memberCall(thisPointerType(record(hasName("Y"))))))));
+
+ EXPECT_TRUE(matches(
+ "class Y {"
+ " public:"
+ " bool x() const;"
+ "};"
+ "void z(const Y &y) {"
+ " bool b = y.x();"
+ " if (b) {}"
+ "}", Reference));
+
+ EXPECT_TRUE(notMatches(
+ "class Y {"
+ " public:"
+ " bool x() const;"
+ "};"
+ "void z(const Y &y) {"
+ " bool b = y.x();"
+ "}", Reference));
+}
+
+TEST(Matcher, FindsVarDeclInFuncitonParameter) {
+ EXPECT_TRUE(matches(
+ "void f(int i) {}",
+ variable(hasName("i"))));
+}
+
+TEST(Matcher, CalledVariable) {
+ StatementMatcher CallOnVariableY = expression(
+ memberCall(on(declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(matches(
+ "class Y { public: void x() { Y y; y.x(); } };", CallOnVariableY));
+ EXPECT_TRUE(matches(
+ "class Y { public: void x() const { Y y; y.x(); } };", CallOnVariableY));
+ EXPECT_TRUE(matches(
+ "class Y { public: void x(); };"
+ "class X : public Y { void z() { X y; y.x(); } };", CallOnVariableY));
+ EXPECT_TRUE(matches(
+ "class Y { public: void x(); };"
+ "class X : public Y { void z() { X *y; y->x(); } };", CallOnVariableY));
+ EXPECT_TRUE(notMatches(
+ "class Y { public: void x(); };"
+ "class X : public Y { void z() { unsigned long y; ((X*)y)->x(); } };",
+ CallOnVariableY));
+}
+
+TEST(UnaryExprOrTypeTraitExpr, MatchesSizeOfAndAlignOf) {
+ EXPECT_TRUE(matches("void x() { int a = sizeof(a); }",
+ unaryExprOrTypeTraitExpr()));
+ EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }",
+ alignOfExpr(anything())));
+ // FIXME: Uncomment once alignof is enabled.
+ // EXPECT_TRUE(matches("void x() { int a = alignof(a); }",
+ // unaryExprOrTypeTraitExpr()));
+ // EXPECT_TRUE(notMatches("void x() { int a = alignof(a); }",
+ // sizeOfExpr()));
+}
+
+TEST(UnaryExpressionOrTypeTraitExpression, MatchesCorrectType) {
+ EXPECT_TRUE(matches("void x() { int a = sizeof(a); }", sizeOfExpr(
+ hasArgumentOfType(asString("int")))));
+ EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(
+ hasArgumentOfType(asString("float")))));
+ EXPECT_TRUE(matches(
+ "struct A {}; void x() { A a; int b = sizeof(a); }",
+ sizeOfExpr(hasArgumentOfType(hasDeclaration(record(hasName("A")))))));
+ EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(
+ hasArgumentOfType(hasDeclaration(record(hasName("string")))))));
+}
+
+TEST(MemberExpression, DoesNotMatchClasses) {
+ EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpression()));
+}
+
+TEST(MemberExpression, MatchesMemberFunctionCall) {
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpression()));
+}
+
+TEST(MemberExpression, MatchesVariable) {
+ EXPECT_TRUE(
+ matches("class Y { void x() { this->y; } int y; };", memberExpression()));
+ EXPECT_TRUE(
+ matches("class Y { void x() { y; } int y; };", memberExpression()));
+ EXPECT_TRUE(
+ matches("class Y { void x() { Y y; y.y; } int y; };",
+ memberExpression()));
+}
+
+TEST(MemberExpression, MatchesStaticVariable) {
+ EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };",
+ memberExpression()));
+ EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };",
+ memberExpression()));
+ EXPECT_TRUE(notMatches("class Y { void x() { Y::y; } static int y; };",
+ memberExpression()));
+}
+
+TEST(IsInteger, MatchesIntegers) {
+ EXPECT_TRUE(matches("int i = 0;", variable(hasType(isInteger()))));
+ EXPECT_TRUE(matches("long long i = 0; void f(long long) { }; void g() {f(i);}",
+ call(hasArgument(0, declarationReference(
+ to(variable(hasType(isInteger()))))))));
+}
+
+TEST(IsInteger, ReportsNoFalsePositives) {
+ EXPECT_TRUE(notMatches("int *i;", variable(hasType(isInteger()))));
+ EXPECT_TRUE(notMatches("struct T {}; T t; void f(T *) { }; void g() {f(&t);}",
+ call(hasArgument(0, declarationReference(
+ to(variable(hasType(isInteger()))))))));
+}
+
+TEST(IsArrow, MatchesMemberVariablesViaArrow) {
+ EXPECT_TRUE(matches("class Y { void x() { this->y; } int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(matches("class Y { void x() { y; } int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
+ memberExpression(isArrow())));
+}
+
+TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) {
+ EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } static int y; };",
+ memberExpression(isArrow())));
+}
+
+TEST(IsArrow, MatchesMemberCallsViaArrow) {
+ EXPECT_TRUE(matches("class Y { void x() { this->x(); } };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };",
+ memberExpression(isArrow())));
+}
+
+TEST(Callee, MatchesDeclarations) {
+ StatementMatcher CallMethodX = call(callee(method(hasName("x"))));
+
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };", CallMethodX));
+ EXPECT_TRUE(notMatches("class Y { void x() {} };", CallMethodX));
+}
+
+TEST(Callee, MatchesMemberExpressions) {
+ EXPECT_TRUE(matches("class Y { void x() { this->x(); } };",
+ call(callee(memberExpression()))));
+ EXPECT_TRUE(
+ notMatches("class Y { void x() { this->x(); } };", call(callee(call()))));
+}
+
+TEST(Function, MatchesFunctionDeclarations) {
+ StatementMatcher CallFunctionF = call(callee(function(hasName("f"))));
+
+ EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF));
+ EXPECT_TRUE(notMatches("void f() { }", CallFunctionF));
+
+#if !defined(_MSC_VER)
+ // FIXME: Make this work for MSVC.
+ // Dependent contexts, but a non-dependent call.
+ EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }",
+ CallFunctionF));
+ EXPECT_TRUE(
+ matches("void f(); template <int N> struct S { void g() { f(); } };",
+ CallFunctionF));
+#endif
+
+ // Depedent calls don't match.
+ EXPECT_TRUE(
+ notMatches("void f(int); template <typename T> void g(T t) { f(t); }",
+ CallFunctionF));
+ EXPECT_TRUE(
+ notMatches("void f(int);"
+ "template <typename T> struct S { void g(T t) { f(t); } };",
+ CallFunctionF));
+}
+
+TEST(Matcher, Argument) {
+ StatementMatcher CallArgumentY = expression(call(
+ hasArgument(0, declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(matches("void x(int) { int y; x(y); }", CallArgumentY));
+ EXPECT_TRUE(
+ matches("class X { void x(int) { int y; x(y); } };", CallArgumentY));
+ EXPECT_TRUE(notMatches("void x(int) { int z; x(z); }", CallArgumentY));
+
+ StatementMatcher WrongIndex = expression(call(
+ hasArgument(42, declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(notMatches("void x(int) { int y; x(y); }", WrongIndex));
+}
+
+TEST(Matcher, AnyArgument) {
+ StatementMatcher CallArgumentY = expression(call(
+ hasAnyArgument(declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
+ EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
+ EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
+}
+
+TEST(Matcher, ArgumentCount) {
+ StatementMatcher Call1Arg = expression(call(argumentCountIs(1)));
+
+ EXPECT_TRUE(matches("void x(int) { x(0); }", Call1Arg));
+ EXPECT_TRUE(matches("class X { void x(int) { x(0); } };", Call1Arg));
+ EXPECT_TRUE(notMatches("void x(int, int) { x(0, 0); }", Call1Arg));
+}
+
+TEST(Matcher, References) {
+ DeclarationMatcher ReferenceClassX = variable(
+ hasType(references(record(hasName("X")))));
+ EXPECT_TRUE(matches("class X {}; void y(X y) { X &x = y; }",
+ ReferenceClassX));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X y) { const X &x = y; }", ReferenceClassX));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X y) { X x = y; }", ReferenceClassX));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX));
+}
+
+TEST(HasParameter, CallsInnerMatcher) {
+ EXPECT_TRUE(matches("class X { void x(int) {} };",
+ method(hasParameter(0, variable()))));
+ EXPECT_TRUE(notMatches("class X { void x(int) {} };",
+ method(hasParameter(0, hasName("x")))));
+}
+
+TEST(HasParameter, DoesNotMatchIfIndexOutOfBounds) {
+ EXPECT_TRUE(notMatches("class X { void x(int) {} };",
+ method(hasParameter(42, variable()))));
+}
+
+TEST(HasType, MatchesParameterVariableTypesStrictly) {
+ EXPECT_TRUE(matches("class X { void x(X x) {} };",
+ method(hasParameter(0, hasType(record(hasName("X")))))));
+ EXPECT_TRUE(notMatches("class X { void x(const X &x) {} };",
+ method(hasParameter(0, hasType(record(hasName("X")))))));
+ EXPECT_TRUE(matches("class X { void x(const X *x) {} };",
+ method(hasParameter(0, hasType(pointsTo(record(hasName("X"))))))));
+ EXPECT_TRUE(matches("class X { void x(const X &x) {} };",
+ method(hasParameter(0, hasType(references(record(hasName("X"))))))));
+}
+
+TEST(HasAnyParameter, MatchesIndependentlyOfPosition) {
+ EXPECT_TRUE(matches("class Y {}; class X { void x(X x, Y y) {} };",
+ method(hasAnyParameter(hasType(record(hasName("X")))))));
+ EXPECT_TRUE(matches("class Y {}; class X { void x(Y y, X x) {} };",
+ method(hasAnyParameter(hasType(record(hasName("X")))))));
+}
+
+TEST(Returns, MatchesReturnTypes) {
+ EXPECT_TRUE(matches("class Y { int f() { return 1; } };",
+ function(returns(asString("int")))));
+ EXPECT_TRUE(notMatches("class Y { int f() { return 1; } };",
+ function(returns(asString("float")))));
+ EXPECT_TRUE(matches("class Y { Y getMe() { return *this; } };",
+ function(returns(hasDeclaration(record(hasName("Y")))))));
+}
+
+TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) {
+ EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
+ method(hasAnyParameter(hasType(record(hasName("X")))))));
+}
+
+TEST(HasAnyParameter, DoesNotMatchThisPointer) {
+ EXPECT_TRUE(notMatches("class Y {}; class X { void x() {} };",
+ method(hasAnyParameter(hasType(pointsTo(record(hasName("X"))))))));
+}
+
+TEST(HasName, MatchesParameterVariableDeclartions) {
+ EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };",
+ method(hasAnyParameter(hasName("x")))));
+ EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
+ method(hasAnyParameter(hasName("x")))));
+}
+
+TEST(Matcher, MatchesClassTemplateSpecialization) {
+ EXPECT_TRUE(matches("template<typename T> struct A {};"
+ "template<> struct A<int> {};",
+ classTemplateSpecialization()));
+ EXPECT_TRUE(matches("template<typename T> struct A {}; A<int> a;",
+ classTemplateSpecialization()));
+ EXPECT_TRUE(notMatches("template<typename T> struct A {};",
+ classTemplateSpecialization()));
+}
+
+TEST(Matcher, MatchesTypeTemplateArgument) {
+ EXPECT_TRUE(matches(
+ "template<typename T> struct B {};"
+ "B<int> b;",
+ classTemplateSpecialization(hasAnyTemplateArgument(refersToType(
+ asString("int"))))));
+}
+
+TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) {
+ EXPECT_TRUE(matches(
+ "struct B { int next; };"
+ "template<int(B::*next_ptr)> struct A {};"
+ "A<&B::next> a;",
+ classTemplateSpecialization(hasAnyTemplateArgument(
+ refersToDeclaration(field(hasName("next")))))));
+}
+
+TEST(Matcher, MatchesSpecificArgument) {
+ EXPECT_TRUE(matches(
+ "template<typename T, typename U> class A {};"
+ "A<bool, int> a;",
+ classTemplateSpecialization(hasTemplateArgument(
+ 1, refersToType(asString("int"))))));
+ EXPECT_TRUE(notMatches(
+ "template<typename T, typename U> class A {};"
+ "A<int, bool> a;",
+ classTemplateSpecialization(hasTemplateArgument(
+ 1, refersToType(asString("int"))))));
+}
+
+TEST(Matcher, ConstructorCall) {
+ StatementMatcher Constructor = expression(constructorCall());
+
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x() { X x; }", Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x() { X x = X(); }",
+ Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x = 0; }",
+ Constructor));
+ EXPECT_TRUE(matches("class X {}; void x(int) { X x; }", Constructor));
+}
+
+TEST(Matcher, ConstructorArgument) {
+ StatementMatcher Constructor = expression(constructorCall(
+ hasArgument(0, declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; X x(y); }",
+ Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; X x = X(y); }",
+ Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; X x = y; }",
+ Constructor));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int z; X x(z); }",
+ Constructor));
+
+ StatementMatcher WrongIndex = expression(constructorCall(
+ hasArgument(42, declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int y; X x(y); }",
+ WrongIndex));
+}
+
+TEST(Matcher, ConstructorArgumentCount) {
+ StatementMatcher Constructor1Arg =
+ expression(constructorCall(argumentCountIs(1)));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x(0); }",
+ Constructor1Arg));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x = X(0); }",
+ Constructor1Arg));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x = 0; }",
+ Constructor1Arg));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int, int); }; void x() { X x(0, 0); }",
+ Constructor1Arg));
+}
+
+TEST(Matcher, BindTemporaryExpression) {
+ StatementMatcher TempExpression = expression(bindTemporaryExpression());
+
+ std::string ClassString = "class string { public: string(); ~string(); }; ";
+
+ EXPECT_TRUE(
+ matches(ClassString +
+ "string GetStringByValue();"
+ "void FunctionTakesString(string s);"
+ "void run() { FunctionTakesString(GetStringByValue()); }",
+ TempExpression));
+
+ EXPECT_TRUE(
+ notMatches(ClassString +
+ "string* GetStringPointer(); "
+ "void FunctionTakesStringPtr(string* s);"
+ "void run() {"
+ " string* s = GetStringPointer();"
+ " FunctionTakesStringPtr(GetStringPointer());"
+ " FunctionTakesStringPtr(s);"
+ "}",
+ TempExpression));
+
+ EXPECT_TRUE(
+ notMatches("class no_dtor {};"
+ "no_dtor GetObjByValue();"
+ "void ConsumeObj(no_dtor param);"
+ "void run() { ConsumeObj(GetObjByValue()); }",
+ TempExpression));
+}
+
+TEST(ConstructorDeclaration, SimpleCase) {
+ EXPECT_TRUE(matches("class Foo { Foo(int i); };",
+ constructor(ofClass(hasName("Foo")))));
+ EXPECT_TRUE(notMatches("class Foo { Foo(int i); };",
+ constructor(ofClass(hasName("Bar")))));
+}
+
+TEST(ConstructorDeclaration, IsImplicit) {
+ // This one doesn't match because the constructor is not added by the
+ // compiler (it is not needed).
+ EXPECT_TRUE(notMatches("class Foo { };",
+ constructor(isImplicit())));
+ // The compiler added the implicit default constructor.
+ EXPECT_TRUE(matches("class Foo { }; Foo* f = new Foo();",
+ constructor(isImplicit())));
+ EXPECT_TRUE(matches("class Foo { Foo(){} };",
+ constructor(unless(isImplicit()))));
+}
+
+TEST(DestructorDeclaration, MatchesVirtualDestructor) {
+ EXPECT_TRUE(matches("class Foo { virtual ~Foo(); };",
+ destructor(ofClass(hasName("Foo")))));
+}
+
+TEST(DestructorDeclaration, DoesNotMatchImplicitDestructor) {
+ EXPECT_TRUE(notMatches("class Foo {};", destructor(ofClass(hasName("Foo")))));
+}
+
+TEST(HasAnyConstructorInitializer, SimpleCase) {
+ EXPECT_TRUE(notMatches(
+ "class Foo { Foo() { } };",
+ constructor(hasAnyConstructorInitializer(anything()))));
+ EXPECT_TRUE(matches(
+ "class Foo {"
+ " Foo() : foo_() { }"
+ " int foo_;"
+ "};",
+ constructor(hasAnyConstructorInitializer(anything()))));
+}
+
+TEST(HasAnyConstructorInitializer, ForField) {
+ static const char Code[] =
+ "class Baz { };"
+ "class Foo {"
+ " Foo() : foo_() { }"
+ " Baz foo_;"
+ " Baz bar_;"
+ "};";
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ forField(hasType(record(hasName("Baz"))))))));
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ forField(hasName("foo_"))))));
+ EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer(
+ forField(hasType(record(hasName("Bar"))))))));
+}
+
+TEST(HasAnyConstructorInitializer, WithInitializer) {
+ static const char Code[] =
+ "class Foo {"
+ " Foo() : foo_(0) { }"
+ " int foo_;"
+ "};";
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ withInitializer(integerLiteral(equals(0)))))));
+ EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer(
+ withInitializer(integerLiteral(equals(1)))))));
+}
+
+TEST(HasAnyConstructorInitializer, IsWritten) {
+ static const char Code[] =
+ "struct Bar { Bar(){} };"
+ "class Foo {"
+ " Foo() : foo_() { }"
+ " Bar foo_;"
+ " Bar bar_;"
+ "};";
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ allOf(forField(hasName("foo_")), isWritten())))));
+ EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer(
+ allOf(forField(hasName("bar_")), isWritten())))));
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ allOf(forField(hasName("bar_")), unless(isWritten()))))));
+}
+
+TEST(Matcher, NewExpression) {
+ StatementMatcher New = expression(newExpression());
+
+ EXPECT_TRUE(matches("class X { public: X(); }; void x() { new X; }", New));
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x() { new X(); }", New));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { new X(0); }", New));
+ EXPECT_TRUE(matches("class X {}; void x(int) { new X; }", New));
+}
+
+TEST(Matcher, NewExpressionArgument) {
+ StatementMatcher New = expression(constructorCall(
+ hasArgument(
+ 0, declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; new X(y); }",
+ New));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; new X(y); }",
+ New));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int z; new X(z); }",
+ New));
+
+ StatementMatcher WrongIndex = expression(constructorCall(
+ hasArgument(
+ 42, declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int y; new X(y); }",
+ WrongIndex));
+}
+
+TEST(Matcher, NewExpressionArgumentCount) {
+ StatementMatcher New = constructorCall(argumentCountIs(1));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { new X(0); }", New));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int, int); }; void x() { new X(0, 0); }",
+ New));
+}
+
+TEST(Matcher, DeleteExpression) {
+ EXPECT_TRUE(matches("struct A {}; void f(A* a) { delete a; }",
+ deleteExpression()));
+}
+
+TEST(Matcher, DefaultArgument) {
+ StatementMatcher Arg = defaultArgument();
+
+ EXPECT_TRUE(matches("void x(int, int = 0) { int y; x(y); }", Arg));
+ EXPECT_TRUE(
+ matches("class X { void x(int, int = 0) { int y; x(y); } };", Arg));
+ EXPECT_TRUE(notMatches("void x(int, int = 0) { int y; x(y, 0); }", Arg));
+}
+
+TEST(Matcher, StringLiterals) {
+ StatementMatcher Literal = expression(stringLiteral());
+ EXPECT_TRUE(matches("const char *s = \"string\";", Literal));
+ // wide string
+ EXPECT_TRUE(matches("const wchar_t *s = L\"string\";", Literal));
+ // with escaped characters
+ EXPECT_TRUE(matches("const char *s = \"\x05five\";", Literal));
+ // no matching -- though the data type is the same, there is no string literal
+ EXPECT_TRUE(notMatches("const char s[1] = {'a'};", Literal));
+}
+
+TEST(Matcher, CharacterLiterals) {
+ StatementMatcher CharLiteral = expression(characterLiteral());
+ EXPECT_TRUE(matches("const char c = 'c';", CharLiteral));
+ // wide character
+ EXPECT_TRUE(matches("const char c = L'c';", CharLiteral));
+ // wide character, Hex encoded, NOT MATCHED!
+ EXPECT_TRUE(notMatches("const wchar_t c = 0x2126;", CharLiteral));
+ EXPECT_TRUE(notMatches("const char c = 0x1;", CharLiteral));
+}
+
+TEST(Matcher, IntegerLiterals) {
+ StatementMatcher HasIntLiteral = expression(integerLiteral());
+ EXPECT_TRUE(matches("int i = 10;", HasIntLiteral));
+ EXPECT_TRUE(matches("int i = 0x1AB;", HasIntLiteral));
+ EXPECT_TRUE(matches("int i = 10L;", HasIntLiteral));
+ EXPECT_TRUE(matches("int i = 10U;", HasIntLiteral));
+
+ // Non-matching cases (character literals, float and double)
+ EXPECT_TRUE(notMatches("int i = L'a';",
+ HasIntLiteral)); // this is actually a character
+ // literal cast to int
+ EXPECT_TRUE(notMatches("int i = 'a';", HasIntLiteral));
+ EXPECT_TRUE(notMatches("int i = 1e10;", HasIntLiteral));
+ EXPECT_TRUE(notMatches("int i = 10.0;", HasIntLiteral));
+}
+
+TEST(Matcher, Conditions) {
+ StatementMatcher Condition = ifStmt(hasCondition(boolLiteral(equals(true))));
+
+ EXPECT_TRUE(matches("void x() { if (true) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { if (false) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { bool a = true; if (a) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { if (true || false) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { if (1) {} }", Condition));
+}
+
+TEST(MatchBinaryOperator, HasOperatorName) {
+ StatementMatcher OperatorOr = binaryOperator(hasOperatorName("||"));
+
+ EXPECT_TRUE(matches("void x() { true || false; }", OperatorOr));
+ EXPECT_TRUE(notMatches("void x() { true && false; }", OperatorOr));
+}
+
+TEST(MatchBinaryOperator, HasLHSAndHasRHS) {
+ StatementMatcher OperatorTrueFalse =
+ binaryOperator(hasLHS(boolLiteral(equals(true))),
+ hasRHS(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true || false; }", OperatorTrueFalse));
+ EXPECT_TRUE(matches("void x() { true && false; }", OperatorTrueFalse));
+ EXPECT_TRUE(notMatches("void x() { false || true; }", OperatorTrueFalse));
+}
+
+TEST(MatchBinaryOperator, HasEitherOperand) {
+ StatementMatcher HasOperand =
+ binaryOperator(hasEitherOperand(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true || false; }", HasOperand));
+ EXPECT_TRUE(matches("void x() { false && true; }", HasOperand));
+ EXPECT_TRUE(notMatches("void x() { true || true; }", HasOperand));
+}
+
+TEST(Matcher, BinaryOperatorTypes) {
+ // Integration test that verifies the AST provides all binary operators in
+ // a way we expect.
+ // FIXME: Operator ','
+ EXPECT_TRUE(
+ matches("void x() { 3, 4; }", binaryOperator(hasOperatorName(","))));
+ EXPECT_TRUE(
+ matches("bool b; bool c = (b = true);",
+ binaryOperator(hasOperatorName("="))));
+ EXPECT_TRUE(
+ matches("bool b = 1 != 2;", binaryOperator(hasOperatorName("!="))));
+ EXPECT_TRUE(
+ matches("bool b = 1 == 2;", binaryOperator(hasOperatorName("=="))));
+ EXPECT_TRUE(matches("bool b = 1 < 2;", binaryOperator(hasOperatorName("<"))));
+ EXPECT_TRUE(
+ matches("bool b = 1 <= 2;", binaryOperator(hasOperatorName("<="))));
+ EXPECT_TRUE(
+ matches("int i = 1 << 2;", binaryOperator(hasOperatorName("<<"))));
+ EXPECT_TRUE(
+ matches("int i = 1; int j = (i <<= 2);",
+ binaryOperator(hasOperatorName("<<="))));
+ EXPECT_TRUE(matches("bool b = 1 > 2;", binaryOperator(hasOperatorName(">"))));
+ EXPECT_TRUE(
+ matches("bool b = 1 >= 2;", binaryOperator(hasOperatorName(">="))));
+ EXPECT_TRUE(
+ matches("int i = 1 >> 2;", binaryOperator(hasOperatorName(">>"))));
+ EXPECT_TRUE(
+ matches("int i = 1; int j = (i >>= 2);",
+ binaryOperator(hasOperatorName(">>="))));
+ EXPECT_TRUE(
+ matches("int i = 42 ^ 23;", binaryOperator(hasOperatorName("^"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i ^= 42);",
+ binaryOperator(hasOperatorName("^="))));
+ EXPECT_TRUE(
+ matches("int i = 42 % 23;", binaryOperator(hasOperatorName("%"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i %= 42);",
+ binaryOperator(hasOperatorName("%="))));
+ EXPECT_TRUE(
+ matches("bool b = 42 &23;", binaryOperator(hasOperatorName("&"))));
+ EXPECT_TRUE(
+ matches("bool b = true && false;",
+ binaryOperator(hasOperatorName("&&"))));
+ EXPECT_TRUE(
+ matches("bool b = true; bool c = (b &= false);",
+ binaryOperator(hasOperatorName("&="))));
+ EXPECT_TRUE(
+ matches("bool b = 42 | 23;", binaryOperator(hasOperatorName("|"))));
+ EXPECT_TRUE(
+ matches("bool b = true || false;",
+ binaryOperator(hasOperatorName("||"))));
+ EXPECT_TRUE(
+ matches("bool b = true; bool c = (b |= false);",
+ binaryOperator(hasOperatorName("|="))));
+ EXPECT_TRUE(
+ matches("int i = 42 *23;", binaryOperator(hasOperatorName("*"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i *= 23);",
+ binaryOperator(hasOperatorName("*="))));
+ EXPECT_TRUE(
+ matches("int i = 42 / 23;", binaryOperator(hasOperatorName("/"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i /= 23);",
+ binaryOperator(hasOperatorName("/="))));
+ EXPECT_TRUE(
+ matches("int i = 42 + 23;", binaryOperator(hasOperatorName("+"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i += 23);",
+ binaryOperator(hasOperatorName("+="))));
+ EXPECT_TRUE(
+ matches("int i = 42 - 23;", binaryOperator(hasOperatorName("-"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i -= 23);",
+ binaryOperator(hasOperatorName("-="))));
+ EXPECT_TRUE(
+ matches("struct A { void x() { void (A::*a)(); (this->*a)(); } };",
+ binaryOperator(hasOperatorName("->*"))));
+ EXPECT_TRUE(
+ matches("struct A { void x() { void (A::*a)(); ((*this).*a)(); } };",
+ binaryOperator(hasOperatorName(".*"))));
+
+ // Member expressions as operators are not supported in matches.
+ EXPECT_TRUE(
+ notMatches("struct A { void x(A *a) { a->x(this); } };",
+ binaryOperator(hasOperatorName("->"))));
+
+ // Initializer assignments are not represented as operator equals.
+ EXPECT_TRUE(
+ notMatches("bool b = true;", binaryOperator(hasOperatorName("="))));
+
+ // Array indexing is not represented as operator.
+ EXPECT_TRUE(notMatches("int a[42]; void x() { a[23]; }", unaryOperator()));
+
+ // Overloaded operators do not match at all.
+ EXPECT_TRUE(notMatches(
+ "struct A { bool operator&&(const A &a) const { return false; } };"
+ "void x() { A a, b; a && b; }",
+ binaryOperator()));
+}
+
+TEST(MatchUnaryOperator, HasOperatorName) {
+ StatementMatcher OperatorNot = unaryOperator(hasOperatorName("!"));
+
+ EXPECT_TRUE(matches("void x() { !true; } ", OperatorNot));
+ EXPECT_TRUE(notMatches("void x() { true; } ", OperatorNot));
+}
+
+TEST(MatchUnaryOperator, HasUnaryOperand) {
+ StatementMatcher OperatorOnFalse =
+ unaryOperator(hasUnaryOperand(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { !false; }", OperatorOnFalse));
+ EXPECT_TRUE(notMatches("void x() { !true; }", OperatorOnFalse));
+}
+
+TEST(Matcher, UnaryOperatorTypes) {
+ // Integration test that verifies the AST provides all unary operators in
+ // a way we expect.
+ EXPECT_TRUE(matches("bool b = !true;", unaryOperator(hasOperatorName("!"))));
+ EXPECT_TRUE(
+ matches("bool b; bool *p = &b;", unaryOperator(hasOperatorName("&"))));
+ EXPECT_TRUE(matches("int i = ~ 1;", unaryOperator(hasOperatorName("~"))));
+ EXPECT_TRUE(
+ matches("bool *p; bool b = *p;", unaryOperator(hasOperatorName("*"))));
+ EXPECT_TRUE(
+ matches("int i; int j = +i;", unaryOperator(hasOperatorName("+"))));
+ EXPECT_TRUE(
+ matches("int i; int j = -i;", unaryOperator(hasOperatorName("-"))));
+ EXPECT_TRUE(
+ matches("int i; int j = ++i;", unaryOperator(hasOperatorName("++"))));
+ EXPECT_TRUE(
+ matches("int i; int j = i++;", unaryOperator(hasOperatorName("++"))));
+ EXPECT_TRUE(
+ matches("int i; int j = --i;", unaryOperator(hasOperatorName("--"))));
+ EXPECT_TRUE(
+ matches("int i; int j = i--;", unaryOperator(hasOperatorName("--"))));
+
+ // We don't match conversion operators.
+ EXPECT_TRUE(notMatches("int i; double d = (double)i;", unaryOperator()));
+
+ // Function calls are not represented as operator.
+ EXPECT_TRUE(notMatches("void f(); void x() { f(); }", unaryOperator()));
+
+ // Overloaded operators do not match at all.
+ // FIXME: We probably want to add that.
+ EXPECT_TRUE(notMatches(
+ "struct A { bool operator!() const { return false; } };"
+ "void x() { A a; !a; }", unaryOperator(hasOperatorName("!"))));
+}
+
+TEST(Matcher, ConditionalOperator) {
+ StatementMatcher Conditional = conditionalOperator(
+ hasCondition(boolLiteral(equals(true))),
+ hasTrueExpression(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true ? false : true; }", Conditional));
+ EXPECT_TRUE(notMatches("void x() { false ? false : true; }", Conditional));
+ EXPECT_TRUE(notMatches("void x() { true ? true : false; }", Conditional));
+
+ StatementMatcher ConditionalFalse = conditionalOperator(
+ hasFalseExpression(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true ? true : false; }", ConditionalFalse));
+ EXPECT_TRUE(
+ notMatches("void x() { true ? false : true; }", ConditionalFalse));
+}
+
+TEST(ArraySubscriptMatchers, ArraySubscripts) {
+ EXPECT_TRUE(matches("int i[2]; void f() { i[1] = 1; }",
+ arraySubscriptExpr()));
+ EXPECT_TRUE(notMatches("int i; void f() { i = 1; }",
+ arraySubscriptExpr()));
+}
+
+TEST(ArraySubscriptMatchers, ArrayIndex) {
+ EXPECT_TRUE(matches(
+ "int i[2]; void f() { i[1] = 1; }",
+ arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
+ EXPECT_TRUE(matches(
+ "int i[2]; void f() { 1[i] = 1; }",
+ arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
+ EXPECT_TRUE(notMatches(
+ "int i[2]; void f() { i[1] = 1; }",
+ arraySubscriptExpr(hasIndex(integerLiteral(equals(0))))));
+}
+
+TEST(ArraySubscriptMatchers, MatchesArrayBase) {
+ EXPECT_TRUE(matches(
+ "int i[2]; void f() { i[1] = 2; }",
+ arraySubscriptExpr(hasBase(implicitCast(
+ hasSourceExpression(declarationReference()))))));
+}
+
+TEST(Matcher, HasNameSupportsNamespaces) {
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("a::b::C"))));
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("::a::b::C"))));
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("b::C"))));
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("c::b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("a::c::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("a::b::A"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("::b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("z::a::b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("a+b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class AC; } }",
+ record(hasName("C"))));
+}
+
+TEST(Matcher, HasNameSupportsOuterClasses) {
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };", record(hasName("A::B::C"))));
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };",
+ record(hasName("::A::B::C"))));
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };", record(hasName("B::C"))));
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };", record(hasName("C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("c::B::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("A::c::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("A::B::A"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };", record(hasName("::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("::B::C"))));
+ EXPECT_TRUE(notMatches("class A { class B { class C; }; };",
+ record(hasName("z::A::B::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("A+B::C"))));
+}
+
+TEST(Matcher, IsDefinition) {
+ DeclarationMatcher DefinitionOfClassA =
+ record(hasName("A"), isDefinition());
+ EXPECT_TRUE(matches("class A {};", DefinitionOfClassA));
+ EXPECT_TRUE(notMatches("class A;", DefinitionOfClassA));
+
+ DeclarationMatcher DefinitionOfVariableA =
+ variable(hasName("a"), isDefinition());
+ EXPECT_TRUE(matches("int a;", DefinitionOfVariableA));
+ EXPECT_TRUE(notMatches("extern int a;", DefinitionOfVariableA));
+
+ DeclarationMatcher DefinitionOfMethodA =
+ method(hasName("a"), isDefinition());
+ EXPECT_TRUE(matches("class A { void a() {} };", DefinitionOfMethodA));
+ EXPECT_TRUE(notMatches("class A { void a(); };", DefinitionOfMethodA));
+}
+
+TEST(Matcher, OfClass) {
+ StatementMatcher Constructor = constructorCall(hasDeclaration(method(
+ ofClass(hasName("X")))));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x(int) { X x; }", Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x(int) { X x = X(); }",
+ Constructor));
+ EXPECT_TRUE(
+ notMatches("class Y { public: Y(); }; void x(int) { Y y; }",
+ Constructor));
+}
+
+TEST(Matcher, VisitsTemplateInstantiations) {
+ EXPECT_TRUE(matches(
+ "class A { public: void x(); };"
+ "template <typename T> class B { public: void y() { T t; t.x(); } };"
+ "void f() { B<A> b; b.y(); }", call(callee(method(hasName("x"))))));
+
+ EXPECT_TRUE(matches(
+ "class A { public: void x(); };"
+ "class C {"
+ " public:"
+ " template <typename T> class B { public: void y() { T t; t.x(); } };"
+ "};"
+ "void f() {"
+ " C::B<A> b; b.y();"
+ "}", record(hasName("C"),
+ hasDescendant(call(callee(method(hasName("x"))))))));
+}
+
+TEST(Matcher, HandlesNullQualTypes) {
+ // FIXME: Add a Type matcher so we can replace uses of this
+ // variable with Type(True())
+ const TypeMatcher AnyType = anything();
+
+ // We don't really care whether this matcher succeeds; we're testing that
+ // it completes without crashing.
+ EXPECT_TRUE(matches(
+ "struct A { };"
+ "template <typename T>"
+ "void f(T t) {"
+ " T local_t(t /* this becomes a null QualType in the AST */);"
+ "}"
+ "void g() {"
+ " f(0);"
+ "}",
+ expression(hasType(TypeMatcher(
+ anyOf(
+ TypeMatcher(hasDeclaration(anything())),
+ pointsTo(AnyType),
+ references(AnyType)
+ // Other QualType matchers should go here.
+ ))))));
+}
+
+// For testing AST_MATCHER_P().
+AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {
+ // Make sure all special variables are used: node, match_finder,
+ // bound_nodes_builder, and the parameter named 'AMatcher'.
+ return AMatcher.matches(Node, Finder, Builder);
+}
+
+TEST(AstMatcherPMacro, Works) {
+ DeclarationMatcher HasClassB = just(has(record(hasName("B")).bind("b")));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+}
+
+AST_POLYMORPHIC_MATCHER_P(
+ polymorphicHas, internal::Matcher<Decl>, AMatcher) {
+ TOOLING_COMPILE_ASSERT((llvm::is_same<NodeType, Decl>::value) ||
+ (llvm::is_same<NodeType, Stmt>::value),
+ assert_node_type_is_accessible);
+ internal::TypedBaseMatcher<Decl> ChildMatcher(AMatcher);
+ return Finder->matchesChildOf(
+ Node, ChildMatcher, Builder,
+ ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
+ ASTMatchFinder::BK_First);
+}
+
+TEST(AstPolymorphicMatcherPMacro, Works) {
+ DeclarationMatcher HasClassB = polymorphicHas(record(hasName("B")).bind("b"));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ StatementMatcher StatementHasClassB =
+ polymorphicHas(record(hasName("B")));
+
+ EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB));
+}
+
+TEST(For, FindsForLoops) {
+ EXPECT_TRUE(matches("void f() { for(;;); }", forStmt()));
+ EXPECT_TRUE(matches("void f() { if(true) for(;;); }", forStmt()));
+}
+
+TEST(For, ForLoopInternals) {
+ EXPECT_TRUE(matches("void f(){ int i; for (; i < 3 ; ); }",
+ forStmt(hasCondition(anything()))));
+ EXPECT_TRUE(matches("void f() { for (int i = 0; ;); }",
+ forStmt(hasLoopInit(anything()))));
+}
+
+TEST(For, NegativeForLoopInternals) {
+ EXPECT_TRUE(notMatches("void f(){ for (int i = 0; ; ++i); }",
+ forStmt(hasCondition(expression()))));
+ EXPECT_TRUE(notMatches("void f() {int i; for (; i < 4; ++i) {} }",
+ forStmt(hasLoopInit(anything()))));
+}
+
+TEST(For, ReportsNoFalsePositives) {
+ EXPECT_TRUE(notMatches("void f() { ; }", forStmt()));
+ EXPECT_TRUE(notMatches("void f() { if(true); }", forStmt()));
+}
+
+TEST(CompoundStatement, HandlesSimpleCases) {
+ EXPECT_TRUE(notMatches("void f();", compoundStatement()));
+ EXPECT_TRUE(matches("void f() {}", compoundStatement()));
+ EXPECT_TRUE(matches("void f() {{}}", compoundStatement()));
+}
+
+TEST(CompoundStatement, DoesNotMatchEmptyStruct) {
+ // It's not a compound statement just because there's "{}" in the source
+ // text. This is an AST search, not grep.
+ EXPECT_TRUE(notMatches("namespace n { struct S {}; }",
+ compoundStatement()));
+ EXPECT_TRUE(matches("namespace n { struct S { void f() {{}} }; }",
+ compoundStatement()));
+}
+
+TEST(HasBody, FindsBodyOfForWhileDoLoops) {
+ EXPECT_TRUE(matches("void f() { for(;;) {} }",
+ forStmt(hasBody(compoundStatement()))));
+ EXPECT_TRUE(notMatches("void f() { for(;;); }",
+ forStmt(hasBody(compoundStatement()))));
+ EXPECT_TRUE(matches("void f() { while(true) {} }",
+ whileStmt(hasBody(compoundStatement()))));
+ EXPECT_TRUE(matches("void f() { do {} while(true); }",
+ doStmt(hasBody(compoundStatement()))));
+}
+
+TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) {
+ // The simplest case: every compound statement is in a function
+ // definition, and the function body itself must be a compound
+ // statement.
+ EXPECT_TRUE(matches("void f() { for (;;); }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(HasAnySubstatement, IsNotRecursive) {
+ // It's really "has any immediate substatement".
+ EXPECT_TRUE(notMatches("void f() { if (true) for (;;); }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(HasAnySubstatement, MatchesInNestedCompoundStatements) {
+ EXPECT_TRUE(matches("void f() { if (true) { for (;;); } }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(HasAnySubstatement, FindsSubstatementBetweenOthers) {
+ EXPECT_TRUE(matches("void f() { 1; 2; 3; for (;;); 4; 5; 6; }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) {
+ EXPECT_TRUE(matches("void f() { }",
+ compoundStatement(statementCountIs(0))));
+ EXPECT_TRUE(notMatches("void f() {}",
+ compoundStatement(statementCountIs(1))));
+}
+
+TEST(StatementCountIs, AppearsToMatchOnlyOneCount) {
+ EXPECT_TRUE(matches("void f() { 1; }",
+ compoundStatement(statementCountIs(1))));
+ EXPECT_TRUE(notMatches("void f() { 1; }",
+ compoundStatement(statementCountIs(0))));
+ EXPECT_TRUE(notMatches("void f() { 1; }",
+ compoundStatement(statementCountIs(2))));
+}
+
+TEST(StatementCountIs, WorksWithMultipleStatements) {
+ EXPECT_TRUE(matches("void f() { 1; 2; 3; }",
+ compoundStatement(statementCountIs(3))));
+}
+
+TEST(StatementCountIs, WorksWithNestedCompoundStatements) {
+ EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(1))));
+ EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(2))));
+ EXPECT_TRUE(notMatches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(3))));
+ EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(4))));
+}
+
+TEST(Member, WorksInSimplestCase) {
+ EXPECT_TRUE(matches("struct { int first; } s; int i(s.first);",
+ memberExpression(member(hasName("first")))));
+}
+
+TEST(Member, DoesNotMatchTheBaseExpression) {
+ // Don't pick out the wrong part of the member expression, this should
+ // be checking the member (name) only.
+ EXPECT_TRUE(notMatches("struct { int i; } first; int i(first.i);",
+ memberExpression(member(hasName("first")))));
+}
+
+TEST(Member, MatchesInMemberFunctionCall) {
+ EXPECT_TRUE(matches("void f() {"
+ " struct { void first() {}; } s;"
+ " s.first();"
+ "};",
+ memberExpression(member(hasName("first")))));
+}
+
+TEST(HasObjectExpression, DoesNotMatchMember) {
+ EXPECT_TRUE(notMatches(
+ "class X {}; struct Z { X m; }; void f(Z z) { z.m; }",
+ memberExpression(hasObjectExpression(hasType(record(hasName("X")))))));
+}
+
+TEST(HasObjectExpression, MatchesBaseOfVariable) {
+ EXPECT_TRUE(matches(
+ "struct X { int m; }; void f(X x) { x.m; }",
+ memberExpression(hasObjectExpression(hasType(record(hasName("X")))))));
+ EXPECT_TRUE(matches(
+ "struct X { int m; }; void f(X* x) { x->m; }",
+ memberExpression(hasObjectExpression(
+ hasType(pointsTo(record(hasName("X"))))))));
+}
+
+TEST(HasObjectExpression,
+ MatchesObjectExpressionOfImplicitlyFormedMemberExpression) {
+ EXPECT_TRUE(matches(
+ "class X {}; struct S { X m; void f() { this->m; } };",
+ memberExpression(hasObjectExpression(
+ hasType(pointsTo(record(hasName("S"))))))));
+ EXPECT_TRUE(matches(
+ "class X {}; struct S { X m; void f() { m; } };",
+ memberExpression(hasObjectExpression(
+ hasType(pointsTo(record(hasName("S"))))))));
+}
+
+TEST(Field, DoesNotMatchNonFieldMembers) {
+ EXPECT_TRUE(notMatches("class X { void m(); };", field(hasName("m"))));
+ EXPECT_TRUE(notMatches("class X { class m {}; };", field(hasName("m"))));
+ EXPECT_TRUE(notMatches("class X { enum { m }; };", field(hasName("m"))));
+ EXPECT_TRUE(notMatches("class X { enum m {}; };", field(hasName("m"))));
+}
+
+TEST(Field, MatchesField) {
+ EXPECT_TRUE(matches("class X { int m; };", field(hasName("m"))));
+}
+
+TEST(IsConstQualified, MatchesConstInt) {
+ EXPECT_TRUE(matches("const int i = 42;",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(IsConstQualified, MatchesConstPointer) {
+ EXPECT_TRUE(matches("int i = 42; int* const p(&i);",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(IsConstQualified, MatchesThroughTypedef) {
+ EXPECT_TRUE(matches("typedef const int const_int; const_int i = 42;",
+ variable(hasType(isConstQualified()))));
+ EXPECT_TRUE(matches("typedef int* int_ptr; const int_ptr p(0);",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(IsConstQualified, DoesNotMatchInappropriately) {
+ EXPECT_TRUE(notMatches("typedef int nonconst_int; nonconst_int i = 42;",
+ variable(hasType(isConstQualified()))));
+ EXPECT_TRUE(notMatches("int const* p;",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(ReinterpretCast, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("char* p = reinterpret_cast<char*>(&p);",
+ expression(reinterpretCast())));
+}
+
+TEST(ReinterpretCast, DoesNotMatchOtherCasts) {
+ EXPECT_TRUE(notMatches("char* p = (char*)(&p);",
+ expression(reinterpretCast())));
+ EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);",
+ expression(reinterpretCast())));
+ EXPECT_TRUE(notMatches("void* p = static_cast<void*>(&p);",
+ expression(reinterpretCast())));
+ EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};"
+ "B b;"
+ "D* p = dynamic_cast<D*>(&b);",
+ expression(reinterpretCast())));
+}
+
+TEST(FunctionalCast, MatchesSimpleCase) {
+ std::string foo_class = "class Foo { public: Foo(char*); };";
+ EXPECT_TRUE(matches(foo_class + "void r() { Foo f = Foo(\"hello world\"); }",
+ expression(functionalCast())));
+}
+
+TEST(FunctionalCast, DoesNotMatchOtherCasts) {
+ std::string FooClass = "class Foo { public: Foo(char*); };";
+ EXPECT_TRUE(
+ notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }",
+ expression(functionalCast())));
+ EXPECT_TRUE(
+ notMatches(FooClass + "void r() { Foo f = \"hello world\"; }",
+ expression(functionalCast())));
+}
+
+TEST(DynamicCast, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("struct B { virtual ~B() {} }; struct D : B {};"
+ "B b;"
+ "D* p = dynamic_cast<D*>(&b);",
+ expression(dynamicCast())));
+}
+
+TEST(StaticCast, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("void* p(static_cast<void*>(&p));",
+ expression(staticCast())));
+}
+
+TEST(StaticCast, DoesNotMatchOtherCasts) {
+ EXPECT_TRUE(notMatches("char* p = (char*)(&p);",
+ expression(staticCast())));
+ EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);",
+ expression(staticCast())));
+ EXPECT_TRUE(notMatches("void* p = reinterpret_cast<char*>(&p);",
+ expression(staticCast())));
+ EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};"
+ "B b;"
+ "D* p = dynamic_cast<D*>(&b);",
+ expression(staticCast())));
+}
+
+TEST(HasDestinationType, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("char* p = static_cast<char*>(0);",
+ expression(
+ staticCast(hasDestinationType(
+ pointsTo(TypeMatcher(anything())))))));
+}
+
+TEST(HasSourceExpression, MatchesImplicitCasts) {
+ EXPECT_TRUE(matches("class string {}; class URL { public: URL(string s); };"
+ "void r() {string a_string; URL url = a_string; }",
+ expression(implicitCast(
+ hasSourceExpression(constructorCall())))));
+}
+
+TEST(HasSourceExpression, MatchesExplicitCasts) {
+ EXPECT_TRUE(matches("float x = static_cast<float>(42);",
+ expression(explicitCast(
+ hasSourceExpression(hasDescendant(
+ expression(integerLiteral())))))));
+}
+
+TEST(Statement, DoesNotMatchDeclarations) {
+ EXPECT_TRUE(notMatches("class X {};", statement()));
+}
+
+TEST(Statement, MatchesCompoundStatments) {
+ EXPECT_TRUE(matches("void x() {}", statement()));
+}
+
+TEST(DeclarationStatement, DoesNotMatchCompoundStatements) {
+ EXPECT_TRUE(notMatches("void x() {}", declarationStatement()));
+}
+
+TEST(DeclarationStatement, MatchesVariableDeclarationStatements) {
+ EXPECT_TRUE(matches("void x() { int a; }", declarationStatement()));
+}
+
+TEST(InitListExpression, MatchesInitListExpression) {
+ EXPECT_TRUE(matches("int a[] = { 1, 2 };",
+ initListExpr(hasType(asString("int [2]")))));
+ EXPECT_TRUE(matches("struct B { int x, y; }; B b = { 5, 6 };",
+ initListExpr(hasType(record(hasName("B"))))));
+}
+
+TEST(UsingDeclaration, MatchesUsingDeclarations) {
+ EXPECT_TRUE(matches("namespace X { int x; } using X::x;",
+ usingDecl()));
+}
+
+TEST(UsingDeclaration, MatchesShadowUsingDelcarations) {
+ EXPECT_TRUE(matches("namespace f { int a; } using f::a;",
+ usingDecl(hasAnyUsingShadowDecl(hasName("a")))));
+}
+
+TEST(UsingDeclaration, MatchesSpecificTarget) {
+ EXPECT_TRUE(matches("namespace f { int a; void b(); } using f::b;",
+ usingDecl(hasAnyUsingShadowDecl(
+ hasTargetDecl(function())))));
+ EXPECT_TRUE(notMatches("namespace f { int a; void b(); } using f::a;",
+ usingDecl(hasAnyUsingShadowDecl(
+ hasTargetDecl(function())))));
+}
+
+TEST(UsingDeclaration, ThroughUsingDeclaration) {
+ EXPECT_TRUE(matches(
+ "namespace a { void f(); } using a::f; void g() { f(); }",
+ declarationReference(throughUsingDecl(anything()))));
+ EXPECT_TRUE(notMatches(
+ "namespace a { void f(); } using a::f; void g() { a::f(); }",
+ declarationReference(throughUsingDecl(anything()))));
+}
+
+TEST(While, MatchesWhileLoops) {
+ EXPECT_TRUE(notMatches("void x() {}", whileStmt()));
+ EXPECT_TRUE(matches("void x() { while(true); }", whileStmt()));
+ EXPECT_TRUE(notMatches("void x() { do {} while(true); }", whileStmt()));
+}
+
+TEST(Do, MatchesDoLoops) {
+ EXPECT_TRUE(matches("void x() { do {} while(true); }", doStmt()));
+ EXPECT_TRUE(matches("void x() { do ; while(false); }", doStmt()));
+}
+
+TEST(Do, DoesNotMatchWhileLoops) {
+ EXPECT_TRUE(notMatches("void x() { while(true) {} }", doStmt()));
+}
+
+TEST(SwitchCase, MatchesCase) {
+ EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }", switchCase()));
+ EXPECT_TRUE(matches("void x() { switch(42) { default:; } }", switchCase()));
+ EXPECT_TRUE(matches("void x() { switch(42) default:; }", switchCase()));
+ EXPECT_TRUE(notMatches("void x() { switch(42) {} }", switchCase()));
+}
+
+TEST(HasConditionVariableStatement, DoesNotMatchCondition) {
+ EXPECT_TRUE(notMatches(
+ "void x() { if(true) {} }",
+ ifStmt(hasConditionVariableStatement(declarationStatement()))));
+ EXPECT_TRUE(notMatches(
+ "void x() { int x; if((x = 42)) {} }",
+ ifStmt(hasConditionVariableStatement(declarationStatement()))));
+}
+
+TEST(HasConditionVariableStatement, MatchesConditionVariables) {
+ EXPECT_TRUE(matches(
+ "void x() { if(int* a = 0) {} }",
+ ifStmt(hasConditionVariableStatement(declarationStatement()))));
+}
+
+TEST(ForEach, BindsOneNode) {
+ EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };",
+ record(hasName("C"), forEach(field(hasName("x")).bind("x"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
+}
+
+TEST(ForEach, BindsMultipleNodes) {
+ EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; int y; int z; };",
+ record(hasName("C"), forEach(field().bind("f"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 3)));
+}
+
+TEST(ForEach, BindsRecursiveCombinations) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { class D { int x; int y; }; class E { int y; int z; }; };",
+ record(hasName("C"), forEach(record(forEach(field().bind("f"))))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
+}
+
+TEST(ForEachDescendant, BindsOneNode) {
+ EXPECT_TRUE(matchAndVerifyResultTrue("class C { class D { int x; }; };",
+ record(hasName("C"), forEachDescendant(field(hasName("x")).bind("x"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
+}
+
+TEST(ForEachDescendant, BindsMultipleNodes) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { class D { int x; int y; }; "
+ " class E { class F { int y; int z; }; }; };",
+ record(hasName("C"), forEachDescendant(field().bind("f"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
+}
+
+TEST(ForEachDescendant, BindsRecursiveCombinations) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { class D { "
+ " class E { class F { class G { int y; int z; }; }; }; }; };",
+ record(hasName("C"), forEachDescendant(record(
+ forEachDescendant(field().bind("f"))))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 8)));
+}
+
+
+TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) {
+ // Make sure that we can both match the class by name (::X) and by the type
+ // the template was instantiated with (via a field).
+
+ EXPECT_TRUE(matches(
+ "template <typename T> class X {}; class A {}; X<A> x;",
+ record(hasName("::X"), isTemplateInstantiation())));
+
+ EXPECT_TRUE(matches(
+ "template <typename T> class X { T t; }; class A {}; X<A> x;",
+ record(isTemplateInstantiation(), hasDescendant(
+ field(hasType(record(hasName("A"))))))));
+}
+
+TEST(IsTemplateInstantiation, MatchesImplicitFunctionTemplateInstantiation) {
+ EXPECT_TRUE(matches(
+ "template <typename T> void f(T t) {} class A {}; void g() { f(A()); }",
+ function(hasParameter(0, hasType(record(hasName("A")))),
+ isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation, MatchesExplicitClassTemplateInstantiation) {
+ EXPECT_TRUE(matches(
+ "template <typename T> class X { T t; }; class A {};"
+ "template class X<A>;",
+ record(isTemplateInstantiation(), hasDescendant(
+ field(hasType(record(hasName("A"))))))));
+}
+
+TEST(IsTemplateInstantiation,
+ MatchesInstantiationOfPartiallySpecializedClassTemplate) {
+ EXPECT_TRUE(matches(
+ "template <typename T> class X {};"
+ "template <typename T> class X<T*> {}; class A {}; X<A*> x;",
+ record(hasName("::X"), isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation,
+ MatchesInstantiationOfClassTemplateNestedInNonTemplate) {
+ EXPECT_TRUE(matches(
+ "class A {};"
+ "class X {"
+ " template <typename U> class Y { U u; };"
+ " Y<A> y;"
+ "};",
+ record(hasName("::X::Y"), isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation, DoesNotMatchInstantiationsInsideOfInstantiation) {
+ // FIXME: Figure out whether this makes sense. It doesn't affect the
+ // normal use case as long as the uppermost instantiation always is marked
+ // as template instantiation, but it might be confusing as a predicate.
+ EXPECT_TRUE(matches(
+ "class A {};"
+ "template <typename T> class X {"
+ " template <typename U> class Y { U u; };"
+ " Y<T> y;"
+ "}; X<A> x;",
+ record(hasName("::X<A>::Y"), unless(isTemplateInstantiation()))));
+}
+
+TEST(IsTemplateInstantiation, DoesNotMatchExplicitClassTemplateSpecialization) {
+ EXPECT_TRUE(notMatches(
+ "template <typename T> class X {}; class A {};"
+ "template <> class X<A> {}; X<A> x;",
+ record(hasName("::X"), isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation, DoesNotMatchNonTemplate) {
+ EXPECT_TRUE(notMatches(
+ "class A {}; class Y { A a; };",
+ record(isTemplateInstantiation())));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h
new file mode 100644
index 0000000..64816f5
--- /dev/null
+++ b/unittests/ASTMatchers/ASTMatchersTest.h
@@ -0,0 +1,128 @@
+//===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
+#define LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using clang::tooling::newFrontendActionFactory;
+using clang::tooling::runToolOnCode;
+using clang::tooling::FrontendActionFactory;
+
+class BoundNodesCallback {
+public:
+ virtual ~BoundNodesCallback() {}
+ virtual bool run(const BoundNodes *BoundNodes) = 0;
+};
+
+// If 'FindResultVerifier' is not NULL, sets *Verified to the result of
+// running 'FindResultVerifier' with the bound nodes as argument.
+// If 'FindResultVerifier' is NULL, sets *Verified to true when Run is called.
+class VerifyMatch : public MatchFinder::MatchCallback {
+public:
+ VerifyMatch(BoundNodesCallback *FindResultVerifier, bool *Verified)
+ : Verified(Verified), FindResultReviewer(FindResultVerifier) {}
+
+ virtual void run(const MatchFinder::MatchResult &Result) {
+ if (FindResultReviewer != NULL) {
+ *Verified = FindResultReviewer->run(&Result.Nodes);
+ } else {
+ *Verified = true;
+ }
+ }
+
+private:
+ bool *const Verified;
+ BoundNodesCallback *const FindResultReviewer;
+};
+
+template <typename T>
+testing::AssertionResult matchesConditionally(const std::string &Code,
+ const T &AMatcher,
+ bool ExpectMatch) {
+ bool Found = false;
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher, new VerifyMatch(0, &Found));
+ OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ if (!runToolOnCode(Factory->create(), Code)) {
+ return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
+ }
+ if (!Found && ExpectMatch) {
+ return testing::AssertionFailure()
+ << "Could not find match in \"" << Code << "\"";
+ } else if (Found && !ExpectMatch) {
+ return testing::AssertionFailure()
+ << "Found unexpected match in \"" << Code << "\"";
+ }
+ return testing::AssertionSuccess();
+}
+
+template <typename T>
+testing::AssertionResult matches(const std::string &Code, const T &AMatcher) {
+ return matchesConditionally(Code, AMatcher, true);
+}
+
+template <typename T>
+testing::AssertionResult notMatches(const std::string &Code,
+ const T &AMatcher) {
+ return matchesConditionally(Code, AMatcher, false);
+}
+
+template <typename T>
+testing::AssertionResult
+matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
+ BoundNodesCallback *FindResultVerifier,
+ bool ExpectResult) {
+ llvm::OwningPtr<BoundNodesCallback> ScopedVerifier(FindResultVerifier);
+ bool VerifiedResult = false;
+ MatchFinder Finder;
+ Finder.addMatcher(
+ AMatcher, new VerifyMatch(FindResultVerifier, &VerifiedResult));
+ OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ if (!runToolOnCode(Factory->create(), Code)) {
+ return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
+ }
+ if (!VerifiedResult && ExpectResult) {
+ return testing::AssertionFailure()
+ << "Could not verify result in \"" << Code << "\"";
+ } else if (VerifiedResult && !ExpectResult) {
+ return testing::AssertionFailure()
+ << "Verified unexpected result in \"" << Code << "\"";
+ }
+ return testing::AssertionSuccess();
+}
+
+// FIXME: Find better names for these functions (or document what they
+// do more precisely).
+template <typename T>
+testing::AssertionResult
+matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher,
+ BoundNodesCallback *FindResultVerifier) {
+ return matchAndVerifyResultConditionally(
+ Code, AMatcher, FindResultVerifier, true);
+}
+
+template <typename T>
+testing::AssertionResult
+matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher,
+ BoundNodesCallback *FindResultVerifier) {
+ return matchAndVerifyResultConditionally(
+ Code, AMatcher, FindResultVerifier, false);
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
diff --git a/unittests/ASTMatchers/CMakeLists.txt b/unittests/ASTMatchers/CMakeLists.txt
new file mode 100644
index 0000000..b56d756
--- /dev/null
+++ b/unittests/ASTMatchers/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_unittest(ASTMatchersTests
+ ASTMatchersTest.cpp)
+
+target_link_libraries(ASTMatchersTests
+ gtest gtest_main clangASTMatchers clangTooling)
diff --git a/unittests/ASTMatchers/Makefile b/unittests/ASTMatchers/Makefile
new file mode 100644
index 0000000..d3e4aa37
--- /dev/null
+++ b/unittests/ASTMatchers/Makefile
@@ -0,0 +1,19 @@
+##===- unittests/ASTMatchers/Makefile ----------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../..
+
+TESTNAME = ASTMatchers
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
+USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
+ clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
+ clangAST.a clangASTMatchers.a clangLex.a clangBasic.a clangEdit.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt
new file mode 100644
index 0000000..300dcd5
--- /dev/null
+++ b/unittests/Basic/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_clang_unittest(BasicTests
+ FileManagerTest.cpp
+ SourceManagerTest.cpp
+ )
+
+target_link_libraries(BasicTests
+ clangBasic
+ clangLex
+ )
diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp
index 429b58d..de3b723 100644
--- a/unittests/Basic/SourceManagerTest.cpp
+++ b/unittests/Basic/SourceManagerTest.cpp
@@ -107,6 +107,54 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnit) {
EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, macroExpEndLoc));
}
+TEST_F(SourceManagerTest, getColumnNumber) {
+ const char *Source =
+ "int x;\n"
+ "int y;";
+
+ MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID MainFileID = SourceMgr.createMainFileIDForMemBuffer(Buf);
+
+ bool Invalid;
+
+ Invalid = false;
+ EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 4, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 7, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 11, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(7U, SourceMgr.getColumnNumber(MainFileID, strlen(Source),
+ &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ SourceMgr.getColumnNumber(MainFileID, strlen(Source)+1, &Invalid);
+ EXPECT_TRUE(Invalid);
+
+ // Test invalid files
+ Invalid = false;
+ SourceMgr.getColumnNumber(FileID(), 0, &Invalid);
+ EXPECT_TRUE(Invalid);
+
+ Invalid = false;
+ SourceMgr.getColumnNumber(FileID(), 1, &Invalid);
+ EXPECT_TRUE(Invalid);
+
+ // Test with no invalid flag.
+ EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, NULL));
+}
+
#if defined(LLVM_ON_UNIX)
TEST_F(SourceManagerTest, getMacroArgExpandedLocation) {
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index 0b3eac9..989025a 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -1,73 +1,17 @@
-include(LLVMParseArguments)
+add_custom_target(ClangUnitTests)
+set_target_properties(ClangUnitTests PROPERTIES FOLDER "Clang tests")
-# add_clang_unittest(test_dirname file1.cpp file2.cpp ...
-# [USED_LIBS lib1 lib2]
-# [LINK_COMPONENTS component1 component2])
+# add_clang_unittest(test_dirname file1.cpp file2.cpp)
#
# Will compile the list of files together and link against the clang
-# libraries in the USED_LIBS list and the llvm-config components in
-# the LINK_COMPONENTS list. Produces a binary named
-# 'basename(test_dirname)Tests'.
-function(add_clang_unittest)
- PARSE_ARGUMENTS(CLANG_UNITTEST "USED_LIBS;LINK_COMPONENTS" "" ${ARGN})
- set(LLVM_LINK_COMPONENTS ${CLANG_UNITTEST_LINK_COMPONENTS})
- set(LLVM_USED_LIBS ${CLANG_UNITTEST_USED_LIBS})
- list(GET CLANG_UNITTEST_DEFAULT_ARGS 0 test_dirname)
- list(REMOVE_AT CLANG_UNITTEST_DEFAULT_ARGS 0)
-
- string(REGEX MATCH "([^/]+)$" test_name ${test_dirname})
- if (CMAKE_BUILD_TYPE)
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
- ${CLANG_BINARY_DIR}/unittests/${test_dirname}/${CMAKE_BUILD_TYPE})
- else()
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
- ${CLANG_BINARY_DIR}/unittests/${test_dirname})
- endif()
- if( NOT LLVM_BUILD_TESTS )
- set(EXCLUDE_FROM_ALL ON)
- endif()
- add_clang_executable(${test_name}Tests ${CLANG_UNITTEST_DEFAULT_ARGS})
- add_dependencies(ClangUnitTests ${test_name}Tests)
- set_target_properties(${test_name}Tests PROPERTIES FOLDER "Clang tests")
+# Produces a binary named 'basename(test_dirname)'.
+function(add_clang_unittest test_dirname)
+ add_unittest(ClangUnitTests ${test_dirname} ${ARGN})
endfunction()
-add_custom_target(ClangUnitTests)
-set_target_properties(ClangUnitTests PROPERTIES FOLDER "Clang tests")
-
-include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include)
-add_definitions(-DGTEST_HAS_RTTI=0)
-if( LLVM_COMPILER_IS_GCC_COMPATIBLE )
- llvm_replace_compiler_option(CMAKE_CXX_FLAGS "-frtti" "-fno-rtti")
-elseif( MSVC )
- llvm_replace_compiler_option(CMAKE_CXX_FLAGS "/GR" "/GR-")
-endif()
-
-if (NOT LLVM_ENABLE_THREADS)
- add_definitions(-DGTEST_HAS_PTHREAD=0)
-endif()
-
-if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
- add_definitions("-Wno-variadic-macros")
-endif()
-
-add_clang_unittest(Basic
- Basic/FileManagerTest.cpp
- Basic/SourceManagerTest.cpp
- USED_LIBS gtest gtest_main clangLex
- )
-
-add_clang_unittest(Lex
- Lex/LexerTest.cpp
- USED_LIBS gtest gtest_main clangLex
- )
-
-add_clang_unittest(Frontend
- Frontend/FrontendActionTest.cpp
- USED_LIBS gtest gtest_main clangFrontend
- )
-
-add_clang_unittest(Tooling
- Tooling/CompilationDatabaseTest.cpp
- Tooling/ToolingTest.cpp
- USED_LIBS gtest gtest_main clangTooling
- )
+add_subdirectory(ASTMatchers)
+add_subdirectory(AST)
+add_subdirectory(Basic)
+add_subdirectory(Lex)
+add_subdirectory(Frontend)
+add_subdirectory(Tooling)
diff --git a/unittests/Frontend/CMakeLists.txt b/unittests/Frontend/CMakeLists.txt
new file mode 100644
index 0000000..139cf42
--- /dev/null
+++ b/unittests/Frontend/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_unittest(FrontendTests
+ FrontendActionTest.cpp
+ )
+target_link_libraries(FrontendTests
+ clangFrontend
+ )
diff --git a/unittests/Frontend/FrontendActionTest.cpp b/unittests/Frontend/FrontendActionTest.cpp
index 2d4befc..84a6545 100644
--- a/unittests/Frontend/FrontendActionTest.cpp
+++ b/unittests/Frontend/FrontendActionTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile
index f3e6396..bfc3494 100644
--- a/unittests/Frontend/Makefile
+++ b/unittests/Frontend/Makefile
@@ -9,7 +9,8 @@
CLANG_LEVEL = ../..
TESTNAME = Frontend
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \
clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \
diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt
new file mode 100644
index 0000000..10c9361
--- /dev/null
+++ b/unittests/Lex/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_clang_unittest(LexTests
+ LexerTest.cpp
+ PreprocessingRecordTest.cpp
+ )
+
+target_link_libraries(LexTests
+ clangLex
+ )
diff --git a/unittests/Makefile b/unittests/Makefile
index 05449d8..f74820b 100644
--- a/unittests/Makefile
+++ b/unittests/Makefile
@@ -14,7 +14,7 @@ ifndef CLANG_LEVEL
IS_UNITTEST_LEVEL := 1
CLANG_LEVEL := ..
-PARALLEL_DIRS = Basic Frontend Lex Tooling
+PARALLEL_DIRS = ASTMatchers Basic AST Frontend Lex Tooling
endif # CLANG_LEVEL
diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt
new file mode 100644
index 0000000..4eaf339
--- /dev/null
+++ b/unittests/Tooling/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_unittest(ToolingTests
+ CommentHandlerTest.cpp
+ CompilationDatabaseTest.cpp
+ ToolingTest.cpp
+ RecursiveASTVisitorTest.cpp
+ RefactoringTest.cpp
+ RewriterTest.cpp
+ RefactoringCallbacksTest.cpp
+ )
+
+target_link_libraries(ToolingTests
+ clangAST
+ clangTooling
+ clangRewrite
+ )
diff --git a/unittests/Tooling/CommentHandlerTest.cpp b/unittests/Tooling/CommentHandlerTest.cpp
new file mode 100644
index 0000000..f0f7797
--- /dev/null
+++ b/unittests/Tooling/CommentHandlerTest.cpp
@@ -0,0 +1,221 @@
+//===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+
+struct Comment {
+ Comment(const std::string &Message, unsigned Line, unsigned Col)
+ : Message(Message), Line(Line), Col(Col) { }
+
+ std::string Message;
+ unsigned Line, Col;
+};
+
+class CommentVerifier;
+typedef std::vector<Comment> CommentList;
+
+class CommentHandlerVisitor : public TestVisitor<CommentHandlerVisitor>,
+ public CommentHandler {
+ typedef TestVisitor<CommentHandlerVisitor> base;
+
+public:
+ CommentHandlerVisitor() : base(), PP(0), Verified(false) { }
+
+ ~CommentHandlerVisitor() {
+ EXPECT_TRUE(Verified) << "CommentVerifier not accessed";
+ }
+
+ virtual bool HandleComment(Preprocessor &PP, SourceRange Loc) {
+ assert(&PP == this->PP && "Preprocessor changed!");
+
+ SourceLocation Start = Loc.getBegin();
+ SourceManager &SM = PP.getSourceManager();
+ std::string C(SM.getCharacterData(Start),
+ SM.getCharacterData(Loc.getEnd()));
+
+ bool Invalid;
+ unsigned CLine = SM.getSpellingLineNumber(Start, &Invalid);
+ EXPECT_TRUE(!Invalid) << "Invalid line number on comment " << C;
+
+ unsigned CCol = SM.getSpellingColumnNumber(Start, &Invalid);
+ EXPECT_TRUE(!Invalid) << "Invalid column number on comment " << C;
+
+ Comments.push_back(Comment(C, CLine, CCol));
+ return false;
+ }
+
+ CommentVerifier GetVerifier();
+
+protected:
+ virtual ASTFrontendAction* CreateTestAction() {
+ return new CommentHandlerAction(this);
+ }
+
+private:
+ Preprocessor *PP;
+ CommentList Comments;
+ bool Verified;
+
+ class CommentHandlerAction : public base::TestAction {
+ public:
+ CommentHandlerAction(CommentHandlerVisitor *Visitor)
+ : TestAction(Visitor) { }
+
+ virtual bool BeginSourceFileAction(CompilerInstance &CI,
+ StringRef FileName) {
+ CommentHandlerVisitor *V =
+ static_cast<CommentHandlerVisitor*>(this->Visitor);
+ V->PP = &CI.getPreprocessor();
+ V->PP->addCommentHandler(V);
+ return true;
+ }
+
+ virtual void EndSourceFileAction() {
+ CommentHandlerVisitor *V =
+ static_cast<CommentHandlerVisitor*>(this->Visitor);
+ V->PP->removeCommentHandler(V);
+ }
+ };
+};
+
+class CommentVerifier {
+ CommentList::const_iterator Current;
+ CommentList::const_iterator End;
+ Preprocessor *PP;
+
+public:
+ CommentVerifier(const CommentList &Comments, Preprocessor *PP)
+ : Current(Comments.begin()), End(Comments.end()), PP(PP)
+ { }
+
+ ~CommentVerifier() {
+ if (Current != End) {
+ EXPECT_TRUE(Current == End) << "Unexpected comment \""
+ << Current->Message << "\" at line " << Current->Line << ", column "
+ << Current->Col;
+ }
+ }
+
+ void Match(const char *Message, unsigned Line, unsigned Col) {
+ EXPECT_TRUE(Current != End) << "Comment " << Message << " not found";
+ if (Current == End) return;
+
+ const Comment &C = *Current;
+ EXPECT_TRUE(C.Message == Message && C.Line == Line && C.Col == Col)
+ << "Expected comment \"" << Message
+ << "\" at line " << Line << ", column " << Col
+ << "\nActual comment \"" << C.Message
+ << "\" at line " << C.Line << ", column " << C.Col;
+
+ ++Current;
+ }
+};
+
+CommentVerifier CommentHandlerVisitor::GetVerifier() {
+ Verified = true;
+ return CommentVerifier(Comments, PP);
+}
+
+
+TEST(CommentHandlerTest, BasicTest1) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver("class X {}; int main() { return 0; }"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+}
+
+TEST(CommentHandlerTest, BasicTest2) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {}; int main() { /* comment */ return 0; }"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("/* comment */", 1, 26);
+}
+
+TEST(CommentHandlerTest, BasicTest3) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {}; // comment 1\n"
+ "int main() {\n"
+ " // comment 2\n"
+ " return 0;\n"
+ "}"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// comment 1", 1, 13);
+ Verifier.Match("// comment 2", 3, 3);
+}
+
+TEST(CommentHandlerTest, IfBlock1) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "#if 0\n"
+ "// ignored comment\n"
+ "#endif\n"
+ "// visible comment\n"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// visible comment", 4, 1);
+}
+
+TEST(CommentHandlerTest, IfBlock2) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "#define TEST // visible_1\n"
+ "#ifndef TEST // visible_2\n"
+ " // ignored_3\n"
+ "# ifdef UNDEFINED // ignored_4\n"
+ "# endif // ignored_5\n"
+ "#elif defined(TEST) // visible_6\n"
+ "# if 1 // visible_7\n"
+ " // visible_8\n"
+ "# else // visible_9\n"
+ " // ignored_10\n"
+ "# ifndef TEST // ignored_11\n"
+ "# endif // ignored_12\n"
+ "# endif // visible_13\n"
+ "#endif // visible_14\n"));
+
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// visible_1", 1, 21);
+ Verifier.Match("// visible_2", 2, 21);
+ Verifier.Match("// visible_6", 6, 21);
+ Verifier.Match("// visible_7", 7, 21);
+ Verifier.Match("// visible_8", 8, 21);
+ Verifier.Match("// visible_9", 9, 21);
+ Verifier.Match("// visible_13", 13, 21);
+ Verifier.Match("// visible_14", 14, 21);
+}
+
+TEST(CommentHandlerTest, IfBlock3) {
+ const char *Source =
+ "/* commented out ...\n"
+ "#if 0\n"
+ "// enclosed\n"
+ "#endif */";
+
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(Source));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match(Source, 1, 1);
+}
+
+TEST(CommentHandlerTest, PPDirectives) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "#warning Y // ignored_1\n" // #warning takes whole line as message
+ "#undef MACRO // visible_2\n"
+ "#line 1 // visible_3\n"));
+
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// visible_2", 2, 14);
+ Verifier.Match("// visible_3", 3, 14);
+}
+
+} // end namespace clang
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index 68d2896..591d48d 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -18,6 +18,55 @@
namespace clang {
namespace tooling {
+static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
+ std::string ErrorMessage;
+ EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase,
+ ErrorMessage))
+ << "Expected an error because of: " << Explanation;
+}
+
+TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
+ expectFailure("", "Empty database");
+ expectFailure("{", "Invalid JSON");
+ expectFailure("[[]]", "Array instead of object");
+ expectFailure("[{\"a\":[]}]", "Array instead of value");
+ expectFailure("[{\"a\":\"b\"}]", "Unknown key");
+ expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
+ expectFailure("[{}]", "Empty entry");
+ expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
+ expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command");
+ expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
+}
+
+static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
+ std::string &ErrorMessage) {
+ llvm::OwningPtr<CompilationDatabase> Database(
+ JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
+ if (!Database) {
+ ADD_FAILURE() << ErrorMessage;
+ return std::vector<std::string>();
+ }
+ return Database->getAllFiles();
+}
+
+TEST(JSONCompilationDatabase, GetAllFiles) {
+ std::string ErrorMessage;
+ EXPECT_EQ(std::vector<std::string>(),
+ getAllFiles("[]", ErrorMessage)) << ErrorMessage;
+
+ std::vector<std::string> expected_files;
+ expected_files.push_back("file1");
+ expected_files.push_back("file2");
+ EXPECT_EQ(expected_files, getAllFiles(
+ "[{\"directory\":\"dir\","
+ "\"command\":\"command\","
+ "\"file\":\"file1\"},"
+ " {\"directory\":\"dir\","
+ "\"command\":\"command\","
+ "\"file\":\"file2\"}]",
+ ErrorMessage)) << ErrorMessage;
+}
+
static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
StringRef JSONDatabase,
std::string &ErrorMessage) {
@@ -235,6 +284,15 @@ TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
}
+TEST(FixedCompilationDatabase, GetAllFiles) {
+ std::vector<std::string> CommandLine;
+ CommandLine.push_back("one");
+ CommandLine.push_back("two");
+ FixedCompilationDatabase Database(".", CommandLine);
+
+ EXPECT_EQ(0ul, Database.getAllFiles().size());
+}
+
TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
int Argc = 0;
llvm::OwningPtr<FixedCompilationDatabase> Database(
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
index 0829da5..5d2224d 100644
--- a/unittests/Tooling/Makefile
+++ b/unittests/Tooling/Makefile
@@ -9,9 +9,10 @@
CLANG_LEVEL = ../..
TESTNAME = Tooling
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
- clangParse.a clangSema.a clangAnalysis.a clangEdit.a clangAST.a \
- clangLex.a clangBasic.a
+ clangParse.a clangRewrite.a clangSema.a clangAnalysis.a clangEdit.a \
+ clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
new file mode 100644
index 0000000..f3ba646
--- /dev/null
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -0,0 +1,388 @@
+//===- unittest/Tooling/RecursiveASTVisitorTest.cpp -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+
+namespace clang {
+
+class TypeLocVisitor : public ExpectedLocationVisitor<TypeLocVisitor> {
+public:
+ bool VisitTypeLoc(TypeLoc TypeLocation) {
+ Match(TypeLocation.getType().getAsString(), TypeLocation.getBeginLoc());
+ return true;
+ }
+};
+
+class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> {
+public:
+ bool VisitDeclRefExpr(DeclRefExpr *Reference) {
+ Match(Reference->getNameInfo().getAsString(), Reference->getLocation());
+ return true;
+ }
+};
+
+class VarDeclVisitor : public ExpectedLocationVisitor<VarDeclVisitor> {
+public:
+ bool VisitVarDecl(VarDecl *Variable) {
+ Match(Variable->getNameAsString(), Variable->getLocStart());
+ return true;
+ }
+};
+
+class CXXMemberCallVisitor
+ : public ExpectedLocationVisitor<CXXMemberCallVisitor> {
+public:
+ bool VisitCXXMemberCallExpr(CXXMemberCallExpr *Call) {
+ Match(Call->getMethodDecl()->getQualifiedNameAsString(),
+ Call->getLocStart());
+ return true;
+ }
+};
+
+class NamedDeclVisitor
+ : public ExpectedLocationVisitor<NamedDeclVisitor> {
+public:
+ bool VisitNamedDecl(NamedDecl *Decl) {
+ std::string NameWithTemplateArgs;
+ Decl->getNameForDiagnostic(NameWithTemplateArgs,
+ Decl->getASTContext().getPrintingPolicy(),
+ true);
+ Match(NameWithTemplateArgs, Decl->getLocation());
+ return true;
+ }
+};
+
+class CXXOperatorCallExprTraverser
+ : public ExpectedLocationVisitor<CXXOperatorCallExprTraverser> {
+public:
+ // Use Traverse, not Visit, to check that data recursion optimization isn't
+ // bypassing the call of this function.
+ bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *CE) {
+ Match(getOperatorSpelling(CE->getOperator()), CE->getExprLoc());
+ return ExpectedLocationVisitor<CXXOperatorCallExprTraverser>::
+ TraverseCXXOperatorCallExpr(CE);
+ }
+};
+
+class ParenExprVisitor : public ExpectedLocationVisitor<ParenExprVisitor> {
+public:
+ bool VisitParenExpr(ParenExpr *Parens) {
+ Match("", Parens->getExprLoc());
+ return true;
+ }
+};
+
+class TemplateArgumentLocTraverser
+ : public ExpectedLocationVisitor<TemplateArgumentLocTraverser> {
+public:
+ bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
+ std::string ArgStr;
+ llvm::raw_string_ostream Stream(ArgStr);
+ const TemplateArgument &Arg = ArgLoc.getArgument();
+
+ Arg.print(Context->getPrintingPolicy(), Stream);
+ Match(Stream.str(), ArgLoc.getLocation());
+ return ExpectedLocationVisitor<TemplateArgumentLocTraverser>::
+ TraverseTemplateArgumentLoc(ArgLoc);
+ }
+};
+
+class CXXBoolLiteralExprVisitor
+ : public ExpectedLocationVisitor<CXXBoolLiteralExprVisitor> {
+public:
+ bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) {
+ if (BE->getValue())
+ Match("true", BE->getLocation());
+ else
+ Match("false", BE->getLocation());
+ return true;
+ }
+};
+
+TEST(RecursiveASTVisitor, VisitsBaseClassDeclarations) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 1, 30);
+ EXPECT_TRUE(Visitor.runOver("class X {}; class Y : public X {};"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 3, 18);
+ EXPECT_TRUE(Visitor.runOver(
+ "class Y;\n"
+ "class X {};\n"
+ "class Y : public X {};"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 2, 18);
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {};\n"
+ "class Y : public X { class Z; };"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("X<class Y>", 2, 18);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> class X {};\n"
+ "class Y : public X<Y> {};"));
+}
+
+TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 2, 3);
+ EXPECT_TRUE(Visitor.runOver(
+ "void x(); template <void (*T)()> class X {};\nX<x> y;"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtRange) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 2, 25);
+ Visitor.ExpectMatch("x", 2, 30);
+ EXPECT_TRUE(Visitor.runOver(
+ "int x[5];\n"
+ "void f() { for (int i : x) { x[0] = 1; } }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
+ VarDeclVisitor Visitor;
+ Visitor.ExpectMatch("i", 2, 17);
+ EXPECT_TRUE(Visitor.runOver(
+ "int x[5];\n"
+ "void f() { for (int i : x) {} }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallExpr) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 1, 22);
+ EXPECT_TRUE(Visitor.runOver(
+ "void x(); void y() { x(); }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallInTemplateInstantiation) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("Y::x", 3, 3);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct Y { void x(); };\n"
+ "template<typename T> void y(T t) {\n"
+ " t.x();\n"
+ "}\n"
+ "void foo() { y<Y>(Y()); }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallInNestedFunctionTemplateInstantiation) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("Y::x", 4, 5);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct Y { void x(); };\n"
+ "template<typename T> struct Z {\n"
+ " template<typename U> static void f() {\n"
+ " T().x();\n"
+ " }\n"
+ "};\n"
+ "void foo() { Z<Y>::f<int>(); }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallInNestedClassTemplateInstantiation) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("A::x", 5, 7);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <typename T1> struct X {\n"
+ " template <typename T2> struct Y {\n"
+ " void f() {\n"
+ " T2 y;\n"
+ " y.x();\n"
+ " }\n"
+ " };\n"
+ "};\n"
+ "struct A { void x(); };\n"
+ "int main() {\n"
+ " (new X<A>::Y<A>())->f();\n"
+ "}"));
+}
+
+/* FIXME: According to Richard Smith this is a bug in the AST.
+TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 3, 43);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <typename T> void x();\n"
+ "template <void (*T)()> class X {};\n"
+ "template <typename T> class Y : public X< x<T> > {};\n"
+ "Y<int> y;"));
+}
+*/
+
+TEST(RecursiveASTVisitor, VisitsCallInPartialTemplateSpecialization) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("A::x", 6, 20);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <typename T1> struct X {\n"
+ " template <typename T2, bool B> struct Y { void g(); };\n"
+ "};\n"
+ "template <typename T1> template <typename T2>\n"
+ "struct X<T1>::Y<T2, true> {\n"
+ " void f() { T2 y; y.x(); }\n"
+ "};\n"
+ "struct A { void x(); };\n"
+ "int main() {\n"
+ " (new X<A>::Y<A, true>())->f();\n"
+ "}\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsExplicitTemplateSpecialization) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("A::f", 4, 5);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct A {\n"
+ " void f() const {}\n"
+ " template<class T> void g(const T& t) const {\n"
+ " t.f();\n"
+ " }\n"
+ "};\n"
+ "template void A::g(const A& a) const;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
+ // From cfe-commits/Week-of-Mon-20100830/033998.html
+ // Contrary to the approach suggested in that email, we visit all
+ // specializations when we visit the primary template. Visiting them when we
+ // visit the associated specialization is problematic for specializations of
+ // template members of class templates.
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<bool>", 1, 26);
+ Visitor.ExpectMatch("A<char *>", 2, 26);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <class T> class A {};\n"
+ "template <class T> class A<T*> {};\n"
+ "A<bool> ab;\n"
+ "A<char*> acp;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>", 1, 29);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> struct A;\n"
+ "A<int> *p;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>::B<char>", 2, 31);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> struct A {\n"
+ " template<typename U> struct B;\n"
+ "};\n"
+ "A<int>::B<char> *p;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>", 1, 26);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> int A();\n"
+ "int k = A<int>();\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>::B<char>", 2, 35);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> struct A {\n"
+ " template<typename U> static int B();\n"
+ "};\n"
+ "int k = A<int>::B<char>();\n"));
+}
+
+TEST(RecursiveASTVisitor, NoRecursionInSelfFriend) {
+ // From cfe-commits/Week-of-Mon-20100830/033977.html
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("vector_iterator<int>", 2, 7);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename Container>\n"
+ "class vector_iterator {\n"
+ " template <typename C> friend class vector_iterator;\n"
+ "};\n"
+ "vector_iterator<int> it_int;\n"));
+}
+
+TEST(RecursiveASTVisitor, TraversesOverloadedOperator) {
+ CXXOperatorCallExprTraverser Visitor;
+ Visitor.ExpectMatch("()", 4, 9);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct A {\n"
+ " int operator()();\n"
+ "} a;\n"
+ "int k = a();\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsParensDuringDataRecursion) {
+ ParenExprVisitor Visitor;
+ Visitor.ExpectMatch("", 1, 9);
+ EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) {
+ CXXBoolLiteralExprVisitor Visitor;
+ Visitor.ExpectMatch("true", 2, 19);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<bool B> class X;\n"
+ "template<bool B = true> class Y;\n"
+ "template<bool B> class Y {};\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 2, 23);
+ EXPECT_TRUE(Visitor.runOver(
+ "class X;\n"
+ "template<typename T = X> class Y;\n"
+ "template<typename T> class Y {};\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsClassTemplateTemplateParmDefaultArgument) {
+ TemplateArgumentLocTraverser Visitor;
+ Visitor.ExpectMatch("X", 2, 40);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> class X;\n"
+ "template<template <typename> class T = X> class Y;\n"
+ "template<template <typename> class T> class Y {};\n"));
+}
+
+// A visitor that visits implicit declarations and matches constructors.
+class ImplicitCtorVisitor
+ : public ExpectedLocationVisitor<ImplicitCtorVisitor> {
+public:
+ bool shouldVisitImplicitCode() const { return true; }
+
+ bool VisitCXXConstructorDecl(CXXConstructorDecl* Ctor) {
+ if (Ctor->isImplicit()) { // Was not written in source code
+ if (const CXXRecordDecl* Class = Ctor->getParent()) {
+ Match(Class->getName(), Ctor->getLocation());
+ }
+ }
+ return true;
+ }
+};
+
+TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) {
+ ImplicitCtorVisitor Visitor;
+ Visitor.ExpectMatch("Simple", 2, 8);
+ // Note: Clang lazily instantiates implicit declarations, so we need
+ // to use them in order to force them to appear in the AST.
+ EXPECT_TRUE(Visitor.runOver(
+ "struct WithCtor { WithCtor(); }; \n"
+ "struct Simple { Simple(); WithCtor w; }; \n"
+ "int main() { Simple s; Simple t(s); }\n"));
+}
+
+} // end namespace clang
diff --git a/unittests/Tooling/RefactoringCallbacksTest.cpp b/unittests/Tooling/RefactoringCallbacksTest.cpp
new file mode 100644
index 0000000..00eb193
--- /dev/null
+++ b/unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -0,0 +1,100 @@
+//===- unittest/ASTMatchers/RefactoringCallbacksTest.cpp ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
+#include "gtest/gtest.h"
+#include "RewriterTestContext.h"
+
+namespace clang {
+namespace tooling {
+
+using namespace ast_matchers;
+
+template <typename T>
+void expectRewritten(const std::string &Code,
+ const std::string &Expected,
+ const T &AMatcher,
+ RefactoringCallback &Callback) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher, &Callback);
+ OwningPtr<tooling::FrontendActionFactory> Factory(
+ tooling::newFrontendActionFactory(&Finder));
+ ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
+ << "Parsing error in \"" << Code << "\"";
+ RewriterTestContext Context;
+ FileID ID = Context.createInMemoryFile("input.cc", Code);
+ EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+ Context.Rewrite));
+ EXPECT_EQ(Expected, Context.getRewrittenText(ID));
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) {
+ std::string Code = "void f() { int i = 1; }";
+ std::string Expected = "void f() { ; }";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) {
+ std::string Code = "#define A void f() { int i = 1; }\nA";
+ std::string Expected = "#define A void f() { ; }\nA";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) {
+ std::string Code = "#define A void f() { int i = 1; }";
+ std::string Expected = "#define A void f() { int i = 1; }";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesInteger) {
+ std::string Code = "void f() { int i = 1; }";
+ std::string Expected = "void f() { int i = 2; }";
+ ReplaceStmtWithText Callback("id", "2");
+ expectRewritten(Code, Expected, id("id", expression(integerLiteral())),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
+ std::string Code = "void f() { int i = false ? 1 : i * 2; }";
+ std::string Expected = "void f() { int i = i * 2; }";
+ ReplaceStmtWithStmt Callback("always-false", "should-be");
+ expectRewritten(Code, Expected,
+ id("always-false", conditionalOperator(
+ hasCondition(boolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expression())))),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
+ std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
+ std::string Expected = "bool a; void f() { f(); }";
+ ReplaceIfStmtWithItsBody Callback("id", true);
+ expectRewritten(Code, Expected,
+ id("id", ifStmt(
+ hasCondition(implicitCast(hasSourceExpression(
+ declarationReference(to(variable(hasName("a"))))))))),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
+ std::string Code = "void f() { if (false) int i = 0; }";
+ std::string Expected = "void f() { }";
+ ReplaceIfStmtWithItsBody Callback("id", false);
+ expectRewritten(Code, Expected,
+ id("id", ifStmt(hasCondition(boolLiteral(equals(false))))),
+ Callback);
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
new file mode 100644
index 0000000..8d96955
--- /dev/null
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -0,0 +1,305 @@
+//===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RewriterTestContext.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+
+class ReplacementTest : public ::testing::Test {
+ protected:
+ Replacement createReplacement(SourceLocation Start, unsigned Length,
+ llvm::StringRef ReplacementText) {
+ return Replacement(Context.Sources, Start, Length, ReplacementText);
+ }
+
+ RewriterTestContext Context;
+};
+
+TEST_F(ReplacementTest, CanDeleteAllText) {
+ FileID ID = Context.createInMemoryFile("input.cpp", "text");
+ SourceLocation Location = Context.getLocation(ID, 1, 1);
+ Replacement Replace(createReplacement(Location, 4, ""));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanDeleteAllTextInTextWithNewlines) {
+ FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3");
+ SourceLocation Location = Context.getLocation(ID, 1, 1);
+ Replacement Replace(createReplacement(Location, 17, ""));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanAddText) {
+ FileID ID = Context.createInMemoryFile("input.cpp", "");
+ SourceLocation Location = Context.getLocation(ID, 1, 1);
+ Replacement Replace(createReplacement(Location, 0, "result"));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("result", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanReplaceTextAtPosition) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ SourceLocation Location = Context.getLocation(ID, 2, 3);
+ Replacement Replace(createReplacement(Location, 12, "x"));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("line1\nlixne4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanReplaceTextAtPositionMultipleTimes) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ SourceLocation Location1 = Context.getLocation(ID, 2, 3);
+ Replacement Replace1(createReplacement(Location1, 12, "x\ny\n"));
+ EXPECT_TRUE(Replace1.apply(Context.Rewrite));
+ EXPECT_EQ("line1\nlix\ny\nne4", Context.getRewrittenText(ID));
+
+ // Since the original source has not been modified, the (4, 4) points to the
+ // 'e' in the original content.
+ SourceLocation Location2 = Context.getLocation(ID, 4, 4);
+ Replacement Replace2(createReplacement(Location2, 1, "f"));
+ EXPECT_TRUE(Replace2.apply(Context.Rewrite));
+ EXPECT_EQ("line1\nlix\ny\nnf4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, ApplyFailsForNonExistentLocation) {
+ Replacement Replace("nonexistent-file.cpp", 0, 1, "");
+ EXPECT_FALSE(Replace.apply(Context.Rewrite));
+}
+
+TEST_F(ReplacementTest, CanRetrivePath) {
+ Replacement Replace("/path/to/file.cpp", 0, 1, "");
+ EXPECT_EQ("/path/to/file.cpp", Replace.getFilePath());
+}
+
+TEST_F(ReplacementTest, ReturnsInvalidPath) {
+ Replacement Replace1(Context.Sources, SourceLocation(), 0, "");
+ EXPECT_TRUE(Replace1.getFilePath().empty());
+
+ Replacement Replace2;
+ EXPECT_TRUE(Replace2.getFilePath().empty());
+}
+
+TEST_F(ReplacementTest, CanApplyReplacements) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1),
+ 5, "other"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
+ // This test depends on the value of the file name of an invalid source
+ // location being in the range ]a, z[.
+ FileID IDa = Context.createInMemoryFile("a.cpp", "text");
+ FileID IDz = Context.createInMemoryFile("z.cpp", "text");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDa, 1, 1),
+ 4, "a"));
+ Replaces.insert(Replacement(Context.Sources, SourceLocation(),
+ 5, "2"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDz, 1, 1),
+ 4, "z"));
+ EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("a", Context.getRewrittenText(IDa));
+ EXPECT_EQ("z", Context.getRewrittenText(IDz));
+}
+
+class FlushRewrittenFilesTest : public ::testing::Test {
+ public:
+ FlushRewrittenFilesTest() {
+ std::string ErrorInfo;
+ TemporaryDirectory = llvm::sys::Path::GetTemporaryDirectory(&ErrorInfo);
+ assert(ErrorInfo.empty());
+ }
+
+ ~FlushRewrittenFilesTest() {
+ std::string ErrorInfo;
+ TemporaryDirectory.eraseFromDisk(true, &ErrorInfo);
+ assert(ErrorInfo.empty());
+ }
+
+ FileID createFile(llvm::StringRef Name, llvm::StringRef Content) {
+ llvm::SmallString<1024> Path(TemporaryDirectory.str());
+ llvm::sys::path::append(Path, Name);
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream OutStream(Path.c_str(),
+ ErrorInfo, llvm::raw_fd_ostream::F_Binary);
+ assert(ErrorInfo.empty());
+ OutStream << Content;
+ OutStream.close();
+ const FileEntry *File = Context.Files.getFile(Path);
+ assert(File != NULL);
+ return Context.Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
+ }
+
+ std::string getFileContentFromDisk(llvm::StringRef Name) {
+ llvm::SmallString<1024> Path(TemporaryDirectory.str());
+ llvm::sys::path::append(Path, Name);
+ // We need to read directly from the FileManager without relaying through
+ // a FileEntry, as otherwise we'd read through an already opened file
+ // descriptor, which might not see the changes made.
+ // FIXME: Figure out whether there is a way to get the SourceManger to
+ // reopen the file.
+ return Context.Files.getBufferForFile(Path, NULL)->getBuffer();
+ }
+
+ llvm::sys::Path TemporaryDirectory;
+ RewriterTestContext Context;
+};
+
+TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
+ FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
+ EXPECT_EQ("line1\nreplaced\nline3\nline4",
+ getFileContentFromDisk("input.cpp"));
+}
+
+namespace {
+template <typename T>
+class TestVisitor : public clang::RecursiveASTVisitor<T> {
+public:
+ bool runOver(StringRef Code) {
+ return runToolOnCode(new TestAction(this), Code);
+ }
+
+protected:
+ clang::SourceManager *SM;
+
+private:
+ class FindConsumer : public clang::ASTConsumer {
+ public:
+ FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual void HandleTranslationUnit(clang::ASTContext &Context) {
+ Visitor->TraverseDecl(Context.getTranslationUnitDecl());
+ }
+
+ private:
+ TestVisitor *Visitor;
+ };
+
+ class TestAction : public clang::ASTFrontendAction {
+ public:
+ TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual clang::ASTConsumer* CreateASTConsumer(
+ clang::CompilerInstance& compiler, llvm::StringRef dummy) {
+ Visitor->SM = &compiler.getSourceManager();
+ /// TestConsumer will be deleted by the framework calling us.
+ return new FindConsumer(Visitor);
+ }
+
+ private:
+ TestVisitor *Visitor;
+ };
+};
+} // end namespace
+
+void expectReplacementAt(const Replacement &Replace,
+ StringRef File, unsigned Offset, unsigned Length) {
+ ASSERT_TRUE(Replace.isApplicable());
+ EXPECT_EQ(File, Replace.getFilePath());
+ EXPECT_EQ(Offset, Replace.getOffset());
+ EXPECT_EQ(Length, Replace.getLength());
+}
+
+class ClassDeclXVisitor : public TestVisitor<ClassDeclXVisitor> {
+public:
+ bool VisitCXXRecordDecl(CXXRecordDecl *Record) {
+ if (Record->getName() == "X") {
+ Replace = Replacement(*SM, Record, "");
+ }
+ return true;
+ }
+ Replacement Replace;
+};
+
+TEST(Replacement, CanBeConstructedFromNode) {
+ ClassDeclXVisitor ClassDeclX;
+ EXPECT_TRUE(ClassDeclX.runOver(" class X;"));
+ expectReplacementAt(ClassDeclX.Replace, "input.cc", 5, 7);
+}
+
+TEST(Replacement, ReplacesAtSpellingLocation) {
+ ClassDeclXVisitor ClassDeclX;
+ EXPECT_TRUE(ClassDeclX.runOver("#define A(Y) Y\nA(class X);"));
+ expectReplacementAt(ClassDeclX.Replace, "input.cc", 17, 7);
+}
+
+class CallToFVisitor : public TestVisitor<CallToFVisitor> {
+public:
+ bool VisitCallExpr(CallExpr *Call) {
+ if (Call->getDirectCallee()->getName() == "F") {
+ Replace = Replacement(*SM, Call, "");
+ }
+ return true;
+ }
+ Replacement Replace;
+};
+
+TEST(Replacement, FunctionCall) {
+ CallToFVisitor CallToF;
+ EXPECT_TRUE(CallToF.runOver("void F(); void G() { F(); }"));
+ expectReplacementAt(CallToF.Replace, "input.cc", 21, 3);
+}
+
+TEST(Replacement, TemplatedFunctionCall) {
+ CallToFVisitor CallToF;
+ EXPECT_TRUE(CallToF.runOver(
+ "template <typename T> void F(); void G() { F<int>(); }"));
+ expectReplacementAt(CallToF.Replace, "input.cc", 43, 8);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/unittests/Tooling/RewriterTest.cpp b/unittests/Tooling/RewriterTest.cpp
new file mode 100644
index 0000000..c53e50a
--- /dev/null
+++ b/unittests/Tooling/RewriterTest.cpp
@@ -0,0 +1,37 @@
+//===- unittest/Tooling/RewriterTest.cpp ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RewriterTestContext.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+
+TEST(Rewriter, OverwritesChangedFiles) {
+ RewriterTestContext Context;
+ FileID ID = Context.createOnDiskFile("t.cpp", "line1\nline2\nline3\nline4");
+ Context.Rewrite.ReplaceText(Context.getLocation(ID, 2, 1), 5, "replaced");
+ EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
+ EXPECT_EQ("line1\nreplaced\nline3\nline4",
+ Context.getFileContentFromDisk("t.cpp"));
+}
+
+TEST(Rewriter, ContinuesOverwritingFilesOnError) {
+ RewriterTestContext Context;
+ FileID FailingID = Context.createInMemoryFile("invalid/failing.cpp", "test");
+ Context.Rewrite.ReplaceText(Context.getLocation(FailingID, 1, 2), 1, "other");
+ FileID WorkingID = Context.createOnDiskFile(
+ "working.cpp", "line1\nline2\nline3\nline4");
+ Context.Rewrite.ReplaceText(Context.getLocation(WorkingID, 2, 1), 5,
+ "replaced");
+ EXPECT_TRUE(Context.Rewrite.overwriteChangedFiles());
+ EXPECT_EQ("line1\nreplaced\nline3\nline4",
+ Context.getFileContentFromDisk("working.cpp"));
+}
+
+} // end namespace clang
diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h
new file mode 100644
index 0000000..f68be6b
--- /dev/null
+++ b/unittests/Tooling/RewriterTestContext.h
@@ -0,0 +1,125 @@
+//===--- RewriterTestContext.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a utility class for Rewriter related tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_REWRITER_TEST_CONTEXT_H
+#define LLVM_CLANG_REWRITER_TEST_CONTEXT_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+/// \brief A class that sets up a ready to use Rewriter.
+///
+/// Useful in unit tests that need a Rewriter. Creates all dependencies
+/// of a Rewriter with default values for testing and provides convenience
+/// methods, which help with writing tests that change files.
+class RewriterTestContext {
+ public:
+ RewriterTestContext()
+ : Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>()),
+ DiagnosticPrinter(llvm::outs(), DiagnosticOptions()),
+ Files((FileSystemOptions())),
+ Sources(Diagnostics, Files),
+ Rewrite(Sources, Options) {
+ Diagnostics.setClient(&DiagnosticPrinter, false);
+ }
+
+ ~RewriterTestContext() {
+ if (!TemporaryDirectory.empty()) {
+ uint32_t RemovedCount = 0;
+ llvm::sys::fs::remove_all(TemporaryDirectory.str(), RemovedCount);
+ }
+ }
+
+ FileID createInMemoryFile(StringRef Name, StringRef Content) {
+ const llvm::MemoryBuffer *Source =
+ llvm::MemoryBuffer::getMemBuffer(Content);
+ const FileEntry *Entry =
+ Files.getVirtualFile(Name, Source->getBufferSize(), 0);
+ Sources.overrideFileContents(Entry, Source, true);
+ assert(Entry != NULL);
+ return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
+ }
+
+ FileID createOnDiskFile(StringRef Name, StringRef Content) {
+ if (TemporaryDirectory.empty()) {
+ int FD;
+ bool error =
+ llvm::sys::fs::unique_file("rewriter-test-%%-%%-%%-%%/anchor", FD,
+ TemporaryDirectory);
+ assert(!error); (void)error;
+ llvm::raw_fd_ostream Closer(FD, /*shouldClose=*/true);
+ TemporaryDirectory = llvm::sys::path::parent_path(TemporaryDirectory);
+ }
+ llvm::SmallString<1024> Path(TemporaryDirectory);
+ llvm::sys::path::append(Path, Name);
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream OutStream(Path.c_str(),
+ ErrorInfo, llvm::raw_fd_ostream::F_Binary);
+ assert(ErrorInfo.empty());
+ OutStream << Content;
+ OutStream.close();
+ const FileEntry *File = Files.getFile(Path);
+ assert(File != NULL);
+ return Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
+ }
+
+ SourceLocation getLocation(FileID ID, unsigned Line, unsigned Column) {
+ SourceLocation Result = Sources.translateFileLineCol(
+ Sources.getFileEntryForID(ID), Line, Column);
+ assert(Result.isValid());
+ return Result;
+ }
+
+ std::string getRewrittenText(FileID ID) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ Rewrite.getEditBuffer(ID).write(OS);
+ OS.flush();
+ return Result;
+ }
+
+ std::string getFileContentFromDisk(StringRef Name) {
+ llvm::SmallString<1024> Path(TemporaryDirectory.str());
+ llvm::sys::path::append(Path, Name);
+ // We need to read directly from the FileManager without relaying through
+ // a FileEntry, as otherwise we'd read through an already opened file
+ // descriptor, which might not see the changes made.
+ // FIXME: Figure out whether there is a way to get the SourceManger to
+ // reopen the file.
+ return Files.getBufferForFile(Path, NULL)->getBuffer();
+ }
+
+ DiagnosticsEngine Diagnostics;
+ TextDiagnosticPrinter DiagnosticPrinter;
+ FileManager Files;
+ SourceManager Sources;
+ LangOptions Options;
+ Rewriter Rewrite;
+
+ // Will be set once on disk files are generated.
+ SmallString<128> TemporaryDirectory;
+};
+
+} // end namespace clang
+
+#endif
diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h
new file mode 100644
index 0000000..d439d81
--- /dev/null
+++ b/unittests/Tooling/TestVisitor.h
@@ -0,0 +1,144 @@
+//===--- TestVisitor.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a utility class for RecursiveASTVisitor related tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TEST_VISITOR_H
+#define LLVM_CLANG_TEST_VISITOR_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+
+/// \brief Base class for simple RecursiveASTVisitor based tests.
+///
+/// This is a drop-in replacement for RecursiveASTVisitor itself, with the
+/// additional capability of running it over a snippet of code.
+///
+/// Visits template instantiations by default.
+template <typename T>
+class TestVisitor : public RecursiveASTVisitor<T> {
+public:
+ TestVisitor() { }
+
+ virtual ~TestVisitor() { }
+
+ /// \brief Runs the current AST visitor over the given code.
+ bool runOver(StringRef Code) {
+ return tooling::runToolOnCode(CreateTestAction(), Code);
+ }
+
+ bool shouldVisitTemplateInstantiations() const {
+ return true;
+ }
+
+protected:
+ virtual ASTFrontendAction* CreateTestAction() {
+ return new TestAction(this);
+ }
+
+ class FindConsumer : public ASTConsumer {
+ public:
+ FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual void HandleTranslationUnit(clang::ASTContext &Context) {
+ Visitor->Context = &Context;
+ Visitor->TraverseDecl(Context.getTranslationUnitDecl());
+ }
+
+ private:
+ TestVisitor *Visitor;
+ };
+
+ class TestAction : public ASTFrontendAction {
+ public:
+ TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual clang::ASTConsumer* CreateASTConsumer(
+ CompilerInstance&, llvm::StringRef dummy) {
+ /// TestConsumer will be deleted by the framework calling us.
+ return new FindConsumer(Visitor);
+ }
+
+ protected:
+ TestVisitor *Visitor;
+ };
+
+ ASTContext *Context;
+};
+
+
+/// \brief A RecursiveASTVisitor for testing the RecursiveASTVisitor itself.
+///
+/// Allows simple creation of test visitors running matches on only a small
+/// subset of the Visit* methods.
+template <typename T, template <typename> class Visitor = TestVisitor>
+class ExpectedLocationVisitor : public Visitor<T> {
+public:
+ ExpectedLocationVisitor()
+ : ExpectedLine(0), ExpectedColumn(0), Found(false) {}
+
+ virtual ~ExpectedLocationVisitor() {
+ EXPECT_TRUE(Found)
+ << "Expected \"" << ExpectedMatch << "\" at " << ExpectedLine
+ << ":" << ExpectedColumn << PartialMatches;
+ }
+
+ /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
+ void ExpectMatch(Twine Match, unsigned Line, unsigned Column) {
+ ExpectedMatch = Match.str();
+ ExpectedLine = Line;
+ ExpectedColumn = Column;
+ }
+
+protected:
+ /// \brief Convenience method to simplify writing test visitors.
+ ///
+ /// Sets 'Found' to true if 'Name' and 'Location' match the expected
+ /// values. If only a partial match is found, record the information
+ /// to produce nice error output when a test fails.
+ ///
+ /// Implementations are required to call this with appropriate values
+ /// for 'Name' during visitation.
+ void Match(StringRef Name, SourceLocation Location) {
+ FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
+ if (Name == ExpectedMatch &&
+ FullLocation.isValid() &&
+ FullLocation.getSpellingLineNumber() == ExpectedLine &&
+ FullLocation.getSpellingColumnNumber() == ExpectedColumn) {
+ EXPECT_TRUE(!Found);
+ Found = true;
+ } else if (Name == ExpectedMatch ||
+ (FullLocation.isValid() &&
+ FullLocation.getSpellingLineNumber() == ExpectedLine &&
+ FullLocation.getSpellingColumnNumber() == ExpectedColumn)) {
+ // If we did not match, record information about partial matches.
+ llvm::raw_string_ostream Stream(PartialMatches);
+ Stream << ", partial match: \"" << Name << "\" at ";
+ Location.print(Stream, this->Context->getSourceManager());
+ }
+ }
+
+ std::string ExpectedMatch;
+ unsigned ExpectedLine;
+ unsigned ExpectedColumn;
+ std::string PartialMatches;
+ bool Found;
+};
+}
+
+#endif /* LLVM_CLANG_TEST_VISITOR_H */
diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp
index c7b2210..fb3af26 100644
--- a/unittests/Tooling/ToolingTest.cpp
+++ b/unittests/Tooling/ToolingTest.cpp
@@ -15,6 +15,7 @@
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
+#include <string>
namespace clang {
namespace tooling {
@@ -52,11 +53,16 @@ class FindTopLevelDeclConsumer : public clang::ASTConsumer {
};
} // end namespace
-TEST(runToolOnCode, FindsTopLevelDeclOnEmptyCode) {
+TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
bool FoundTopLevelDecl = false;
EXPECT_TRUE(runToolOnCode(
new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), ""));
+#if !defined(_MSC_VER)
+ EXPECT_FALSE(FoundTopLevelDecl);
+#else
+ // FIXME: LangOpts.MicrosoftExt appends "class type_info;"
EXPECT_TRUE(FoundTopLevelDecl);
+#endif
}
namespace {
@@ -98,7 +104,9 @@ TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
}
struct IndependentFrontendActionCreator {
- FrontendAction *newFrontendAction() { return new SyntaxOnlyAction; }
+ ASTConsumer *newASTConsumer() {
+ return new FindTopLevelDeclConsumer(NULL);
+ }
};
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
@@ -109,5 +117,18 @@ TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
EXPECT_TRUE(Action.get() != NULL);
}
+TEST(ToolInvocation, TestMapVirtualFile) {
+ clang::FileManager Files((clang::FileSystemOptions()));
+ std::vector<std::string> Args;
+ Args.push_back("tool-executable");
+ Args.push_back("-Idef");
+ Args.push_back("-fsyntax-only");
+ Args.push_back("test.cpp");
+ clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files);
+ Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
+ Invocation.mapVirtualFile("def/abc", "\n");
+ EXPECT_TRUE(Invocation.run());
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/utils/TableGen/ClangASTNodesEmitter.cpp b/utils/TableGen/ClangASTNodesEmitter.cpp
index d9d5a3c..c51ca96 100644
--- a/utils/TableGen/ClangASTNodesEmitter.cpp
+++ b/utils/TableGen/ClangASTNodesEmitter.cpp
@@ -11,10 +11,58 @@
//
//===----------------------------------------------------------------------===//
-#include "ClangASTNodesEmitter.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <cctype>
+#include <map>
#include <set>
+#include <string>
using namespace llvm;
+/// ClangASTNodesEmitter - The top-level class emits .inc files containing
+/// declarations of Clang statements.
+///
+namespace {
+class ClangASTNodesEmitter {
+ // A map from a node to each of its derived nodes.
+ typedef std::multimap<Record*, Record*> ChildMap;
+ typedef ChildMap::const_iterator ChildIterator;
+
+ RecordKeeper &Records;
+ Record Root;
+ const std::string &BaseSuffix;
+
+ // Create a macro-ized version of a name
+ static std::string macroName(std::string S) {
+ for (unsigned i = 0; i < S.size(); ++i)
+ S[i] = std::toupper(S[i]);
+
+ return S;
+ }
+
+ // Return the name to be printed in the base field. Normally this is
+ // the record's name plus the base suffix, but if it is the root node and
+ // the suffix is non-empty, it's just the suffix.
+ std::string baseName(Record &R) {
+ if (&R == &Root && !BaseSuffix.empty())
+ return BaseSuffix;
+
+ return R.getName() + BaseSuffix;
+ }
+
+ std::pair<Record *, Record *> EmitNode (const ChildMap &Tree, raw_ostream& OS,
+ Record *Base);
+public:
+ explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N,
+ const std::string &S)
+ : Records(R), Root(N, SMLoc(), R), BaseSuffix(S)
+ {}
+
+ // run - Output the .inc file contents
+ void run(raw_ostream &OS);
+};
+} // end anonymous namespace
+
//===----------------------------------------------------------------------===//
// Statement Node Tables (.inc file) generation.
//===----------------------------------------------------------------------===//
@@ -124,7 +172,15 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) {
OS << "#undef ABSTRACT_" << macroName(Root.getName()) << "\n";
}
-void ClangDeclContextEmitter::run(raw_ostream &OS) {
+namespace clang {
+void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS,
+ const std::string &N, const std::string &S) {
+ ClangASTNodesEmitter(RK, N, S).run(OS);
+}
+
+// Emits and addendum to a .inc file to enumerate the clang declaration
+// contexts.
+void EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) {
// FIXME: Find a .td file format to allow for this to be represented better.
OS << "#ifndef DECL_CONTEXT\n";
@@ -166,3 +222,4 @@ void ClangDeclContextEmitter::run(raw_ostream &OS) {
OS << "#undef DECL_CONTEXT\n";
OS << "#undef DECL_CONTEXT_BASE\n";
}
+} // end namespace clang
diff --git a/utils/TableGen/ClangASTNodesEmitter.h b/utils/TableGen/ClangASTNodesEmitter.h
deleted file mode 100644
index edd9316..0000000
--- a/utils/TableGen/ClangASTNodesEmitter.h
+++ /dev/null
@@ -1,84 +0,0 @@
-//===- ClangASTNodesEmitter.h - Generate Clang AST node tables -*- C++ -*--===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// These tablegen backends emit Clang AST node tables
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANGAST_EMITTER_H
-#define CLANGAST_EMITTER_H
-
-#include "llvm/TableGen/TableGenBackend.h"
-#include "llvm/TableGen/Record.h"
-#include <string>
-#include <cctype>
-#include <map>
-
-namespace llvm {
-
-/// ClangASTNodesEmitter - The top-level class emits .inc files containing
-/// declarations of Clang statements.
-///
-class ClangASTNodesEmitter : public TableGenBackend {
- // A map from a node to each of its derived nodes.
- typedef std::multimap<Record*, Record*> ChildMap;
- typedef ChildMap::const_iterator ChildIterator;
-
- RecordKeeper &Records;
- Record Root;
- const std::string &BaseSuffix;
-
- // Create a macro-ized version of a name
- static std::string macroName(std::string S) {
- for (unsigned i = 0; i < S.size(); ++i)
- S[i] = std::toupper(S[i]);
-
- return S;
- }
-
- // Return the name to be printed in the base field. Normally this is
- // the record's name plus the base suffix, but if it is the root node and
- // the suffix is non-empty, it's just the suffix.
- std::string baseName(Record &R) {
- if (&R == &Root && !BaseSuffix.empty())
- return BaseSuffix;
-
- return R.getName() + BaseSuffix;
- }
-
- std::pair<Record *, Record *> EmitNode (const ChildMap &Tree, raw_ostream& OS,
- Record *Base);
-public:
- explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N,
- const std::string &S)
- : Records(R), Root(N, SMLoc(), R), BaseSuffix(S)
- {}
-
- // run - Output the .inc file contents
- void run(raw_ostream &OS);
-};
-
-/// ClangDeclContextEmitter - Emits an addendum to a .inc file to enumerate the
-/// clang declaration contexts.
-///
-class ClangDeclContextEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
-public:
- explicit ClangDeclContextEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- // run - Output the .inc file contents
- void run(raw_ostream &OS);
-};
-
-} // End llvm namespace
-
-#endif
diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp
index 7951fc4..1b1a478 100644
--- a/utils/TableGen/ClangAttrEmitter.cpp
+++ b/utils/TableGen/ClangAttrEmitter.cpp
@@ -11,12 +11,13 @@
//
//===----------------------------------------------------------------------===//
-#include "ClangAttrEmitter.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringMatcher.h"
+#include "llvm/TableGen/TableGenBackend.h"
#include <algorithm>
#include <cctype>
-#include <set>
using namespace llvm;
@@ -660,7 +661,10 @@ static void writeAvailabilityValue(raw_ostream &OS) {
<< " OS << \"";
}
-void ClangAttrClassEmitter::run(raw_ostream &OS) {
+namespace clang {
+
+// Emits the class definitions for attributes.
+void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n";
OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n";
@@ -670,6 +674,10 @@ void ClangAttrClassEmitter::run(raw_ostream &OS) {
for (std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end();
i != e; ++i) {
Record &R = **i;
+
+ if (!R.getValueAsBit("ASTNode"))
+ continue;
+
const std::string &SuperName = R.getSuperClasses().back()->getName();
OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n";
@@ -745,7 +753,8 @@ void ClangAttrClassEmitter::run(raw_ostream &OS) {
OS << "#endif\n";
}
-void ClangAttrImplEmitter::run(raw_ostream &OS) {
+// Emits the class method definitions for attributes.
+void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
@@ -754,8 +763,12 @@ void ClangAttrImplEmitter::run(raw_ostream &OS) {
for (; i != e; ++i) {
Record &R = **i;
+
+ if (!R.getValueAsBit("ASTNode"))
+ continue;
+
std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
- std::vector<StringRef> Spellings = getValueAsListOfStrings(R, "Spellings");
+ std::vector<Record*> Spellings = R.getValueAsListOfDefs("Spellings");
std::vector<Argument*> Args;
for (ri = ArgRecords.begin(), re = ArgRecords.end(); ri != re; ++ri)
Args.push_back(createArgument(**ri, R.getName()));
@@ -775,9 +788,10 @@ void ClangAttrImplEmitter::run(raw_ostream &OS) {
OS << "void " << R.getName() << "Attr::printPretty("
<< "llvm::raw_ostream &OS, ASTContext &Ctx) const {\n";
if (Spellings.begin() != Spellings.end()) {
- OS << " OS << \" __attribute__((" << *Spellings.begin();
+ std::string Spelling = (*Spellings.begin())->getValueAsString("Name");
+ OS << " OS << \" __attribute__((" << Spelling;
if (Args.size()) OS << "(";
- if (*Spellings.begin()=="availability") {
+ if (Spelling == "availability") {
writeAvailabilityValue(OS);
} else {
for (ai = Args.begin(); ai != ae; ++ai) {
@@ -792,20 +806,29 @@ void ClangAttrImplEmitter::run(raw_ostream &OS) {
}
}
+} // end namespace clang
+
static void EmitAttrList(raw_ostream &OS, StringRef Class,
const std::vector<Record*> &AttrList) {
std::vector<Record*>::const_iterator i = AttrList.begin(), e = AttrList.end();
if (i != e) {
// Move the end iterator back to emit the last attribute.
- for(--e; i != e; ++i)
+ for(--e; i != e; ++i) {
+ if (!(*i)->getValueAsBit("ASTNode"))
+ continue;
+
OS << Class << "(" << (*i)->getName() << ")\n";
+ }
OS << "LAST_" << Class << "(" << (*i)->getName() << ")\n\n";
}
}
-void ClangAttrListEmitter::run(raw_ostream &OS) {
+namespace clang {
+
+// Emits the enumeration list for attributes.
+void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
OS << "#ifndef LAST_ATTR\n";
@@ -835,6 +858,9 @@ void ClangAttrListEmitter::run(raw_ostream &OS) {
NonInhAttrs, InhAttrs, InhParamAttrs;
for (std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end();
i != e; ++i) {
+ if (!(*i)->getValueAsBit("ASTNode"))
+ continue;
+
if ((*i)->isSubClassOf(InhParamClass))
InhParamAttrs.push_back(*i);
else if ((*i)->isSubClassOf(InhClass))
@@ -854,7 +880,8 @@ void ClangAttrListEmitter::run(raw_ostream &OS) {
OS << "#undef ATTR\n";
}
-void ClangAttrPCHReadEmitter::run(raw_ostream &OS) {
+// Emits the code to read an attribute from a precompiled header.
+void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
Record *InhClass = Records.getClass("InheritableAttr");
@@ -870,6 +897,9 @@ void ClangAttrPCHReadEmitter::run(raw_ostream &OS) {
OS << " break;\n";
for (; i != e; ++i) {
Record &R = **i;
+ if (!R.getValueAsBit("ASTNode"))
+ continue;
+
OS << " case attr::" << R.getName() << ": {\n";
if (R.isSubClassOf(InhClass))
OS << " bool isInherited = Record[Idx++];\n";
@@ -894,7 +924,8 @@ void ClangAttrPCHReadEmitter::run(raw_ostream &OS) {
OS << " }\n";
}
-void ClangAttrPCHWriteEmitter::run(raw_ostream &OS) {
+// Emits the code to write an attribute to a precompiled header.
+void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) {
Record *InhClass = Records.getClass("InheritableAttr");
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args;
std::vector<Record*>::iterator i = Attrs.begin(), e = Attrs.end(), ai, ae;
@@ -905,6 +936,8 @@ void ClangAttrPCHWriteEmitter::run(raw_ostream &OS) {
OS << " break;\n";
for (; i != e; ++i) {
Record &R = **i;
+ if (!R.getValueAsBit("ASTNode"))
+ continue;
OS << " case attr::" << R.getName() << ": {\n";
Args = R.getValueAsListOfDefs("Args");
if (R.isSubClassOf(InhClass) || !Args.empty())
@@ -920,7 +953,8 @@ void ClangAttrPCHWriteEmitter::run(raw_ostream &OS) {
OS << " }\n";
}
-void ClangAttrSpellingListEmitter::run(raw_ostream &OS) {
+// Emits the list of spellings for attributes.
+void EmitClangAttrSpellingList(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
@@ -928,17 +962,17 @@ void ClangAttrSpellingListEmitter::run(raw_ostream &OS) {
for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end(); I != E; ++I) {
Record &Attr = **I;
- std::vector<StringRef> Spellings = getValueAsListOfStrings(Attr, "Spellings");
+ std::vector<Record*> Spellings = Attr.getValueAsListOfDefs("Spellings");
- for (std::vector<StringRef>::const_iterator I = Spellings.begin(), E = Spellings.end(); I != E; ++I) {
- StringRef Spelling = *I;
- OS << ".Case(\"" << Spelling << "\", true)\n";
+ for (std::vector<Record*>::const_iterator I = Spellings.begin(), E = Spellings.end(); I != E; ++I) {
+ OS << ".Case(\"" << (*I)->getValueAsString("Name") << "\", true)\n";
}
}
}
-void ClangAttrLateParsedListEmitter::run(raw_ostream &OS) {
+// Emits the LateParsed property for attributes.
+void EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
@@ -950,19 +984,23 @@ void ClangAttrLateParsedListEmitter::run(raw_ostream &OS) {
bool LateParsed = Attr.getValueAsBit("LateParsed");
if (LateParsed) {
- std::vector<StringRef> Spellings =
- getValueAsListOfStrings(Attr, "Spellings");
+ std::vector<Record*> Spellings =
+ Attr.getValueAsListOfDefs("Spellings");
- for (std::vector<StringRef>::const_iterator I = Spellings.begin(),
+ // FIXME: Handle non-GNU attributes
+ for (std::vector<Record*>::const_iterator I = Spellings.begin(),
E = Spellings.end(); I != E; ++I) {
- OS << ".Case(\"" << (*I) << "\", " << LateParsed << ")\n";
+ if ((*I)->getValueAsString("Variety") != "GNU")
+ continue;
+ OS << ".Case(\"" << (*I)->getValueAsString("Name") << "\", "
+ << LateParsed << ")\n";
}
}
}
}
-
-void ClangAttrTemplateInstantiateEmitter::run(raw_ostream &OS) {
+// Emits code to instantiate dependent attributes on templates.
+void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
@@ -979,8 +1017,18 @@ void ClangAttrTemplateInstantiateEmitter::run(raw_ostream &OS) {
for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end();
I != E; ++I) {
Record &R = **I;
+ if (!R.getValueAsBit("ASTNode"))
+ continue;
OS << " case attr::" << R.getName() << ": {\n";
+ bool ShouldClone = R.getValueAsBit("Clone");
+
+ if (!ShouldClone) {
+ OS << " return NULL;\n";
+ OS << " }\n";
+ continue;
+ }
+
OS << " const " << R.getName() << "Attr *A = cast<"
<< R.getName() << "Attr>(At);\n";
bool TDependent = R.getValueAsBit("TemplateDependent");
@@ -1024,7 +1072,8 @@ void ClangAttrTemplateInstantiateEmitter::run(raw_ostream &OS) {
<< "} // end namespace clang\n";
}
-void ClangAttrParsedAttrListEmitter::run(raw_ostream &OS) {
+// Emits the list of parsed attributes.
+void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
OS << "#ifndef PARSED_ATTR\n";
@@ -1032,61 +1081,85 @@ void ClangAttrParsedAttrListEmitter::run(raw_ostream &OS) {
OS << "#endif\n\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
- std::set<StringRef> ProcessedAttrs;
for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end();
I != E; ++I) {
Record &Attr = **I;
bool SemaHandler = Attr.getValueAsBit("SemaHandler");
-
+ bool DistinctSpellings = Attr.getValueAsBit("DistinctSpellings");
+
if (SemaHandler) {
- std::vector<StringRef> Spellings =
- getValueAsListOfStrings(Attr, "Spellings");
-
- for (std::vector<StringRef>::const_iterator I = Spellings.begin(),
- E = Spellings.end(); I != E; ++I) {
- StringRef AttrName = *I;
+ if (DistinctSpellings) {
+ std::vector<Record*> Spellings = Attr.getValueAsListOfDefs("Spellings");
+
+ for (std::vector<Record*>::const_iterator I = Spellings.begin(),
+ E = Spellings.end(); I != E; ++I) {
+ std::string AttrName = (*I)->getValueAsString("Name");
- AttrName = NormalizeAttrName(AttrName);
- // skip if a normalized version has been processed.
- if (ProcessedAttrs.find(AttrName) != ProcessedAttrs.end())
- continue;
- else
- ProcessedAttrs.insert(AttrName);
+ StringRef Spelling = NormalizeAttrName(AttrName);
+ OS << "PARSED_ATTR(" << Spelling << ")\n";
+ }
+ } else {
+ StringRef AttrName = Attr.getName();
+ AttrName = NormalizeAttrName(AttrName);
OS << "PARSED_ATTR(" << AttrName << ")\n";
}
}
}
}
-void ClangAttrParsedAttrKindsEmitter::run(raw_ostream &OS) {
+// Emits the kind list of parsed attributes
+void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
OS << "// This file is generated by TableGen. Do not edit.\n\n";
-
+ OS << "\n";
+
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
+ std::vector<StringMatcher::StringPair> Matches;
for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end();
I != E; ++I) {
Record &Attr = **I;
bool SemaHandler = Attr.getValueAsBit("SemaHandler");
-
- if (SemaHandler) {
- std::vector<StringRef> Spellings =
- getValueAsListOfStrings(Attr, "Spellings");
+ bool Ignored = Attr.getValueAsBit("Ignored");
+ bool DistinctSpellings = Attr.getValueAsBit("DistinctSpellings");
+ if (SemaHandler || Ignored) {
+ std::vector<Record*> Spellings = Attr.getValueAsListOfDefs("Spellings");
- for (std::vector<StringRef>::const_iterator I = Spellings.begin(),
+ for (std::vector<Record*>::const_iterator I = Spellings.begin(),
E = Spellings.end(); I != E; ++I) {
- StringRef AttrName = *I, Spelling = *I;
-
- AttrName = NormalizeAttrName(AttrName);
- Spelling = NormalizeAttrSpelling(Spelling);
+ std::string RawSpelling = (*I)->getValueAsString("Name");
+ StringRef AttrName = NormalizeAttrName(DistinctSpellings
+ ? StringRef(RawSpelling)
+ : StringRef(Attr.getName()));
+
+ SmallString<64> Spelling;
+ if ((*I)->getValueAsString("Variety") == "CXX11") {
+ Spelling += (*I)->getValueAsString("Namespace");
+ Spelling += "::";
+ }
+ Spelling += NormalizeAttrSpelling(RawSpelling);
- OS << ".Case(\"" << Spelling << "\", " << "AT_" << AttrName << ")\n";
+ if (SemaHandler)
+ Matches.push_back(
+ StringMatcher::StringPair(
+ StringRef(Spelling),
+ "return AttributeList::AT_" + AttrName.str() + ";"));
+ else
+ Matches.push_back(
+ StringMatcher::StringPair(
+ StringRef(Spelling),
+ "return AttributeList::IgnoredAttribute;"));
}
}
}
+
+ OS << "static AttributeList::Kind getAttrKind(StringRef Name) {\n";
+ StringMatcher("Name", Matches, OS).Emit();
+ OS << "return AttributeList::UnknownAttribute;\n"
+ << "}\n";
}
-
+} // end namespace clang
diff --git a/utils/TableGen/ClangAttrEmitter.h b/utils/TableGen/ClangAttrEmitter.h
deleted file mode 100644
index d119a09..0000000
--- a/utils/TableGen/ClangAttrEmitter.h
+++ /dev/null
@@ -1,153 +0,0 @@
-//===- ClangAttrEmitter.h - Generate Clang attribute handling =-*- C++ -*--===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// These tablegen backends emit Clang attribute processing code
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANGATTR_EMITTER_H
-#define CLANGATTR_EMITTER_H
-
-#include "llvm/TableGen/TableGenBackend.h"
-
-namespace llvm {
-
-/// ClangAttrClassEmitter - class emits the class defintions for attributes for
-/// clang.
-class ClangAttrClassEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
- public:
- explicit ClangAttrClassEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrImplEmitter - class emits the class method defintions for
-/// attributes for clang.
-class ClangAttrImplEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
- public:
- explicit ClangAttrImplEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrListEmitter - class emits the enumeration list for attributes for
-/// clang.
-class ClangAttrListEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
- public:
- explicit ClangAttrListEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrPCHReadEmitter - class emits the code to read an attribute from
-/// a clang precompiled header.
-class ClangAttrPCHReadEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
-public:
- explicit ClangAttrPCHReadEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrPCHWriteEmitter - class emits the code to read an attribute from
-/// a clang precompiled header.
-class ClangAttrPCHWriteEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
-public:
- explicit ClangAttrPCHWriteEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrSpellingListEmitter - class emits the list of spellings for attributes for
-/// clang.
-class ClangAttrSpellingListEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
- public:
- explicit ClangAttrSpellingListEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrLateParsedListEmitter emits the LateParsed property for attributes
-/// for clang.
-class ClangAttrLateParsedListEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
- public:
- explicit ClangAttrLateParsedListEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrTemplateInstantiateEmitter emits code to instantiate dependent
-/// attributes on templates.
-class ClangAttrTemplateInstantiateEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
- public:
- explicit ClangAttrTemplateInstantiateEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrParsedAttrListEmitter emits the list of parsed attributes
-/// for clang.
-class ClangAttrParsedAttrListEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
-public:
- explicit ClangAttrParsedAttrListEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-/// ClangAttrParsedAttrKindsEmitter emits the kind list of parsed attributes
-/// for clang.
-class ClangAttrParsedAttrKindsEmitter : public TableGenBackend {
- RecordKeeper &Records;
-
-public:
- explicit ClangAttrParsedAttrKindsEmitter(RecordKeeper &R)
- : Records(R)
- {}
-
- void run(raw_ostream &OS);
-};
-
-}
-
-#endif
diff --git a/utils/TableGen/ClangDiagnosticsEmitter.cpp b/utils/TableGen/ClangDiagnosticsEmitter.cpp
index 8a49619..8615d2d 100644
--- a/utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ b/utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -11,16 +11,19 @@
//
//===----------------------------------------------------------------------===//
-#include "ClangDiagnosticsEmitter.h"
-#include "llvm/TableGen/Record.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Compiler.h"
+#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/SmallString.h"
-#include <map>
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
#include <algorithm>
+#include <cctype>
#include <functional>
+#include <map>
#include <set>
using namespace llvm;
@@ -78,7 +81,7 @@ static std::string getDiagnosticCategory(const Record *R,
DiagGroupParents);
if (!CatName.empty()) return CatName;
}
-
+
// If the diagnostic itself has a category, get it.
return R->getValueAsString("CategoryName");
}
@@ -135,6 +138,8 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
const Record *R = Diags[i];
DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group"));
if (DI == 0) continue;
+ assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" &&
+ "Note can't be in a DiagGroup");
std::string GroupName = DI->getDef()->getValueAsString("GroupName");
DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
}
@@ -158,10 +163,201 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
}
//===----------------------------------------------------------------------===//
+// Infer members of -Wpedantic.
+//===----------------------------------------------------------------------===//
+
+typedef std::vector<const Record *> RecordVec;
+typedef llvm::DenseSet<const Record *> RecordSet;
+typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet;
+
+namespace {
+class InferPedantic {
+ typedef llvm::DenseMap<const Record*,
+ std::pair<unsigned, llvm::Optional<unsigned> > > GMap;
+
+ DiagGroupParentMap &DiagGroupParents;
+ const std::vector<Record*> &Diags;
+ const std::vector<Record*> DiagGroups;
+ std::map<std::string, GroupInfo> &DiagsInGroup;
+ llvm::DenseSet<const Record*> DiagsSet;
+ GMap GroupCount;
+public:
+ InferPedantic(DiagGroupParentMap &DiagGroupParents,
+ const std::vector<Record*> &Diags,
+ const std::vector<Record*> &DiagGroups,
+ std::map<std::string, GroupInfo> &DiagsInGroup)
+ : DiagGroupParents(DiagGroupParents),
+ Diags(Diags),
+ DiagGroups(DiagGroups),
+ DiagsInGroup(DiagsInGroup) {}
+
+ /// Compute the set of diagnostics and groups that are immediately
+ /// in -Wpedantic.
+ void compute(VecOrSet DiagsInPedantic,
+ VecOrSet GroupsInPedantic);
+
+private:
+ /// Determine whether a group is a subgroup of another group.
+ bool isSubGroupOfGroup(const Record *Group,
+ llvm::StringRef RootGroupName);
+
+ /// Determine if the diagnostic is an extension.
+ bool isExtension(const Record *Diag);
+
+ /// Determine if the diagnostic is off by default.
+ bool isOffByDefault(const Record *Diag);
+
+ /// Increment the count for a group, and transitively marked
+ /// parent groups when appropriate.
+ void markGroup(const Record *Group);
+
+ /// Return true if the diagnostic is in a pedantic group.
+ bool groupInPedantic(const Record *Group, bool increment = false);
+};
+} // end anonymous namespace
+
+bool InferPedantic::isSubGroupOfGroup(const Record *Group,
+ llvm::StringRef GName) {
+
+ const std::string &GroupName = Group->getValueAsString("GroupName");
+ if (GName == GroupName)
+ return true;
+
+ const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
+ for (unsigned i = 0, e = Parents.size(); i != e; ++i)
+ if (isSubGroupOfGroup(Parents[i], GName))
+ return true;
+
+ return false;
+}
+
+/// Determine if the diagnostic is an extension.
+bool InferPedantic::isExtension(const Record *Diag) {
+ const std::string &ClsName = Diag->getValueAsDef("Class")->getName();
+ return ClsName == "CLASS_EXTENSION";
+}
+
+bool InferPedantic::isOffByDefault(const Record *Diag) {
+ const std::string &DefMap = Diag->getValueAsDef("DefaultMapping")->getName();
+ return DefMap == "MAP_IGNORE";
+}
+
+bool InferPedantic::groupInPedantic(const Record *Group, bool increment) {
+ GMap::mapped_type &V = GroupCount[Group];
+ // Lazily compute the threshold value for the group count.
+ if (!V.second.hasValue()) {
+ const GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
+ V.second = GI.SubGroups.size() + GI.DiagsInGroup.size();
+ }
+
+ if (increment)
+ ++V.first;
+
+ // Consider a group in -Wpendatic IFF if has at least one diagnostic
+ // or subgroup AND all of those diagnostics and subgroups are covered
+ // by -Wpedantic via our computation.
+ return V.first != 0 && V.first == V.second.getValue();
+}
+
+void InferPedantic::markGroup(const Record *Group) {
+ // If all the diagnostics and subgroups have been marked as being
+ // covered by -Wpedantic, increment the count of parent groups. Once the
+ // group's count is equal to the number of subgroups and diagnostics in
+ // that group, we can safely add this group to -Wpedantic.
+ if (groupInPedantic(Group, /* increment */ true)) {
+ const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
+ for (unsigned i = 0, e = Parents.size(); i != e; ++i)
+ markGroup(Parents[i]);
+ }
+}
+
+void InferPedantic::compute(VecOrSet DiagsInPedantic,
+ VecOrSet GroupsInPedantic) {
+ // All extensions that are not on by default are implicitly in the
+ // "pedantic" group. For those that aren't explicitly included in -Wpedantic,
+ // mark them for consideration to be included in -Wpedantic directly.
+ for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
+ Record *R = Diags[i];
+ if (isExtension(R) && isOffByDefault(R)) {
+ DiagsSet.insert(R);
+ if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group"))) {
+ const Record *GroupRec = Group->getDef();
+ if (!isSubGroupOfGroup(GroupRec, "pedantic")) {
+ markGroup(GroupRec);
+ }
+ }
+ }
+ }
+
+ // Compute the set of diagnostics that are directly in -Wpedantic. We
+ // march through Diags a second time to ensure the results are emitted
+ // in deterministic order.
+ for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
+ Record *R = Diags[i];
+ if (!DiagsSet.count(R))
+ continue;
+ // Check if the group is implicitly in -Wpedantic. If so,
+ // the diagnostic should not be directly included in the -Wpedantic
+ // diagnostic group.
+ if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group")))
+ if (groupInPedantic(Group->getDef()))
+ continue;
+
+ // The diagnostic is not included in a group that is (transitively) in
+ // -Wpedantic. Include it in -Wpedantic directly.
+ if (RecordVec *V = DiagsInPedantic.dyn_cast<RecordVec*>())
+ V->push_back(R);
+ else {
+ DiagsInPedantic.get<RecordSet*>()->insert(R);
+ }
+ }
+
+ if (!GroupsInPedantic)
+ return;
+
+ // Compute the set of groups that are directly in -Wpedantic. We
+ // march through the groups to ensure the results are emitted
+ /// in a deterministc order.
+ for (unsigned i = 0, ei = DiagGroups.size(); i != ei; ++i) {
+ Record *Group = DiagGroups[i];
+ if (!groupInPedantic(Group))
+ continue;
+
+ unsigned ParentsInPedantic = 0;
+ const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
+ for (unsigned j = 0, ej = Parents.size(); j != ej; ++j) {
+ if (groupInPedantic(Parents[j]))
+ ++ParentsInPedantic;
+ }
+ // If all the parents are in -Wpedantic, this means that this diagnostic
+ // group will be indirectly included by -Wpedantic already. In that
+ // case, do not add it directly to -Wpedantic. If the group has no
+ // parents, obviously it should go into -Wpedantic.
+ if (Parents.size() > 0 && ParentsInPedantic == Parents.size())
+ continue;
+
+ if (RecordVec *V = GroupsInPedantic.dyn_cast<RecordVec*>())
+ V->push_back(Group);
+ else {
+ GroupsInPedantic.get<RecordSet*>()->insert(Group);
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
// Warning Tables (.inc file) generation.
//===----------------------------------------------------------------------===//
-void ClangDiagsDefsEmitter::run(raw_ostream &OS) {
+static bool isError(const Record &Diag) {
+ const std::string &ClsName = Diag.getValueAsDef("Class")->getName();
+ return ClsName == "CLASS_ERROR";
+}
+
+/// ClangDiagsDefsEmitter - The top-level class emits .def files containing
+/// declarations of Clang diagnostics.
+namespace clang {
+void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
+ const std::string &Component) {
// Write the #if guard
if (!Component.empty()) {
std::string ComponentName = StringRef(Component).upper();
@@ -184,8 +380,25 @@ void ClangDiagsDefsEmitter::run(raw_ostream &OS) {
DiagCategoryIDMap CategoryIDs(Records);
DiagGroupParentMap DGParentMap(Records);
+ // Compute the set of diagnostics that are in -Wpedantic.
+ RecordSet DiagsInPedantic;
+ InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
+ inferPedantic.compute(&DiagsInPedantic, (RecordVec*)0);
+
for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
const Record &R = *Diags[i];
+
+ // Check if this is an error that is accidentally in a warning
+ // group.
+ if (isError(R)) {
+ if (DefInit *Group = dynamic_cast<DefInit*>(R.getValueInit("Group"))) {
+ const Record *GroupRec = Group->getDef();
+ const std::string &GroupName = GroupRec->getValueAsString("GroupName");
+ throw "Error " + R.getName() + " cannot be in a warning group [" +
+ GroupName + "]";
+ }
+ }
+
// Filter by component.
if (!Component.empty() && Component != R.getValueAsString("Component"))
continue;
@@ -205,6 +418,11 @@ void ClangDiagsDefsEmitter::run(raw_ostream &OS) {
DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName"));
assert(I != DiagsInGroup.end());
OS << ", " << I->second.IDNo;
+ } else if (DiagsInPedantic.count(&R)) {
+ std::map<std::string, GroupInfo>::iterator I =
+ DiagsInGroup.find("pedantic");
+ assert(I != DiagsInGroup.end() && "pedantic group not defined");
+ OS << ", " << I->second.IDNo;
} else {
OS << ", 0";
}
@@ -242,6 +460,7 @@ void ClangDiagsDefsEmitter::run(raw_ostream &OS) {
OS << ")\n";
}
}
+} // end namespace clang
//===----------------------------------------------------------------------===//
// Warning Group Tables generation
@@ -255,11 +474,12 @@ static std::string getDiagCategoryEnum(llvm::StringRef name) {
enumName += isalnum(*I) ? *I : '_';
return enumName.str();
}
-
-void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
+
+namespace clang {
+void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
// Compute a mapping from a DiagGroup to all of its parents.
DiagGroupParentMap DGParentMap(Records);
-
+
std::vector<Record*> Diags =
Records.getAllDerivedDefinitions("Diagnostic");
@@ -268,7 +488,15 @@ void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
std::map<std::string, GroupInfo> DiagsInGroup;
groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
-
+
+ // All extensions are implicitly in the "pedantic" group. Record the
+ // implicit set of groups in the "pedantic" group, and use this information
+ // later when emitting the group information for Pedantic.
+ RecordVec DiagsInPedantic;
+ RecordVec GroupsInPedantic;
+ InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
+ inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
+
// Walk through the groups emitting an array for each diagnostic of the diags
// that are mapped to.
OS << "\n#ifdef GET_DIAG_ARRAYS\n";
@@ -276,17 +504,23 @@ void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
for (std::map<std::string, GroupInfo>::iterator
I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) {
MaxLen = std::max(MaxLen, (unsigned)I->first.size());
-
+ const bool IsPedantic = I->first == "pedantic";
+
std::vector<const Record*> &V = I->second.DiagsInGroup;
- if (!V.empty()) {
+ if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
OS << "static const short DiagArray" << I->second.IDNo << "[] = { ";
for (unsigned i = 0, e = V.size(); i != e; ++i)
OS << "diag::" << V[i]->getName() << ", ";
+ // Emit the diagnostics implicitly in "pedantic".
+ if (IsPedantic) {
+ for (unsigned i = 0, e = DiagsInPedantic.size(); i != e; ++i)
+ OS << "diag::" << DiagsInPedantic[i]->getName() << ", ";
+ }
OS << "-1 };\n";
}
const std::vector<std::string> &SubGroups = I->second.SubGroups;
- if (!SubGroups.empty()) {
+ if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
OS << "static const short DiagSubGroup" << I->second.IDNo << "[] = { ";
for (unsigned i = 0, e = SubGroups.size(); i != e; ++i) {
std::map<std::string, GroupInfo>::iterator RI =
@@ -294,6 +528,18 @@ void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
assert(RI != DiagsInGroup.end() && "Referenced without existing?");
OS << RI->second.IDNo << ", ";
}
+ // Emit the groups implicitly in "pedantic".
+ if (IsPedantic) {
+ for (unsigned i = 0, e = GroupsInPedantic.size(); i != e; ++i) {
+ const std::string &GroupName =
+ GroupsInPedantic[i]->getValueAsString("GroupName");
+ std::map<std::string, GroupInfo>::iterator RI =
+ DiagsInGroup.find(GroupName);
+ assert(RI != DiagsInGroup.end() && "Referenced without existing?");
+ OS << RI->second.IDNo << ", ";
+ }
+ }
+
OS << "-1 };\n";
}
}
@@ -313,15 +559,22 @@ void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
throw "Invalid character in diagnostic group '" + I->first + "'";
OS.write_escaped(I->first) << "\","
<< std::string(MaxLen-I->first.size()+1, ' ');
-
+
+ // Special handling for 'pedantic'.
+ const bool IsPedantic = I->first == "pedantic";
+
// Diagnostics in the group.
- if (I->second.DiagsInGroup.empty())
+ const bool hasDiags = !I->second.DiagsInGroup.empty() ||
+ (IsPedantic && !DiagsInPedantic.empty());
+ if (!hasDiags)
OS << "0, ";
else
OS << "DiagArray" << I->second.IDNo << ", ";
// Subgroups.
- if (I->second.SubGroups.empty())
+ const bool hasSubGroups = !I->second.SubGroups.empty() ||
+ (IsPedantic && !GroupsInPedantic.empty());
+ if (!hasSubGroups)
OS << 0;
else
OS << "DiagSubGroup" << I->second.IDNo;
@@ -337,6 +590,7 @@ void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
OS << "CATEGORY(\"" << *I << "\", " << getDiagCategoryEnum(*I) << ")\n";
OS << "#endif // GET_CATEGORY_TABLE\n\n";
}
+} // end namespace clang
//===----------------------------------------------------------------------===//
// Diagnostic name index generation
@@ -364,7 +618,8 @@ struct RecordIndexElementSorter :
} // end anonymous namespace.
-void ClangDiagsIndexNameEmitter::run(raw_ostream &OS) {
+namespace clang {
+void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
const std::vector<Record*> &Diags =
Records.getAllDerivedDefinitions("Diagnostic");
@@ -383,3 +638,4 @@ void ClangDiagsIndexNameEmitter::run(raw_ostream &OS) {
OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
}
}
+} // end namespace clang
diff --git a/utils/TableGen/ClangDiagnosticsEmitter.h b/utils/TableGen/ClangDiagnosticsEmitter.h
deleted file mode 100644
index 73d3c4d..0000000
--- a/utils/TableGen/ClangDiagnosticsEmitter.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//===- ClangDiagnosticsEmitter.h - Generate Clang diagnostics tables -*- C++ -*-
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// These tablegen backends emit Clang diagnostics tables.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANGDIAGS_EMITTER_H
-#define CLANGDIAGS_EMITTER_H
-
-#include "llvm/TableGen/TableGenBackend.h"
-
-namespace llvm {
-
-/// ClangDiagsDefsEmitter - The top-level class emits .def files containing
-/// declarations of Clang diagnostics.
-///
-class ClangDiagsDefsEmitter : public TableGenBackend {
- RecordKeeper &Records;
- const std::string& Component;
-public:
- explicit ClangDiagsDefsEmitter(RecordKeeper &R, const std::string& component)
- : Records(R), Component(component) {}
-
- // run - Output the .def file contents
- void run(raw_ostream &OS);
-};
-
-class ClangDiagGroupsEmitter : public TableGenBackend {
- RecordKeeper &Records;
-public:
- explicit ClangDiagGroupsEmitter(RecordKeeper &R) : Records(R) {}
-
- void run(raw_ostream &OS);
-};
-
-class ClangDiagsIndexNameEmitter : public TableGenBackend {
- RecordKeeper &Records;
-public:
- explicit ClangDiagsIndexNameEmitter(RecordKeeper &R) : Records(R) {}
-
- void run(raw_ostream &OS);
-};
-
-
-} // End llvm namespace
-
-#endif
diff --git a/utils/TableGen/ClangSACheckersEmitter.cpp b/utils/TableGen/ClangSACheckersEmitter.cpp
index 423b68a..5a0db50 100644
--- a/utils/TableGen/ClangSACheckersEmitter.cpp
+++ b/utils/TableGen/ClangSACheckersEmitter.cpp
@@ -11,9 +11,9 @@
//
//===----------------------------------------------------------------------===//
-#include "ClangSACheckersEmitter.h"
-#include "llvm/TableGen/Record.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
#include <map>
#include <string>
using namespace llvm;
@@ -93,7 +93,8 @@ static void addPackageToCheckerGroup(const Record *package, const Record *group,
addPackageToCheckerGroup(*I, group, recordGroupMap);
}
-void ClangSACheckersEmitter::run(raw_ostream &OS) {
+namespace clang {
+void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");
llvm::DenseMap<const Record *, unsigned> checkerRecIndexMap;
for (unsigned i = 0, e = checkers.size(); i != e; ++i)
@@ -317,3 +318,4 @@ void ClangSACheckersEmitter::run(raw_ostream &OS) {
}
OS << "#endif // GET_CHECKNAME_TABLE\n\n";
}
+} // end namespace clang
diff --git a/utils/TableGen/ClangSACheckersEmitter.h b/utils/TableGen/ClangSACheckersEmitter.h
deleted file mode 100644
index 5a0e148..0000000
--- a/utils/TableGen/ClangSACheckersEmitter.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//===- ClangSACheckersEmitter.h - Generate Clang SA checkers tables -*- C++ -*-
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This tablegen backend emits Clang Static Analyzer checkers tables.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANGSACHECKERS_EMITTER_H
-#define CLANGSACHECKERS_EMITTER_H
-
-#include "llvm/TableGen/TableGenBackend.h"
-
-namespace llvm {
-
-class ClangSACheckersEmitter : public TableGenBackend {
- RecordKeeper &Records;
-public:
- explicit ClangSACheckersEmitter(RecordKeeper &R) : Records(R) {}
-
- void run(raw_ostream &OS);
-};
-
-} // End llvm namespace
-
-#endif
diff --git a/utils/TableGen/NeonEmitter.cpp b/utils/TableGen/NeonEmitter.cpp
index e6f2e53..6837306 100644
--- a/utils/TableGen/NeonEmitter.cpp
+++ b/utils/TableGen/NeonEmitter.cpp
@@ -23,16 +23,206 @@
//
//===----------------------------------------------------------------------===//
-#include "NeonEmitter.h"
-#include "llvm/TableGen/Error.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
#include <string>
-
using namespace llvm;
+enum OpKind {
+ OpNone,
+ OpUnavailable,
+ OpAdd,
+ OpAddl,
+ OpAddw,
+ OpSub,
+ OpSubl,
+ OpSubw,
+ OpMul,
+ OpMla,
+ OpMlal,
+ OpMls,
+ OpMlsl,
+ OpMulN,
+ OpMlaN,
+ OpMlsN,
+ OpMlalN,
+ OpMlslN,
+ OpMulLane,
+ OpMullLane,
+ OpMlaLane,
+ OpMlsLane,
+ OpMlalLane,
+ OpMlslLane,
+ OpQDMullLane,
+ OpQDMlalLane,
+ OpQDMlslLane,
+ OpQDMulhLane,
+ OpQRDMulhLane,
+ OpEq,
+ OpGe,
+ OpLe,
+ OpGt,
+ OpLt,
+ OpNeg,
+ OpNot,
+ OpAnd,
+ OpOr,
+ OpXor,
+ OpAndNot,
+ OpOrNot,
+ OpCast,
+ OpConcat,
+ OpDup,
+ OpDupLane,
+ OpHi,
+ OpLo,
+ OpSelect,
+ OpRev16,
+ OpRev32,
+ OpRev64,
+ OpReinterpret,
+ OpAbdl,
+ OpAba,
+ OpAbal
+};
+
+enum ClassKind {
+ ClassNone,
+ ClassI, // generic integer instruction, e.g., "i8" suffix
+ ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix
+ ClassW, // width-specific instruction, e.g., "8" suffix
+ ClassB // bitcast arguments with enum argument to specify type
+};
+
+/// NeonTypeFlags - Flags to identify the types for overloaded Neon
+/// builtins. These must be kept in sync with the flags in
+/// include/clang/Basic/TargetBuiltins.h.
+namespace {
+class NeonTypeFlags {
+ enum {
+ EltTypeMask = 0xf,
+ UnsignedFlag = 0x10,
+ QuadFlag = 0x20
+ };
+ uint32_t Flags;
+
+public:
+ enum EltType {
+ Int8,
+ Int16,
+ Int32,
+ Int64,
+ Poly8,
+ Poly16,
+ Float16,
+ Float32
+ };
+
+ NeonTypeFlags(unsigned F) : Flags(F) {}
+ NeonTypeFlags(EltType ET, bool IsUnsigned, bool IsQuad) : Flags(ET) {
+ if (IsUnsigned)
+ Flags |= UnsignedFlag;
+ if (IsQuad)
+ Flags |= QuadFlag;
+ }
+
+ uint32_t getFlags() const { return Flags; }
+};
+} // end anonymous namespace
+
+namespace {
+class NeonEmitter {
+ RecordKeeper &Records;
+ StringMap<OpKind> OpMap;
+ DenseMap<Record*, ClassKind> ClassMap;
+
+public:
+ NeonEmitter(RecordKeeper &R) : Records(R) {
+ OpMap["OP_NONE"] = OpNone;
+ OpMap["OP_UNAVAILABLE"] = OpUnavailable;
+ OpMap["OP_ADD"] = OpAdd;
+ OpMap["OP_ADDL"] = OpAddl;
+ OpMap["OP_ADDW"] = OpAddw;
+ OpMap["OP_SUB"] = OpSub;
+ OpMap["OP_SUBL"] = OpSubl;
+ OpMap["OP_SUBW"] = OpSubw;
+ OpMap["OP_MUL"] = OpMul;
+ OpMap["OP_MLA"] = OpMla;
+ OpMap["OP_MLAL"] = OpMlal;
+ OpMap["OP_MLS"] = OpMls;
+ OpMap["OP_MLSL"] = OpMlsl;
+ OpMap["OP_MUL_N"] = OpMulN;
+ OpMap["OP_MLA_N"] = OpMlaN;
+ OpMap["OP_MLS_N"] = OpMlsN;
+ OpMap["OP_MLAL_N"] = OpMlalN;
+ OpMap["OP_MLSL_N"] = OpMlslN;
+ OpMap["OP_MUL_LN"]= OpMulLane;
+ OpMap["OP_MULL_LN"] = OpMullLane;
+ OpMap["OP_MLA_LN"]= OpMlaLane;
+ OpMap["OP_MLS_LN"]= OpMlsLane;
+ OpMap["OP_MLAL_LN"] = OpMlalLane;
+ OpMap["OP_MLSL_LN"] = OpMlslLane;
+ OpMap["OP_QDMULL_LN"] = OpQDMullLane;
+ OpMap["OP_QDMLAL_LN"] = OpQDMlalLane;
+ OpMap["OP_QDMLSL_LN"] = OpQDMlslLane;
+ OpMap["OP_QDMULH_LN"] = OpQDMulhLane;
+ OpMap["OP_QRDMULH_LN"] = OpQRDMulhLane;
+ OpMap["OP_EQ"] = OpEq;
+ OpMap["OP_GE"] = OpGe;
+ OpMap["OP_LE"] = OpLe;
+ OpMap["OP_GT"] = OpGt;
+ OpMap["OP_LT"] = OpLt;
+ OpMap["OP_NEG"] = OpNeg;
+ OpMap["OP_NOT"] = OpNot;
+ OpMap["OP_AND"] = OpAnd;
+ OpMap["OP_OR"] = OpOr;
+ OpMap["OP_XOR"] = OpXor;
+ OpMap["OP_ANDN"] = OpAndNot;
+ OpMap["OP_ORN"] = OpOrNot;
+ OpMap["OP_CAST"] = OpCast;
+ OpMap["OP_CONC"] = OpConcat;
+ OpMap["OP_HI"] = OpHi;
+ OpMap["OP_LO"] = OpLo;
+ OpMap["OP_DUP"] = OpDup;
+ OpMap["OP_DUP_LN"] = OpDupLane;
+ OpMap["OP_SEL"] = OpSelect;
+ OpMap["OP_REV16"] = OpRev16;
+ OpMap["OP_REV32"] = OpRev32;
+ OpMap["OP_REV64"] = OpRev64;
+ OpMap["OP_REINT"] = OpReinterpret;
+ OpMap["OP_ABDL"] = OpAbdl;
+ OpMap["OP_ABA"] = OpAba;
+ OpMap["OP_ABAL"] = OpAbal;
+
+ Record *SI = R.getClass("SInst");
+ Record *II = R.getClass("IInst");
+ Record *WI = R.getClass("WInst");
+ ClassMap[SI] = ClassS;
+ ClassMap[II] = ClassI;
+ ClassMap[WI] = ClassW;
+ }
+
+ // run - Emit arm_neon.h.inc
+ void run(raw_ostream &o);
+
+ // runHeader - Emit all the __builtin prototypes used in arm_neon.h
+ void runHeader(raw_ostream &o);
+
+ // runTests - Emit tests for all the Neon intrinsics.
+ void runTests(raw_ostream &o);
+
+private:
+ void emitIntrinsic(raw_ostream &OS, Record *R);
+};
+} // end anonymous namespace
+
/// ParseTypes - break down a string such as "fQf" into a vector of StringRefs,
/// which each StringRef representing a single type declared in the string.
/// for "fQf" we would end up with 2 StringRefs, "f", and "Qf", representing
@@ -1012,7 +1202,7 @@ static std::string GenIntrinsic(const std::string &name,
StringRef outTypeStr, StringRef inTypeStr,
OpKind kind, ClassKind classKind) {
assert(!proto.empty() && "");
- bool define = UseMacro(proto);
+ bool define = UseMacro(proto) && kind != OpUnavailable;
std::string s;
// static always inline + return type
@@ -1040,9 +1230,11 @@ static std::string GenIntrinsic(const std::string &name,
if (define) {
s += " __extension__ ({ \\\n ";
s += GenMacroLocals(proto, inTypeStr);
- } else {
- s += " { \\\n ";
- }
+ } else if (kind == OpUnavailable) {
+ s += " __attribute__((unavailable));\n";
+ return s;
+ } else
+ s += " {\n ";
if (kind != OpNone)
s += GenOpString(kind, proto, outTypeStr);
@@ -1238,7 +1430,7 @@ static unsigned RangeFromType(const char mod, StringRef typestr) {
/// runHeader - Emit a file with sections defining:
/// 1. the NEON section of BuiltinsARM.def.
/// 2. the SemaChecking code for the type overload checking.
-/// 3. the SemaChecking code for validation of intrinsic immedate arguments.
+/// 3. the SemaChecking code for validation of intrinsic immediate arguments.
void NeonEmitter::runHeader(raw_ostream &OS) {
std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst");
@@ -1312,7 +1504,7 @@ void NeonEmitter::runHeader(raw_ostream &OS) {
throw TGError(R->getLoc(), "Builtin has no class kind");
int si = -1, qi = -1;
- unsigned mask = 0, qmask = 0;
+ uint64_t mask = 0, qmask = 0;
for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) {
// Generate the switch case(s) for this builtin for the type validation.
bool quad = false, poly = false, usgn = false;
@@ -1320,10 +1512,10 @@ void NeonEmitter::runHeader(raw_ostream &OS) {
if (quad) {
qi = ti;
- qmask |= 1 << GetNeonEnum(Proto, TypeVec[ti]);
+ qmask |= 1ULL << GetNeonEnum(Proto, TypeVec[ti]);
} else {
si = ti;
- mask |= 1 << GetNeonEnum(Proto, TypeVec[ti]);
+ mask |= 1ULL << GetNeonEnum(Proto, TypeVec[ti]);
}
}
@@ -1360,7 +1552,7 @@ void NeonEmitter::runHeader(raw_ostream &OS) {
if (mask) {
OS << "case ARM::BI__builtin_neon_"
<< MangleName(name, TypeVec[si], ClassB)
- << ": mask = " << "0x" << utohexstr(mask);
+ << ": mask = " << "0x" << utohexstr(mask) << "ULL";
if (PtrArgNum >= 0)
OS << "; PtrArgNum = " << PtrArgNum;
if (HasConstPtr)
@@ -1370,7 +1562,7 @@ void NeonEmitter::runHeader(raw_ostream &OS) {
if (qmask) {
OS << "case ARM::BI__builtin_neon_"
<< MangleName(name, TypeVec[qi], ClassB)
- << ": mask = " << "0x" << utohexstr(qmask);
+ << ": mask = " << "0x" << utohexstr(qmask) << "ULL";
if (PtrArgNum >= 0)
OS << "; PtrArgNum = " << PtrArgNum;
if (HasConstPtr)
@@ -1505,7 +1697,7 @@ static std::string GenTest(const std::string &name,
s.push_back(arg);
comma = ", ";
}
- s += ") { \\\n ";
+ s += ") {\n ";
if (proto[0] != 'v')
s += "return ";
@@ -1551,6 +1743,8 @@ void NeonEmitter::runTests(raw_ostream &OS) {
ParseTypes(R, Types, TypeVec);
OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()];
+ if (kind == OpUnavailable)
+ continue;
for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) {
if (kind == OpReinterpret) {
bool outQuad = false;
@@ -1572,3 +1766,14 @@ void NeonEmitter::runTests(raw_ostream &OS) {
}
}
+namespace clang {
+void EmitNeon(RecordKeeper &Records, raw_ostream &OS) {
+ NeonEmitter(Records).run(OS);
+}
+void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) {
+ NeonEmitter(Records).runHeader(OS);
+}
+void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) {
+ NeonEmitter(Records).runTests(OS);
+}
+} // End namespace clang
diff --git a/utils/TableGen/NeonEmitter.h b/utils/TableGen/NeonEmitter.h
deleted file mode 100644
index dec7451..0000000
--- a/utils/TableGen/NeonEmitter.h
+++ /dev/null
@@ -1,210 +0,0 @@
-//===- NeonEmitter.h - Generate arm_neon.h for use with clang ---*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This tablegen backend is responsible for emitting arm_neon.h, which includes
-// a declaration and definition of each function specified by the ARM NEON
-// compiler interface. See ARM document DUI0348B.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef NEON_EMITTER_H
-#define NEON_EMITTER_H
-
-#include "llvm/TableGen/Record.h"
-#include "llvm/TableGen/TableGenBackend.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringMap.h"
-
-enum OpKind {
- OpNone,
- OpAdd,
- OpAddl,
- OpAddw,
- OpSub,
- OpSubl,
- OpSubw,
- OpMul,
- OpMla,
- OpMlal,
- OpMls,
- OpMlsl,
- OpMulN,
- OpMlaN,
- OpMlsN,
- OpMlalN,
- OpMlslN,
- OpMulLane,
- OpMullLane,
- OpMlaLane,
- OpMlsLane,
- OpMlalLane,
- OpMlslLane,
- OpQDMullLane,
- OpQDMlalLane,
- OpQDMlslLane,
- OpQDMulhLane,
- OpQRDMulhLane,
- OpEq,
- OpGe,
- OpLe,
- OpGt,
- OpLt,
- OpNeg,
- OpNot,
- OpAnd,
- OpOr,
- OpXor,
- OpAndNot,
- OpOrNot,
- OpCast,
- OpConcat,
- OpDup,
- OpDupLane,
- OpHi,
- OpLo,
- OpSelect,
- OpRev16,
- OpRev32,
- OpRev64,
- OpReinterpret,
- OpAbdl,
- OpAba,
- OpAbal
-};
-
-enum ClassKind {
- ClassNone,
- ClassI, // generic integer instruction, e.g., "i8" suffix
- ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix
- ClassW, // width-specific instruction, e.g., "8" suffix
- ClassB // bitcast arguments with enum argument to specify type
-};
-
-/// NeonTypeFlags - Flags to identify the types for overloaded Neon
-/// builtins. These must be kept in sync with the flags in
-/// include/clang/Basic/TargetBuiltins.h.
-class NeonTypeFlags {
- enum {
- EltTypeMask = 0xf,
- UnsignedFlag = 0x10,
- QuadFlag = 0x20
- };
- uint32_t Flags;
-
-public:
- enum EltType {
- Int8,
- Int16,
- Int32,
- Int64,
- Poly8,
- Poly16,
- Float16,
- Float32
- };
-
- NeonTypeFlags(unsigned F) : Flags(F) {}
- NeonTypeFlags(EltType ET, bool IsUnsigned, bool IsQuad) : Flags(ET) {
- if (IsUnsigned)
- Flags |= UnsignedFlag;
- if (IsQuad)
- Flags |= QuadFlag;
- }
-
- uint32_t getFlags() const { return Flags; }
-};
-
-namespace llvm {
-
- class NeonEmitter : public TableGenBackend {
- RecordKeeper &Records;
- StringMap<OpKind> OpMap;
- DenseMap<Record*, ClassKind> ClassMap;
-
- public:
- NeonEmitter(RecordKeeper &R) : Records(R) {
- OpMap["OP_NONE"] = OpNone;
- OpMap["OP_ADD"] = OpAdd;
- OpMap["OP_ADDL"] = OpAddl;
- OpMap["OP_ADDW"] = OpAddw;
- OpMap["OP_SUB"] = OpSub;
- OpMap["OP_SUBL"] = OpSubl;
- OpMap["OP_SUBW"] = OpSubw;
- OpMap["OP_MUL"] = OpMul;
- OpMap["OP_MLA"] = OpMla;
- OpMap["OP_MLAL"] = OpMlal;
- OpMap["OP_MLS"] = OpMls;
- OpMap["OP_MLSL"] = OpMlsl;
- OpMap["OP_MUL_N"] = OpMulN;
- OpMap["OP_MLA_N"] = OpMlaN;
- OpMap["OP_MLS_N"] = OpMlsN;
- OpMap["OP_MLAL_N"] = OpMlalN;
- OpMap["OP_MLSL_N"] = OpMlslN;
- OpMap["OP_MUL_LN"]= OpMulLane;
- OpMap["OP_MULL_LN"] = OpMullLane;
- OpMap["OP_MLA_LN"]= OpMlaLane;
- OpMap["OP_MLS_LN"]= OpMlsLane;
- OpMap["OP_MLAL_LN"] = OpMlalLane;
- OpMap["OP_MLSL_LN"] = OpMlslLane;
- OpMap["OP_QDMULL_LN"] = OpQDMullLane;
- OpMap["OP_QDMLAL_LN"] = OpQDMlalLane;
- OpMap["OP_QDMLSL_LN"] = OpQDMlslLane;
- OpMap["OP_QDMULH_LN"] = OpQDMulhLane;
- OpMap["OP_QRDMULH_LN"] = OpQRDMulhLane;
- OpMap["OP_EQ"] = OpEq;
- OpMap["OP_GE"] = OpGe;
- OpMap["OP_LE"] = OpLe;
- OpMap["OP_GT"] = OpGt;
- OpMap["OP_LT"] = OpLt;
- OpMap["OP_NEG"] = OpNeg;
- OpMap["OP_NOT"] = OpNot;
- OpMap["OP_AND"] = OpAnd;
- OpMap["OP_OR"] = OpOr;
- OpMap["OP_XOR"] = OpXor;
- OpMap["OP_ANDN"] = OpAndNot;
- OpMap["OP_ORN"] = OpOrNot;
- OpMap["OP_CAST"] = OpCast;
- OpMap["OP_CONC"] = OpConcat;
- OpMap["OP_HI"] = OpHi;
- OpMap["OP_LO"] = OpLo;
- OpMap["OP_DUP"] = OpDup;
- OpMap["OP_DUP_LN"] = OpDupLane;
- OpMap["OP_SEL"] = OpSelect;
- OpMap["OP_REV16"] = OpRev16;
- OpMap["OP_REV32"] = OpRev32;
- OpMap["OP_REV64"] = OpRev64;
- OpMap["OP_REINT"] = OpReinterpret;
- OpMap["OP_ABDL"] = OpAbdl;
- OpMap["OP_ABA"] = OpAba;
- OpMap["OP_ABAL"] = OpAbal;
-
- Record *SI = R.getClass("SInst");
- Record *II = R.getClass("IInst");
- Record *WI = R.getClass("WInst");
- ClassMap[SI] = ClassS;
- ClassMap[II] = ClassI;
- ClassMap[WI] = ClassW;
- }
-
- // run - Emit arm_neon.h.inc
- void run(raw_ostream &o);
-
- // runHeader - Emit all the __builtin prototypes used in arm_neon.h
- void runHeader(raw_ostream &o);
-
- // runTests - Emit tests for all the Neon intrinsics.
- void runTests(raw_ostream &o);
-
- private:
- void emitIntrinsic(raw_ostream &OS, Record *R);
- };
-
-} // End llvm namespace
-
-#endif
diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp
index dea22d3..b0431a9 100644
--- a/utils/TableGen/OptParserEmitter.cpp
+++ b/utils/TableGen/OptParserEmitter.cpp
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#include "OptParserEmitter.h"
#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;
@@ -69,16 +69,20 @@ static raw_ostream &write_cstring(raw_ostream &OS, llvm::StringRef Str) {
return OS;
}
-void OptParserEmitter::run(raw_ostream &OS) {
+/// OptParserEmitter - This tablegen backend takes an input .td file
+/// describing a list of options and emits a data structure for parsing and
+/// working with those options when given an input command line.
+namespace clang {
+void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs) {
// Get the option groups and options.
const std::vector<Record*> &Groups =
Records.getAllDerivedDefinitions("OptionGroup");
std::vector<Record*> Opts = Records.getAllDerivedDefinitions("Option");
if (GenDefs)
- EmitSourceFileHeader("Option Parsing Definitions", OS);
+ emitSourceFileHeader("Option Parsing Definitions", OS);
else
- EmitSourceFileHeader("Option Parsing Table", OS);
+ emitSourceFileHeader("Option Parsing Table", OS);
array_pod_sort(Opts.begin(), Opts.end(), CompareOptionRecords);
if (GenDefs) {
@@ -192,3 +196,4 @@ void OptParserEmitter::run(raw_ostream &OS) {
}
}
}
+} // end namespace clang
diff --git a/utils/TableGen/OptParserEmitter.h b/utils/TableGen/OptParserEmitter.h
deleted file mode 100644
index ca667ca..0000000
--- a/utils/TableGen/OptParserEmitter.h
+++ /dev/null
@@ -1,34 +0,0 @@
-//===- OptParserEmitter.h - Table Driven Command Line Parsing ---*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef UTILS_TABLEGEN_OPTPARSEREMITTER_H
-#define UTILS_TABLEGEN_OPTPARSEREMITTER_H
-
-#include "llvm/TableGen/TableGenBackend.h"
-
-namespace llvm {
- /// OptParserEmitter - This tablegen backend takes an input .td file
- /// describing a list of options and emits a data structure for parsing and
- /// working with those options when given an input command line.
- class OptParserEmitter : public TableGenBackend {
- RecordKeeper &Records;
- bool GenDefs;
-
- public:
- OptParserEmitter(RecordKeeper &R, bool _GenDefs)
- : Records(R), GenDefs(_GenDefs) {}
-
- /// run - Output the option parsing information.
- ///
- /// \param GenHeader - Generate the header describing the option IDs.x
- void run(raw_ostream &OS);
- };
-}
-
-#endif
diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp
index 5ff88db..d3408ed 100644
--- a/utils/TableGen/TableGen.cpp
+++ b/utils/TableGen/TableGen.cpp
@@ -11,12 +11,7 @@
//
//===----------------------------------------------------------------------===//
-#include "ClangASTNodesEmitter.h"
-#include "ClangAttrEmitter.h"
-#include "ClangDiagnosticsEmitter.h"
-#include "ClangSACheckersEmitter.h"
-#include "NeonEmitter.h"
-#include "OptParserEmitter.h"
+#include "TableGenBackends.h" // Declares all backends.
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/PrettyStackTrace.h"
@@ -27,6 +22,7 @@
#include "llvm/TableGen/TableGenAction.h"
using namespace llvm;
+using namespace clang;
enum ActionType {
GenClangAttrClasses,
@@ -42,6 +38,7 @@ enum ActionType {
GenClangDiagsDefs,
GenClangDiagGroups,
GenClangDiagsIndexName,
+ GenClangCommentNodes,
GenClangDeclNodes,
GenClangStmtNodes,
GenClangSACheckers,
@@ -90,6 +87,8 @@ namespace {
clEnumValN(GenClangDiagsIndexName,
"gen-clang-diags-index-name",
"Generate Clang diagnostic name index"),
+ clEnumValN(GenClangCommentNodes, "gen-clang-comment-nodes",
+ "Generate Clang AST comment nodes"),
clEnumValN(GenClangDeclNodes, "gen-clang-decl-nodes",
"Generate Clang AST declaration nodes"),
clEnumValN(GenClangStmtNodes, "gen-clang-stmt-nodes",
@@ -114,68 +113,71 @@ public:
bool operator()(raw_ostream &OS, RecordKeeper &Records) {
switch (Action) {
case GenClangAttrClasses:
- ClangAttrClassEmitter(Records).run(OS);
+ EmitClangAttrClass(Records, OS);
break;
case GenClangAttrImpl:
- ClangAttrImplEmitter(Records).run(OS);
+ EmitClangAttrImpl(Records, OS);
break;
case GenClangAttrList:
- ClangAttrListEmitter(Records).run(OS);
+ EmitClangAttrList(Records, OS);
break;
case GenClangAttrPCHRead:
- ClangAttrPCHReadEmitter(Records).run(OS);
+ EmitClangAttrPCHRead(Records, OS);
break;
case GenClangAttrPCHWrite:
- ClangAttrPCHWriteEmitter(Records).run(OS);
+ EmitClangAttrPCHWrite(Records, OS);
break;
case GenClangAttrSpellingList:
- ClangAttrSpellingListEmitter(Records).run(OS);
+ EmitClangAttrSpellingList(Records, OS);
break;
case GenClangAttrLateParsedList:
- ClangAttrLateParsedListEmitter(Records).run(OS);
+ EmitClangAttrLateParsedList(Records, OS);
break;
case GenClangAttrTemplateInstantiate:
- ClangAttrTemplateInstantiateEmitter(Records).run(OS);
+ EmitClangAttrTemplateInstantiate(Records, OS);
break;
case GenClangAttrParsedAttrList:
- ClangAttrParsedAttrListEmitter(Records).run(OS);
+ EmitClangAttrParsedAttrList(Records, OS);
break;
case GenClangAttrParsedAttrKinds:
- ClangAttrParsedAttrKindsEmitter(Records).run(OS);
+ EmitClangAttrParsedAttrKinds(Records, OS);
break;
case GenClangDiagsDefs:
- ClangDiagsDefsEmitter(Records, ClangComponent).run(OS);
+ EmitClangDiagsDefs(Records, OS, ClangComponent);
break;
case GenClangDiagGroups:
- ClangDiagGroupsEmitter(Records).run(OS);
+ EmitClangDiagGroups(Records, OS);
break;
case GenClangDiagsIndexName:
- ClangDiagsIndexNameEmitter(Records).run(OS);
+ EmitClangDiagsIndexName(Records, OS);
+ break;
+ case GenClangCommentNodes:
+ EmitClangASTNodes(Records, OS, "Comment", "");
break;
case GenClangDeclNodes:
- ClangASTNodesEmitter(Records, "Decl", "Decl").run(OS);
- ClangDeclContextEmitter(Records).run(OS);
+ EmitClangASTNodes(Records, OS, "Decl", "Decl");
+ EmitClangDeclContext(Records, OS);
break;
case GenClangStmtNodes:
- ClangASTNodesEmitter(Records, "Stmt", "").run(OS);
+ EmitClangASTNodes(Records, OS, "Stmt", "");
break;
case GenClangSACheckers:
- ClangSACheckersEmitter(Records).run(OS);
+ EmitClangSACheckers(Records, OS);
break;
case GenOptParserDefs:
- OptParserEmitter(Records, true).run(OS);
+ EmitOptParser(Records, OS, true);
break;
case GenOptParserImpl:
- OptParserEmitter(Records, false).run(OS);
+ EmitOptParser(Records, OS, false);
break;
case GenArmNeon:
- NeonEmitter(Records).run(OS);
+ EmitNeon(Records, OS);
break;
case GenArmNeonSema:
- NeonEmitter(Records).runHeader(OS);
+ EmitNeonSema(Records, OS);
break;
case GenArmNeonTest:
- NeonEmitter(Records).runTests(OS);
+ EmitNeonTest(Records, OS);
break;
}
diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h
new file mode 100644
index 0000000..779de7c
--- /dev/null
+++ b/utils/TableGen/TableGenBackends.h
@@ -0,0 +1,56 @@
+//===- TableGenBackends.h - Declarations for Clang TableGen Backends ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the declarations for all of the Clang TableGen
+// backends. A "TableGen backend" is just a function. See
+// "$LLVM_ROOT/utils/TableGen/TableGenBackends.h" for more info.
+//
+//===----------------------------------------------------------------------===//
+
+#include <string>
+
+namespace llvm {
+ class raw_ostream;
+ class RecordKeeper;
+}
+
+using llvm::raw_ostream;
+using llvm::RecordKeeper;
+
+namespace clang {
+
+void EmitClangDeclContext(RecordKeeper &RK, raw_ostream &OS);
+void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS,
+ const std::string &N, const std::string &S);
+
+void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrSpellingList(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS);
+
+void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
+ const std::string &Component);
+void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS);
+
+void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS);
+
+void EmitNeon(RecordKeeper &Records, raw_ostream &OS);
+void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS);
+void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS);
+
+void EmitOptParser(RecordKeeper &Records, raw_ostream &OS, bool GenDefs);
+
+} // end namespace clang
diff --git a/utils/analyzer/CmpRuns.py b/utils/analyzer/CmpRuns.py
index e68c45df..f2961cf 100755
--- a/utils/analyzer/CmpRuns.py
+++ b/utils/analyzer/CmpRuns.py
@@ -11,12 +11,67 @@ two perspectives:
2. For use by end users who want to integrate regular static analyzer testing
into a buildbot like environment.
+
+Usage:
+
+ # Load the results of both runs, to obtain lists of the corresponding
+ # AnalysisDiagnostic objects.
+ #
+ # root - the name of the root directory, which will be disregarded when
+ # determining the source file name
+ #
+ resultsA = loadResults(dirA, opts, root, deleteEmpty)
+ resultsB = loadResults(dirB, opts, root, deleteEmpty)
+
+ # Generate a relation from diagnostics in run A to diagnostics in run B
+ # to obtain a list of triples (a, b, confidence).
+ diff = compareResults(resultsA, resultsB)
+
"""
import os
import plistlib
#
+class AnalysisDiagnostic:
+ def __init__(self, data, report, htmlReport):
+ self._data = data
+ self._loc = self._data['location']
+ self._report = report
+ self._htmlReport = htmlReport
+
+ def getFileName(self):
+ return self._report.run.getSourceName(self._report.files[self._loc['file']])
+
+ def getLine(self):
+ return self._loc['line']
+
+ def getColumn(self):
+ return self._loc['col']
+
+ def getCategory(self):
+ return self._data['category']
+
+ def getDescription(self):
+ return self._data['description']
+
+ def getIssueIdentifier(self) :
+ id = ''
+ if 'issue_context' in self._data :
+ id += self._data['issue_context'] + ":"
+ if 'issue_hash' in self._data :
+ id += str(self._data['issue_hash']) + ":"
+ return id + ":" + self.getFileName()
+
+ def getReport(self):
+ if self._htmlReport is None:
+ return " "
+ return os.path.join(self._report.run.path, self._htmlReport)
+
+ def getReadableName(self):
+ return '%s:%d:%d, %s: %s' % (self.getFileName(), self.getLine(),
+ self.getColumn(), self.getCategory(),
+ self.getDescription())
class multidict:
def __init__(self, elts=()):
@@ -45,8 +100,9 @@ class multidict:
#
class CmpOptions:
- def __init__(self, verboseLog=None, root=""):
- self.root = root
+ def __init__(self, verboseLog=None, rootA="", rootB=""):
+ self.rootA = rootA
+ self.rootB = rootB
self.verboseLog = verboseLog
class AnalysisReport:
@@ -54,49 +110,22 @@ class AnalysisReport:
self.run = run
self.files = files
-class AnalysisDiagnostic:
- def __init__(self, data, report, htmlReport):
- self.data = data
- self.report = report
- self.htmlReport = htmlReport
-
- def getReadableName(self):
- loc = self.data['location']
- filename = self.report.run.getSourceName(self.report.files[loc['file']])
- line = loc['line']
- column = loc['col']
- category = self.data['category']
- description = self.data['description']
-
- # FIXME: Get a report number based on this key, to 'distinguish'
- # reports, or something.
-
- return '%s:%d:%d, %s: %s' % (filename, line, column, category,
- description)
-
- def getReportData(self):
- if self.htmlReport is None:
- return " "
- return os.path.join(self.report.run.path, self.htmlReport)
- # We could also dump the report with:
- # return open(os.path.join(self.report.run.path,
- # self.htmlReport), "rb").read()
-
class AnalysisRun:
- def __init__(self, path, opts):
+ def __init__(self, path, root, opts):
self.path = path
+ self.root = root
self.reports = []
self.diagnostics = []
self.opts = opts
def getSourceName(self, path):
- if path.startswith(self.opts.root):
- return path[len(self.opts.root):]
+ if path.startswith(self.root):
+ return path[len(self.root):]
return path
-def loadResults(path, opts, deleteEmpty=True):
- run = AnalysisRun(path, opts)
-
+def loadResults(path, opts, root = "", deleteEmpty=True):
+ run = AnalysisRun(path, root, opts)
+
for f in os.listdir(path):
if (not f.startswith('report') or
not f.endswith('plist')):
@@ -134,6 +163,9 @@ def loadResults(path, opts, deleteEmpty=True):
return run
+def cmpAnalysisDiagnostic(d) :
+ return d.getIssueIdentifier()
+
def compareResults(A, B):
"""
compareResults - Generate a relation from diagnostics in run A to
@@ -152,14 +184,14 @@ def compareResults(A, B):
neqB = []
eltsA = list(A.diagnostics)
eltsB = list(B.diagnostics)
- eltsA.sort(key = lambda d: d.data)
- eltsB.sort(key = lambda d: d.data)
+ eltsA.sort(key = cmpAnalysisDiagnostic)
+ eltsB.sort(key = cmpAnalysisDiagnostic)
while eltsA and eltsB:
a = eltsA.pop()
b = eltsB.pop()
- if a.data['location'] == b.data['location']:
+ if (a.getIssueIdentifier() == b.getIssueIdentifier()) :
res.append((a, b, 0))
- elif a.data > b.data:
+ elif a._data > b._data:
neqA.append(a)
eltsB.append(b)
else:
@@ -181,10 +213,10 @@ def compareResults(A, B):
return res
-def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True):
+def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True):
# Load the run results.
- resultsA = loadResults(dirA, opts, deleteEmpty)
- resultsB = loadResults(dirB, opts, deleteEmpty)
+ resultsA = loadResults(dirA, opts, opts.rootA, deleteEmpty)
+ resultsB = loadResults(dirB, opts, opts.rootB, deleteEmpty)
# Open the verbose log, if given.
if opts.verboseLog:
@@ -201,13 +233,13 @@ def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True):
foundDiffs += 1
if auxLog:
print >>auxLog, ("('ADDED', %r, %r)" % (b.getReadableName(),
- b.getReportData()))
+ b.getReport()))
elif b is None:
print "REMOVED: %r" % a.getReadableName()
foundDiffs += 1
if auxLog:
print >>auxLog, ("('REMOVED', %r, %r)" % (a.getReadableName(),
- a.getReportData()))
+ a.getReport()))
elif confidence:
print "CHANGED: %r to %r" % (a.getReadableName(),
b.getReadableName())
@@ -216,8 +248,8 @@ def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True):
print >>auxLog, ("('CHANGED', %r, %r, %r, %r)"
% (a.getReadableName(),
b.getReadableName(),
- a.getReportData(),
- b.getReportData()))
+ a.getReport(),
+ b.getReport()))
else:
pass
@@ -233,8 +265,11 @@ def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True):
def main():
from optparse import OptionParser
parser = OptionParser("usage: %prog [options] [dir A] [dir B]")
- parser.add_option("", "--root", dest="root",
- help="Prefix to ignore on source files",
+ parser.add_option("", "--rootA", dest="rootA",
+ help="Prefix to ignore on source files for directory A",
+ action="store", type=str, default="")
+ parser.add_option("", "--rootB", dest="rootB",
+ help="Prefix to ignore on source files for directory B",
action="store", type=str, default="")
parser.add_option("", "--verbose-log", dest="verboseLog",
help="Write additional information to LOG [default=None]",
@@ -247,7 +282,7 @@ def main():
dirA,dirB = args
- cmpScanBuildResults(dirA, dirB, opts)
+ dumpScanBuildResultsDiff(dirA, dirB, opts)
if __name__ == '__main__':
main()
diff --git a/utils/analyzer/SATestAdd.py b/utils/analyzer/SATestAdd.py
index ce64bc8..2d32533 100644
--- a/utils/analyzer/SATestAdd.py
+++ b/utils/analyzer/SATestAdd.py
@@ -41,7 +41,7 @@ def addNewProject(ID, IsScanBuild) :
sys.exit(-1)
# Build the project.
- SATestBuild.testProject(ID, True, IsScanBuild, Dir)
+ SATestBuild.testProject(ID, IsScanBuild, IsReferenceBuild=True, Dir=Dir)
# Add the project ID to the project map.
ProjectMapPath = os.path.join(CurDir, SATestBuild.ProjectMapFile)
diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py
index 3fccb9a..fd4bc8a 100644
--- a/utils/analyzer/SATestBuild.py
+++ b/utils/analyzer/SATestBuild.py
@@ -72,12 +72,10 @@ SBOutputDirReferencePrefix = "Ref"
# The list of checkers used during analyzes.
# Currently, consists of all the non experimental checkers.
-Checkers="experimental.security.taint,core,deadcode,cplusplus,security,unix,osx,cocoa"
+Checkers="experimental.security.taint,core,deadcode,security,unix,osx"
Verbose = 1
-IsReferenceBuild = False
-
# Make sure we flush the output after every print statement.
class flushfile(object):
def __init__(self, f):
@@ -100,7 +98,7 @@ def getProjectMapPath():
def getProjectDir(ID):
return os.path.join(os.path.abspath(os.curdir), ID)
-def getSBOutputDirName() :
+def getSBOutputDirName(IsReferenceBuild) :
if IsReferenceBuild == True :
return SBOutputDirReferencePrefix + SBOutputDirName
else :
@@ -210,7 +208,7 @@ def runAnalyzePreprocessed(Dir, SBOutputDir):
if Failed == False:
os.remove(LogFile.name);
-def buildProject(Dir, SBOutputDir, IsScanBuild):
+def buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild):
TBegin = time.time()
BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName)
@@ -295,7 +293,7 @@ def checkBuild(SBOutputDir):
FailuresCopied = NumOfFailuresInSummary
Idx = 0
- for FailLogPathI in glob.glob(SBOutputDir + "/*/failures/*.stderr.txt"):
+ for FailLogPathI in Failures:
if Idx >= NumOfFailuresInSummary:
break;
Idx += 1
@@ -359,7 +357,7 @@ def runCmpResults(Dir):
OLD_STDOUT = sys.stdout
sys.stdout = Discarder()
# Scan the results, delete empty plist files.
- NumDiffs = CmpRuns.cmpScanBuildResults(RefDir, NewDir, Opts, False)
+ NumDiffs = CmpRuns.dumpScanBuildResultsDiff(RefDir, NewDir, Opts, False)
sys.stdout = OLD_STDOUT
if (NumDiffs > 0) :
print "Warning: %r differences in diagnostics. See %s" % \
@@ -373,7 +371,7 @@ def updateSVN(Mode, ProjectsMap):
ProjectsMap.seek(0)
for I in csv.reader(ProjectsMap):
ProjName = I[0]
- Path = os.path.join(ProjName, getSBOutputDirName())
+ Path = os.path.join(ProjName, getSBOutputDirName(True))
if Mode == "delete":
Command = "svn delete %s" % (Path,)
@@ -382,7 +380,7 @@ def updateSVN(Mode, ProjectsMap):
if Verbose == 1:
print " Executing: %s" % (Command,)
- check_call(Command, shell=True)
+ check_call(Command, shell=True)
if Mode == "delete":
CommitCommand = "svn commit -m \"[analyzer tests] Remove " \
@@ -392,12 +390,12 @@ def updateSVN(Mode, ProjectsMap):
"reference results.\""
if Verbose == 1:
print " Executing: %s" % (CommitCommand,)
- check_call(CommitCommand, shell=True)
+ check_call(CommitCommand, shell=True)
except:
print "Error: SVN update failed."
sys.exit(-1)
-def testProject(ID, IsScanBuild, Dir=None):
+def testProject(ID, IsScanBuild, IsReferenceBuild=False, Dir=None):
print " \n\n--- Building project %s" % (ID,)
TBegin = time.time()
@@ -408,10 +406,10 @@ def testProject(ID, IsScanBuild, Dir=None):
print " Build directory: %s." % (Dir,)
# Set the build results directory.
- RelOutputDir = getSBOutputDirName()
+ RelOutputDir = getSBOutputDirName(IsReferenceBuild)
SBOutputDir = os.path.join(Dir, RelOutputDir)
- buildProject(Dir, SBOutputDir, IsScanBuild)
+ buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild)
checkBuild(SBOutputDir)
@@ -421,10 +419,7 @@ def testProject(ID, IsScanBuild, Dir=None):
print "Completed tests for project %s (time: %.2f)." % \
(ID, (time.time()-TBegin))
-def testAll(InIsReferenceBuild = False, UpdateSVN = False):
- global IsReferenceBuild
- IsReferenceBuild = InIsReferenceBuild
-
+def testAll(IsReferenceBuild = False, UpdateSVN = False):
PMapFile = open(getProjectMapPath(), "rb")
try:
# Validate the input.
@@ -439,13 +434,13 @@ def testAll(InIsReferenceBuild = False, UpdateSVN = False):
# When we are regenerating the reference results, we might need to
# update svn. Remove reference results from SVN.
if UpdateSVN == True:
- assert(InIsReferenceBuild == True);
+ assert(IsReferenceBuild == True);
updateSVN("delete", PMapFile);
# Test the projects.
PMapFile.seek(0)
for I in csv.reader(PMapFile):
- testProject(I[0], int(I[1]))
+ testProject(I[0], int(I[1]), IsReferenceBuild)
# Add reference results to SVN.
if UpdateSVN == True:
diff --git a/utils/analyzer/SumTimerInfo.py b/utils/analyzer/SumTimerInfo.py
new file mode 100644
index 0000000..a6731bb
--- /dev/null
+++ b/utils/analyzer/SumTimerInfo.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+
+"""
+Script to Summarize statistics in the scan-build output.
+
+Statistics are enabled by passing '-internal-stats' option to scan-build
+(or '-analyzer-stats' to the analyzer).
+
+"""
+
+import string
+from operator import itemgetter
+import sys
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print >> sys.stderr, 'Usage: ', sys.argv[0],\
+ 'scan_build_output_file'
+ sys.exit(-1)
+
+ f = open(sys.argv[1], 'r')
+ Time = 0.0
+ TotalTime = 0.0
+ MaxTime = 0.0
+ Warnings = 0
+ Count = 0
+ FunctionsAnalyzed = 0
+ ReachableBlocks = 0
+ ReachedMaxSteps = 0
+ NumSteps = 0
+ MaxCFGSize = 0
+ Mode = 1
+ for line in f:
+ if ("Miscellaneous Ungrouped Timers" in line) :
+ Mode = 1
+ if (("Analyzer Total Time" in line) and (Mode == 1)) :
+ s = line.split()
+ Time = Time + float(s[6])
+ Count = Count + 1
+ if (float(s[6]) > MaxTime) :
+ MaxTime = float(s[6])
+ if ((("warning generated." in line) or ("warnings generated." in line)) and Mode == 1) :
+ s = line.split()
+ Warnings = Warnings + int(s[0])
+ if (("The # of functions analysed (as top level)." in line) and (Mode == 1)) :
+ s = line.split()
+ FunctionsAnalyzed = FunctionsAnalyzed + int(s[0])
+ if (("The % of reachable basic blocks" in line) and (Mode == 1)) :
+ s = line.split()
+ ReachableBlocks = ReachableBlocks + int(s[0])
+ if (("The # of times we reached the max number of steps." in line) and (Mode == 1)) :
+ s = line.split()
+ ReachedMaxSteps = ReachedMaxSteps + int(s[0])
+ if (("The maximum number of basic blocks in a function" in line) and (Mode == 1)) :
+ s = line.split()
+ if (MaxCFGSize < int(s[0])) :
+ MaxCFGSize = int(s[0])
+ if (("The # of steps executed." in line) and (Mode == 1)) :
+ s = line.split()
+ NumSteps = NumSteps + int(s[0])
+ if ((") Total" in line) and (Mode == 1)) :
+ s = line.split()
+ TotalTime = TotalTime + float(s[6])
+
+ print "TU Count %d" % (Count)
+ print "Time %f" % (Time)
+ print "Warnings %d" % (Warnings)
+ print "Functions Analyzed %d" % (FunctionsAnalyzed)
+ print "Reachable Blocks %d" % (ReachableBlocks)
+ print "Reached Max Steps %d" % (ReachedMaxSteps)
+ print "Number of Steps %d" % (NumSteps)
+ print "MaxTime %f" % (MaxTime)
+ print "TotalTime %f" % (TotalTime)
+ print "Max CFG Size %d" % (MaxCFGSize)
+ \ No newline at end of file
diff --git a/utils/analyzer/reducer.pl b/utils/analyzer/reducer.pl
new file mode 100755
index 0000000..872f61b
--- /dev/null
+++ b/utils/analyzer/reducer.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/perl -w
+use strict;
+use File::Temp qw/ tempdir /;
+my $prog = "reducer";
+
+die "$prog <code file> <error string> [optional command]\n" if ($#ARGV < 0);
+my $file = shift @ARGV;
+die "$prog: [error] cannot read file $file\n" if (! -r $file);
+
+my $magic = shift @ARGV;
+die "$prog: [error] no error string specified\n" if (! defined $magic);
+
+# Create a backup of the file.
+my $dir = tempdir( CLEANUP => 1 );
+print "$prog: created temporary directory '$dir'\n";
+my $srcFile = "$dir/$file";
+`cp $file $srcFile`;
+
+# Create the script.
+my $scriptFile = "$dir/script";
+open(OUT, ">$scriptFile") or die "$prog: cannot create '$scriptFile'\n";
+my $reduceOut = "$dir/reduceOut";
+
+my $command;
+if (scalar(@ARGV) > 0) { $command = \@ARGV; }
+else {
+ my $compiler = "clang";
+ $command = [$compiler, "-fsyntax-only", "-Wfatal-errors", "-Wno-deprecated-declarations", "-Wimplicit-function-declaration"];
+}
+push @$command, $srcFile;
+my $commandStr = "@$command";
+
+print OUT <<ENDTEXT;
+#!/usr/bin/perl -w
+use strict;
+my \$BAD = 1;
+my \$GOOD = 0;
+`rm -f $reduceOut`;
+my \$command = "$commandStr > $reduceOut 2>&1";
+system(\$command);
+open(IN, "$reduceOut") or exit(\$BAD);
+my \$found = 0;
+while(<IN>) {
+ if (/$magic/) { exit \$GOOD; }
+}
+exit \$BAD;
+ENDTEXT
+close(OUT);
+`chmod +x $scriptFile`;
+
+print "$prog: starting reduction\n";
+sub multidelta($) {
+ my ($level) = @_;
+ system("multidelta -level=$level $scriptFile $srcFile");
+}
+
+for (my $i = 1 ; $i <= 5; $i++) {
+ foreach my $level (0,0,1,1,2,2,10) {
+ multidelta($level);
+ }
+}
+
+# Copy the final file.
+`cp $srcFile $file.reduced`;
+print "$prog: generated '$file.reduced";
diff --git a/utils/clang-completion-mode.el b/utils/clang-completion-mode.el
index 36d8181..6cf5cb5 100644
--- a/utils/clang-completion-mode.el
+++ b/utils/clang-completion-mode.el
@@ -32,8 +32,12 @@
;;
;; (load-library "clang-completion-mode")
;;
+;; Once you have done this, you can set various parameters with
+;;
+;; M-x customize-group RET clang-completion-mode RET
+;;
;; Finally, to try Clang-based code completion in a particular buffer,
-;; use M-x clang-completion-mode. When "Clang-CC" shows up in the mode
+;; use M-x clang-completion-mode. When "Clang" shows up in the mode
;; line, Clang's code-completion is enabled.
;;
;; Clang's code completion is based on parsing the complete source
@@ -95,24 +99,29 @@ This variable will typically contain include paths, e.g., -I~/MyProject."
(delq nil
(mapcar (lambda (x) (and (funcall condp x) x)) lst)))
-;; Determine whether
+;; Determine whether FIXME: explain better
(defun is-completion-line (line)
(or (string-match "OVERLOAD:" line)
(string-match (concat "COMPLETION: " clang-completion-substring) line)))
+
+;; re-process the completions when further input narrows the field
(defun clang-completion-display (buffer)
+ (fill-buffer buffer))
+
+(defun fill-buffer (buffer)
(let* ((all-lines (split-string clang-result-string "\n"))
(completion-lines (filter 'is-completion-line all-lines)))
(if (consp completion-lines)
(progn
- ;; Erase the process buffer
+ ;; Erase the process buffer.
(let ((cur (current-buffer)))
(set-buffer buffer)
(goto-char (point-min))
(erase-buffer)
(set-buffer cur))
- ;; Display the process buffer
+ ;; Display the process buffer.
(display-buffer buffer)
;; Insert the code-completion string into the process buffer.
@@ -120,28 +129,11 @@ This variable will typically contain include paths, e.g., -I~/MyProject."
(insert (mapconcat 'identity completion-lines "\n")))
))))
-;; Process "sentinal" that, on successful code completion, replaces the
+;; Process "sentinel" that, on successful code completion, replaces the
;; contents of the code-completion buffer with the new code-completion results
;; and ensures that the buffer is visible.
(defun clang-completion-sentinel (proc event)
- (let* ((all-lines (split-string clang-result-string "\n"))
- (completion-lines (filter 'is-completion-line all-lines)))
- (if (consp completion-lines)
- (progn
- ;; Erase the process buffer
- (let ((cur (current-buffer)))
- (set-buffer (process-buffer proc))
- (goto-char (point-min))
- (erase-buffer)
- (set-buffer cur))
-
- ;; Display the process buffer
- (display-buffer (process-buffer proc))
-
- ;; Insert the code-completion string into the process buffer.
- (with-current-buffer (process-buffer proc)
- (insert (mapconcat 'identity completion-lines "\n")))
- ))))
+ (fill-buffer (process-buffer proc)))
(defun clang-complete ()
(let* ((cc-point (concat (buffer-file-name)
@@ -159,7 +151,7 @@ This variable will typically contain include paths, e.g., -I~/MyProject."
`("-code-completion-at" ,cc-point)
(list (buffer-file-name))))
(cc-buffer-name (concat "*Clang Completion for " (buffer-name) "*")))
- ;; Start the code-completion process
+ ;; Start the code-completion process.
(if (buffer-file-name)
(progn
;; If there is already a code-completion process, kill it first.
diff --git a/utils/clangVisualizers.txt b/utils/clangVisualizers.txt
index 0fef65f..8019b9c 100644
--- a/utils/clangVisualizers.txt
+++ b/utils/clangVisualizers.txt
@@ -3,15 +3,47 @@
llvm::SmallVector<*,*>{
preview (
+ #if ((($T1*)$e.EndX - ($T1*)$e.BeginX) == 0) ( "empty" )
+ #else (
+ #(
+ "[",
+ ($T1*)$e.EndX - ($T1*)$e.BeginX,
+ "](",
+ #array(
+ expr: (($T1*)$e.BeginX)[$i],
+ size: ($T1*)$e.EndX - ($T1*)$e.BeginX
+ ),
+ ")"
+ )
+ )
+ )
+
+ children (
#(
- "[",
- ($T1*)$e.EndX - ($T1*)$e.BeginX,
- "](",
+ #([size] : ($T1*)$e.EndX - ($T1*)$e.BeginX),
+ #([capacity] : ($T1*)$e.CapacityX - ($T1*)$e.BeginX),
#array(
expr: (($T1*)$e.BeginX)[$i],
size: ($T1*)$e.EndX - ($T1*)$e.BeginX
- ),
- ")"
+ )
+ )
+ )
+}
+
+llvm::SmallVectorImpl<*>{
+ preview (
+ #if ((($T1*)$e.EndX - ($T1*)$e.BeginX) == 0) ( "empty" )
+ #else (
+ #(
+ "[",
+ ($T1*)$e.EndX - ($T1*)$e.BeginX,
+ "](",
+ #array(
+ expr: (($T1*)$e.BeginX)[$i],
+ size: ($T1*)$e.EndX - ($T1*)$e.BeginX
+ ),
+ ")"
+ )
)
)
@@ -27,6 +59,11 @@ llvm::SmallVector<*,*>{
)
}
+llvm::SmallString<*>{
+ preview ([$e.BeginX,s])
+ stringview ([$e.BeginX,sb])
+}
+
llvm::StringRef{
preview ([$e.Data,s])
stringview ([$e.Data,sb])
@@ -55,6 +92,7 @@ llvm::PointerIntPair<*,*,*,*>{
children (
#(
+ #([raw members] : [$e,!]),
#([ptr] : ($T1*)($e.Value & $e.PointerBitMask)),
#([int] : ($T3)($e.Value >> $e.IntShift) & $e.IntMask)
)
@@ -63,18 +101,16 @@ llvm::PointerIntPair<*,*,*,*>{
llvm::PointerUnion<*,*>{
preview (
- #if ((($e.Val.Value >> $e.Val.IntShift) & $e.Val.IntMask) == 0) (
- "PT1"
- ) #else (
- "PT2"
- )
+ #if ((($e.Val.Value >> $e.Val.IntShift) & $e.Val.IntMask) == 0) ( "PT1" )
+ #else ( "PT2" )
)
children (
#(
+ #([raw members] : [$e,!]),
#if ((($e.Val.Value >> $e.Val.IntShift) & $e.Val.IntMask) == 0) (
#([ptr] : ($T1)($e.Val.Value & $e.Val.PointerBitMask))
- ) #else (
+ ) #else (
#([ptr] : ($T2)($e.Val.Value & $e.Val.PointerBitMask))
)
)
@@ -83,13 +119,9 @@ llvm::PointerUnion<*,*>{
llvm::PointerUnion3<*,*,*>{
preview (
- #if (($e.Val.Val.Value & 0x2) == 2) (
- "PT2"
- ) #elif (($e.Val.Val.Value & 0x1) == 1) (
- "PT3"
- ) #else (
- "PT1"
- )
+ #if (($e.Val.Val.Value & 0x2) == 2) ( "PT2" )
+ #elif (($e.Val.Val.Value & 0x1) == 1) ( "PT3" )
+ #else ( "PT1" )
)
children (
@@ -107,22 +139,17 @@ llvm::PointerUnion3<*,*,*>{
llvm::PointerUnion4<*,*,*,*>{
preview (
- #if (($e.Val.Val.Value & 0x3) == 3) (
- "PT4"
- ) #elif (($e.Val.Val.Value & 0x2) == 2) (
- "PT2"
- ) #elif (($e.Val.Val.Value & 0x1) == 1) (
- "PT3"
- ) #else (
- "PT1"
- )
+ #if (($e.Val.Val.Value & 0x3) == 3) ( "PT4" )
+ #elif (($e.Val.Val.Value & 0x2) == 2) ( "PT2" )
+ #elif (($e.Val.Val.Value & 0x1) == 1) ( "PT3" )
+ #else ( "PT1" )
)
children (
#(
#if (($e.Val.Val.Value & 0x3) == 3) (
#([ptr] : ($T4)(($e.Val.Val.Value >> 2) << 2))
- ) #elif (($e.Val.Val.Value & 0x2) == 2) (
+ ) #elif (($e.Val.Val.Value & 0x2) == 2) (
#([ptr] : ($T2)(($e.Val.Val.Value >> 2) << 2))
) #elif (($e.Val.Val.Value & 0x1) == 1) (
#([ptr] : ($T3)(($e.Val.Val.Value >> 2) << 2))
@@ -132,3 +159,233 @@ llvm::PointerUnion4<*,*,*,*>{
)
)
}
+
+llvm::IntrusiveRefCntPtr<*>{
+ preview (
+ #if ($e.Obj == 0) ( "empty" )
+ #else (
+ #(
+ "[RefCnt=", $e.Obj->ref_cnt,
+ ", ",
+ "Obj=", $e.Obj,
+ "]"
+ )
+ )
+ )
+
+ children (
+ #if ($e.Obj == 0) ( #array(expr: 0, size: 0) )
+ #else (
+ #(
+ #(RefCnt : $e.Obj->ref_cnt),
+ #(Obj : $e.Obj)
+ )
+ )
+ )
+}
+
+llvm::OwningPtr<*>{
+ preview (
+ #if ($e.Ptr == 0) ( "empty" )
+ #else ( $e.Ptr )
+ )
+
+ children (
+ #if ($e.Ptr == 0) ( #array(expr: 0, size: 0) )
+ #else ( #(Ptr : $e.Ptr) )
+ )
+}
+
+llvm::SmallPtrSet<*,*>{
+ preview (
+ #(
+ #if (($e.CurArray) == ($e.SmallArray)) ( "[Small Mode] " )
+ #else ( "[Big Mode] " ),
+ "NumElements=", $e.NumElements,
+ " CurArraySize=", $e.CurArraySize
+ )
+ )
+
+ children (
+ #(
+ #([raw members] : [$c,!]),
+ #(NumElements : $e.NumElements),
+ #(CurArraySize : $e.CurArraySize),
+ #array(
+ expr: $e.CurArray[$i],
+ size: $e.CurArraySize + 1
+ ) : ($T1*)&$e
+ )
+ )
+}
+
+llvm::DenseMap<*,*,*>{
+ preview (
+ #if ($e.NumEntries == 0) ( "empty" )
+ #else (
+ #(
+ "[NumEntries=", $e.NumEntries,
+ " NumBuckets=", $e.NumBuckets,
+ "]"
+ )
+ )
+ )
+
+ children (
+ #if ($e.NumEntries == 0) ( #array(expr: 0, size: 0) )
+ #else (
+ #(
+ #([raw members] : [$c,!]),
+ #(NumEntries : $e.NumEntries),
+ #(NumBuckets : $e.NumBuckets),
+ #array(
+ expr: $e.Buckets[$i],
+ size: $e.NumBuckets
+ )
+ )
+ )
+ )
+}
+
+llvm::StringMap<*,*>{
+ preview (
+ #(
+ "[NumBuckets=", $e.NumBuckets,
+ " ItemSize=", $e.ItemSize,
+ "]"
+ )
+ )
+
+ children (
+ #(
+ #([raw members] : [$c,!]),
+ #(NumBuckets : $e.NumBuckets),
+ #(ItemSize : $e.ItemSize),
+ #array(
+ expr: $e.TheTable[$i],
+ size: $e.NumBuckets,
+ ) : (llvm::StringMapEntry<$T1>*)&$e
+ )
+ )
+}
+
+llvm::StringMapEntry<*>{
+ preview (
+ #if ($e.StrLen == 0) ( "empty" )
+ #else ( #(Entry : $e.second) )
+ )
+
+ children (
+ #if ($e.StrLen == 0) ( "empty" )
+ #else ( #(Entry : $e.second) )
+ )
+}
+
+clang::DirectoryEntry|clang::FileEntry|clang::PragmaHandler{
+ preview ( [$e.Name,s] )
+ children (
+ #(
+ #([raw members] : [$c,!]),
+ #(Name : [$e.Name,s])
+ )
+ )
+}
+
+clang::DeclarationName{
+ preview (
+ ; enum values from clang::DeclarationName::StoredNameKind
+ #if ($e.Ptr == 0) (
+ "empty"
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredIdentifier) (
+ #else ( #("Identifier, ", (clang::IdentifierInfo*)($e.Ptr & ~$e.PtrMask)) )
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredObjCZeroArgSelector) (
+ #("ZeroArgSelector, ", (clang::IdentifierInfo*)($e.Ptr & ~$e.PtrMask))
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredObjCOneArgSelector) (
+ #("OneArgSelector, ", (clang::IdentifierInfo*)($e.Ptr & ~$e.PtrMask))
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredDeclarationNameExtra) (
+ #switch (((clang::DeclarationNameExtra*)($e.Ptr & ~$e.PtrMask)).ExtraKindOrNumArgs)
+ #case 0 ( ;DeclarationNameExtra::CXXConstructor
+ #("CXXConstructorName, ", (clang::CXXSpecialName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 1 ( ;DeclarationNameExtra::CXXDestructor
+ #("CXXDestructorName, ", (clang::CXXSpecialName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 2 ( ;DeclarationNameExtra::CXXConversionFunction
+ #("CXXConversionFunctionName, ", (clang::CXXSpecialName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 46 ( ;DeclarationNameExtra::CXXLiteralOperator
+ #("CXXLiteralOperatorName, ", (clang::CXXLiteralOperatorIdName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 47 ( ;DeclarationNameExtra::CXXUsingDirective
+ #("CXXUsingDirective") ;TODO What to add here?
+ )
+ #default (
+ #if (((clang::DeclarationNameExtra*)($e.Ptr & ~$e.PtrMask)).ExtraKindOrNumArgs < 47) (
+ #("CXXOperatorName, ", (clang::CXXOperatorIdName*)($e.Ptr & ~$e.PtrMask))
+ ) #else (
+ #("ObjCMultiArgSelector, ", (clang::MultiKeywordSelector*)($e.Ptr & ~$e.PtrMask))
+ )
+ )
+ )
+ )
+
+ children (
+ #(
+ ; enum values from clang::DeclarationName::StoredNameKind
+ #if ($e.Ptr == 0) (
+ #array( expr: 0, size: 0 )
+ ) #else (
+ #(
+ #([raw members] : [$e.Ptr,!]),
+ if (($e.Ptr & $e.PtrMask) == $e.StoredIdentifier) (
+ #(Ptr : (clang::IdentifierInfo*)($e.Ptr & ~$e.PtrMask))
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredObjCZeroArgSelector) (
+ #(Ptr : (clang::IdentifierInfo*)($e.Ptr & ~$e.PtrMask))
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredObjCOneArgSelector) (
+ #(Ptr : (clang::IdentifierInfo*)($e.Ptr & ~$e.PtrMask))
+ ) #elif (($e.Ptr & $e.PtrMask) == $e.StoredDeclarationNameExtra) (
+ #switch (((clang::DeclarationNameExtra*)($e.Ptr & ~$e.PtrMask)).ExtraKindOrNumArgs)
+ #case 0 ( ;DeclarationNameExtra::CXXConstructor
+ #(Ptr : (clang::CXXSpecialName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 1 ( ;DeclarationNameExtra::CXXDestructor
+ #(Ptr : (clang::CXXSpecialName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 2 ( ;DeclarationNameExtra::CXXConversionFunction
+ #(Ptr : (clang::CXXSpecialName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 46 ( ;DeclarationNameExtra::CXXLiteralOperator
+ #(Ptr : (clang::CXXLiteralOperatorIdName*)($e.Ptr & ~$e.PtrMask))
+ )
+ #case 47 ( ;DeclarationNameExtra::CXXUsingDirective
+ #(Ptr : $e.Ptr) ;TODO What to add here?
+ )
+ #default (
+ #if (((clang::DeclarationNameExtra*)($e.Ptr & ~$e.PtrMask)).ExtraKindOrNumArgs < 47) (
+ #(Ptr : (CXXOperatorIdName*)($e.Ptr & ~$e.PtrMask))
+ ) #else (
+ #(Ptr : (clang::MultiKeywordSelector*)($e.Ptr & ~$e.PtrMask))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+}
+
+clang::DeclSpec{
+ preview (
+ #(
+ "[",
+ (clang::DeclSpec::SCS)$e.StorageClassSpec,
+ ", ",
+ (clang::TypeSpecifierType)$e.TypeSpecType,
+ "]"
+ )
+ )
+}
+
+llvm::Triple{
+ preview ( $e.Data )
+}
diff --git a/www/analyzer/annotations.html b/www/analyzer/annotations.html
index 9e3583d..bb4bc44 100644
--- a/www/analyzer/annotations.html
+++ b/www/analyzer/annotations.html
@@ -541,7 +541,8 @@ some action that depends on that condition (e.g., dereferencing a pointer).</p>
<p>The analyzer knows about several well-known assertion handlers, but can
automatically infer if a function should be treated as an assertion handler if
it is annotated with the 'noreturn' attribute or the (Clang-specific)
-'analyzer_noreturn' attribute.</p>
+'analyzer_noreturn' attribute. Note that, currently, clang does not support
+these attributes on Objective-C methods and C++ methods.</p>
<h4 id="attr_noreturn">Attribute 'noreturn'</h4>
diff --git a/www/analyzer/available_checks.html b/www/analyzer/available_checks.html
index 3f40d32..3a902a3 100644
--- a/www/analyzer/available_checks.html
+++ b/www/analyzer/available_checks.html
@@ -74,9 +74,11 @@
<tr>
<td><b>deadcode.DeadStores</b></td><td>Check for values stored to variables that are never read afterwards.</td>
</tr>
+<!--
<tr>
<td><b>deadcode.IdempotentOperations</b></td><td>Warn about idempotent operations.</td>
</tr>
+-->
<tr>
<td><b>osx.API</b></td><td>Check for proper uses of various Mac OS X APIs.</td>
</tr>
@@ -108,6 +110,9 @@
<td><b>osx.cocoa.RetainCount</b></td><td>Check for leaks and improper reference count management.</td>
</tr>
<tr>
+<td><b>osx.cocoa.SelfInit</b></td><td>Check that 'self' is properly initialized inside an initializer method.</td>
+</tr>
+<tr>
<td><b>osx.cocoa.UnusedIvars</b></td><td>Warn about private ivars that are never used.</td>
</tr>
<tr>
@@ -122,9 +127,52 @@
<tr>
<td><b>osx.coreFoundation.CFRetainRelease</b></td><td>Check for null arguments to CFRetain/CFRelease.</td>
</tr>
+<td><b>osx.coreFoundation.containers.OutOfBounds</b></td><td>Checks for index out-of-bounds when using 'CFArray' API.</td>
+</tr>
+<tr>
+<td><b>osx.coreFoundation.containers.PointerSizedValues</b></td><td>Warns if 'CFArray', 'CFDictionary', 'CFSet' are created with non-pointer-size values.</td>
+</tr>
+<tr>
+<td><b>security.FloatLoopCounter</b></td><td>Warn on using a floating point value as a loop counter (CERT: FLP30-C, FLP30-CPP).</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.UncheckedReturn</b></td><td>Warn on uses of functions whose return values must be always checked.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.getpw</b></td><td>Warn on uses of the 'getpw' function.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.gets</b></td><td>Warn on uses of the 'gets' function.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.mkstemp</b></td><td>Warn when 'mkstemp' is passed fewer than 6 X's in the format string.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.mktemp</b></td><td>Warn on uses of the 'mktemp' function.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.rand</b></td><td>Warn on uses of the 'rand', 'random', and related functions.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.strcpy</b></td><td>Warn on uses of the 'strcpy' and 'strcat' functions.</td>
+</tr>
+<tr>
+<td><b>security.insecureAPI.vfork</b></td><td>Warn on uses of the 'vfork' function.</td>
+</tr>
<tr>
<td><b>unix.API</b></td><td>Check calls to various UNIX/Posix functions.</td>
</tr>
+<tr>
+<td><b>unix.Malloc</b></td><td>Check for memory leaks, double free, and use-after-free problems.</td>
+</tr>
+<tr>
+<td><b>unix.MallocSizeof</b></td><td>Check for dubious malloc arguments involving sizeof.</td>
+</tr>
+<tr>
+<td><b>unix.cstring.BadSizeArg</b></td><td>Check the size argument passed into C string functions for common erroneous patterns.</td>
+</tr>
+<tr>
+<td><b>unix.cstring.NullArg</b></td><td>Check for null pointers being passed as arguments to C string functions.</td>
</table>
<p>In addition to these the analyzer contains numerous experimental (beta) checkers.</p>
diff --git a/www/analyzer/content.css b/www/analyzer/content.css
index b22cca9..ea75fba 100644
--- a/www/analyzer/content.css
+++ b/www/analyzer/content.css
@@ -15,6 +15,7 @@ h1 { padding-top:0px; margin-top:0px;}
h2 { color:#333333; padding-top:0.5em; }
h3 { padding-top: 0.5em; margin-bottom: -0.25em; color:#2d58b7 }
h4 { color:#2d58b7 }
+h4.faq { color:#2d58b7 }
li { padding-bottom: 0.5em }
ul { padding-left:1.5em; }
diff --git a/www/analyzer/faq.html b/www/analyzer/faq.html
new file mode 100644
index 0000000..5c132b5
--- /dev/null
+++ b/www/analyzer/faq.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>FAQ and How to Deal with Common False Positives</title>
+ <link type="text/css" rel="stylesheet" href="menu.css">
+ <link type="text/css" rel="stylesheet" href="content.css">
+ <script type="text/javascript" src="scripts/menu.js"></script>
+ <style type="text/css">
+ tr:first-child { width:20%; }
+ </style>
+</head>
+<body>
+
+<div id="page">
+<!--#include virtual="menu.html.incl"-->
+
+<div id="content">
+
+<h1>FAQ and How to Deal with Common False Positives</h1>
+
+<ol>
+ <li><a href="#custom_assert">How do I tell the analyzer that I do not want the bug being
+reported here since my custom error handler will safely end the execution before
+the bug is reached?</a></li>
+ <li><a href="#null_pointer">The analyzer reports a null dereference, but I know that the
+pointer is never null. How can I tell the analyzer that a pointer can never be
+null?</a></li>
+ <li><a href="#use_assert">The analyzer assumes that a loop body is never entered. How can I tell it that the loop body will be entered at least once?</a></li>
+ <li><a href="#suppress_issue">How can I suppress a specific analyzer warning?</a></li>
+ <li><a href="#exclude_code">How can I selectively exclude code the analyzer examines?</a></li>
+</ol>
+
+
+<h4 id="custom_assert" class="faq">Q: How do I tell the analyzer that I do not want the bug being
+reported here since my custom error handler will safely end the execution before
+the bug is reached?</h4>
+
+<img src="images/example_custom_assert.png" alt="example custom assert">
+
+<p>You can tell the analyzer that this path is unreachable by teaching it about your <a href = "annotations.html#custom_assertions" >custom assertion handlers</a>. For example, you can modify the code segment as following.</p>
+
+<pre class="code_example">
+void customAssert() <span class="code_highlight">__attribute__((analyzer_noreturn))</span>;
+int foo(int *b) {
+ if (!b)
+ customAssert();
+ return *b;
+}</pre>
+
+
+<h4 id="null_pointer" class="faq">Q: The analyzer reports a null dereference, but I know that the
+pointer is never null. How can I tell the analyzer that a pointer can never be
+null?</h4>
+
+<img src="images/example_null_pointer.png" alt="example null pointer">
+
+<p>The reason the analyzer often thinks that a pointer can be null is because the preceding code checked compared it against null. So if you are absolutely sure that it cannot be null, remove the preceding check and, preferably, add an assertion as well. For example, in the code segment above, it will be sufficient to remove the <tt>if (!b)</tt> check. </p>
+
+<pre class="code_example">
+void usePointer(int *b);
+int foo(int *b) {
+ usePointer(b);
+ return *b;
+}</pre>
+
+<h4 id="use_assert" class="faq">Q: The analyzer assumes that a loop body is never entered. How can I tell it that the loop body will be entered at least once?</h4>
+
+<img src="images/example_use_assert.png" alt="example use assert">
+
+<p>You can teach the analyzer facts about your code as well as document it by
+using assertions. In the contrived example above, the analyzer reports an error
+on the path which assumes that the loop is never entered. However, the owner of
+the code might know that the loop is always entered because the input parameter
+<tt>length</tt> is always greater than <tt>0</tt>. The false positive can be
+suppressed by asserting this knowledge, adding <tt>assert(length > 0)</tt> in
+the beginning of the function.</p>
+
+<pre class="code_example">
+int foo(int length) {
+ int x = 0;
+ <span class="code_highlight">assert(length > 0);</span>
+ for (int i = 0; i < length; i++)
+ x += 1;
+ return length/x;
+}
+</pre>
+
+<h4 id="suppress_issue" class="faq">Q: How can I suppress a specific analyzer warning?</h4>
+
+<p>There is currently no solid mechanism for suppressing an analyzer warning,
+although this is currently being investigated. When you encounter an analyzer
+bug/false positive, check if it's one of the issues discussed above or if the
+analyzer <a href = "annotations.html#custom_assertions" >annotations</a> can
+resolve the issue. Second, please <a href = "filing_bugs.html">report it</a> to
+help us improve user experience. As the last resort, consider using <tt>__clang_analyzer__</tt> macro
+<a href = "faq.html#exclude_code" >described below</a>.</p>
+
+<h4 id="exclude_code" class="faq">Q: How can I selectively exclude code the analyzer examines?</h4>
+
+<p>When the static analyzer is using clang to parse source files, it implicitly
+defines the preprocessor macro <tt>__clang_analyzer__</tt>. One can use this
+macro to selectively exclude code the analyzer examines. Here is an example:
+
+<pre class="code_example">
+#ifndef __clang_analyzer__
+// Code not to be analyzed
+#endif
+</pre>
+
+This usage is discouraged because it makes the code dead to the analyzer from
+now on. Instead, we prefer that users file bugs against the analyzer when it flags
+false positives.
+</p>
+
+</div>
+</div>
+</body>
+</html>
+
diff --git a/www/analyzer/images/example_custom_assert.png b/www/analyzer/images/example_custom_assert.png
new file mode 100644
index 0000000..a56bb89
--- /dev/null
+++ b/www/analyzer/images/example_custom_assert.png
Binary files differ
diff --git a/www/analyzer/images/example_null_pointer.png b/www/analyzer/images/example_null_pointer.png
new file mode 100644
index 0000000..defc394
--- /dev/null
+++ b/www/analyzer/images/example_null_pointer.png
Binary files differ
diff --git a/www/analyzer/images/example_use_assert.png b/www/analyzer/images/example_use_assert.png
new file mode 100644
index 0000000..9e46d76
--- /dev/null
+++ b/www/analyzer/images/example_use_assert.png
Binary files differ
diff --git a/www/analyzer/installation.html b/www/analyzer/installation.html
index ebccd07..2eb7937 100644
--- a/www/analyzer/installation.html
+++ b/www/analyzer/installation.html
@@ -93,7 +93,7 @@ source code</a>.<p>
<p>For example, if you built a <em>Debug+Asserts</em> build of LLVM/Clang (the
default), the resultant <tt>clang</tt> binary will be in <tt>$(OBJDIR)/Debug+Asserts/bin</tt>
(where <tt>$(OBJDIR)</tt> is often the same as the root source directory). You
-can also do <tt>make install</tt> to install the LLVM/Clang libaries and
+can also do <tt>make install</tt> to install the LLVM/Clang libraries and
binaries to the installation directory of your choice (specified when you run
<tt>configure</tt>).</p></li>
diff --git a/www/analyzer/latest_checker.html.incl b/www/analyzer/latest_checker.html.incl
index d1b0d06..74c6ab0 100644
--- a/www/analyzer/latest_checker.html.incl
+++ b/www/analyzer/latest_checker.html.incl
@@ -1 +1 @@
-<b><a href="http://bit.ly/GUmtVB">checker-263.tar.bz2</a></b> (built March 22, 2012)
+<b><a href="http://bit.ly/OIdyI7">checker-267.tar.bz2</a></b> (built June 1, 2012)
diff --git a/www/analyzer/menu.html.incl b/www/analyzer/menu.html.incl
index 53a4276..d1581a6 100644
--- a/www/analyzer/menu.html.incl
+++ b/www/analyzer/menu.html.incl
@@ -16,9 +16,10 @@
User Manual
<ul>
<li><a href="/installation.html">Obtaining the Analyzer</a></li>
- <li><a href="/scan-build.html">Command line usage</a></li>
+ <li><a href="/scan-build.html">Command Line Usage</a></li>
<li><a href="/xcode.html">Using within Xcode</a></li>
<li><a href="/available_checks.html">Available Checks</a></li>
+ <li><a href="/faq.html">FAQ</a></li>
<li><a href="/annotations.html">Source-level Annotations</a></li>
</ul>
</li>
@@ -26,7 +27,7 @@
Development
<ul>
<li><a href="/checker_dev_manual.html">Checker Developer Manual</a></li>
- <li><a href="/dev_cxx.html">Analysis support for C++</a></li>
+ <li><a href="/dev_cxx.html">Analysis Support for C++</a></li>
</ul>
</li>
<li>
diff --git a/www/analyzer/release_notes.html b/www/analyzer/release_notes.html
index 42de9dd..79f0e0d 100644
--- a/www/analyzer/release_notes.html
+++ b/www/analyzer/release_notes.html
@@ -15,10 +15,42 @@
<h1>Release notes for <tt>checker-XXX</tt> builds</h1>
+<h4 id="checker_267">checker-267</h4>
+<p><b>built:</b> June 1, 2012</br>
+ <b>download:</b> <a href="http://bit.ly/OIdyI7">checker-267.tar.bz2</a></p>
+ <p><b>highlights:</b></p>
+
+<p>Adds basic interprocedural analysis support for blocks.</p>
+
+<h4 id="checker_266">checker-266</h4>
+<p><b>built:</b> May 23, 2012</br>
+ <b>download:</b> <a href="http://bit.ly/LgtUWx">checker-266.tar.bz2</a></p>
+ <p><b>highlights:</b></p>
+
+<p>Contains numerous stability fixes over checker-266, especially when analyzing C++11 code.</p>
+
+<h4 id="checker_265">checker-265</h4>
+<p><b>built:</b> May 8, 2012</br>
+ <b>download:</b> <a href="http://bit.ly/JceZBE">checker-265.tar.bz2</a></p>
+ <p><b>highlights:</b></p>
+
+<p>This release contains a fix for a major crasher introduced in checker-264, and various refinements to
+improve the precision and reduce the false positive rate of the analyzer. It also enables a new unix.MallocSizeof check, which reports
+inconsistencies between the casted type of the return value of a 'malloc/calloc/realloc' call and the operand
+of sizeof expressions contained within its argument(s).</p>
+
+<h4 id="checker_264">checker-264</h4>
+
+<p><b>built:</b> April 26, 2012</br>
+ <b>download:</b> <a href="http://bit.ly/JATSI8">checker-264.tar.bz2</a></p>
+ <p><b>highlights:</b></p>
+
+<p>This release contains misc. bug fixes and performance enhancements over checker-263, including
+ a reduction of some kinds of false positives related to the malloc() checker.</p>
+
<h4 id="checker_263">checker-263</h4>
<p><b>built:</b> March 22, 2012</br>
- <b>download:</b> <a href="http://bit.ly/GUmtVB">checker-263.tar.bz2</a></p>
<p><b>highlights:</b></p>
<ul>
@@ -28,7 +60,6 @@
<h4 id="checker_262">checker-262</h4>
<p><b>built: </b>March 15, 2012</br>
- <b>download:</b> <a href="http://bit.ly/xETQF0">checker-262.tar.bz2</a></p>
<p><b>highlights:</b></p>
<ul>
@@ -42,7 +73,6 @@
<h4 id="checker_261">checker-261</h4>
<p><b>built: </b>February 22, 2012<br>
-<b>download:</b> <a href="http://bit.ly/yN1Awv">checker-261.tar.bz2</a></p>
<p><b>highlights:</b></p>
<ul>
@@ -58,7 +88,6 @@
<h4 id="checker_260">checker-260</h4>
<p><b>built: </b>January 25, 2012<br>
-<b>download:</b> <a href="http://bit.ly/wpAqVP">checker-260.tar.bz2</a></p>
<p><b>highlights:</b></p>
<p>This is essentially the same as checker-259, but enables the following <i>experimental</i> checkers (please provide feedback):</p>
@@ -74,7 +103,6 @@
<h4 id="checker_259">checker-259</h4>
<p><b>built: </b>January 25, 2012<br>
-<b>download:</b> <a href="http://bit.ly/zOWf1P">checker-259.tar.bz2</a></p>
<p><b>highlights:</b></p>
<ul>
diff --git a/www/builtins.py b/www/builtins.py
index 5e85770..18f86ab 100755
--- a/www/builtins.py
+++ b/www/builtins.py
@@ -45,6 +45,7 @@ repl_map = {
'__builtin_ia32_xorpd': '_mm_xor_pd',
'__builtin_ia32_xorps': '_mm_xor_ps',
'__builtin_ia32_pxor128': '_mm_xor_si128',
+'__builtin_ia32_cvtps2dq': '_mm_cvtps_epi32',
'__builtin_ia32_cvtsd2ss': '_mm_cvtsd_ss',
'__builtin_ia32_cvtsi2sd': '_mm_cvtsi32_sd',
'__builtin_ia32_cvtss2sd': '_mm_cvtss_sd',
@@ -120,6 +121,8 @@ repl_map = {
'__builtin_ia32_pshufd': '_mm_shuffle_epi32',
'__builtin_ia32_movshdup': '_mm_movehdup_ps',
'__builtin_ia32_movsldup': '_mm_moveldup_ps',
+'__builtin_ia32_maxps': '_mm_max_ps',
+'__builtin_ia32_pslldi128': '_mm_slli_epi32',
'__builtin_ia32_vec_set_v16qi': '_mm_insert_epi8',
'__builtin_ia32_vec_set_v8hi': '_mm_insert_epi16',
'__builtin_ia32_vec_set_v4si': '_mm_insert_epi32',
@@ -157,4 +160,4 @@ for line in fileinput.input(inplace=1):
report_cant(unh)
sys.stdout.write(line)
-sys.exit(err) \ No newline at end of file
+sys.exit(err)
diff --git a/www/comparison.html b/www/comparison.html
index 2898ce8..58c4b31 100644
--- a/www/comparison.html
+++ b/www/comparison.html
@@ -50,8 +50,6 @@
<ul>
<li>GCC supports languages that clang does not aim to, such as Java, Ada,
FORTRAN, etc.</li>
- <li>GCC has a few <a href="cxx_status.html">C++'11 features</a> that Clang
- does not yet support.</li>
<li>GCC supports more targets than LLVM.</li>
<li>GCC is popular and widely adopted.</li>
<li>GCC does not require a C++ compiler to build it.</li>
diff --git a/www/content.css b/www/content.css
index 574b98e..8572d7c 100644
--- a/www/content.css
+++ b/www/content.css
@@ -24,7 +24,10 @@ IMG.img_slide {
.itemTitle { color:#2d58b7 }
span.error { color:red }
+span.warning { color:purple }
+span.note { color:gray }
span.caret { color:green; font-weight:bold }
+span.template-highlight { color:cyan }
/* Tables */
tr { vertical-align:top }
diff --git a/www/cxx_status.html b/www/cxx_status.html
index 3ee6b72..50ec736 100644
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -23,7 +23,7 @@
<!--*************************************************************************-->
<h1>C++98 and C++11 Support in Clang</h1>
<!--*************************************************************************-->
-<p>Last updated: $Date: 2012-04-17 03:04:22 +0200 (Tue, 17 Apr 2012) $</p>
+<p>Last updated: $Date: 2012-05-23 03:38:11 +0200 (Wed, 23 May 2012) $</p>
<h2 id="cxx98">C++98 implementation status</h2>
@@ -83,7 +83,7 @@ C++11 mode.</p>
<tr>
<td>Initializer lists</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm">N2672</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Static assertions</td>
@@ -113,7 +113,7 @@ C++11 mode.</p>
<tr>
<td>Lambda expressions</td>
<td><a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2927.pdf">N2927</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Declared type of an expression</td>
@@ -123,7 +123,7 @@ C++11 mode.</p>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;Incomplete return types</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3276.pdf">N3276</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Right angle brackets</td>
@@ -164,7 +164,7 @@ C++11 mode.</p>
<td>Forward declarations for enums</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf">N2764</a>
<br><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1206">DR1206</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Generalized attributes</td>
@@ -174,7 +174,7 @@ C++11 mode.</p>
<tr>
<td>Generalized constant expressions</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf">N2235</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Alignment support</td>
@@ -216,12 +216,12 @@ C++11 mode.</p>
<tr>
<td>Universal character names in literals</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2170.html">N2170</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>User-defined literals</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2765.pdf">N2765</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Standard Layout Types</td>
@@ -247,7 +247,7 @@ C++11 mode.</p>
<td>Extending <code>sizeof</code></td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html">N2253</a>
<br><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#850">DR850</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Inline namespaces</td>
@@ -257,7 +257,7 @@ C++11 mode.</p>
<tr>
<td>Unrestricted unions</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf">N2544</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Local and unnamed types as template arguments</td>
@@ -303,17 +303,17 @@ C++11 mode.</p>
<tr>
<td>Atomic operations</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html">N2427</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Strong Compare and Exchange</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2748.html">N2748</a></td>
- <td class="none" align="center">No</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Bidirectional Fences</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2752.htm">N2752</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
@@ -339,7 +339,7 @@ C++11 mode.</p>
<tr>
<td>Allow atomics use in signal handlers</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2547.htm">N2547</a></td>
- <td class="svn" align="center">SVN</td>
+ <td class="full" align="center">Clang 3.1</td>
</tr>
<tr>
<td>Thread-local storage</td>
diff --git a/www/diagnostics.html b/www/diagnostics.html
index 45f6907..84c6f8b 100644
--- a/www/diagnostics.html
+++ b/www/diagnostics.html
@@ -263,6 +263,39 @@ diagnostic.<p>
<span class="snip">template&lt;&gt; </span>
</pre>
+<h2>Template Type Diffing</h2>
+
+<p>Templates types can be long and difficult to read. Moreso when part of an
+error message. Instead of just printing out the type name, Clang has enough
+information to remove the common elements and highlight the differences. To
+show the template structure more clearly, the templated type can also be
+printed as an indented text tree.</p>
+
+Default: template diff with type elision
+<pre>
+t.cc:4:5: <span class="note">note:</span> candidate function not viable: no known conversion from 'vector&lt;map&lt;[...], <span class="template-highlight">float</span>&gt;&gt;' to 'vector&lt;map&lt;[...], <span class="template-highlight">double</span>&gt;&gt;' for 1st argument;
+</pre>
+-fno-elide-type: template diff without elision
+<pre>
+t.cc:4:5: <span class="note">note:</span> candidate function not viable: no known conversion from 'vector&lt;map&lt;int, <span class="template-highlight">float</span>&gt;&gt;' to 'vector&lt;map&lt;int, <span class="template-highlight">double</span>&gt;&gt;' for 1st argument;
+</pre>
+-fdiagnostics-show-template-tree: template tree printing with elision
+<pre>
+t.cc:4:5: <span class="note">note:</span> candidate function not viable: no known conversion for 1st argument;
+ vector&lt;
+ map&lt;
+ [...],
+ [<span class="template-highlight">float</span> != <span class="template-highlight">double</span>]&gt;&gt;
+</pre>
+-fdiagnostics-show-template-tree -fno-elide-type: template tree printing with no elision
+<pre>
+t.cc:4:5: <span class="note">note:M</span> candidate function not viable: no known conversion for 1st argument;
+ vector&lt;
+ map&lt;
+ int,
+ [<span class="template-highlight">float</span> != <span class="template-highlight">double</span>]&gt;&gt;
+</pre>
+
<h2>Automatic Macro Expansion</h2>
<p>Many errors happen in macros that are sometimes deeply nested. With
diff --git a/www/features.html b/www/features.html
index d55391a..2d3191e 100644
--- a/www/features.html
+++ b/www/features.html
@@ -329,13 +329,15 @@ specifically designed and built to capture this information.
<!--=======================================================================-->
<p>We actively intend for clang (and LLVM as a whole) to be used for
-commercial projects, and the BSD license is the simplest way to allow this. We
-feel that the license encourages contributors to pick up the source and work
-with it, and believe that those individuals and organizations will contribute
-back their work if they do not want to have to maintain a fork forever (which is
-time consuming and expensive when merges are involved). Further, nobody makes
-money on compilers these days, but many people need them to get bigger goals
-accomplished: it makes sense for everyone to work together.</p>
+commercial projects, not only as a stand-alone compiler but also as a library
+embedded inside a proprietary application. The BSD license is the simplest way
+to allow this. We feel that the license encourages contributors to pick up the
+source and work with it, and believe that those individuals and organizations
+will contribute back their work if they do not want to have to maintain a fork
+forever (which is time consuming and expensive when merges are involved).
+Further, nobody makes money on compilers these days, but many people need them
+to get bigger goals accomplished: it makes sense for everyone to work
+together.</p>
<p>For more information about the LLVM/clang license, please see the <a
href="http://llvm.org/docs/DeveloperPolicy.html#license">LLVM License
diff --git a/www/get_started.html b/www/get_started.html
index d819532..7756f9e 100644
--- a/www/get_started.html
+++ b/www/get_started.html
@@ -53,21 +53,29 @@ follows:</p>
</li>
<li>Checkout Clang:
<ul>
- <li><tt>cd llvm/tools</tt>
+ <li><tt>cd llvm/tools</tt></li>
<li><tt>svn co http://llvm.org/svn/llvm-project/cfe/trunk clang</tt></li>
+ <li><tt>cd ../..</tt></li>
+ </ul>
+ </li>
+ <li>Checkout extra Clang Tools: (optional)
+ <ul>
+ <li><tt>cd llvm/tools/clang/tools</tt></li>
+ <li><tt>svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk
+ extra</tt></li>
+ <li><tt>cd ../../../..</tt></li>
</ul>
</li>
<li>Checkout Compiler-RT:
<ul>
- <li><tt>cd ../..</tt> (back to where you started)</li>
- <li><tt>cd llvm/projects</tt>
+ <li><tt>cd llvm/projects</tt></li>
<li><tt>svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk
compiler-rt</tt></li>
+ <li><tt>cd ../..</tt></li>
</ul>
</li>
<li>Build LLVM and Clang:
<ul>
- <li><tt>cd ../..</tt> (back to where you started)</li>
<li><tt>mkdir build</tt> (for building without polluting the source dir)
</li>
<li><tt>cd build</tt></li>
@@ -83,15 +91,12 @@ follows:</p>
</li>
<li>If you intend to work on Clang C++ support, you may need to tell it how
- to find your C++ standard library headers. If Clang cannot find your
- system libstdc++ headers, please follow these instructions:
- <ul>
- <li>'<tt>gcc -v -x c++ /dev/null -fsyntax-only</tt>' to get the
- path.</li>
- <li>Look for the comment "FIXME: temporary hack:
- hard-coded paths" in <tt>clang/lib/Frontend/InitHeaderSearch.cpp</tt> and
- change the lines below to include that path.</li>
- </ul>
+ to find your C++ standard library headers. In general, Clang will detect
+ the best version of libstdc++ headers available and use them - it will
+ look both for system installations of libstdc++ as well as installations
+ adjacent to Clang itself. If your configuration fits neither of these
+ scenarios, you can use the <tt>--with-gcc-toolchain</tt> configure option
+ to tell Clang where the gcc containing the desired libstdc++ is installed.
</li>
<li>Try it out (assuming you add llvm/Debug+Asserts/bin to your path):
<ul>
@@ -267,7 +272,7 @@ $ <b>clang -fsyntax-only ~/t.c -pedantic</b>
<h3>Pretty printing from the AST:</h3>
-<p>Note, the <tt>-cc1</tt> argument indicates the the compiler front-end, and
+<p>Note, the <tt>-cc1</tt> argument indicates the compiler front-end, and
not the driver, should be run. The compiler front-end has several additional
Clang specific features which are not exposed through the GCC compatible driver
interface.</p>
diff --git a/www/menu.html.incl b/www/menu.html.incl
index 878e678..2c00d0f 100644
--- a/www/menu.html.incl
+++ b/www/menu.html.incl
@@ -28,6 +28,7 @@
<div class="submenu">
<label>Clang Tools</label>
<a href="http://clang-analyzer.llvm.org">Automatic Bug-Finding</a>
+ <a href="/docs/Tooling.html">Writing Clang Tools</a>
</div>
<div class="submenu">
OpenPOWER on IntegriCloud