void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { SourceManager &SM = *Result.SourceManager; if (const auto *E = Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) { CharSourceRange Range = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions()); if (Range.isInvalid()) return; auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership") << FixItHint::CreateInsertion(Range.getBegin(), "std::move(") << FixItHint::CreateInsertion(Range.getEnd(), ")"); auto Insertion = Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", /*IsAngled=*/true); if (Insertion.hasValue()) Diag << Insertion.getValue(); return; } SourceLocation IdentifierLoc; if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { IdentifierLoc = locateFromTypeLoc(TL, SM); } else if (const auto *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { IdentifierLoc = locateFromUsingDecl(D, SM); } else { llvm_unreachable("Bad Callback. No node provided."); } if (IdentifierLoc.isMacroID()) IdentifierLoc = SM.getSpellingLoc(IdentifierLoc); // Ensure that only the 'auto_ptr' token is replaced and not the template // aliases. if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions())) return; SourceLocation EndLoc = IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1); diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead") << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, EndLoc), "unique_ptr"); }
void WhitespaceManager::replaceWhitespaceInToken( const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars, StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective, unsigned Newlines, unsigned Spaces) { Changes.push_back(Change( true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset), Tok.getStartOfNonWhitespace().getLocWithOffset( Offset + ReplaceChars)), Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix, // If we don't add a newline this change doesn't start a comment. Thus, // when we align line comments, we don't need to treat this change as one. // FIXME: We still need to take this change in account to properly // calculate the new length of the comment and to calculate the changes // for which to do the alignment when aligning comments. Tok.Type == TT_LineComment && Newlines > 0 ? tok::comment : tok::unknown, InPPDirective && !Tok.IsFirst)); }
BlockCommandComment *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++; } return S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs)); }
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)); }
void InspectorStyleTextEditor::internalReplaceProperty(const InspectorStyleProperty& property, const String& newText, SourceRange* removedRange, unsigned* insertedLength) { const SourceRange& range = property.sourceData.range; long replaceRangeStart = range.start; long replaceRangeEnd = range.end; const UChar* characters = m_styleText.characters(); long newTextLength = newText.length(); String finalNewText = newText; // Removing a property - remove preceding prefix. String fullPrefix = m_format.first + m_format.second; long fullPrefixLength = fullPrefix.length(); if (!newTextLength && fullPrefixLength) { if (replaceRangeStart >= fullPrefixLength && m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) == fullPrefix) replaceRangeStart -= fullPrefixLength; } else if (newTextLength) { if (isHTMLLineBreak(newText.characters()[newTextLength - 1])) { // Coalesce newlines of the original and new property values (to avoid a lot of blank lines while incrementally applying property values). bool foundNewline = false; bool isLastNewline = false; int i; int textLength = m_styleText.length(); for (i = replaceRangeEnd; i < textLength && isSpaceOrNewline(characters[i]); ++i) { isLastNewline = isHTMLLineBreak(characters[i]); if (isLastNewline) foundNewline = true; else if (foundNewline && !isLastNewline) { replaceRangeEnd = i; break; } } if (foundNewline && isLastNewline) replaceRangeEnd = i; } if (fullPrefixLength > replaceRangeStart || m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) != fullPrefix) finalNewText.insert(fullPrefix, 0); } int replacedLength = replaceRangeEnd - replaceRangeStart; m_styleText.replace(replaceRangeStart, replacedLength, finalNewText); *removedRange = SourceRange(replaceRangeStart, replaceRangeEnd); *insertedLength = finalNewText.length(); }
void RedundantVoidArgCheck::processFunctionDecl( const MatchFinder::MatchResult &Result, const FunctionDecl *Function) { if (Function->isThisDeclarationADefinition()) { SourceLocation Start = Function->getBeginLoc(); SourceLocation End = Function->getEndLoc(); if (const Stmt *Body = Function->getBody()) { End = Body->getBeginLoc(); if (End.isMacroID() && Result.SourceManager->isAtStartOfImmediateMacroExpansion(End)) End = Result.SourceManager->getExpansionLoc(End); End = End.getLocWithOffset(-1); } removeVoidArgumentTokens(Result, SourceRange(Start, End), "function definition"); } else { removeVoidArgumentTokens(Result, Function->getSourceRange(), "function declaration"); } }
void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled, SourceLocation HashLocation, SourceLocation EndLocation) { int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation)); // Record the relevant location information for this inclusion directive. IncludeLocations[FileName].push_back( SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset))); SourceLocations.push_back(IncludeLocations[FileName].back()); // Stop if this inclusion is a duplicate. if (IncludeLocations[FileName].size() > 1) return; // Add the included file's name to the appropriate bucket. IncludeKinds Kind = DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style); if (Kind != IK_InvalidInclude) IncludeBucket[Kind].push_back(FileName.str()); }
// Checks if 'typedef' keyword can be removed - we do it only if // it is the only declaration in a declaration chain. static bool CheckRemoval(SourceManager &SM, const SourceLocation &LocStart, const SourceLocation &LocEnd, ASTContext &Context, SourceRange &ResultRange) { FileID FID = SM.getFileID(LocEnd); llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd); Lexer DeclLexer(SM.getLocForStartOfFile(FID), Context.getLangOpts(), Buffer->getBufferStart(), SM.getCharacterData(LocStart), Buffer->getBufferEnd()); Token DeclToken; bool result = false; int parenthesisLevel = 0; while (!DeclLexer.LexFromRawLexer(DeclToken)) { if (DeclToken.getKind() == tok::TokenKind::l_paren) parenthesisLevel++; if (DeclToken.getKind() == tok::TokenKind::r_paren) parenthesisLevel--; if (DeclToken.getKind() == tok::TokenKind::semi) break; // if there is comma and we are not between open parenthesis then it is // two or more declatarions in this chain if (parenthesisLevel == 0 && DeclToken.getKind() == tok::TokenKind::comma) return false; if (DeclToken.isOneOf(tok::TokenKind::identifier, tok::TokenKind::raw_identifier)) { auto TokenStr = DeclToken.getRawIdentifier().str(); if (TokenStr == "typedef") { ResultRange = SourceRange(DeclToken.getLocation(), DeclToken.getEndLoc()); result = true; } } } // assert if there was keyword 'typedef' in declaration assert(result && "No typedef found"); return result; }
void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) { const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E"); if (E->getIdentType() != PredefinedExpr::Func && E->getIdentType() != PredefinedExpr::Function) { // We don't care about other PredefinedExprs. return; } if (E->getLocation().isMacroID()) { auto ER = Result.SourceManager->getImmediateExpansionRange(E->getLocation()); if (SuppressMacroExpansions.find(SourceRange(ER.first, ER.second)) != SuppressMacroExpansions.end()) { // This is a macro expansion for which we should not warn. return; } } diag(E->getLocation(), "inside a lambda, '%0' expands to the name of the function call " "operator; consider capturing the name of the enclosing function " "explicitly") << PredefinedExpr::getIdentTypeName(E->getIdentType()); }
Decl *Sema::ActOnExternalEntityDecl(ASTContext &C, QualType T, SourceLocation IDLoc, const IdentifierInfo *IDInfo) { SourceLocation TypeLoc; VarDecl *ArgumentExternal = nullptr; if (auto Prev = LookupIdentifier(IDInfo)) { auto Quals = getDeclQualifiers(Prev); if(Quals.hasAttributeSpec(Qualifiers::AS_external)) { Diags.Report(IDLoc, diag::err_duplicate_attr_spec) << DeclSpec::getSpecifierName(Qualifiers::AS_external); return Prev; } // apply EXTERNAL to an unused symbol or an argument. auto VD = dyn_cast<VarDecl>(Prev); if(VD && (VD->isUnusedSymbol() || VD->isArgument()) ) { T = VD->getType(); TypeLoc = VD->getLocation(); CurContext->removeDecl(VD); if(VD->isArgument()) ArgumentExternal = VD; } else { DiagnoseRedefinition(IDLoc, IDInfo, Prev); return nullptr; } } if(T.isNull()) T = C.VoidTy; DeclarationNameInfo DeclName(IDInfo,IDLoc); auto Decl = FunctionDecl::Create(C, ArgumentExternal? FunctionDecl::ExternalArgument : FunctionDecl::External, CurContext, DeclName, T); SetFunctionType(Decl, T, TypeLoc, SourceRange()); //FIXME: proper loc, and range CurContext->addDecl(Decl); if(ArgumentExternal) ArgumentExternal->setType(C.getFunctionType(Decl)); return Decl; }
void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok, unsigned Newlines, unsigned Spaces, unsigned StartOfTokenColumn, bool InPPDirective) { Changes.push_back(Change( true, SourceRange(Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceStart.getLocWithOffset( Tok.FormatTok.WhiteSpaceLength)), Spaces, StartOfTokenColumn, Newlines, "", "", Tok.FormatTok.Tok.getKind(), InPPDirective && !Tok.FormatTok.IsFirst)); // Align line comments if they are trailing or if they continue other // trailing comments. // FIXME: Pull this out and generalize so it works the same way in broken // comments and unbroken comments with trailing whitespace. if (Tok.isTrailingComment()) { SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace() .getLocWithOffset(Tok.FormatTok.TokenLength); // Remove the comment's trailing whitespace. if (Tok.FormatTok.TrailingWhiteSpaceLength != 0) Replaces.insert(tooling::Replacement( SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, "")); } }
StmtResult Sema::ActOnDATA(ASTContext &C, SourceLocation Loc, ArrayRef<Expr*> Objects, ArrayRef<Expr*> Values, Expr *StmtLabel) { DataValueIterator ValuesIt(Values); DataStmtEngine LHSVisitor(ValuesIt, *this, Diags, Loc); for(auto I : Objects) { LHSVisitor.Visit(I); if(LHSVisitor.IsDone()) break; } if(!ValuesIt.isEmpty()) { // more items than values auto FirstVal = ValuesIt.getActualValue(); auto LastVal = ValuesIt.getActualLastValue(); Diags.Report(FirstVal->getLocation(), diag::err_data_stmt_too_many_values) << SourceRange(FirstVal->getLocStart(), LastVal->getLocEnd()); } auto Result = DataStmt::Create(C, Loc, Objects, Values, StmtLabel); if(StmtLabel) DeclareStatementLabel(StmtLabel, Result); return Result; }
bool DeclExtractor::CheckTagDeclaration(TagDecl* NewTD, LookupResult& Previous){ // If the decl is already known invalid, don't check it. if (NewTD->isInvalidDecl()) return false; IdentifierInfo* Name = NewTD->getIdentifier(); // If this is not a definition, it must have a name. assert((Name != 0 || NewTD->isThisDeclarationADefinition()) && "Nameless record must be a definition!"); // Figure out the underlying type if this a enum declaration. We need to do // this early, because it's needed to detect if this is an incompatible // redeclaration. TagDecl::TagKind Kind = NewTD->getTagKind(); bool Invalid = false; assert(NewTD->getNumTemplateParameterLists() == 0 && "Cannot handle that yet!"); bool isExplicitSpecialization = false; if (Kind == TTK_Enum) { EnumDecl* ED = cast<EnumDecl>(NewTD); bool ScopedEnum = ED->isScoped(); const QualType QT = ED->getIntegerType(); if (QT.isNull() && ScopedEnum) // No underlying type explicitly specified, or we failed to parse the // type, default to int. ; //EnumUnderlying = m_Context->IntTy.getTypePtr(); else if (!QT.isNull()) { // C++0x 7.2p2: The type-specifier-seq of an enum-base shall name an // integral type; any cv-qualification is ignored. SourceLocation UnderlyingLoc; TypeSourceInfo* TI = 0; if ((TI = ED->getIntegerTypeSourceInfo())) UnderlyingLoc = TI->getTypeLoc().getBeginLoc(); if (!QT->isDependentType() && !QT->isIntegralType(*m_Context)) { m_Sema->Diag(UnderlyingLoc, diag::err_enum_invalid_underlying) << QT; } if (TI) m_Sema->DiagnoseUnexpandedParameterPack(UnderlyingLoc, TI, Sema::UPPC_FixedUnderlyingType); } } DeclContext *SearchDC = m_Sema->CurContext; DeclContext *DC = m_Sema->CurContext; //bool isStdBadAlloc = false; SourceLocation NameLoc = NewTD->getLocation(); // if (Name && SS.isNotEmpty()) { // // We have a nested-name tag ('struct foo::bar'). // // Check for invalid 'foo::'. // if (SS.isInvalid()) { // Name = 0; // goto CreateNewDecl; // } // // If this is a friend or a reference to a class in a dependent // // context, don't try to make a decl for it. // if (TUK == TUK_Friend || TUK == TUK_Reference) { // DC = computeDeclContext(SS, false); // if (!DC) { // IsDependent = true; // return 0; // } // } else { // DC = computeDeclContext(SS, true); // if (!DC) { // Diag(SS.getRange().getBegin(), // diag::err_dependent_nested_name_spec) // << SS.getRange(); // return 0; // } // } // if (RequireCompleteDeclContext(SS, DC)) // return 0; // SearchDC = DC; // // Look-up name inside 'foo::'. // LookupQualifiedName(Previous, DC); // if (Previous.isAmbiguous()) // return 0; // if (Previous.empty()) { // // Name lookup did not find anything. However, if the // // nested-name-specifier refers to the current instantiation, // // and that current instantiation has any dependent base // // classes, we might find something at instantiation time: treat // // this as a dependent elaborated-type-specifier. // // But this only makes any sense for reference-like lookups. // if (Previous.wasNotFoundInCurrentInstantiation() && // (TUK == TUK_Reference || TUK == TUK_Friend)) { // IsDependent = true; // return 0; // } // // A tag 'foo::bar' must already exist. // Diag(NameLoc, diag::err_not_tag_in_scope) // << Kind << Name << DC << SS.getRange(); // Name = 0; // Invalid = true; // goto CreateNewDecl; // } //} else if (Name) { // If this is a named struct, check to see if there was a previous forward // declaration or definition. // FIXME: We're looking into outer scopes here, even when we // shouldn't be. Doing so can result in ambiguities that we // shouldn't be diagnosing. //LookupName(Previous, S); if (Previous.isAmbiguous()) { LookupResult::Filter F = Previous.makeFilter(); while (F.hasNext()) { NamedDecl *ND = F.next(); if (ND->getDeclContext()->getRedeclContext() != SearchDC) F.erase(); } F.done(); } // Note: there used to be some attempt at recovery here. if (Previous.isAmbiguous()) { return false; } if (!m_Sema->getLangOpts().CPlusPlus) { // FIXME: This makes sure that we ignore the contexts associated // with C structs, unions, and enums when looking for a matching // tag declaration or definition. See the similar lookup tweak // in Sema::LookupName; is there a better way to deal with this? while (isa<RecordDecl>(SearchDC) || isa<EnumDecl>(SearchDC)) SearchDC = SearchDC->getParent(); } } else if (m_Sema->getScopeForContext(m_Sema->CurContext) ->isFunctionPrototypeScope()) { // If this is an enum declaration in function prototype scope, set its // initial context to the translation unit. SearchDC = m_Context->getTranslationUnitDecl(); } if (Previous.isSingleResult() && Previous.getFoundDecl()->isTemplateParameter()) { // Maybe we will complain about the shadowed template parameter. m_Sema->DiagnoseTemplateParameterShadow(NameLoc, Previous.getFoundDecl()); // Just pretend that we didn't see the previous declaration. Previous.clear(); } if (m_Sema->getLangOpts().CPlusPlus && Name && DC && m_Sema->StdNamespace && DC->Equals(m_Sema->getStdNamespace()) && Name->isStr("bad_alloc")) { // This is a declaration of or a reference to "std::bad_alloc". //isStdBadAlloc = true; if (Previous.empty() && m_Sema->StdBadAlloc) { // std::bad_alloc has been implicitly declared (but made invisible to // name lookup). Fill in this implicit declaration as the previous // declaration, so that the declarations get chained appropriately. Previous.addDecl(m_Sema->getStdBadAlloc()); } } if (!Previous.empty()) { NamedDecl *PrevDecl = (*Previous.begin())->getUnderlyingDecl(); // It's okay to have a tag decl in the same scope as a typedef // which hides a tag decl in the same scope. Finding this // insanity with a redeclaration lookup can only actually happen // in C++. // // This is also okay for elaborated-type-specifiers, which is // technically forbidden by the current standard but which is // okay according to the likely resolution of an open issue; // see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#407 if (m_Sema->getLangOpts().CPlusPlus) { if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(PrevDecl)) { if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) { TagDecl *Tag = TT->getDecl(); if (Tag->getDeclName() == Name && Tag->getDeclContext()->getRedeclContext() ->Equals(TD->getDeclContext()->getRedeclContext())) { PrevDecl = Tag; Previous.clear(); Previous.addDecl(Tag); Previous.resolveKind(); } } } } if (TagDecl *PrevTagDecl = dyn_cast<TagDecl>(PrevDecl)) { // If this is a use of a previous tag, or if the tag is already declared // in the same scope (so that the definition/declaration completes or // rementions the tag), reuse the decl. if (m_Sema->isDeclInScope(PrevDecl, SearchDC, m_Sema->getScopeForContext(m_Sema->CurContext), isExplicitSpecialization)) { // Make sure that this wasn't declared as an enum and now used as a // struct or something similar. SourceLocation KWLoc = NewTD->getLocStart(); if (!m_Sema->isAcceptableTagRedeclaration(PrevTagDecl, Kind, NewTD->isThisDeclarationADefinition(), KWLoc, *Name)) { bool SafeToContinue = (PrevTagDecl->getTagKind() != TTK_Enum && Kind != TTK_Enum); if (SafeToContinue) m_Sema->Diag(KWLoc, diag::err_use_with_wrong_tag) << Name << FixItHint::CreateReplacement(SourceRange(KWLoc), PrevTagDecl->getKindName()); else m_Sema->Diag(KWLoc, diag::err_use_with_wrong_tag) << Name; m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use); if (SafeToContinue) Kind = PrevTagDecl->getTagKind(); else { // Recover by making this an anonymous redefinition. Name = 0; Previous.clear(); Invalid = true; } } if (Kind == TTK_Enum && PrevTagDecl->getTagKind() == TTK_Enum) { const EnumDecl *NewEnum = cast<EnumDecl>(NewTD); const EnumDecl *PrevEnum = cast<EnumDecl>(PrevTagDecl); // All conflicts with previous declarations are recovered by // returning the previous declaration. if (NewEnum->isScoped() != PrevEnum->isScoped()) { m_Sema->Diag(KWLoc, diag::err_enum_redeclare_scoped_mismatch) << PrevEnum->isScoped(); m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use); return false; } else if (PrevEnum->isFixed()) { QualType T = NewEnum->getIntegerType(); if (!m_Context->hasSameUnqualifiedType(T, PrevEnum->getIntegerType())) { m_Sema->Diag(NameLoc.isValid() ? NameLoc : KWLoc, diag::err_enum_redeclare_type_mismatch) << T << PrevEnum->getIntegerType(); m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use); return false; } } else if (NewEnum->isFixed() != PrevEnum->isFixed()) { m_Sema->Diag(KWLoc, diag::err_enum_redeclare_fixed_mismatch) << PrevEnum->isFixed(); m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use); return false; } } if (!Invalid) { // If this is a use, just return the declaration we found. // Diagnose attempts to redefine a tag. if (NewTD->isThisDeclarationADefinition()) { if (TagDecl* Def = PrevTagDecl->getDefinition()) { // If we're defining a specialization and the previous // definition is from an implicit instantiation, don't emit an // error here; we'll catch this in the general case below. if (!isExplicitSpecialization || !isa<CXXRecordDecl>(Def) || cast<CXXRecordDecl>(Def)->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { m_Sema->Diag(NameLoc, diag::err_redefinition) << Name; m_Sema->Diag(Def->getLocation(), diag::note_previous_definition); // If this is a redefinition, recover by making this // struct be anonymous, which will make any later // references get the previous definition. Name = 0; Previous.clear(); Invalid = true; } } else { // If the type is currently being defined, complain // about a nested redefinition. const TagType *Tag = cast<TagType>(m_Context->getTagDeclType(PrevTagDecl)); if (Tag->isBeingDefined()) { m_Sema->Diag(NameLoc, diag::err_nested_redefinition) << Name; m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_definition); Name = 0; Previous.clear(); Invalid = true; } } // Okay, this is definition of a previously declared or referenced // tag PrevDecl. We're going to create a new Decl for it. } } // If we get here we have (another) forward declaration or we // have a definition. Just create a new decl. } else { // If we get here, this is a definition of a new tag type in a nested // scope, e.g. "struct foo; void bar() { struct foo; }", just create a // new decl/type. We set PrevDecl to NULL so that the entities // have distinct types. Previous.clear(); } // If we get here, we're going to create a new Decl. If PrevDecl // is non-NULL, it's a definition of the tag declared by // PrevDecl. If it's NULL, we have a new definition. // Otherwise, PrevDecl is not a tag, but was found with tag // lookup. This is only actually possible in C++, where a few // things like templates still live in the tag namespace. } else { assert(m_Sema->getLangOpts().CPlusPlus); // Diagnose if the declaration is in scope. if (!m_Sema->isDeclInScope(PrevDecl, SearchDC, m_Sema->getScopeForContext(m_Sema->CurContext), isExplicitSpecialization)) { // do nothing // Otherwise it's a declaration. Call out a particularly common // case here. } else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(PrevDecl)) { unsigned Kind = 0; if (isa<TypeAliasDecl>(PrevDecl)) Kind = 1; m_Sema->Diag(NameLoc, diag::err_tag_definition_of_typedef) << Name << Kind << TND->getUnderlyingType(); m_Sema->Diag(PrevDecl->getLocation(), diag::note_previous_decl) << PrevDecl; Invalid = true; // Otherwise, diagnose. } else { // The tag name clashes with something else in the target scope, // issue an error and recover by making this tag be anonymous. m_Sema->Diag(NameLoc, diag::err_redefinition_different_kind) << Name; m_Sema->Diag(PrevDecl->getLocation(), diag::note_previous_definition); Name = 0; Invalid = true; } // The existing declaration isn't relevant to us; we're in a // new scope, so clear out the previous declaration. Previous.clear(); } } if (Invalid) { return false; } return true; }
virtual SourceRange getRange(const TypeLoc &Node) { TemplateSpecializationTypeLoc T = Node.getUnqualifiedLoc().castAs<TemplateSpecializationTypeLoc>(); assert(!T.isNull()); return SourceRange(T.getLAngleLoc(), T.getRAngleLoc()); }
virtual SourceRange getRange(const TypeLoc &Node) { UnaryTransformTypeLoc T = Node.getUnqualifiedLoc().castAs<UnaryTransformTypeLoc>(); assert(!T.isNull()); return SourceRange(T.getLParenLoc(), T.getRParenLoc()); }
SourceRange Cursor::getExtent() const { return SourceRange(clang().getCursorExtent(cursor_)); }
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); const SourceManager &Sources = *Result.SourceManager; if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc())) return; // Don't require closing comments for namespaces spanning less than certain // number of lines. unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart()); unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc()); if (EndLine - StartLine + 1 <= ShortNamespaceLines) return; // Find next token after the namespace closing brace. SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1); SourceLocation Loc = AfterRBrace; Token Tok; // Skip whitespace until we find the next token. while (Lexer::getRawToken(Loc, Tok, Sources, Result.Context->getLangOpts()) || Tok.is(tok::semi)) { Loc = Loc.getLocWithOffset(1); } if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc)) return; bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine; // If we insert a line comment before the token in the same line, we need // to insert a line break. bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof); SourceRange OldCommentRange(AfterRBrace, AfterRBrace); std::string Message = "%0 not terminated with a closing comment"; // Try to find existing namespace closing comment on the same line. if (Tok.is(tok::comment) && NextTokenIsOnSameLine) { StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength()); SmallVector<StringRef, 7> Groups; if (NamespaceCommentPattern.match(Comment, &Groups)) { StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; StringRef Anonymous = Groups.size() > 3 ? Groups[3] : ""; // Check if the namespace in the comment is the same. if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) || (ND->getNameAsString() == NamespaceNameInComment && Anonymous.empty())) { // FIXME: Maybe we need a strict mode, where we always fix namespace // comments with different format. return; } // Otherwise we need to fix the comment. NeedLineBreak = Comment.startswith("/*"); OldCommentRange = SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength())); Message = (llvm::Twine( "%0 ends with a comment that refers to a wrong namespace '") + NamespaceNameInComment + "'").str(); } else if (Comment.startswith("//")) { // Assume that this is an unrecognized form of a namespace closing line // comment. Replace it. NeedLineBreak = false; OldCommentRange = SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength())); Message = "%0 ends with an unrecognized comment"; } // If it's a block comment, just move it to the next line, as it can be // multi-line or there may be other tokens behind it. } std::string NamespaceName = ND->isAnonymousNamespace() ? "anonymous namespace" : ("namespace '" + ND->getNameAsString() + "'"); diag(AfterRBrace, Message) << NamespaceName << FixItHint::CreateReplacement( CharSourceRange::getCharRange(OldCommentRange), std::string(SpacesBeforeComments, ' ') + getNamespaceComment(ND, NeedLineBreak)); diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note) << NamespaceName; }
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: HST = S.actOnHTMLStartTagFinish(HST, copyArray(llvm::makeArrayRef(Attrs)), Tok.getLocation(), /* IsSelfClosing = */ false); consumeToken(); return HST; case tok::html_slash_greater: HST = S.actOnHTMLStartTagFinish(HST, 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; return S.actOnHTMLStartTagFinish(HST, copyArray(llvm::makeArrayRef(Attrs)), SourceLocation(), /* IsSelfClosing = */ false); default: // Not a token from an HTML start tag. Thus HTML tag prematurely ended. HST = S.actOnHTMLStartTagFinish(HST, 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; } } }
Expr* ValueExtractionSynthesizer::SynthesizeSVRInit(Expr* E) { if (!m_gClingVD) FindAndCacheRuntimeDecls(); // Build a reference to gCling ExprResult gClingDRE = m_Sema->BuildDeclRefExpr(m_gClingVD, m_Context->VoidPtrTy, VK_RValue, SourceLocation()); // We have the wrapper as Sema's CurContext FunctionDecl* FD = cast<FunctionDecl>(m_Sema->CurContext); ExprWithCleanups* Cleanups = 0; // In case of ExprWithCleanups we need to extend its 'scope' to the call. if (E && isa<ExprWithCleanups>(E)) { Cleanups = cast<ExprWithCleanups>(E); E = Cleanups->getSubExpr(); } // Build a reference to Value* in the wrapper, should be // the only argument of the wrapper. SourceLocation locStart = (E) ? E->getLocStart() : FD->getLocStart(); SourceLocation locEnd = (E) ? E->getLocEnd() : FD->getLocEnd(); ExprResult wrapperSVRDRE = m_Sema->BuildDeclRefExpr(FD->getParamDecl(0), m_Context->VoidPtrTy, VK_RValue, locStart); QualType ETy = (E) ? E->getType() : m_Context->VoidTy; QualType desugaredTy = ETy.getDesugaredType(*m_Context); // The expr result is transported as reference, pointer, array, float etc // based on the desugared type. We should still expose the typedef'ed // (sugared) type to the cling::Value. if (desugaredTy->isRecordType() && E->getValueKind() == VK_LValue) { // returning a lvalue (not a temporary): the value should contain // a reference to the lvalue instead of copying it. desugaredTy = m_Context->getLValueReferenceType(desugaredTy); ETy = m_Context->getLValueReferenceType(ETy); } Expr* ETyVP = utils::Synthesize::CStyleCastPtrExpr(m_Sema, m_Context->VoidPtrTy, (uint64_t)ETy.getAsOpaquePtr()); Expr* ETransaction = utils::Synthesize::CStyleCastPtrExpr(m_Sema, m_Context->VoidPtrTy, (uint64_t)getTransaction()); llvm::SmallVector<Expr*, 6> CallArgs; CallArgs.push_back(gClingDRE.take()); CallArgs.push_back(wrapperSVRDRE.take()); CallArgs.push_back(ETyVP); CallArgs.push_back(ETransaction); ExprResult Call; SourceLocation noLoc; if (desugaredTy->isVoidType()) { // In cases where the cling::Value gets reused we need to reset the // previous settings to void. // We need to synthesize setValueNoAlloc(...), E, because we still need // to run E. // FIXME: Suboptimal: this discards the already created AST nodes. QualType vpQT = m_Context->VoidPtrTy; QualType vQT = m_Context->VoidTy; Expr* vpQTVP = utils::Synthesize::CStyleCastPtrExpr(m_Sema, vpQT, (uint64_t)vQT.getAsOpaquePtr()); CallArgs[2] = vpQTVP; Call = m_Sema->ActOnCallExpr(/*Scope*/0, m_UnresolvedNoAlloc, locStart, CallArgs, locEnd); if (E) Call = m_Sema->CreateBuiltinBinOp(locStart, BO_Comma, Call.take(), E); } else if (desugaredTy->isRecordType() || desugaredTy->isConstantArrayType()){ // 2) object types : // check existance of copy constructor before call if (!availableCopyConstructor(desugaredTy, m_Sema)) return E; // call new (setValueWithAlloc(gCling, &SVR, ETy)) (E) Call = m_Sema->ActOnCallExpr(/*Scope*/0, m_UnresolvedWithAlloc, locStart, CallArgs, locEnd); Expr* placement = Call.take(); if (const ConstantArrayType* constArray = dyn_cast<ConstantArrayType>(desugaredTy.getTypePtr())) { CallArgs.clear(); CallArgs.push_back(E); CallArgs.push_back(placement); uint64_t arrSize = m_Context->getConstantArrayElementCount(constArray); Expr* arrSizeExpr = utils::Synthesize::IntegerLiteralExpr(*m_Context, arrSize); CallArgs.push_back(arrSizeExpr); // 2.1) arrays: // call copyArray(T* src, void* placement, int size) Call = m_Sema->ActOnCallExpr(/*Scope*/0, m_UnresolvedCopyArray, locStart, CallArgs, locEnd); } else { TypeSourceInfo* ETSI = m_Context->getTrivialTypeSourceInfo(ETy, noLoc); Call = m_Sema->BuildCXXNew(E->getSourceRange(), /*useGlobal ::*/true, /*placementLParen*/ noLoc, MultiExprArg(placement), /*placementRParen*/ noLoc, /*TypeIdParens*/ SourceRange(), /*allocType*/ ETSI->getType(), /*allocTypeInfo*/ETSI, /*arraySize*/0, /*directInitRange*/E->getSourceRange(), /*initializer*/E, /*mayContainAuto*/false ); } } else if (desugaredTy->isIntegralOrEnumerationType() || desugaredTy->isReferenceType() || desugaredTy->isPointerType() || desugaredTy->isFloatingType()) { if (desugaredTy->isIntegralOrEnumerationType()) { // 1) enum, integral, float, double, referece, pointer types : // call to cling::internal::setValueNoAlloc(...); // If the type is enum or integral we need to force-cast it into // uint64 in order to pick up the correct overload. if (desugaredTy->isIntegralOrEnumerationType()) { QualType UInt64Ty = m_Context->UnsignedLongLongTy; TypeSourceInfo* TSI = m_Context->getTrivialTypeSourceInfo(UInt64Ty, noLoc); Expr* castedE = m_Sema->BuildCStyleCastExpr(noLoc, TSI, noLoc, E).take(); CallArgs.push_back(castedE); } } else if (desugaredTy->isReferenceType()) { // we need to get the address of the references Expr* AddrOfE = m_Sema->BuildUnaryOp(/*Scope*/0, noLoc, UO_AddrOf, E).take(); CallArgs.push_back(AddrOfE); } else if (desugaredTy->isPointerType()) { // function pointers need explicit void* cast. QualType VoidPtrTy = m_Context->VoidPtrTy; TypeSourceInfo* TSI = m_Context->getTrivialTypeSourceInfo(VoidPtrTy, noLoc); Expr* castedE = m_Sema->BuildCStyleCastExpr(noLoc, TSI, noLoc, E).take(); CallArgs.push_back(castedE); } else if (desugaredTy->isFloatingType()) { // floats and double will fall naturally in the correct // case, because of the overload resolution. CallArgs.push_back(E); } Call = m_Sema->ActOnCallExpr(/*Scope*/0, m_UnresolvedNoAlloc, locStart, CallArgs, locEnd); } else assert(0 && "Unhandled code path?"); assert(!Call.isInvalid() && "Invalid Call"); // Extend the scope of the temporary cleaner if applicable. if (Cleanups) { Cleanups->setSubExpr(Call.take()); Cleanups->setValueKind(Call.take()->getValueKind()); Cleanups->setType(Call.take()->getType()); return Cleanups; } return Call.take(); }
void RemoveStatement(clang::Rewriter& TheRewriter, clang::CallExpr const * const Statement) { ExpandSourceRange SourceRange{TheRewriter}; TheRewriter.RemoveText(SourceRange(Statement->getSourceRange())); }
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); const SourceManager &Sources = *Result.SourceManager; if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc())) return; // Don't require closing comments for namespaces spanning less than certain // number of lines. unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart()); unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc()); if (EndLine - StartLine + 1 <= ShortNamespaceLines) return; // Find next token after the namespace closing brace. SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1); SourceLocation Loc = AfterRBrace; Token Tok; // Skip whitespace until we find the next token. while (Lexer::getRawToken(Loc, Tok, Sources, Result.Context->getLangOpts())) { Loc = Loc.getLocWithOffset(1); } if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc)) return; bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine; // If we insert a line comment before the token in the same line, we need // to insert a line break. bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof); // Try to find existing namespace closing comment on the same line. if (Tok.is(tok::comment) && NextTokenIsOnSameLine) { StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength()); SmallVector<StringRef, 6> Groups; if (NamespaceCommentPattern.match(Comment, &Groups)) { StringRef NamespaceNameInComment = Groups.size() >= 6 ? Groups[5] : ""; // Check if the namespace in the comment is the same. if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) || ND->getNameAsString() == NamespaceNameInComment) { // FIXME: Maybe we need a strict mode, where we always fix namespace // comments with different format. return; } // Otherwise we need to fix the comment. NeedLineBreak = Comment.startswith("/*"); CharSourceRange OldCommentRange = CharSourceRange::getCharRange( SourceRange(Loc, Loc.getLocWithOffset(Tok.getLength()))); diag(Loc, "namespace closing comment refers to a wrong namespace '%0'") << NamespaceNameInComment << FixItHint::CreateReplacement( OldCommentRange, getNamespaceComment(ND, NeedLineBreak)); return; } // This is not a recognized form of a namespace closing comment. // Leave line comment on the same line. Move block comment to the next line, // as it can be multi-line or there may be other tokens behind it. if (Comment.startswith("//")) NeedLineBreak = false; } diag(ND->getLocation(), "namespace not terminated with a closing comment") << FixItHint::CreateInsertion(AfterRBrace, std::string(SpacesBeforeComments, ' ') + getNamespaceComment(ND, NeedLineBreak)); }
SourceRange getExpansionRange(SourceManager& sourceManager, const Decl* decl) { return SourceRange(getExpansionStart(sourceManager, decl), getExpansionEnd(sourceManager, decl)); }
void ProBoundsConstantArrayIndexCheck::check( const MatchFinder::MatchResult &Result) { const auto *Matched = Result.Nodes.getNodeAs<Expr>("expr"); const auto *IndexExpr = Result.Nodes.getNodeAs<Expr>("index"); if (IndexExpr->isValueDependent()) return; // We check in the specialization. llvm::APSInt Index; if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr, /*isEvaluated=*/true)) { SourceRange BaseRange; if (const auto *ArraySubscriptE = dyn_cast<ArraySubscriptExpr>(Matched)) BaseRange = ArraySubscriptE->getBase()->getSourceRange(); else BaseRange = dyn_cast<CXXOperatorCallExpr>(Matched)->getArg(0)->getSourceRange(); SourceRange IndexRange = IndexExpr->getSourceRange(); auto Diag = diag(Matched->getExprLoc(), "do not use array subscript when the index is " "not an integer constant expression; use gsl::at() " "instead"); if (!GslHeader.empty()) { Diag << FixItHint::CreateInsertion(BaseRange.getBegin(), "gsl::at(") << FixItHint::CreateReplacement( SourceRange(BaseRange.getEnd().getLocWithOffset(1), IndexRange.getBegin().getLocWithOffset(-1)), ", ") << FixItHint::CreateReplacement(Matched->getLocEnd(), ")"); Optional<FixItHint> Insertion = Inserter->CreateIncludeInsertion( Result.SourceManager->getMainFileID(), GslHeader, /*IsAngled=*/false); if (Insertion) Diag << Insertion.getValue(); } return; } const auto *StdArrayDecl = Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("type"); // For static arrays, this is handled in clang-diagnostic-array-bounds. if (!StdArrayDecl) return; if (Index.isSigned() && Index.isNegative()) { diag(Matched->getExprLoc(), "std::array<> index %0 is negative") << Index.toString(10); return; } const TemplateArgumentList &TemplateArgs = StdArrayDecl->getTemplateArgs(); if (TemplateArgs.size() < 2) return; // First template arg of std::array is the type, second arg is the size. const auto &SizeArg = TemplateArgs[1]; if (SizeArg.getKind() != TemplateArgument::Integral) return; llvm::APInt ArraySize = SizeArg.getAsIntegral(); // Get uint64_t values, because different bitwidths would lead to an assertion // in APInt::uge. if (Index.getZExtValue() >= ArraySize.getZExtValue()) { diag(Matched->getExprLoc(), "std::array<> index %0 is past the end of the array " "(which contains %1 elements)") << Index.toString(10) << ArraySize.toString(10, false); } }
std::vector<FixItHint> IncludeSorter::GetEdits() { if (SourceLocations.empty()) return {}; typedef std::map<int, std::pair<SourceRange, std::string>> FileLineToSourceEditMap; FileLineToSourceEditMap Edits; auto SourceLocationIterator = SourceLocations.begin(); auto SourceLocationIteratorEnd = SourceLocations.end(); // Compute the Edits that need to be done to each line to add, replace, or // delete inclusions. for (int IncludeKind = 0; IncludeKind < IK_InvalidInclude; ++IncludeKind) { std::sort(IncludeBucket[IncludeKind].begin(), IncludeBucket[IncludeKind].end()); for (const auto &IncludeEntry : IncludeBucket[IncludeKind]) { auto &Location = IncludeLocations[IncludeEntry]; SourceRangeVector::iterator LocationIterator = Location.begin(); SourceRangeVector::iterator LocationIteratorEnd = Location.end(); SourceRange FirstLocation = *LocationIterator; // If the first occurrence of a particular include is on the current // source line we are examining, leave it alone. if (FirstLocation == *SourceLocationIterator) ++LocationIterator; // Add the deletion Edits for any (remaining) instances of this inclusion, // and remove their Locations from the source Locations to be processed. for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) { int LineNumber = SourceMgr->getSpellingLineNumber(LocationIterator->getBegin()); Edits[LineNumber] = std::make_pair(*LocationIterator, ""); SourceLocationIteratorEnd = std::remove(SourceLocationIterator, SourceLocationIteratorEnd, *LocationIterator); } if (FirstLocation == *SourceLocationIterator) { // Do nothing except move to the next source Location (Location of an // inclusion in the original, unchanged source file). ++SourceLocationIterator; continue; } // Add (or append to) the replacement text for this line in source file. int LineNumber = SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin()); if (Edits.find(LineNumber) == Edits.end()) { Edits[LineNumber].first = SourceRange(SourceLocationIterator->getBegin()); } StringRef SourceText = Lexer::getSourceText( CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts); Edits[LineNumber].second.append(SourceText.data(), SourceText.size()); } // Clear the bucket. IncludeBucket[IncludeKind].clear(); } // Go through the single-line Edits and combine them into blocks of Edits. int CurrentEndLine = 0; SourceRange CurrentRange; std::string CurrentText; std::vector<FixItHint> Fixes; for (const auto &LineEdit : Edits) { // If the current edit is on the next line after the previous edit, add it // to the current block edit. if (LineEdit.first == CurrentEndLine + 1 && CurrentRange.getBegin() != CurrentRange.getEnd()) { SourceRange EditRange = LineEdit.second.first; if (EditRange.getBegin() != EditRange.getEnd()) { ++CurrentEndLine; CurrentRange.setEnd(EditRange.getEnd()); } CurrentText += LineEdit.second.second; // Otherwise report the current block edit and start a new block. } else { if (CurrentEndLine) { Fixes.push_back(CreateFixIt(CurrentRange, CurrentText)); } CurrentEndLine = LineEdit.first; CurrentRange = LineEdit.second.first; CurrentText = LineEdit.second.second; } } // Finally, report the current block edit if there is one. if (CurrentEndLine) { Fixes.push_back(CreateFixIt(CurrentRange, CurrentText)); } // Reset the remaining internal state. SourceLocations.clear(); IncludeLocations.clear(); return Fixes; }
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; }
bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag, const CXXNewExpr *New, SourceManager& SM) { SourceLocation NewStart = New->getSourceRange().getBegin(); SourceLocation NewEnd = New->getSourceRange().getEnd(); // Skip when the source location of the new expression is invalid. if (NewStart.isInvalid() || NewEnd.isInvalid()) return false; std::string ArraySizeExpr; if (const auto* ArraySize = New->getArraySize()) { ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange( ArraySize->getSourceRange()), SM, getLangOpts()) .str(); } switch (New->getInitializationStyle()) { case CXXNewExpr::NoInit: { if (ArraySizeExpr.empty()) { Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd)); } else { // New array expression without written initializer: // smart_ptr<Foo[]>(new Foo[5]); Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd), ArraySizeExpr); } break; } case CXXNewExpr::CallInit: { // FIXME: Add fixes for constructors with parameters that can be created // with a C++11 braced-init-list (e.g. std::vector, std::map). // Unlike ordinal cases, braced list can not be deduced in // std::make_smart_ptr, we need to specify the type explicitly in the fixes: // struct S { S(std::initializer_list<int>, int); }; // struct S2 { S2(std::vector<int>); }; // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization // smart_ptr<S>(new S({}, 1)); // smart_ptr<S2>(new S2({1})); // implicit conversion: // // std::initializer_list => std::vector // The above samples have to be replaced with: // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1); // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1); // std::make_smart_ptr<S2>(std::vector<int>({1})); if (const auto *CE = New->getConstructExpr()) { for (const auto *Arg : CE->arguments()) { if (isa<CXXStdInitializerListExpr>(Arg)) { return false; } // Check whether we construct a class from a std::initializer_list. // If so, we won't generate the fixes. auto IsStdInitListInitConstructExpr = [](const Expr* E) { assert(E); if (const auto *ImplicitCE = dyn_cast<CXXConstructExpr>(E)) { if (ImplicitCE->isStdInitListInitialization()) return true; } return false; }; if (IsStdInitListInitConstructExpr(Arg->IgnoreImplicit())) return false; } } if (ArraySizeExpr.empty()) { SourceRange InitRange = New->getDirectInitRange(); Diag << FixItHint::CreateRemoval( SourceRange(NewStart, InitRange.getBegin())); Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd)); } else { // New array expression with default/value initialization: // smart_ptr<Foo[]>(new int[5]()); // smart_ptr<Foo[]>(new Foo[5]()); Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd), ArraySizeExpr); } break; } case CXXNewExpr::ListInit: { // Range of the substring that we do not want to remove. SourceRange InitRange; if (const auto *NewConstruct = New->getConstructExpr()) { if (NewConstruct->isStdInitListInitialization()) { // FIXME: Add fixes for direct initialization with the initializer-list // constructor. Similar to the above CallInit case, the type has to be // specified explicitly in the fixes. // struct S { S(std::initializer_list<int>); }; // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization // smart_ptr<S>(new S{}); // use initializer-list consturctor // The above cases have to be replaced with: // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3})); // std::make_smart_ptr<S>(std::initializer_list<int>({})); return false; } else { // Direct initialization with ordinary constructors. // struct S { S(int x); S(); }; // smart_ptr<S>(new S{5}); // smart_ptr<S>(new S{}); // use default constructor // The arguments in the initialization list are going to be forwarded to // the constructor, so this has to be replaced with: // std::make_smart_ptr<S>(5); // std::make_smart_ptr<S>(); InitRange = SourceRange( NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1), NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1)); } } else { // Aggregate initialization. // smart_ptr<Pair>(new Pair{first, second}); // Has to be replaced with: // smart_ptr<Pair>(Pair{first, second}); InitRange = SourceRange( New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(), New->getInitializer()->getSourceRange().getEnd()); } Diag << FixItHint::CreateRemoval( CharSourceRange::getCharRange(NewStart, InitRange.getBegin())); Diag << FixItHint::CreateRemoval( SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd)); break; } } return true; }
SourceRange TranslationUnit::sourceRange(uint fromLine, uint fromColumn, uint toLine, uint toColumn) const { return SourceRange(sourceLocationAt(fromLine, fromColumn), sourceLocationAt(toLine, toColumn)); }
// ECMA 15.3.2 The Function Constructor JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifier& functionName, const UString& sourceURL, int lineNumber) { UString p(""); UString body; int argsSize = args.size(); if (argsSize == 0) body = ""; else if (argsSize == 1) body = args[0]->toString(exec); else { p = args[0]->toString(exec); for (int k = 1; k < argsSize - 1; k++) p += "," + args[k]->toString(exec); body = args[argsSize - 1]->toString(exec); } // parse the source code int sourceId; int errLine; UString errMsg; RefPtr<SourceProvider> source = UStringSourceProvider::create(body); RefPtr<FunctionBodyNode> functionBody = exec->parser()->parse<FunctionBodyNode>(exec, sourceURL, lineNumber, source, &sourceId, &errLine, &errMsg); // No program node == syntax error - throw a syntax error if (!functionBody) // We can't return a Completion(Throw) here, so just set the exception // and return it return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL); functionBody->setSource(SourceRange(source, 0, source->length())); ScopeChain scopeChain(exec->lexicalGlobalObject(), exec->globalThisValue()); JSFunction* function = new (exec) JSFunction(exec, functionName, functionBody.get(), scopeChain.node()); // parse parameter list. throw syntax error on illegal identifiers int len = p.size(); const UChar* c = p.data(); int i = 0, params = 0; UString param; while (i < len) { while (*c == ' ' && i < len) c++, i++; if (Lexer::isIdentStart(c[0])) { // else error param = UString(c, 1); c++, i++; while (i < len && (Lexer::isIdentPart(c[0]))) { param.append(*c); c++, i++; } while (i < len && *c == ' ') c++, i++; if (i == len) { functionBody->parameters().append(Identifier(exec, param)); params++; break; } else if (*c == ',') { functionBody->parameters().append(Identifier(exec, param)); params++; c++, i++; continue; } // else error } return throwError(exec, SyntaxError, "Syntax error in parameter list"); } JSObject* prototype = constructEmptyObject(exec); prototype->putDirect(exec->propertyNames().constructor, function, DontEnum); function->putDirect(exec->propertyNames().prototype, prototype, DontDelete); return function; }
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 OMPPragmaHandler::HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, SourceRange IntroducerRange, Token &FirstTok) { Diags.Report(IntroducerRange.getBegin(), DiagFoundPragmaStmt); // TODO: Clean this up because I'm too lazy to now PragmaDirective * DirectivePointer = new PragmaDirective; PragmaDirective &Directive = *DirectivePointer; // First lex the pragma statement extracting the variable names SourceLocation Loc = IntroducerRange.getBegin(); Token Tok = FirstTok; StringRef ident = getIdentifier(Tok); if (ident != "omp") { LexUntil(PP, Tok, clang::tok::eod); return; } PP.Lex(Tok); ident = getIdentifier(Tok); bool isParallel = false; bool isThreadPrivate = false; if (ident == "parallel") { PragmaConstruct C; C.Type = ParallelConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); isParallel = true; } else if (ident == "sections" || ident == "section" || ident == "task" || ident == "taskyield" || ident == "taskwait" || ident == "atomic" || ident == "ordered") { Diags.Report(Tok.getLocation(), DiagUnsupportedConstruct); LexUntil(PP, Tok, clang::tok::eod); return; } else if (ident == "for") { PragmaConstruct C; C.Type = ForConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); } else if (ident == "threadprivate") { isThreadPrivate = true; PragmaConstruct C; C.Type = ThreadprivateConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); } else if (ident == "single") { PragmaConstruct C; C.Type = SingleConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); } else if (ident == "master") { PragmaConstruct C; C.Type = MasterConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); } else if (ident == "critical" || ident == "flush") { // Ignored Directive // (Critical, Flush) LexUntil(PP, Tok, clang::tok::eod); return; } else if (ident == "barrier") { PragmaConstruct C; C.Type = BarrierConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); } else { Diags.Report(Tok.getLocation(), DiagUnknownDirective); return; } if (!isThreadPrivate) { PP.Lex(Tok); } if (isParallel) { ident = getIdentifier(Tok); if (ident == "sections") { Diags.Report(Tok.getLocation(), DiagUnsupportedConstruct); LexUntil(PP, Tok, clang::tok::eod); return; } else if (ident == "for") { PragmaConstruct C; C.Type = ForConstruct; C.Range = getTokenRange(Tok, PP); Directive.insertConstruct(C); PP.Lex(Tok); } else { // Just a standard "#pragma omp parallel" clause if (Tok.isNot(clang::tok::eod) && PragmaDirective::getClauseType(ident) == UnknownClause) { Diags.Report(Tok.getLocation(), DiagUnknownClause); return; } } } // If we've made it this far then we either have: // "#pragma omp parallel", // "#pragma omp parallel for", // "#pragma omp for", // "#pragma omp threadprivate // Need to read in the options, if they exists // Don't really care about them unless there exists a private(...) list // In which case, get the variables inside that list // But we read them all in anyway. // There's also threadprivate, which won't have any clauses, but will have // a list of private variables just after the threadprivate directive // Treating threadprivate as a clause and directive at the same time. while(Tok.isNot(clang::tok::eod)) { PragmaClause C; ident = getIdentifier(Tok); C.Type = PragmaDirective::getClauseType(ident); if (C.Type == UnknownClause) { Diags.Report(Tok.getLocation(), DiagUnknownClause); return; } SourceLocation clauseStart = Tok.getLocation(); SourceLocation clauseEnd = PP.getLocForEndOfToken(clauseStart); PP.Lex(Tok); if (Tok.is(clang::tok::l_paren)) { if (!handleList(Tok, PP, C)) { Diags.Report(clauseStart, DiagMalformedStatement); LexUntil(PP, Tok, clang::tok::eod); return; } clauseEnd = PP.getLocForEndOfToken(Tok.getLocation()); // Eat the clang::tok::r_paren PP.Lex(Tok); } C.Range = SourceRange(clauseStart, clauseEnd); Directive.insertClause(C); } SourceLocation EndLoc = PP.getLocForEndOfToken(Tok.getLocation()); Directive.setRange(SourceRange(Loc, EndLoc)); Directives.insert(std::make_pair(Loc.getRawEncoding(), DirectivePointer)); // Then replace with parseable compound statement to catch in Sema, and // references to private variables; // { // i; // j; // k; // } // If it's a threadprivate directive, then we skip this completely if (isThreadPrivate) { return; } set<IdentifierInfo *> PrivateVars = Directive.getPrivateIdentifiers(); int tokenCount = 2 + 2 * PrivateVars.size(); int currentToken = 0; Token * Toks = new Token[tokenCount]; Toks[currentToken++] = createToken(Loc, clang::tok::l_brace); set<IdentifierInfo *>::iterator PrivIt; for (PrivIt = PrivateVars.begin(); PrivIt != PrivateVars.end(); PrivIt++) { Toks[currentToken++] = createToken(Loc, clang::tok::identifier, *PrivIt); Toks[currentToken++] = createToken(Loc, clang::tok::semi); } Toks[currentToken++] = createToken(EndLoc, clang::tok::r_brace); assert(currentToken == tokenCount); Diags.setDiagnosticGroupMapping("unused-value", clang::diag::MAP_IGNORE, Loc); Diags.setDiagnosticGroupMapping("unused-value", clang::diag::MAP_WARNING, EndLoc); PP.EnterTokenStream(Toks, tokenCount, true, true); }